001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.core.security;
034:
035: import com.flexive.shared.exceptions.FxLoginFailedException;
036: import com.flexive.shared.exceptions.FxNotFoundException;
037: import com.flexive.shared.security.UserTicket;
038: import org.apache.commons.logging.Log;
039: import org.apache.commons.logging.LogFactory;
040:
041: import javax.security.auth.Subject;
042: import javax.security.auth.callback.*;
043: import javax.security.auth.login.FailedLoginException;
044: import javax.security.auth.login.LoginException;
045: import javax.security.auth.spi.LoginModule;
046: import java.io.IOException;
047: import java.util.Iterator;
048: import java.util.Map;
049: import java.util.Vector;
050:
051: /**
052: * Default login module.
053: *
054: * @author Gregor Schober (gregor.schober@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
055: * @version $Rev: 261 $
056: */
057: public class FxDefaultLogin implements LoginModule {
058: private static final transient Log LOG = LogFactory
059: .getLog(FxDefaultLogin.class);
060: private CallbackHandler callbackHandler = null;
061: private Subject subject = null;
062:
063: // temporary state, set in the commit function
064: private Vector<FxPrincipal> tempPrincipals = null;
065: private boolean success = false;
066:
067: /**
068: * Returns the UserTicket stored within the subject.
069: *
070: * @param sub the suubject
071: * @return the UserTicket stored within the subject
072: * @throws FxNotFoundException if the subject doesnt hold a UserTicket.
073: * This should never happen since the Login Module fills out
074: * a UserTicket for every new subject.
075: */
076: public static UserTicket getUserTicket(Subject sub)
077: throws FxNotFoundException {
078: Iterator it = sub.getPrincipals(FxPrincipal.class).iterator();
079: if (it.hasNext()) {
080: FxPrincipal p = (FxPrincipal) it.next();
081: return p.getUserTicket();
082: } else {
083: FxNotFoundException nfe = new FxNotFoundException(
084: "Subject without UserTicket encountered");
085: LOG.fatal(nfe);
086: throw nfe;
087: }
088: }
089:
090: /**
091: * Sets the user ticket within a subject
092: *
093: * @param sub the subject
094: * @param ticket the new ticket for the subject
095: * @return the user ticket
096: */
097: public static Subject updateUserTicket(Subject sub,
098: UserTicket ticket) {
099: // remove the old user ticket
100: for (FxPrincipal p : sub.getPrincipals(FxPrincipal.class))
101: sub.getPrincipals().remove(p);
102: // Set the credentials and principals
103: sub.getPrincipals().add(new FxPrincipal(ticket));
104: return sub;
105: }
106:
107: /**
108: * Constructor
109: */
110: public FxDefaultLogin() {
111: this .tempPrincipals = new Vector<FxPrincipal>(5);
112: this .success = false;
113: }
114:
115: /**
116: * This method is called if the LoginContext's overall authentication failed.
117: * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did not succeed).
118: * <p/>
119: * If this LoginModule's own authentication attempt succeeded (checked by retrieving the private state saved by the
120: * <code>login</code> and <code>commit</code> methods), then this method cleans up any state that was
121: * originally saved.
122: *
123: * @return false if this LoginModule's own login and/or commit attempts failed, and true otherwise.
124: * @throws LoginException if the abort fails.
125: */
126: public boolean abort() throws LoginException {
127: clearTemporaryStates();
128: // Login aborted
129: success = false;
130: return true;
131: }
132:
133: /**
134: * Abstract method to commit the authentication process (phase 2).
135: * <p/>
136: * This method is called if the LoginContext's overall authentication succeeded
137: * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules succeeded).
138: * <p/>
139: * If this LoginModule's own authentication attempt succeeded (checked by retrieving the private state saved by the
140: * <code>login</code> method), then this method associates a <code>RdbmsPrincipal</code>
141: * with the <code>Subject</code> located in the
142: * <code>LoginModule</code>. If this LoginModule's own
143: * authentication attempted failed, then this method removes
144: * any state that was originally saved.
145: *
146: * @return true if this LoginModule's own login and commit attempts succeeded, or false otherwise.
147: * @throws LoginException if the commit fails
148: */
149:
150: public boolean commit() throws LoginException {
151: if (success) {
152: // Subject may not be read only
153: if (subject.isReadOnly()) {
154: LoginException le = new LoginException(
155: "Subject is Readonly");
156: LOG.error(le);
157: throw le;
158: }
159: // Set the principials and credentials
160: subject.getPrincipals().addAll(tempPrincipals);
161: //subject.getPublicCredentials().add(tempUserTicket);
162: }
163: // Clear all temp variables
164: clearTemporaryStates();
165: return true;
166: }
167:
168: /**
169: * Clears all temporary variables.
170: */
171: private void clearTemporaryStates() {
172: tempPrincipals.clear();
173: if (callbackHandler instanceof PassiveCallbackHandler)
174: ((PassiveCallbackHandler) callbackHandler).clearPassword();
175: }
176:
177: /**
178: * Verify the name/password combination.
179: *
180: * @return true always, since this LoginModule should not be ignored.
181: * @throws FailedLoginException if the authentication fails.
182: * @throws LoginException if this LoginModule is unable to perform the authentication.
183: */
184: public boolean login() throws LoginException {
185: LoginException le = null;
186: try {
187: // Determine username and password using the callback handler
188: final Callback[] callbacks = new Callback[] {
189: new NameCallback("user: "),
190: new PasswordCallback("password: ", true),
191: new FxCallback() };
192: callbackHandler.handle(callbacks);
193:
194: FxCallback ac = ((FxCallback) callbacks[2]);
195: final String username = ((NameCallback) callbacks[0])
196: .getName();
197: final PasswordCallback pc = (PasswordCallback) callbacks[1];
198: final String password = new String((pc.getPassword()));
199: pc.clearPassword();
200: UserTicket ticket = FxDBAuthentication.login(username,
201: password, ac);
202: // Set the credentials and principals
203: this .tempPrincipals.add(new FxPrincipal(ticket));
204: // The login was successfull
205: success = true;
206: if (LOG.isInfoEnabled())
207: LOG.info("User [" + ticket.getUserName()
208: + "] successfully logged in, ticket=" + ticket);
209: } catch (IOException exc) {
210: le = new FxLoginFailedException("IOException: "
211: + exc.getMessage(),
212: FxLoginFailedException.TYPE_UNKNOWN_ERROR);
213: LOG.error(le);
214: } catch (UnsupportedCallbackException exc) {
215: le = new FxLoginFailedException("IOException: "
216: + exc.getMessage(),
217: FxLoginFailedException.TYPE_UNKNOWN_ERROR);
218: LOG.error(le);
219: }
220: // Log and throw exceptions
221: if (le != null) {
222: success = false;
223: throw le;
224: }
225: return true;
226: }
227:
228: /**
229: * Logout a user.
230: * <p/>
231: * This method removes the Principals that were added by the commit method.
232: *
233: * @return true in all cases
234: * @throws LoginException if the logout fails.
235: */
236: public boolean logout() throws LoginException {
237: // Clear all temp variables
238: clearTemporaryStates();
239:
240: // remove the principals the login module added
241: for (FxPrincipal p : subject.getPrincipals(FxPrincipal.class))
242: FxDBAuthentication.logout(p.getUserTicket());
243: return true;
244: }
245:
246: /**
247: * Initialize this LoginModule.
248: *
249: * @param sub the Subject to be authenticated.
250: * @param callback a CallbackHandler for communicating with the end user (prompting for usernames and
251: * passwords, for example).
252: * @param sharedState shared LoginModule state.
253: * @param options options specified in the login Configuration for this particular LoginModule.
254: */
255: public void initialize(Subject sub, CallbackHandler callback,
256: Map<String, ?> sharedState, Map<String, ?> options) {
257: this.callbackHandler = callback;
258: this.subject = sub;
259: }
260:
261: }
|