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: package org.josso.gateway.identity.service;
022:
023: import org.josso.gateway.identity.exceptions.SSOIdentityException;
024: import org.josso.gateway.identity.exceptions.IdentityProvisioningException;
025: import org.josso.gateway.session.SSOSession;
026: import org.josso.gateway.session.service.SSOSessionManager;
027: import org.josso.gateway.session.exceptions.SSOSessionException;
028: import org.josso.gateway.session.exceptions.NoSuchSessionException;
029: import org.josso.gateway.assertion.exceptions.AssertionNotValidException;
030: import org.josso.gateway.assertion.AuthenticationAssertion;
031: import org.josso.gateway.assertion.AssertionManager;
032: import org.josso.gateway.SSOContext;
033: import org.josso.gateway.SSOException;
034: import org.josso.gateway.SSOContextImpl;
035: import org.josso.gateway.event.security.SSOSecurityEventManager;
036: import org.josso.auth.Credential;
037: import org.josso.auth.Authenticator;
038: import org.josso.auth.SimplePrincipal;
039: import org.josso.auth.exceptions.SSOAuthenticationException;
040: import org.josso.auth.exceptions.AuthenticationFailureException;
041: import org.josso.SecurityDomain;
042: import org.josso.Lookup;
043: import org.apache.commons.logging.Log;
044: import org.apache.commons.logging.LogFactory;
045:
046: import javax.security.auth.Subject;
047: import java.util.Set;
048: import java.security.Principal;
049:
050: /**
051: * TODO: Comment Me!
052: *
053: * @author <a href="mailto:gbrigand@josso.org">Gianluca Brigandi</a>
054: * @version CVS $Id$
055: */
056: public class SSOIdentityProviderImpl implements SSOIdentityProvider {
057:
058: private static final Log logger = LogFactory
059: .getLog(SSOIdentityProvider.class);
060:
061: ///////////////////////////////////////////////////////////////////
062: // Exposed operations to remote clients (i.e. WS Client).
063: ///////////////////////////////////////////////////////////////////
064:
065: /**
066: * Request an authentication assertion using simple authentication through the
067: * supplied username/password credentials.
068: *
069: * @param username
070: * @param password
071: * @return the assertion identifier
072: */
073: public String assertIdentityWithSimpleAuthentication(
074: String username, String password)
075: throws IdentityProvisioningException {
076:
077: try {
078: Credential cUsername = null;
079: Credential cPassword = null;
080:
081: // Prepare the context needed for authenticating
082: cUsername = newCredential("basic-authentication",
083: "username", username);
084: cPassword = newCredential("basic-authentication",
085: "password", password);
086:
087: Credential[] c = { cUsername, cPassword };
088:
089: SSOContextImpl ctx = new SSOContextImpl();
090: ctx.setCurrentSession(null);
091: ctx.setUserLocation("remote-application");
092: ctx.setScheme("basic-authentication");
093:
094: // Perform the assertion and open the corresponding SSO session for the user
095: // in case successful
096: SSOSession userSsoSession = login(c,
097: "basic-authentication", ctx);
098:
099: // Return the assertion identifier which maps to the new session
100: AssertionManager am = Lookup.getInstance()
101: .lookupAssertionManager();
102: AuthenticationAssertion aa = am
103: .requestAssertion(userSsoSession);
104:
105: return aa.getId();
106:
107: } catch (SSOAuthenticationException e) {
108: throw new IdentityProvisioningException(
109: "Failed to assert identity of user : " + username);
110: } catch (SSOException e) {
111: throw new IdentityProvisioningException(
112: "Error asserting identity of user : " + username);
113: } catch (Exception e) {
114: throw new IdentityProvisioningException(
115: "Unknown error asserting identity of user : "
116: + username);
117: }
118:
119: }
120:
121: /**
122: * Resolves an authentication assertion given its identifier.
123: *
124: */
125: public String resolveAuthenticationAssertion(
126: String authenticationAssertionId)
127: throws IdentityProvisioningException {
128:
129: try {
130: AssertionManager am = Lookup.getInstance()
131: .lookupAssertionManager();
132:
133: AuthenticationAssertion aa = am
134: .consumeAssertion(authenticationAssertionId);
135:
136: if (aa == null) {
137: throw new AssertionNotValidException(
138: authenticationAssertionId);
139: }
140:
141: return aa.getSSOSession().getId();
142: } catch (Exception e) {
143: throw new IdentityProvisioningException(
144: "Error dereferencing authentication assertion : "
145: + authenticationAssertionId, e);
146: }
147:
148: }
149:
150: public void globalSignoff(String sessionId)
151: throws IdentityProvisioningException {
152:
153: try {
154: SecurityDomain domain = Lookup.getInstance()
155: .lookupSecurityDomain();
156: SSOSessionManager sm = domain.getSessionManager();
157: SSOSession session = sm.getSession(sessionId);
158:
159: SSOContextImpl ctx = new SSOContextImpl();
160: ctx.setCurrentSession(session);
161: ctx.setUserLocation("remote-application");
162: ctx.setScheme("basic-authentication");
163:
164: logout(ctx);
165:
166: } catch (SSOAuthenticationException e) {
167: throw new IdentityProvisioningException(
168: "Failed to signoff user with session : "
169: + sessionId);
170: } catch (SSOException e) {
171: throw new IdentityProvisioningException(
172: "Error signing off user with sessin : " + sessionId);
173: } catch (Exception e) {
174: throw new IdentityProvisioningException(
175: "Unknown error signing off user with session : "
176: + sessionId);
177: }
178:
179: }
180:
181: ///////////////////////////////////////////////////////////////////
182: // Internal operations used only within the gateway application
183: ///////////////////////////////////////////////////////////////////
184:
185: /**
186: * This method logins a user into de SSO infrastructure.
187: *
188: * @param cred the user credentials used as user identity proof.
189: * @param scheme the authentication scheme name to be used for logging in the user.
190: *
191: * @throws AuthenticationFailureException if authentication fails.
192: * @throws SSOException if an error occurs.
193: */
194: public SSOSession login(Credential[] cred, String scheme,
195: SSOContext ctx) throws SSOException,
196: SSOAuthenticationException {
197:
198: try {
199:
200: SecurityDomain domain = Lookup.getInstance()
201: .lookupSecurityDomain();
202:
203: // Configure this ...!
204: SSOIdentityManager im = domain.getIdentityManager();
205: SSOSessionManager sm = domain.getSessionManager();
206: Authenticator au = domain.getAuthenticator();
207:
208: // 1. Invalidate current session
209: SSOSession currentSession = ctx.getCurrentSession();
210: if (currentSession != null) {
211: try {
212: if (logger.isDebugEnabled())
213: logger.debug("Invalidating existing session : "
214: + currentSession.getId());
215: sm.invalidate(currentSession.getId());
216: } catch (Exception e) {
217: logger.warn("Can't ivalidate current session : "
218: + currentSession.getId() + "\n"
219: + e.getMessage(), e);
220: }
221: }
222:
223: // 2. Authenticate using credentials :
224: Subject s = au.check(cred, scheme);
225: Set principals = s.getPrincipals(SimplePrincipal.class);
226: if (principals.size() != 1) {
227: // The Set should NEVER be empty or have more than one Principal ...
228: // In the future, we could have more than one principal if authenticated with multiple schemes.
229: throw new SSOException(
230: "Assertion failed : principals.size() == 1");
231: }
232:
233: // 3. Find SSO User, authentication was successfull and we have only one principal
234: // Check the username with the IdentityManager, just to be sure it's a valid user:
235: Principal p = (Principal) principals.iterator().next();
236: im.userExists(p.getName());
237:
238: // 4. Create a new sso session :
239: String ssoSessionId = sm.initiateSession(p.getName());
240: SSOSession session = sm.getSession(ssoSessionId);
241:
242: notifyLoginSuccess(ctx, session.getUsername(), session,
243: scheme);
244:
245: return session;
246:
247: } catch (AuthenticationFailureException e) {
248: if (logger.isDebugEnabled())
249: logger.debug(e.getMessage(), e);
250: // Re-throw current exception ...
251: notifyLoginFailed(ctx, cred, scheme, e);
252: throw e;
253:
254: } catch (SSOAuthenticationException e) {
255: if (logger.isDebugEnabled())
256: logger.debug(e.getMessage(), e);
257: // Re-throw current exception ...
258: notifyLoginFailed(ctx, cred, scheme, e);
259: throw e;
260:
261: } catch (SSOIdentityException e) {
262: if (logger.isDebugEnabled())
263: logger.debug(e.getMessage(), e);
264: notifyLoginFailed(ctx, cred, scheme, e);
265: throw new SSOException(e.getMessage(), e);
266:
267: } catch (SSOSessionException e) {
268: if (logger.isDebugEnabled())
269: logger.debug(e.getMessage(), e);
270: notifyLoginFailed(ctx, cred, scheme, e);
271: throw new SSOException(e.getMessage(), e);
272: } catch (Exception e) {
273: logger.error(e.getMessage(), e);
274: notifyLoginFailed(ctx, cred, scheme, e);
275: throw new SSOException(e.getMessage(), e);
276:
277: }
278: }
279:
280: /**
281: * Create an authentication assertion based on the supplied credentials. If assertion is successful a new session
282: * is created for the subject which can be referenced through the corresponding assertion identifier.
283: *
284: * @param credentials
285: * @param scheme
286: * @param ctx
287: * @return
288: * @throws AuthenticationFailureException if authentication fails
289: * @throws SSOException
290: * @throws SSOAuthenticationException
291: */
292: public AuthenticationAssertion assertIdentity(
293: Credential[] credentials, String scheme, SSOContext ctx)
294: throws SSOException, SSOAuthenticationException {
295:
296: try {
297: SSOSession session;
298:
299: session = login(credentials, scheme, ctx);
300: AssertionManager assertionManager = Lookup.getInstance()
301: .lookupAssertionManager();
302: return assertionManager.requestAssertion(session);
303: } catch (AuthenticationFailureException e) {
304: throw e;
305: } catch (SSOAuthenticationException e) {
306: throw e;
307: } catch (Exception e) {
308: logger.error(e.getMessage(), e);
309: // TODO : Notify assertion failed ...
310: throw new SSOException(e.getMessage(), e);
311: }
312:
313: }
314:
315: /**
316: * Create an authentication assertion from a previous existing and valid one.
317: *
318: * @param sessionId SSO session identifier for the session to be bound to the new assertion.
319: * @return
320: * @throws SSOException
321: */
322: public AuthenticationAssertion assertIdentity(String sessionId)
323: throws SSOException {
324:
325: try {
326: SSOSession session;
327:
328: SSOSessionManager sm = Lookup.getInstance()
329: .lookupSecurityDomain().getSessionManager();
330: AssertionManager assertionManager = Lookup.getInstance()
331: .lookupAssertionManager();
332:
333: session = sm.getSession(sessionId);
334:
335: return assertionManager.requestAssertion(session);
336:
337: } catch (Exception e) {
338: logger.error(e.getMessage(), e);
339: // TODO: notify assertion failed event
340: throw new SSOException(e.getMessage(), e);
341: }
342:
343: }
344:
345: /**
346: * Logouts a user from the SSO infrastructure.
347: *
348: * @param ctx the sso external context during method execution
349: * @throws SSOException
350: */
351: public void logout(SSOContext ctx) throws SSOException {
352:
353: SSOSession session = ctx.getCurrentSession();
354: if (session == null)
355: return;
356:
357: String ssoSessionId = session.getId();
358:
359: try {
360:
361: SecurityDomain domain = Lookup.getInstance()
362: .lookupSecurityDomain();
363: SSOSessionManager sm = domain.getSessionManager();
364: sm.invalidate(ssoSessionId);
365: notifyLogoutSuccess(ctx);
366: } catch (NoSuchSessionException e) {
367: // Ignore this ....
368: if (logger.isDebugEnabled())
369: logger.debug("[logout()] Session is not valid : "
370: + ssoSessionId);
371:
372: } catch (SSOSessionException e) {
373: logger.error(e.getMessage(), e);
374: notifyLogoutFail(ctx, e);
375: throw new SSOException(e.getMessage(), e);
376: } catch (Exception e) {
377: logger.error(e.getMessage(), e);
378: notifyLogoutFail(ctx, e);
379: throw new SSOException(e.getMessage(), e);
380: }
381: }
382:
383: protected void notifyLoginFailed(SSOContext ctx,
384: Credential[] credentials, String scheme, Throwable error) {
385: try {
386: // We expect a spetial Event Manager ...
387: SSOSecurityEventManager em = (SSOSecurityEventManager) Lookup
388: .getInstance().lookupSecurityDomain()
389: .getEventManager();
390: em.fireAuthenticationFailureEvent(ctx.getUserLocation(),
391: scheme, credentials, error);
392:
393: } catch (Exception e) {
394: logger.error("Can't notify login failure : "
395: + e.getMessage(), e);
396: }
397: }
398:
399: protected void notifyLoginSuccess(SSOContext ctx, String username,
400: SSOSession session, String scheme) {
401: try {
402: // We expect a spetial Event Manager ...
403: SSOSecurityEventManager em = (SSOSecurityEventManager) Lookup
404: .getInstance().lookupSecurityDomain()
405: .getEventManager();
406: em.fireAuthenticationSuccessEvent(ctx.getUserLocation(),
407: scheme, username, session.getId());
408:
409: } catch (Exception e) {
410: logger.error("Can't notify login success : "
411: + e.getMessage(), e);
412: }
413: }
414:
415: private void notifyLogoutFail(SSOContext ctx, Throwable error) {
416: try {
417: // We expect a spetial Event Manager ...
418: SSOSecurityEventManager em = (SSOSecurityEventManager) Lookup
419: .getInstance().lookupSecurityDomain()
420: .getEventManager();
421: em.fireLogoutFailureEvent(ctx.getUserLocation(), ctx
422: .getCurrentSession().getUsername(), ctx
423: .getCurrentSession().getId(), error);
424:
425: } catch (Exception e) {
426: logger.error("Can't notify login success : "
427: + e.getMessage(), e);
428: }
429: }
430:
431: protected void notifyLogoutSuccess(SSOContext ctx) {
432: try {
433: // We expect a spetial Event Manager ...
434: SSOSecurityEventManager em = (SSOSecurityEventManager) Lookup
435: .getInstance().lookupSecurityDomain()
436: .getEventManager();
437: em.fireLogoutSuccessEvent(ctx.getUserLocation(), ctx
438: .getCurrentSession().getUsername(), ctx
439: .getCurrentSession().getId());
440:
441: } catch (Exception e) {
442: logger.error("Can't notify login success : "
443: + e.getMessage(), e);
444: }
445:
446: }
447:
448: protected Credential newCredential(String schemeName, String name,
449: Object value) throws SSOAuthenticationException {
450: try {
451:
452: SecurityDomain domain = Lookup.getInstance()
453: .lookupSecurityDomain();
454: Authenticator au = domain.getAuthenticator();
455:
456: return au.newCredential(schemeName, name, value);
457: } catch (Exception e) {
458: logger.error(e.getMessage(), e);
459: return null;
460: }
461: }
462:
463: }
|