001: /*
002: * JOSSO: Java Open Single Sign-On
003: *
004: * Copyright 2004-2008, Atricore, Inc.
005: *
006: * This is free software; you can redistribute it and/or modify it
007: * under the terms of the GNU Lesser General Public License as
008: * published by the Free Software Foundation; either version 2.1 of
009: * the License, or (at your option) any later version.
010: *
011: * This software is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this software; if not, write to the Free
018: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
020: */
021:
022: package org.josso.servlet.agent.jaas;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026: import org.josso.Lookup;
027: import org.josso.gateway.identity.SSORole;
028: import org.josso.gateway.identity.SSOUser;
029: import org.josso.gateway.identity.exceptions.SSOIdentityException;
030: import org.josso.gateway.identity.service.SSOIdentityManager;
031:
032: import javax.security.auth.Subject;
033: import javax.security.auth.callback.*;
034: import javax.security.auth.login.FailedLoginException;
035: import javax.security.auth.login.LoginException;
036: import javax.security.auth.spi.LoginModule;
037: import java.util.Map;
038:
039: /**
040: * JAAS Login Module that expects SSO Session TOKEN credentials to authenticate users.
041: *
042: * Date: Nov 27, 2007
043: * Time: 12:01:10 PM
044: *
045: * @author <a href="mailto:sgonzalez@josso.org">Sebastian Gonzalez Oyuela</a>
046: */
047: public class SSOGatewayLoginModule implements LoginModule {
048:
049: private static final Log logger = LogFactory
050: .getLog(SSOGatewayLoginModule.class);
051:
052: // initial state
053: private Subject _subject;
054: private CallbackHandler _callbackHandler;
055:
056: // the authentication status
057: protected boolean _succeeded;
058: protected boolean commitSucceeded;
059:
060: // the logged user and his roles.
061: protected SSOUser _ssoUserPrincipal;
062: protected SSORole[] _ssoRolePrincipals;
063:
064: /**
065: * Initialize this LoginModule
066: *
067: * @param subject the Subject to be authenticated.
068: *
069: * @param callbackHandler a CallbackHandler for communicating
070: * with the end user (prompting for user names and
071: * passwords, for example).
072: *
073: * @param sharedState shared LoginModule state.
074: *
075: * @param options options specified in the login Configuration
076: * for this particular LoginModule.
077: */
078: public void initialize(Subject subject,
079: CallbackHandler callbackHandler, Map sharedState,
080: Map options) {
081:
082: this ._subject = subject;
083: this ._callbackHandler = callbackHandler;
084: }
085:
086: /**
087: * Authenticate the user by prompting for the SSO Session Identifier assigned by the SSO Gateway on logon.
088: *
089: * This method obtains from the gateway, using the provided session identifier, the user associated with
090: * such session identifier.
091: * Only the NameCallBack is used, since its not a user/password pair but only one value containing the session
092: * identifier. Any other callback type is ignored.
093: *
094: * @return true in all cases since this LoginModule
095: * should not be ignored.
096: *
097: * @exception javax.security.auth.login.FailedLoginException if the authentication fails.
098: *
099: * @exception javax.security.auth.login.LoginException if this LoginModule
100: * is unable to perform the authentication.
101: */
102: public boolean login() throws LoginException {
103:
104: if (_callbackHandler == null)
105: throw new LoginException(
106: "Error: no CallbackHandler available "
107: + "to garner authentication information from the user");
108:
109: Callback[] callbacks = new Callback[2];
110:
111: // Just ask for the session identifier
112: callbacks[0] = new NameCallback("JOSSO Session Identifier");
113: callbacks[1] = new PasswordCallback("password", false);
114:
115: String ssoSessionId;
116: String ssoSessionId2 = null;
117: try {
118: _callbackHandler.handle(callbacks);
119: ssoSessionId = ((NameCallback) callbacks[0]).getName();
120: if (((PasswordCallback) callbacks[1]).getPassword() != null)
121: ssoSessionId2 = String
122: .valueOf(((PasswordCallback) callbacks[1])
123: .getPassword());
124: } catch (java.io.IOException ioe) {
125: throw new LoginException(ioe.toString());
126: } catch (UnsupportedCallbackException uce) {
127: throw new LoginException(
128: "Error: "
129: + uce.getCallback().toString()
130: + " not available to garner authentication information "
131: + "from the user");
132: }
133:
134: logger.debug("Session requested authentication to gateway : "
135: + ssoSessionId + "/" + ssoSessionId2);
136:
137: try {
138:
139: if (ssoSessionId2 != null
140: && !ssoSessionId2.equals(ssoSessionId))
141: ssoSessionId = ssoSessionId2;
142:
143: // If no session is found, ignore this module.
144: if (ssoSessionId == null) {
145: logger.debug("Session authentication failed : "
146: + ssoSessionId);
147: _succeeded = false;
148: return false;
149: }
150:
151: SSOIdentityManager im = Lookup.getInstance()
152: .lookupSSOAgent().getSSOIdentityManager();
153: SSOUser ssoUser = im.findUserInSession(ssoSessionId);
154:
155: logger.debug("Session authentication succeeded : "
156: + ssoSessionId);
157: _ssoUserPrincipal = ssoUser;
158: _succeeded = true;
159:
160: } catch (SSOIdentityException e) {
161: // Ignore this ... (user does not exist for this session)
162: if (logger.isDebugEnabled())
163: logger.debug(e.getMessage());
164: _succeeded = false;
165: return false;
166:
167: } catch (Exception e) {
168: logger.error("Session authentication failed : "
169: + ssoSessionId, e);
170: _succeeded = false;
171: clearCredentials();
172: throw new FailedLoginException(
173: "Fatal error authenticating session : " + e);
174: }
175:
176: return true;
177: }
178:
179: /**
180: * This method is called if the LoginContext's overall authentication succeeded.
181: *
182: * Using the SSO user name, saved by the previosuly executed login() operation, obtains from the gateway
183: * the roles associated with the user and fills the Subject with the user and role principals.
184: * If this LoginModule's own authentication attempted failed, then this method removes any state that was
185: * originally saved.
186: *
187: * @exception javax.security.auth.login.LoginException if the commit fails.
188: *
189: * @return true if this LoginModule's own login and commit
190: * attempts succeeded, or false otherwise.
191: */
192: public boolean commit() throws LoginException {
193: if (_succeeded == false) {
194: return false;
195: } else {
196:
197: try {
198:
199: // Add the SSOUser as a Principal
200: if (!_subject.getPrincipals().contains(
201: _ssoUserPrincipal)) {
202: _subject.getPrincipals().add(_ssoUserPrincipal);
203: }
204:
205: logger
206: .debug("Added SSOUser Principal to the Subject : "
207: + _ssoUserPrincipal);
208:
209: _ssoRolePrincipals = getRoleSets();
210:
211: // Add to the Subject the SSORoles associated with the SSOUser .
212: for (int i = 0; i < _ssoRolePrincipals.length; i++) {
213: if (_subject.getPrincipals().contains(
214: _ssoRolePrincipals[i]))
215: continue;
216:
217: _subject.getPrincipals().add(_ssoRolePrincipals[i]);
218: logger
219: .debug("Added SSORole Principal to the Subject : "
220: + _ssoRolePrincipals[i]);
221: }
222:
223: commitSucceeded = true;
224: return true;
225: } catch (Exception e) {
226: logger.error("Session login failed for Principal : "
227: + _ssoUserPrincipal, e);
228: throw new LoginException(
229: "Session login failed for Principal : "
230: + _ssoUserPrincipal);
231: } finally {
232: // in any case, clean out state
233: clearCredentials();
234: }
235:
236: }
237: }
238:
239: /**
240: * This method is called if the LoginContext's
241: * overall authentication failed.
242: *
243: * @exception javax.security.auth.login.LoginException if the abort fails.
244: *
245: * @return false if this LoginModule's own login and/or commit attempts
246: * failed, and true otherwise.
247: */
248: public boolean abort() throws LoginException {
249: if (_succeeded == false) {
250: return false;
251: } else if (_succeeded == true && commitSucceeded == false) {
252: // login _succeeded but overall authentication failed
253: _succeeded = false;
254: clearCredentials();
255: } else {
256: // overall authentication _succeeded and commit _succeeded,
257: // but someone else's commit failed
258: logout();
259: }
260: return true;
261: }
262:
263: /**
264: * Logout the user.
265: *
266: * This method removes the SSO User and Role Principals from the Subject that were added by the commit()
267: * method.
268: *
269: * @exception javax.security.auth.login.LoginException if the logout fails.
270: *
271: * @return true in all cases since this LoginModule
272: * should not be ignored.
273: */
274: public boolean logout() throws LoginException {
275: _subject.getPrincipals().remove(_ssoUserPrincipal);
276: logger.debug("Removed SSOUser Principal from Subject : "
277: + _ssoUserPrincipal);
278:
279: // Remove all the SSORole Principals from the Subject.
280: for (int i = 0; i < _ssoRolePrincipals.length; i++) {
281: _subject.getPrincipals().remove(_ssoRolePrincipals[i]);
282: logger.debug("Removed SSORole Principal from Subject : "
283: + _ssoRolePrincipals[i]);
284: }
285:
286: _succeeded = commitSucceeded;
287: clearCredentials();
288: return true;
289: }
290:
291: /**
292: * Reset the login module state.
293: */
294: private void clearCredentials() {
295: _ssoUserPrincipal = null;
296: _ssoRolePrincipals = null;
297: }
298:
299: /**
300: * Retreives the list of roles associated to current principal
301: */
302: protected SSORole[] getRoleSets() throws LoginException {
303: try {
304: // obtain user roles principals and add it to the subject
305: SSOIdentityManager im = Lookup.getInstance()
306: .lookupSSOAgent().getSSOIdentityManager();
307:
308: return im.findRolesByUsername(_ssoUserPrincipal.getName());
309: } catch (Exception e) {
310: logger.error("Session login failed for Principal : "
311: + _ssoUserPrincipal, e);
312: throw new LoginException(
313: "Session login failed for Principal : "
314: + _ssoUserPrincipal);
315: }
316:
317: }
318: }
|