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.tc55.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: * SSO Gateway JAAS Login Module.
041: *
042: * This Login Module authenticates an SSO Session against the Single Sign-on Gateway
043: * by getting the associated user and roles and filling it to the provided Subject.
044: * This way clients can obtain the authenticated identity associated with the session and use it
045: * to protect resources (ie: web, etc.).
046: *
047: * @author <a href="mailto:gbrigand@josso.org">Gianluca Brigandi</a>
048: * @version CVS $Id: SSOGatewayLoginModule.java 508 2008-02-18 13:32:29Z sgonzalez $
049: */
050: public class SSOGatewayLoginModule implements LoginModule {
051:
052: private static final Log logger = LogFactory
053: .getLog(SSOGatewayLoginModule.class);
054:
055: // initial state
056: private Subject _subject;
057: private CallbackHandler _callbackHandler;
058:
059: // the authentication status
060: protected boolean _succeeded;
061: protected boolean commitSucceeded;
062:
063: // the logged user and his roles.
064: protected SSOUser _ssoUserPrincipal;
065: protected SSORole[] _ssoRolePrincipals;
066:
067: /**
068: * Initialize this LoginModule
069: *
070: * @param subject the Subject to be authenticated.
071: *
072: * @param callbackHandler a CallbackHandler for communicating
073: * with the end user (prompting for user names and
074: * passwords, for example).
075: *
076: * @param sharedState shared LoginModule state.
077: *
078: * @param options options specified in the login Configuration
079: * for this particular LoginModule.
080: */
081: public void initialize(Subject subject,
082: CallbackHandler callbackHandler, Map sharedState,
083: Map options) {
084:
085: this ._subject = subject;
086: this ._callbackHandler = callbackHandler;
087: }
088:
089: /**
090: * Authenticate the user by prompting for the SSO Session Identifier assigned by the SSO Gateway on logon.
091: *
092: * This method obtains from the gateway, using the provided session identifier, the user associated with
093: * such session identifier.
094: * Only the NameCallBack is used, since its not a user/password pair but only one value containing the session
095: * identifier. Any other callback type is ignored.
096: *
097: * @return true in all cases since this LoginModule
098: * should not be ignored.
099: *
100: * @exception javax.security.auth.login.FailedLoginException if the authentication fails.
101: *
102: * @exception javax.security.auth.login.LoginException if this LoginModule
103: * is unable to perform the authentication.
104: */
105: public boolean login() throws LoginException {
106:
107: if (_callbackHandler == null)
108: throw new LoginException(
109: "Error: no CallbackHandler available "
110: + "to garner authentication information from the user");
111:
112: Callback[] callbacks = new Callback[2];
113:
114: // Just ask for the session identifier
115: callbacks[0] = new NameCallback("JOSSO Session Identifier");
116: callbacks[1] = new PasswordCallback("password", false);
117:
118: String ssoSessionId;
119: String ssoSessionId2 = null;
120: try {
121: _callbackHandler.handle(callbacks);
122: ssoSessionId = ((NameCallback) callbacks[0]).getName();
123: if (((PasswordCallback) callbacks[1]).getPassword() != null)
124: ssoSessionId2 = String
125: .valueOf(((PasswordCallback) callbacks[1])
126: .getPassword());
127: } catch (java.io.IOException ioe) {
128: throw new LoginException(ioe.toString());
129: } catch (UnsupportedCallbackException uce) {
130: throw new LoginException(
131: "Error: "
132: + uce.getCallback().toString()
133: + " not available to garner authentication information "
134: + "from the user");
135: }
136:
137: logger.debug("Session requested authentication to gateway : "
138: + ssoSessionId + "/" + ssoSessionId2);
139:
140: try {
141:
142: if (ssoSessionId2 != null
143: && !ssoSessionId2.equals(ssoSessionId))
144: ssoSessionId = ssoSessionId2;
145:
146: // If no session is found, ignore this module.
147: if (ssoSessionId == null) {
148: logger.debug("Session authentication failed : "
149: + ssoSessionId);
150: _succeeded = false;
151: return false;
152: }
153:
154: SSOIdentityManager im = Lookup.getInstance()
155: .lookupSSOAgent().getSSOIdentityManager();
156: SSOUser ssoUser = im.findUserInSession(ssoSessionId);
157:
158: logger.debug("Session authentication succeeded : "
159: + ssoSessionId);
160: _ssoUserPrincipal = ssoUser;
161: _succeeded = true;
162:
163: } catch (SSOIdentityException e) {
164: // Ignore this ... (user does not exist for this session)
165: if (logger.isDebugEnabled())
166: logger.debug(e.getMessage());
167: _succeeded = false;
168: return false;
169:
170: } catch (Exception e) {
171: logger.error("Session authentication failed : "
172: + ssoSessionId, e);
173: _succeeded = false;
174: clearCredentials();
175: throw new FailedLoginException(
176: "Fatal error authenticating session : " + e);
177: }
178:
179: return true;
180: }
181:
182: /**
183: * This method is called if the LoginContext's overall authentication succeeded.
184: *
185: * Using the SSO user name, saved by the previosuly executed login() operation, obtains from the gateway
186: * the roles associated with the user and fills the Subject with the user and role principals.
187: * If this LoginModule's own authentication attempted failed, then this method removes any state that was
188: * originally saved.
189: *
190: * @exception LoginException if the commit fails.
191: *
192: * @return true if this LoginModule's own login and commit
193: * attempts succeeded, or false otherwise.
194: */
195: public boolean commit() throws LoginException {
196: if (_succeeded == false) {
197: return false;
198: } else {
199:
200: try {
201:
202: // Add the SSOUser as a Principal
203: if (!_subject.getPrincipals().contains(
204: _ssoUserPrincipal)) {
205: _subject.getPrincipals().add(_ssoUserPrincipal);
206: }
207:
208: logger
209: .debug("Added SSOUser Principal to the Subject : "
210: + _ssoUserPrincipal);
211:
212: _ssoRolePrincipals = getRoleSets();
213:
214: // Add to the Subject the SSORoles associated with the SSOUser .
215: for (int i = 0; i < _ssoRolePrincipals.length; i++) {
216: if (_subject.getPrincipals().contains(
217: _ssoRolePrincipals[i]))
218: continue;
219:
220: _subject.getPrincipals().add(_ssoRolePrincipals[i]);
221: logger
222: .debug("Added SSORole Principal to the Subject : "
223: + _ssoRolePrincipals[i]);
224: }
225:
226: commitSucceeded = true;
227: return true;
228: } catch (Exception e) {
229: logger.error("Session login failed for Principal : "
230: + _ssoUserPrincipal, e);
231: throw new LoginException(
232: "Session login failed for Principal : "
233: + _ssoUserPrincipal);
234: } finally {
235: // in any case, clean out state
236: clearCredentials();
237: }
238:
239: }
240: }
241:
242: /**
243: * This method is called if the LoginContext's
244: * overall authentication failed.
245: *
246: * @exception LoginException if the abort fails.
247: *
248: * @return false if this LoginModule's own login and/or commit attempts
249: * failed, and true otherwise.
250: */
251: public boolean abort() throws LoginException {
252: if (_succeeded == false) {
253: return false;
254: } else if (_succeeded == true && commitSucceeded == false) {
255: // login _succeeded but overall authentication failed
256: _succeeded = false;
257: clearCredentials();
258: } else {
259: // overall authentication _succeeded and commit _succeeded,
260: // but someone else's commit failed
261: logout();
262: }
263: return true;
264: }
265:
266: /**
267: * Logout the user.
268: *
269: * This method removes the SSO User and Role Principals from the Subject that were added by the commit()
270: * method.
271: *
272: * @exception LoginException if the logout fails.
273: *
274: * @return true in all cases since this LoginModule
275: * should not be ignored.
276: */
277: public boolean logout() throws LoginException {
278: _subject.getPrincipals().remove(_ssoUserPrincipal);
279: logger.debug("Removed SSOUser Principal from Subject : "
280: + _ssoUserPrincipal);
281:
282: // Remove all the SSORole Principals from the Subject.
283: for (int i = 0; i < _ssoRolePrincipals.length; i++) {
284: _subject.getPrincipals().remove(_ssoRolePrincipals[i]);
285: logger.debug("Removed SSORole Principal from Subject : "
286: + _ssoRolePrincipals[i]);
287: }
288:
289: _succeeded = commitSucceeded;
290: clearCredentials();
291: return true;
292: }
293:
294: /**
295: * Reset the login module state.
296: */
297: private void clearCredentials() {
298: _ssoUserPrincipal = null;
299: _ssoRolePrincipals = null;
300: }
301:
302: /**
303: * Retreives the list of roles associated to current principal
304: */
305: protected SSORole[] getRoleSets() throws LoginException {
306: try {
307: // obtain user roles principals and add it to the subject
308: SSOIdentityManager im = Lookup.getInstance()
309: .lookupSSOAgent().getSSOIdentityManager();
310:
311: return im.findRolesByUsername(_ssoUserPrincipal.getName());
312: } catch (Exception e) {
313: logger.error("Session login failed for Principal : "
314: + _ssoUserPrincipal, e);
315: throw new LoginException(
316: "Session login failed for Principal : "
317: + _ssoUserPrincipal);
318: }
319:
320: }
321:
322: }
|