Changes in Release 2.1.0
=============================================
-[SIDP-20] - Cannot deploy on Windows. Sping and DOS device names?
+NOTE, this release requires a small change to the service.xml file, if you are upgrading from 2.0.0.
+In the last service, shibboleth.ServiceServletContextAttributeExporter, you MUST add the service
+'shibboleth.StorageService' to the whitespace delimited list of services already present.
+
+[SIDP-20] - Cannot deploy on Windows. Spring and DOS device names?
[SIDP-164] - Option to make session cookie secure
[SIDP-165] - Support for SessionNotOnOrAfter
[SIDP-167] - Missing tags and incomplete login.jsp
[SIDP-185] - NullPointerException after AttributeQuery when Security Rule fails
[SIDP-189] - NPE in AbstractSAML2ProfileHandler
[SIDP-194] - Installer can remember the wrong thing
+[SIDP-196] - IdP continues to use old principal name after forced reauthentication
[SIDP-197] - Misleading error message for ValidationInfo element in relying-party.xml
[SIDP-199] - loss of login context when deploying the IdP to tomcat's ROOT context
[SIDP-201] - IdP sends SAML 1 authentication responses without audience conditions
<Service id="shibboleth.ServiceServletContextAttributeExporter"
depends-on="shibboleth.AttributeResolver shibboleth.AttributeFilterEngine
shibboleth.SAML1AttributeAuthority shibboleth.SAML2AttributeAuthority
- shibboleth.RelyingPartyConfigurationManager shibboleth.HandlerManager"
+ shibboleth.RelyingPartyConfigurationManager shibboleth.HandlerManager
+ shibboleth.StorageService"
xsi:type="ServletContextAttributeExporter" />
</Services>
\ No newline at end of file
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package edu.internet2.middleware.shibboleth.idp.authn;
import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import java.util.Map.Entry;
import javax.security.auth.Subject;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
import org.joda.time.DateTime;
+import org.opensaml.common.IdentifierGenerator;
+import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
import org.opensaml.saml2.core.AuthnContext;
+import org.opensaml.util.storage.ExpiringObject;
+import org.opensaml.util.storage.StorageService;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.internet2.middleware.shibboleth.common.session.SessionManager;
import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
-import edu.internet2.middleware.shibboleth.idp.authn.provider.PreviousSessionLoginHandler;
import edu.internet2.middleware.shibboleth.idp.profile.IdPProfileHandlerManager;
import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
import edu.internet2.middleware.shibboleth.idp.session.impl.AuthenticationMethodInformationImpl;
import edu.internet2.middleware.shibboleth.idp.session.impl.ServiceInformationImpl;
-/**
- * Manager responsible for handling authentication requests.
- */
+/** Manager responsible for handling authentication requests. */
public class AuthenticationEngine extends HttpServlet {
+ /** Name of the Servlet config init parameter that holds the partition name for login contexts. */
+ public static final String LOGIN_CONTEXT_PARTITION_NAME_INIT_PARAM_NAME = "loginContextPartitionName";
+
+ /** Name of the Servlet config init parameter that holds lifetime of a login context in the storage service. */
+ public static final String LOGIN_CONTEXT_LIFETIME_INIT_PARAM_NAME = "loginContextEntryLifetime";
+
/** Name of the IdP Cookie containing the IdP session ID. */
public static final String IDP_SESSION_COOKIE_NAME = "_idp_session";
+ /** Name of the key under which to bind the storage service key for a login context. */
+ public static final String LOGIN_CONTEXT_KEY_NAME = "_idp_authn_lc_key";
+
/** Serial version UID. */
- private static final long serialVersionUID = 8494202791991613148L;
/** Class logger. */
private static final Logger LOG = LoggerFactory.getLogger(AuthenticationEngine.class);
+ /** Storage service used to store {@link LoginContext}s while authentication is in progress. */
+ private static StorageService<String, LoginContextEntry> storageService;
+
+ /** Name of the storage service partition used to store login contexts. */
+ private static String loginContextPartitionName;
+
+ /** Lifetime of stored login contexts. */
+ private static long loginContextEntryLifetime;
+
+ /** ID generator. */
+ private static IdentifierGenerator idGen;
+
/** Profile handler manager. */
private IdPProfileHandlerManager handlerManager;
if (DatatypeHelper.isEmpty(sessionManagerId)) {
sessionManagerId = "shibboleth.SessionManager";
}
-
sessionManager = (SessionManager<Session>) getServletContext().getAttribute(sessionManagerId);
+
+ String storageServiceId = config.getInitParameter("storageServiceId");
+ if (DatatypeHelper.isEmpty(storageServiceId)) {
+ storageServiceId = "shibboleth.StorageService";
+ }
+ storageService = (StorageService<String, LoginContextEntry>) getServletContext().getAttribute(storageServiceId);
+
+ String partitionName = DatatypeHelper.safeTrimOrNullString(config
+ .getInitParameter(LOGIN_CONTEXT_PARTITION_NAME_INIT_PARAM_NAME));
+ if (partitionName != null) {
+ loginContextPartitionName = partitionName;
+ } else {
+ loginContextPartitionName = "loginContexts";
+ }
+
+ String lifetime = DatatypeHelper.safeTrimOrNullString(config
+ .getInitParameter(LOGIN_CONTEXT_LIFETIME_INIT_PARAM_NAME));
+ if (lifetime != null) {
+ loginContextEntryLifetime = Long.parseLong(lifetime);
+ } else {
+ loginContextEntryLifetime = 1000 * 60 * 30;
+ }
+
+ try {
+ idGen = new SecureRandomIdentifierGenerator();
+ } catch (NoSuchAlgorithmException e) {
+ throw new ServletException("Error create random number generator", e);
+ }
+ }
+
+ /**
+ * Retrieves a login context.
+ *
+ * @param httpRequest current HTTP request
+ * @param removeFromStorageService whether the login context should be removed from the storage service as it is
+ * retrieved
+ *
+ * @return the login context or null if one is not available (e.g. because it has expired)
+ */
+ protected static LoginContext retrieveLoginContext(HttpServletRequest httpRequest, boolean removeFromStorageService) {
+ // When the login context comes from the profile handlers its attached to the request
+ // Prior to the authentication engine handing control over to a login handler it stores
+ // the login context into the storage service so that the login handlers do not have to
+ // maintain a reference to the context and return it to the engine.
+ LoginContext loginContext = (LoginContext) httpRequest.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+ if (loginContext != null) {
+ LOG.trace("Login context retrieved from HTTP request attribute");
+ return loginContext;
+ }
+
+ String contextId = DatatypeHelper.safeTrimOrNullString((String) httpRequest
+ .getAttribute(LOGIN_CONTEXT_KEY_NAME));
+
+ if (contextId == null) {
+ Cookie[] requestCookies = httpRequest.getCookies();
+ if (requestCookies != null) {
+ for (Cookie requestCookie : requestCookies) {
+ if (DatatypeHelper.safeEquals(requestCookie.getName(), LOGIN_CONTEXT_KEY_NAME)) {
+ LOG.trace("Located cookie with login context key");
+ contextId = requestCookie.getValue();
+ break;
+ }
+ }
+ }
+ }
+
+ LOG.trace("Using login context key {} to look up login context", contextId);
+ LoginContextEntry entry;
+ if (removeFromStorageService) {
+ entry = storageService.remove(loginContextPartitionName, contextId);
+ } else {
+ entry = storageService.get(loginContextPartitionName, contextId);
+ }
+ if (entry == null) {
+ LOG.trace("No entry for login context found in storage service.");
+ return null;
+ } else if (entry.isExpired()) {
+ LOG.trace("Login context entry found in storage service but it was expired.");
+ return null;
+ } else {
+ LOG.trace("Login context entry found in storage service.");
+ return entry.getLoginContext();
+ }
}
/**
* Returns control back to the authentication engine.
*
- * @param httpRequest current http request
- * @param httpResponse current http response
+ * @param httpRequest current HTTP request
+ * @param httpResponse current HTTP response
*/
public static void returnToAuthenticationEngine(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
LOG.debug("Returning control to authentication engine");
- HttpSession httpSession = httpRequest.getSession();
- LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+ LoginContext loginContext = retrieveLoginContext(httpRequest, false);
if (loginContext == null) {
- LOG.error("User HttpSession did not contain a login context. Unable to return to authentication engine");
+ LOG.error("No login context available, unable to return to authentication engine");
forwardRequest("/idp-error.jsp", httpRequest, httpResponse);
} else {
forwardRequest(loginContext.getAuthenticationEngineURL(), httpRequest, httpResponse);
* Returns control back to the profile handler that invoked the authentication engine.
*
* @param loginContext current login context
- * @param httpRequest current http request
- * @param httpResponse current http response
+ * @param httpRequest current HTTP request
+ * @param httpResponse current HTTP response
*/
public static void returnToProfileHandler(LoginContext loginContext, HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
LOG.debug("Returning control to profile handler at: {}", loginContext.getProfileHandlerURL());
- httpRequest.getSession().removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
httpRequest.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
forwardRequest(loginContext.getProfileHandlerURL(), httpRequest, httpResponse);
}
LOG.error("HTTP Response already committed");
}
- LoginContext loginContext = (LoginContext) httpRequest.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
- if (loginContext == null) {
- // When the login context comes from the profile handlers its attached to the request
- // The authn engine attaches it to the session to allow the handlers to do any number of
- // request/response pairs without maintaining or losing the login context
- loginContext = (LoginContext) httpRequest.getSession().getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
- }
-
+ LoginContext loginContext = retrieveLoginContext(httpRequest, true);
if (loginContext == null) {
LOG.error("Incoming request does not have attached login context");
throw new ServletException("Incoming request does not have attached login context");
// If the user already has a session and its usage is acceptable than use it
// otherwise just use the first candidate login handler
LOG.debug("Possible authentication handlers after filtering: {}", possibleLoginHandlers);
+ LoginHandler loginHandler;
if (idpSession != null && possibleLoginHandlers.containsKey(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX)) {
- authenticateUserWithPreviousSession(loginContext, possibleLoginHandlers, httpRequest, httpResponse);
+ loginContext.setAttemptedAuthnMethod(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
+ loginHandler = possibleLoginHandlers.get(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
} else {
possibleLoginHandlers.remove(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
Entry<String, LoginHandler> chosenLoginHandler = possibleLoginHandlers.entrySet().iterator().next();
- authenticateUser(chosenLoginHandler.getKey(), chosenLoginHandler.getValue(), loginContext, httpRequest,
- httpResponse);
+ loginContext.setAttemptedAuthnMethod(chosenLoginHandler.getKey());
+ loginHandler = chosenLoginHandler.getValue();
}
+
+ // Send the request to the login handler
+ LOG.debug("Authenticating user with login handler of type {}", loginHandler.getClass().getName());
+ loginContext.setAuthenticationAttempted();
+ loginContext.setAuthenticationEngineURL(HttpHelper.getRequestUriWithoutContext(httpRequest));
+ storeLoginContext(loginContext, httpRequest, httpResponse);
+ loginHandler.login(httpRequest, httpResponse);
} catch (AuthenticationException e) {
loginContext.setAuthenticationFailure(e);
returnToProfileHandler(loginContext, httpRequest, httpResponse);
}
/**
- * Completes the authentication request using an existing, active, authentication method for the current user.
+ * Stores the login context in the storage service. The key for the stored login context is then bound to an HTTP
+ * request attribute and set a cookie.
*
- * @param loginContext current login context
- * @param possibleLoginHandlers login handlers that meet the peers authentication requirements
+ * @param loginContext login context to store
* @param httpRequest current HTTP request
* @param httpResponse current HTTP response
*/
- protected void authenticateUserWithPreviousSession(LoginContext loginContext,
- Map<String, LoginHandler> possibleLoginHandlers, HttpServletRequest httpRequest,
+ protected void storeLoginContext(LoginContext loginContext, HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
- LOG.debug("Authenticating user by way of existing session.");
+ String contextId = idGen.generateIdentifier();
- Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
- PreviousSessionLoginHandler loginHandler = (PreviousSessionLoginHandler) handlerManager.getLoginHandlers().get(
- AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
-
- AuthenticationMethodInformation authenticationMethod = null;
- for (String possibleAuthnMethod : idpSession.getAuthenticationMethods().keySet()) {
- authenticationMethod = idpSession.getAuthenticationMethods().get(possibleAuthnMethod);
- if (authenticationMethod != null) {
- break;
- }
- }
+ storageService.put(loginContextPartitionName, contextId, new LoginContextEntry(loginContext,
+ loginContextEntryLifetime));
- if (loginHandler.reportPreviousSessionAuthnMethod()) {
- loginContext.setAuthenticationDuration(loginHandler.getAuthenticationDuration());
- loginContext.setAuthenticationInstant(new DateTime());
- loginContext.setAuthenticationMethod(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
+ httpRequest.setAttribute(LOGIN_CONTEXT_KEY_NAME, contextId);
+
+ Cookie cookie = new Cookie(LOGIN_CONTEXT_KEY_NAME, contextId);
+ String contextPath = httpRequest.getContextPath();
+ if (DatatypeHelper.isEmpty(contextPath)) {
+ cookie.setPath("/");
} else {
- loginContext.setAuthenticationDuration(authenticationMethod.getAuthenticationDuration());
- loginContext.setAuthenticationInstant(authenticationMethod.getAuthenticationInstant());
- loginContext.setAuthenticationMethod(authenticationMethod.getAuthenticationMethod());
+ cookie.setPath(contextPath);
}
- loginContext.setPrincipalName(idpSession.getPrincipalName());
-
- loginContext.setAuthenticationAttempted();
- httpRequest.getSession().setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
- loginHandler.login(httpRequest, httpResponse);
- }
-
- /**
- * Authenticates the user with the given authentication method provided by the given login handler.
- *
- * @param authnMethod the authentication method that will be used to authenticate the user
- * @param loginHandler login handler that will authenticate user
- * @param loginContext current login context
- * @param httpRequest current HTTP request
- * @param httpResponse current HTTP response
- */
- protected void authenticateUser(String authnMethod, LoginHandler loginHandler, LoginContext loginContext,
- HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
- LOG.debug("Authenticating user with login handler of type {}", loginHandler.getClass().getName());
-
- loginContext.setAuthenticationAttempted();
- loginContext.setAuthenticationInstant(new DateTime());
- loginContext.setAuthenticationDuration(loginHandler.getAuthenticationDuration());
- loginContext.setAuthenticationMethod(authnMethod);
- loginContext.setAuthenticationEngineURL(HttpHelper.getRequestUriWithoutContext(httpRequest));
- httpRequest.getSession().setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
- loginHandler.login(httpRequest, httpResponse);
+ cookie.setSecure(httpRequest.isSecure());
+ cookie.setMaxAge(-1);
+ httpResponse.addCookie(cookie);
}
/**
HttpServletResponse httpResponse) {
LOG.debug("Completing user authentication process");
- // We check if the principal name was already set in the login context
- // if not attempt to pull it from where login handlers are supposed to provide it
- String principalName = DatatypeHelper.safeTrimOrNullString(loginContext.getPrincipalName());
- if (principalName == null) {
- principalName = DatatypeHelper.safeTrimOrNullString((String) httpRequest
- .getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
- if (principalName != null) {
- loginContext.setPrincipalName(principalName);
- } else {
- loginContext.setPrincipalAuthenticated(false);
- loginContext.setAuthenticationFailure(new AuthenticationException(
- "No principal name returned from authentication handler."));
- LOG.error("No principal name returned from authentication method: "
- + loginContext.getAuthenticationMethod());
- returnToProfileHandler(loginContext, httpRequest, httpResponse);
- return;
+ Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
+
+ try {
+ // Check to make sure the login handler did the right thing
+ validateSuccessfulAuthentication(loginContext, httpRequest);
+
+ // We allow a login handler to override the authentication method in the
+ // event that it supports multiple methods
+ String actualAuthnMethod = DatatypeHelper.safeTrimOrNullString((String) httpRequest
+ .getAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY));
+ if (actualAuthnMethod == null) {
+ actualAuthnMethod = loginContext.getAttemptedAuthnMethod();
}
- }
- loginContext.setPrincipalAuthenticated(true);
- // We allow a login handler to override the authentication method in the event that it supports multiple methods
- String actualAuthnMethod = DatatypeHelper.safeTrimOrNullString((String) httpRequest
- .getAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY));
- if (actualAuthnMethod != null) {
- loginContext.setAuthenticationMethod(actualAuthnMethod);
+ // Get the Subject from the request. If force authentication was required then make sure the
+ // Subject identifies the same user that authenticated before
+ Subject subject = getLoginHandlerSubject(httpRequest);
+ if (loginContext.isForceAuthRequired()) {
+ validateForcedReauthentication(idpSession, actualAuthnMethod, subject);
+ }
+
+ loginContext.setPrincipalAuthenticated(true);
+ updateUserSession(loginContext, subject, actualAuthnMethod, httpRequest, httpResponse);
+ LOG.debug("User {} authenticated with method {}", loginContext.getPrincipalName(), actualAuthnMethod);
+ } catch (AuthenticationException e) {
+ LOG.error("Authentication failed with the error:", e);
+ loginContext.setPrincipalAuthenticated(false);
+ loginContext.setAuthenticationFailure(e);
}
- LOG.debug("User {} authenticated with method {}", loginContext.getPrincipalName(), loginContext
- .getAuthenticationMethod());
- updateUserSession(loginContext, httpRequest, httpResponse);
returnToProfileHandler(loginContext, httpRequest, httpResponse);
}
/**
+ * Validates that the authentication was successfully performed by the login handler. An authentication is
+ * considered successful if no error is bound to the request attribute {@link LoginHandler#AUTHENTICATION_ERROR_KEY}
+ * and there is a value for at least one of the following request attributes: {@link LoginHandler#SUBJECT_KEY},
+ * {@link LoginHandler#PRINCIPAL_KEY}, or {@link LoginHandler#PRINCIPAL_NAME_KEY}.
+ *
+ * @param loginContext current login context
+ * @param httpRequest current HTTP request
+ *
+ * @throws AuthenticationException thrown if the authentication was not successful
+ */
+ protected void validateSuccessfulAuthentication(LoginContext loginContext, HttpServletRequest httpRequest)
+ throws AuthenticationException {
+ String errorMessage = DatatypeHelper.safeTrimOrNullString((String) httpRequest
+ .getAttribute(LoginHandler.AUTHENTICATION_ERROR_KEY));
+ if (errorMessage != null) {
+ LOG.error("Error returned from login handler for authentication method {}:\n{}", loginContext
+ .getAttemptedAuthnMethod(), errorMessage);
+ throw new AuthenticationException(errorMessage);
+ }
+
+ Subject subject = (Subject) httpRequest.getAttribute(LoginHandler.SUBJECT_KEY);
+ Principal principal = (Principal) httpRequest.getAttribute(LoginHandler.PRINCIPAL_KEY);
+ String principalName = DatatypeHelper.safeTrimOrNullString((String) httpRequest
+ .getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
+
+ if (subject == null && principal == null && principalName == null) {
+ LOG.error("No user identified by login handler.");
+ throw new AuthenticationException("No user identified by login handler.");
+ }
+ }
+
+ /**
+ * Gets the subject from the request coming back from the login handler.
+ *
+ * @param httpRequest request coming back from the login handler
+ *
+ * @return the {@link Subject} created from the request
+ *
+ * @throws AuthenticationException thrown if no subject can be retrieved from the request
+ */
+ protected Subject getLoginHandlerSubject(HttpServletRequest httpRequest) throws AuthenticationException {
+ Subject subject = (Subject) httpRequest.getAttribute(LoginHandler.SUBJECT_KEY);
+ Principal principal = (Principal) httpRequest.getAttribute(LoginHandler.PRINCIPAL_KEY);
+ String principalName = DatatypeHelper.safeTrimOrNullString((String) httpRequest
+ .getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
+
+ if (subject == null && (principal != null || principalName != null)) {
+ subject = new Subject();
+ if (principal == null) {
+ principal = new UsernamePrincipal(principalName);
+ }
+ subject.getPrincipals().add(principal);
+ }
+
+ return subject;
+ }
+
+ /**
+ * If forced authentication was required this method checks to ensure that the re-authenticated subject contains a
+ * principal name that is equal to the principal name associated with the authentication method. If this is the
+ * first time the subject has authenticated with this method than this check always passes.
+ *
+ * @param idpSession user's IdP session
+ * @param authnMethod method used to authenticate the user
+ * @param subject subject that was authenticated
+ *
+ * @throws AuthenticationException thrown if this check fails
+ */
+ protected void validateForcedReauthentication(Session idpSession, String authnMethod, Subject subject)
+ throws AuthenticationException {
+ if (idpSession != null) {
+ AuthenticationMethodInformation authnMethodInfo = idpSession.getAuthenticationMethods().get(authnMethod);
+ if (authnMethodInfo != null) {
+ boolean princpalMatch = false;
+ for (Principal princpal : subject.getPrincipals()) {
+ if (authnMethodInfo.getAuthenticationPrincipal().equals(princpal)) {
+ princpalMatch = true;
+ break;
+ }
+ }
+
+ if (!princpalMatch) {
+ throw new ForceAuthenticationException(
+ "Authenticated principal does not match previously authenticated principal");
+ }
+ }
+ }
+ }
+
+ /**
* Updates the user's Shibboleth session with authentication information. If no session exists a new one will be
* created.
*
* @param loginContext current login context
+ * @param authenticationSubject subject created from the authentication method
+ * @param authenticationMethod the method used to authenticate the subject
* @param httpRequest current HTTP request
* @param httpResponse current HTTP response
*/
- protected void updateUserSession(LoginContext loginContext, HttpServletRequest httpRequest,
- HttpServletResponse httpResponse) {
+ protected void updateUserSession(LoginContext loginContext, Subject authenticationSubject,
+ String authenticationMethod, HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
+
+ Principal authenticationPrincipal = authenticationSubject.getPrincipals().iterator().next();
+
Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
if (idpSession == null) {
- LOG.debug("Creating shibboleth session for principal {}", loginContext.getPrincipalName());
- idpSession = (Session) sessionManager.createSession(loginContext.getPrincipalName());
+ LOG.debug("Creating shibboleth session for principal {}", authenticationPrincipal.getName());
+ idpSession = (Session) sessionManager.createSession();
loginContext.setSessionID(idpSession.getSessionID());
addSessionCookie(httpRequest, httpResponse, idpSession);
}
- LOG.debug("Recording authentication and service information in Shibboleth session for principal: {}",
- loginContext.getPrincipalName());
- Subject subject = (Subject) httpRequest.getAttribute(LoginHandler.SUBJECT_KEY);
- String authnMethod = (String) httpRequest.getAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY);
- if (DatatypeHelper.isEmpty(authnMethod)) {
- authnMethod = loginContext.getAuthenticationMethod();
- }
+ // Merge the information in the current session subject with the information from the
+ // login handler subject
+ idpSession.setSubject(mergeSubjects(idpSession.getSubject(), authenticationSubject));
- AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(subject, authnMethod,
- loginContext.getAuthenticationInstant(), loginContext.getAuthenticationDuration());
+ LOG.debug("Recording authentication and service information in Shibboleth session for principal: {}",
+ authenticationPrincipal.getName());
+ LoginHandler loginHandler = handlerManager.getLoginHandlers().get(authenticationMethod);
+ AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(idpSession
+ .getSubject(), authenticationPrincipal, authenticationMethod, new DateTime(), loginHandler
+ .getAuthenticationDuration());
+ loginContext.setAuthenticationMethodInformation(authnMethodInfo);
idpSession.getAuthenticationMethods().put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
+ sessionManager.indexSession(idpSession, authnMethodInfo.getAuthenticationPrincipal().getName());
ServiceInformation serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(),
authnMethodInfo);
}
/**
+ * Merges the principals and public and private credentials from two subjects into a new subject.
+ *
+ * @param subject1 first subject to merge, may be null
+ * @param subject2 second subject to merge, may be null
+ *
+ * @return subject containing the merged information
+ */
+ protected Subject mergeSubjects(Subject subject1, Subject subject2) {
+ if (subject1 == null) {
+ return subject2;
+ }
+
+ if (subject2 == null) {
+ return subject1;
+ }
+
+ if (subject1 == null && subject2 == null) {
+ return new Subject();
+ }
+
+ Set<Principal> principals = new HashSet<Principal>();
+ principals.addAll(subject1.getPrincipals());
+ principals.addAll(subject2.getPrincipals());
+
+ Set<Object> publicCredentials = new HashSet<Object>();
+ publicCredentials.addAll(subject1.getPublicCredentials());
+ publicCredentials.addAll(subject2.getPublicCredentials());
+
+ Set<Object> privateCredentials = new HashSet<Object>();
+ privateCredentials.addAll(subject1.getPrivateCredentials());
+ privateCredentials.addAll(subject2.getPrivateCredentials());
+
+ return new Subject(false, principals, publicCredentials, privateCredentials);
+ }
+
+ /**
* Adds an IdP session cookie to the outbound response.
*
* @param httpRequest current request
LOG.debug("Adding IdP session cookie to HTTP response");
Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, userSession.getSessionID());
-
+
String contextPath = httpRequest.getContextPath();
- if(DatatypeHelper.isEmpty(contextPath)){
+ if (DatatypeHelper.isEmpty(contextPath)) {
sessionCookie.setPath("/");
- }else{
+ } else {
sessionCookie.setPath(contextPath);
}
-
+
sessionCookie.setSecure(httpRequest.isSecure());
sessionCookie.setMaxAge(-1);
httpResponse.addCookie(sessionCookie);
}
+
+ /** Storage service entry for login contexts. */
+ public class LoginContextEntry implements ExpiringObject {
+
+ /** Stored login context. */
+ private LoginContext loginCtx;
+
+ /** Time the entry expires. */
+ private DateTime expirationTime;
+
+ /**
+ * Constructor.
+ *
+ * @param ctx context to store
+ * @param lifetime lifetime of the entry
+ */
+ public LoginContextEntry(LoginContext ctx, long lifetime) {
+ loginCtx = ctx;
+ expirationTime = new DateTime().plus(lifetime);
+ }
+
+ /**
+ * Gets the login context.
+ *
+ * @return login context
+ */
+ public LoginContext getLoginContext() {
+ return loginCtx;
+ }
+
+ /** {@inheritDoc} */
+ public DateTime getExpirationTime() {
+ return expirationTime;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isExpired() {
+ return expirationTime.isBeforeNow();
+ }
+
+ /** {@inheritDoc} */
+ public void onExpire() {
+
+ }
+ }
}
\ No newline at end of file
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import org.joda.time.DateTime;
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
+
/**
* Login context created by a profile handler and interpreted by the authentication package.
*
* Two properties are tracked by default:
+ * <ul>
+ * <li><code>forceAuth</code> - Should user authentication be forced (default value: false).</li>
+ * <li><code>passiveAuth</code> - Should user authentication not control the UI (default value: false).</li>
+ * </ul>
*
- * <code>forceAuth</code> - Should user authentication be forced. <code>passiveAuth</code> - Should user
- * authentication not control the UI.
- *
- * A Map<String, Object> is provided to store other properties. Alternatively, a profile handler may create a
- * subclass of LoginContext with extra fields.
+ * A {@link Map}<String, Object> is provided to store other properties. Alternatively, a profile handler may
+ * create a subclass of LoginContext with extra fields.
*
* LoginContexts should be created by a profile handler when authentication is needed. Once control has returned to the
* profile handler, it should remove the LoginContext from the HttpSession.
*
- * The {@link AuthenticationEngine} or an {@link LoginHandler} should set the
- * {@link LoginContext#setAuthenticationAttempted()}, {@link LoginContext#setPrincipalAuthenticated(boolean)},
+ * The {@link AuthenticationEngine} should set the {@link LoginContext#setAuthenticationAttempted()},
+ * {@link LoginContext#setPrincipalAuthenticated(boolean)},
* {@link LoginContext#setAuthenticationFailure(AuthenticationException)},
- * {@link LoginContext#setAuthenticationDuration(long)}, {@link LoginContext#setAuthenticationInstant(DateTime)}
+ *
* appropriately.
*/
public class LoginContext implements Serializable {
/** Must authentication not interact with the UI. */
private boolean passiveAuth;
- /** a catch-all map for other properties. */
+ /** A catch-all map for other properties. */
private Map<String, Serializable> propsMap = new ConcurrentHashMap<String, Serializable>();
/** The ProfileHandler URL. */
/** The authentication engine's URL. */
private String authnEngineURL;
- /** has authentication been attempted yet. */
+ /** Whether authentication been attempted yet. */
private boolean authnAttempted;
- /** The id of the authenticated user. */
- private String principalName;
+ /** Attempted user authentication method. */
+ private String attemptedAuthnMethod;
/** Did authentication succeed? */
private boolean principalAuthenticated;
- /** Exception that occured during authentication. */
+ /** Exception that occurred during authentication. */
private AuthenticationException authnException;
- /** The instant of authentication. */
- private DateTime authnInstant;
-
- /** The duration of authentication. */
- private long authnDuration;
-
- /** The method used to authenticate the user. */
- private String authnMethod;
-
/** The session id. */
private String sessionID;
/** List of request authentication methods. */
private ArrayList<String> requestAuthenticationMethods;
+ /** Information about the authentication method. */
+ private AuthenticationMethodInformation authenticationMethodInformation;
+
/** Creates a new instance of LoginContext. */
public LoginContext() {
requestAuthenticationMethods = new ArrayList<String>();
}
/**
- * Gets the entity ID of the relying party.
+ * Gets the authentication method that was used when attempting to authenticate the user. Note, this may be
+ * different than the authentication method reported within {@link #getAuthenticationMethodInformation()}.
*
- * @return entity ID of the relying party
+ * @return authentication method that was used when attempting to authenticate the user
*/
- public String getRelyingPartyId() {
- return relyingPartyId;
+ public String getAttemptedAuthnMethod() {
+ return attemptedAuthnMethod;
}
/**
- * Gets the entity ID of the relying party.
+ * Returns if authentication has been attempted for this user.
*
- * @param id entity ID of the relying party
+ * @return if authentication has been attempted for this user
*/
- public void setRelyingParty(String id) {
- relyingPartyId = id;
+ public boolean getAuthenticationAttempted() {
+ return authnAttempted;
}
/**
- * Returns if authentication must be forced.
+ * Gets the duration of authentication.
*
- * @return <code>true</code> if the authentication manager must re-authenticate the user.
+ * @return The duration of authentication, or zero if none was set.
*/
- public boolean isForceAuthRequired() {
- return forceAuth;
+ public long getAuthenticationDuration() {
+ return authenticationMethodInformation.getAuthenticationDuration();
}
/**
- * Returns if authentication must be passive.
+ * Gets the authentication engine's URL.
*
- * @return <code>true</code> if the authentication manager must not interact with the users UI.
+ * @return the URL of the authentication engine
*/
- public boolean isPassiveAuthRequired() {
- return passiveAuth;
+ public String getAuthenticationEngineURL() {
+ return authnEngineURL;
}
/**
- * Sets if authentication must be forced.
+ * Gets the error that occurred during authentication.
*
- * @param force if the authentication manager must re-authenticate the user.
+ * @return error that occurred during authentication
*/
- public void setForceAuthRequired(boolean force) {
- forceAuth = force;
+ public AuthenticationException getAuthenticationFailure() {
+ return authnException;
}
/**
- * Sets if authentication must be passive.
+ * Gets the authentication instant.
*
- * @param passive if the authentication manager must not interact with the users UI.
+ * @return The instant of authentication, or <code>null</code> if none was set.
*/
- public void setPassiveAuthRequired(boolean passive) {
- passiveAuth = passive;
+ public DateTime getAuthenticationInstant() {
+ return authenticationMethodInformation.getAuthenticationInstant();
}
/**
- * Get an optional property object.
- *
- * @param key The key in the properties Map.
+ * Gets the method used to authenticate the user.
*
- * @return The object, or <code>null</code> is no object exists for the key.
+ * @return The method used to authenticate the user.
*/
- public Object getProperty(String key) {
- return propsMap.get(key);
+ public String getAuthenticationMethod() {
+ return authenticationMethodInformation.getAuthenticationMethod();
}
/**
- * Sets an optional property object.
+ * Gets information about the authentication event.
*
- * If an object is already associated with key, it will be overwritten.
+ * @return information about the authentication event.
+ */
+ public AuthenticationMethodInformation getAuthenticationMethodInformation() {
+ return authenticationMethodInformation;
+ }
+
+ /**
+ * Returns the ID of the authenticated user.
*
- * @param key The key to set.
- * @param obj The object to associate with key.
+ * @return the ID of the user, or <code>null</code> if authentication failed.
*/
- public void setProperty(String key, final Serializable obj) {
- propsMap.put(key, obj);
+ public String getPrincipalName() {
+ return authenticationMethodInformation.getAuthenticationPrincipal().getName();
}
/**
- * Sets if authentication succeeded.
+ * Gets the ProfileHandler URL.
*
- * @param authnOK if authentication succeeded;
+ * @return the URL of the profile handler that is invoking the Authentication Manager.
*/
- public void setPrincipalAuthenticated(boolean authnOK) {
- this.principalAuthenticated = authnOK;
+ public String getProfileHandlerURL() {
+ return profileHandlerURL;
}
/**
- * Returns if authentication succeeded.
+ * Get an optional property object.
*
- * @return <code>true</code> is the user was successfully authenticated.
+ * @param key The key in the properties Map.
+ *
+ * @return The object, or <code>null</code> is no object exists for the key.
*/
- public boolean isPrincipalAuthenticated() {
- return principalAuthenticated;
+ public Object getProperty(String key) {
+ return propsMap.get(key);
}
/**
- * Sets the error that occurred during authentication.
+ * Gets the entity ID of the relying party.
*
- * @param error error that occurred during authentication
+ * @return entity ID of the relying party
*/
- public void setAuthenticationFailure(AuthenticationException error) {
- authnException = error;
+ public String getRelyingPartyId() {
+ return relyingPartyId;
}
/**
- * Gets the error that occurred during authentication.
+ * Return the acceptable authentication handler URIs, in preference order, for authenticating this user. If no
+ * authentication methods are preferred the resultant list will be empty.
*
- * @return error that occurred during authentication
+ * @return an list of authentication method identifiers
*/
- public AuthenticationException getAuthenticationFailure() {
- return authnException;
+ public List<String> getRequestedAuthenticationMethods() {
+ return requestAuthenticationMethods;
}
/**
- * Set if authentication has been attempted.
+ * Gets the {@link edu.internet2.middleware.shibboleth.idp.session.Session} ID.
*
- * This method should be called by an {@link LoginHandler} while processing a request.
+ * @return the Session id
*/
- public void setAuthenticationAttempted() {
- authnAttempted = true;
+ public String getSessionID() {
+ return sessionID;
}
/**
- * Returns if authentication has been attempted for this user.
+ * Returns if authentication must be forced.
*
- * @return if authentication has been attempted for this user
+ * @return <code>true</code> if the authentication manager must re-authenticate the user.
*/
- public boolean getAuthenticationAttempted() {
- return authnAttempted;
+ public boolean isForceAuthRequired() {
+ return forceAuth;
}
/**
- * Sets the ID of the authenticated user.
+ * Returns if authentication must be passive.
*
- * @param id The userid.
+ * @return <code>true</code> if the authentication manager must not interact with the users UI.
*/
- public void setPrincipalName(String id) {
- principalName = id;
+ public boolean isPassiveAuthRequired() {
+ return passiveAuth;
}
/**
- * Returns the ID of the authenticated user.
+ * Returns if authentication succeeded.
*
- * @return the ID of the user, or <code>null</code> if authentication failed.
+ * @return <code>true</code> is the user was successfully authenticated.
*/
- public String getPrincipalName() {
- return principalName;
+ public boolean isPrincipalAuthenticated() {
+ return principalAuthenticated;
}
/**
- * Gets the ProfileHandler URL.
+ * Sets the authentication method that was used when attempting to authenticate the user.
*
- * @return the URL of the profile handler that is invoking the Authentication Manager.
+ * @param method authentication method that was used when attempting to authenticate the user
*/
- public String getProfileHandlerURL() {
- return profileHandlerURL;
+ public void setAttemptedAuthnMethod(String method) {
+ attemptedAuthnMethod = method;
}
/**
- * Sets the ProfileHandler URL.
+ * Set if authentication has been attempted.
*
- * @param url The URL of the profile handler that invoked the AuthenticationManager/
+ * This method should be called by an {@link LoginHandler} while processing a request.
*/
- public void setProfileHandlerURL(String url) {
- profileHandlerURL = url;
+ public void setAuthenticationAttempted() {
+ authnAttempted = true;
}
/**
- * Gets the authentication engine's URL.
+ * Sets the duration of authentication.
*
- * @return the URL of the authentication engine
+ * @param duration The duration of authentication.
+ *
+ * @deprecated this information is contained in the {@link AuthenticationMethodInformation}
*/
- public String getAuthenticationEngineURL() {
- return authnEngineURL;
+ public void setAuthenticationDuration(long duration) {
}
/**
}
/**
- * Gets the authentication instant.
+ * Sets the error that occurred during authentication.
*
- * @return The instant of authentication, or <code>null</code> if none was set.
+ * @param error error that occurred during authentication
*/
- public DateTime getAuthenticationInstant() {
- return authnInstant;
+ public void setAuthenticationFailure(AuthenticationException error) {
+ authnException = error;
}
/**
* Sets the authentication instant.
*
* @param instant The instant of authentication.
+ *
+ * @deprecated this information is contained in the {@link AuthenticationMethodInformation}
*/
public void setAuthenticationInstant(final DateTime instant) {
- authnInstant = instant;
}
/**
- * Gets the duration of authentication.
+ * Sets the method used to authenticate the user.
*
- * @return The duration of authentication, or zero if none was set.
+ * @param method The method used to authenticate the user.
+ *
+ * @deprecated this information is contained in the {@link AuthenticationMethodInformation}
*/
- public long getAuthenticationDuration() {
- return authnDuration;
+ public void setAuthenticationMethod(String method) {
}
/**
- * Sets the duration of authentication.
+ * Sets the information about the authentication event.
*
- * @param duration The duration of authentication.
+ * @param info information about the authentication event
*/
- public void setAuthenticationDuration(long duration) {
- authnDuration = duration;
+ public void setAuthenticationMethodInformation(AuthenticationMethodInformation info) {
+ authenticationMethodInformation = info;
}
/**
- * Gets the method used to authenticate the user.
+ * Sets if authentication must be forced.
*
- * @return The method used to authenticate the user.
+ * @param force if the authentication manager must re-authenticate the user.
*/
- public String getAuthenticationMethod() {
- return authnMethod;
+ public void setForceAuthRequired(boolean force) {
+ forceAuth = force;
}
/**
- * Sets the method used to authenticate the user.
+ * Sets if authentication must be passive.
*
- * @param method The method used to authenticate the user.
+ * @param passive if the authentication manager must not interact with the users UI.
*/
- public void setAuthenticationMethod(String method) {
- authnMethod = method;
+ public void setPassiveAuthRequired(boolean passive) {
+ passiveAuth = passive;
}
/**
- * Gets the {@link edu.internet2.middleware.shibboleth.idp.session.Session} ID.
+ * Sets if authentication succeeded.
*
- * @return the Session id
+ * @param authnOK if authentication succeeded;
*/
- public String getSessionID() {
- return sessionID;
+ public void setPrincipalAuthenticated(boolean authnOK) {
+ this.principalAuthenticated = authnOK;
}
/**
- * Sets the {@link edu.internet2.middleware.shibboleth.idp.session.Session} ID.
+ * Sets the ID of the authenticated user.
*
- * @param id the Session ID
+ * @param id The userid.
+ *
+ * @deprecated this information is contained in the {@link AuthenticationMethodInformation}
*/
- public void setSessionID(String id) {
- sessionID = id;
+ public void setPrincipalName(String id) {
+
}
/**
- * Return the acceptable authentication handler URIs, in preference order, for authenticating this user. If no
- * authentication methods are preferred the resultant list will be empty.
+ * Sets the ProfileHandler URL.
*
- * @return an list of authentication method identifiers
+ * @param url The URL of the profile handler that invoked the AuthenticationManager/
*/
- public List<String> getRequestedAuthenticationMethods() {
- return requestAuthenticationMethods;
+ public void setProfileHandlerURL(String url) {
+ profileHandlerURL = url;
+ }
+
+ /**
+ * Sets an optional property object.
+ *
+ * If an object is already associated with key, it will be overwritten.
+ *
+ * @param key The key to set.
+ * @param obj The object to associate with key.
+ */
+ public void setProperty(String key, final Serializable obj) {
+ propsMap.put(key, obj);
+ }
+
+ /**
+ * Gets the entity ID of the relying party.
+ *
+ * @param id entity ID of the relying party
+ */
+ public void setRelyingParty(String id) {
+ relyingPartyId = id;
+ }
+
+ /**
+ * Sets the {@link edu.internet2.middleware.shibboleth.idp.session.Session} ID.
+ *
+ * @param id the Session ID
+ */
+ public void setSessionID(String id) {
+ sessionID = id;
}
}
\ No newline at end of file
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import java.util.List;
-import javax.security.auth.Subject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
-
/**
* Authentication handlers authenticate a user in an implementation specific manner. Some examples of this might be by
- * collecting a user name and password and validating it against an LDAP directory or collecting and validating a client
- * certificate or one-time password.
- *
- * After the handler has authenticated the user it <strong>MUST</strong> bind the user's principal name to the
- * {@link HttpServletRequest} attribute identified by {@link LoginHandler#PRINCIPAL_NAME_KEY}.
+ * collecting a user name and password and validating it against an LDAP directory, validating a client certificate, or
+ * validating one-time password.
*
- * The handler may bind a {@link Subject} to the attribute identified by {@link #SUBJECT_KEY} if one was created during
- * the authentication process. This Subject is stored in the {@link AuthenticationMethodInformation}, created for this
- * authentication, in the user's session.
+ * When a login handler is invoked the user's {@link edu.internet2.middleware.shibboleth.idp.session.Session} is bound
+ * to the {@link javax.servlet.http.HttpSession} under the attribute with the name
+ * {@link edu.internet2.middleware.shibboleth.idp.session.Session#HTTP_SESSION_BINDING_ATTRIBUTE}.
*
- * The handler may designate the a URI representing the authentication method actually used, for example if a handler is
- * capable of performing multiple types of authentication, by binding the URI, as a String, to a request attribute
- * identified by {@link #AUTHENTICATION_METHOD_KEY}.
+ * After a successful authentication has been completed the handler <strong>MUST</strong> either:
+ * <ul>
+ * <li>Bind a {@link javax.security.auth.Subject} to the attribute identified by {@link #SUBJECT_KEY} if one was
+ * created during the authentication process. The principals, public, and private credentials from this subject will be
+ * merged with those in the {@link javax.security.auth.Subject} within the
+ * {@link edu.internet2.middleware.shibboleth.idp.session.Session}.</li>
+ * <li>Bind a {@link java.security.Principal} for the user to the request attribute identified by
+ * {@link #PRINCIPAL_KEY}. Such a {@link java.security.Principal} <strong>MUST</strong> implement
+ * {@link java.io.Serializable}. This principal will be added to the {@link javax.security.auth.Subject} within the
+ * {@link edu.internet2.middleware.shibboleth.idp.session.Session}.</li>
+ * <li>Bind a principal name string to the request attribute identified by {@link #PRINCIPAL_NAME_KEY}. In this case
+ * the {@link AuthenticationEngine} will create a {@link java.security.Principal} object of type
+ * {@link edu.internet2.middleware.shibboleth.idp.authn.UsernamePrincipal} and add that to the
+ * {@link javax.security.auth.Subject} within the {@link edu.internet2.middleware.shibboleth.idp.session.Session}.</li>
+ * </ul>
*
- * The handler may also bind an error message, if an error occurred during authentication to the request attribute
- * identified by {@link LoginHandler#AUTHENTICATION_ERROR_KEY}.
+ * The handler <strong>MAY</strong> also:
+ * <ul>
+ * <li>Bind a URI string, representing the authentication method actually used, to a request attribute identified by
+ * {@link #AUTHENTICATION_METHOD_KEY}. This may be used if a handler is capable of performing multiple types of
+ * authentication.</li>
+ * <li>bind an error message, if an error occurred during authentication to the request attribute identified by
+ * {@link LoginHandler#AUTHENTICATION_ERROR_KEY}.</li>
+ * </ul>
*
* Finally, the handler must return control to the authentication engine by invoking
* {@link AuthenticationEngine#returnToAuthenticationEngine(HttpServletRequest, HttpServletResponse)}. After which the
*/
public interface LoginHandler {
+ /** Request attribute to which user's principal should be bound. */
+ public static final String PRINCIPAL_KEY = "principal";
+
/** Request attribute to which user's principal name should be bound. */
- public static final String PRINCIPAL_NAME_KEY = "principal";
+ public static final String PRINCIPAL_NAME_KEY = "principal_name";
/** Request attribute to which user's subject should be bound. */
public static final String SUBJECT_KEY = "subject";
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
public class Saml2LoginContext extends LoginContext implements Serializable {
/** Serial version UID. */
- private static final long serialVersionUID = -2518779446947534977L;
+ private static final long serialVersionUID = -7117092606828289070L;
- /** Class logger. */
- private final Logger log = LoggerFactory.getLogger(Saml2LoginContext.class);
-
/** Relay state from authentication request. */
private String relayState;
// For the immediate future, we only support the "exact" comparator.
AuthnContextComparisonTypeEnumeration comparator = authnContext.getComparison();
if (comparator != null && comparator != AuthnContextComparisonTypeEnumeration.EXACT) {
+ Logger log = LoggerFactory.getLogger(Saml2LoginContext.class);
log.error("Unsupported comparision operator ( " + comparator
+ ") in RequestedAuthnContext. Only exact comparisions are supported.");
return requestedMethods;
--- /dev/null
+/*
+ * Copyright 2008 University Corporation for Advanced Internet Development, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.authn;
+
+import java.security.Principal;
+
+import org.opensaml.xml.util.DatatypeHelper;
+
+/** A basic implementation of {@link Principal}. */
+public class UsernamePrincipal implements Principal {
+
+ /** Name of the principal. */
+ private String name;
+
+ /**
+ * Constructor.
+ *
+ * @param principalName name of the principal
+ */
+ public UsernamePrincipal(String principalName) {
+ name = DatatypeHelper.safeTrimOrNullString(principalName);
+ }
+
+ /** {@inheritDoc} */
+ public String getName() {
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "{BasicPrincipal}" + getName();
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj instanceof UsernamePrincipal) {
+ return DatatypeHelper.safeEquals(getName(), ((UsernamePrincipal) obj).getName());
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
ipList.add(new edu.internet2.middleware.shibboleth.idp.authn.provider.IPAddressLoginHandler.IPEntry(
addr));
} catch (UnknownHostException ex) {
- log.error("IPAddressHandler: Error parsing entry \"" + addr + "\". Ignoring.");
+ log.error("IPAddressHandler: Error parsing IP entry \"" + addr + "\". Ignoring.");
}
}
}
boolean ipAllowed = searchIpList(request);
if (ipAllowed) {
+ log.debug("Authenticated user by IP address");
request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
}
}
boolean ipDenied = searchIpList(request);
if (!ipDenied) {
+ log.debug("Authenticated user by IP address");
request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
}
}
}
} catch (UnknownHostException ex) {
- log.error("IPAddressHandler: Error resolving hostname.", ex);
+ log.error("Error resolving hostname.", ex);
return false;
}
int cidrOffset = entry.indexOf("/");
if (cidrOffset == -1) {
- log.error("IPAddressHandler: invalid entry \"" + entry + "\" -- it lacks a netmask component.");
+ log.error("Invalid entry \"" + entry + "\" -- it lacks a netmask component.");
throw new UnknownHostException("entry lacks a netmask component.");
}
// ensure that only one "/" is present.
if (entry.indexOf("/", cidrOffset + 1) != -1) {
- log.error("IPAddressHandler: invalid entry \"" + entry + "\" -- too many \"/\" present.");
+ log.error("Invalid entry \"" + entry + "\" -- too many \"/\" present.");
throw new UnknownHostException("entry has too many netmask components.");
}
// ensure that the netmask isn't too large
if ((tempAddr instanceof Inet4Address) && (masklen > 32)) {
- throw new UnknownHostException("IPAddressHandler: Netmask is too large for an IPv4 address: " + masklen);
+ throw new UnknownHostException("Netmask is too large for an IPv4 address: " + masklen);
} else if ((tempAddr instanceof Inet6Address) && masklen > 128) {
- throw new UnknownHostException("IPAddressHandler: Netmask is too large for an IPv6 address: " + masklen);
+ throw new UnknownHostException("Netmask is too large for an IPv6 address: " + masklen);
}
netmask = new BitSet(addrlen);
package edu.internet2.middleware.shibboleth.idp.authn.provider;
-import java.io.IOException;
-
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.opensaml.util.URLBuilder;
+import org.opensaml.saml2.core.AuthnContext;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationEngine;
+import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
-/**
- * Login handler that is called when user is logged in under a previously existing session.
- *
- * This login handler can optionally redirect the browser to a given URL. This provides a mechanism for extensions to
- * hook into the authentication process on every request. If this option is used and the servlet to which the browser is
- * redirected does not take visible control of the request be sure to indicate passive authentication support by means
- * of {@link PreviousSessionLoginHandler#setSupportsPassive(boolean)}.
- *
- * When the servlet has completed it's work it <strong>MUST</strong> call
- * {@link AuthenticationEngine#returnToAuthenticationEngine(HttpServletRequest, HttpServletResponse)} in order to
- * transfer control back to the authentication engine.
- */
+/** Login handler that is called when user is logged in under a previously existing session. */
public class PreviousSessionLoginHandler extends AbstractLoginHandler {
-
+
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(PreviousSessionLoginHandler.class);
public PreviousSessionLoginHandler() {
super();
servletPath = null;
+ setSupportsPassive(true);
setSupportsForceAuthentication(false);
}
* Get the path of the servlet to which the user agent may be redirected.
*
* @return path of the servlet to which the user agent may be redirected
+ *
+ * @deprecated
*/
public String getServletPath() {
return servletPath;
* Set the path of the servlet to which the user agent may be redirected.
*
* @param path path of the servlet to which the user agent may be redirected
+ *
+ * @deprecated
*/
public void setServletPath(String path) {
servletPath = DatatypeHelper.safeTrimOrNullString(path);
/** {@inheritDoc} */
public void login(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
- if (servletPath == null) {
- AuthenticationEngine.returnToAuthenticationEngine(httpRequest, httpResponse);
- } else {
- try {
- StringBuilder pathBuilder = new StringBuilder();
- pathBuilder.append(httpRequest.getContextPath());
- if (!servletPath.startsWith("/")) {
- pathBuilder.append("/");
- }
- pathBuilder.append(servletPath);
-
- URLBuilder urlBuilder = new URLBuilder();
- urlBuilder.setScheme(httpRequest.getScheme());
- urlBuilder.setHost(httpRequest.getServerName());
- urlBuilder.setPort(httpRequest.getServerPort());
- urlBuilder.setPath(pathBuilder.toString());
-
- log.debug("Redirecting to {}", urlBuilder.buildURL());
- httpResponse.sendRedirect(urlBuilder.buildURL());
- return;
- } catch (IOException ex) {
- log.error("Unable to redirect to previous session authentication servlet.", ex);
- }
+ if (reportPreviousSessionAuthnMethod) {
+ httpRequest.setAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY, AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
}
+
+ Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
+ if(idpSession != null){
+ log.error("No existing IdP session available.");
+ }
+ httpRequest.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, idpSession.getPrincipalName());
+
+ AuthenticationEngine.returnToAuthenticationEngine(httpRequest, httpResponse);
}
}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2008 University Corporation for Advanced Internet Development, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.authn.provider;
+
+/** Represents a username and password entered used to authenticate a subject. */
+public class UsernamePasswordCredential {
+
+ /** Username of a subject. */
+ private String username;
+
+ /** Password of a subject. */
+ private String password;
+
+ /**
+ * Constructor.
+ *
+ * @param name username of the subject
+ * @param pass password of the subject
+ */
+ public UsernamePasswordCredential(String name, String pass) {
+ username = name;
+ password = pass;
+ }
+
+ /**
+ * Gets the username of the subject.
+ *
+ * @return username of the subject
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * Gets the password of the subject.
+ *
+ * @return password of the subject
+ */
+ public String getPassword() {
+ return password;
+ }
+}
\ No newline at end of file
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/**
* Authenticate a username and password against a JAAS source.
*
- * This authenticaiton handler requires a JSP to collect a username and password from the user. It also requires a JAAS
- * configuration file to validate the username and password.
- *
- * If an Authentication Context Class or DeclRef URI is not specified, it will default to
- * "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport".
+ * This login handler creates a {@link javax.security.auth.Subject} and binds it to the request as described in the
+ * {@link edu.internet2.middleware.shibboleth.idp.authn.LoginHandler} documentation. If the JAAS module does not create
+ * a principal for the user a {@link edu.internet2.middleware.shibboleth.idp.authn.UsernamePrincipal} is created, using the
+ * entered username. If the <code>storeCredentialsInSubject</code> init parameter of the authentication servlet is set
+ * to true a {@link UsernamePasswordCredential} is created, based on the entered username and password, and stored in
+ * the Subject's private credentials.
*/
public class UsernamePasswordLoginHandler extends AbstractLoginHandler {
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 [University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import org.slf4j.LoggerFactory;
import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationEngine;
+import edu.internet2.middleware.shibboleth.idp.authn.UsernamePrincipal;
import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
/**
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(UsernamePasswordLoginServlet.class);
+ /** Whether to store a user's credentials within the {@link Subject}. */
+ private boolean storeCredentialsInSubject;
+
/** Name of JAAS configuration used to authenticate users. */
private String jaasConfigName = "ShibUserPassAuth";
if (queryParams == null) {
queryParams = new ArrayList<Pair<String, String>>();
}
-
- queryParams.add(new Pair<String, String>("actionUrl", request.getContextPath()
- + request.getServletPath()));
+
+ queryParams.add(new Pair<String, String>("actionUrl", request.getContextPath() + request.getServletPath()));
urlBuilder.getQueryParams().addAll(queryParams);
log.debug("Redirecting to login page {}", urlBuilder.buildURL());
* @return true of authentication succeeds, false if not
*/
protected boolean authenticateUser(HttpServletRequest request) {
-
try {
String username = DatatypeHelper.safeTrimOrNullString(request.getParameter(usernameAttribute));
String password = DatatypeHelper.safeTrimOrNullString(request.getParameter(passwordAttribute));
jaasLoginCtx.login();
log.debug("Successfully authenticated user {}", username);
- Subject subject = jaasLoginCtx.getSubject();
- Set<Principal> principals = subject.getPrincipals();
+ Subject loginSubject = jaasLoginCtx.getSubject();
+ Set<Principal> principals = loginSubject.getPrincipals();
if (principals.isEmpty()) {
- request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
- } else {
- Principal principal = principals.iterator().next();
- String principalName = DatatypeHelper.safeTrimOrNullString(principal.getName());
- if (principalName == null) {
- request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
- } else {
- request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, principal.getName());
- }
- request.setAttribute(LoginHandler.SUBJECT_KEY, jaasLoginCtx.getSubject());
+ principals.add(new UsernamePrincipal(username));
}
+ Set<Object> publicCredentials = loginSubject.getPublicCredentials();
+
+ Set<Object> privateCredentials = loginSubject.getPrivateCredentials();
+ if (storeCredentialsInSubject) {
+ privateCredentials.add(new UsernamePasswordCredential(username, password));
+ }
+
+ Subject userSubject = new Subject(false, principals, publicCredentials, privateCredentials);
+ request.setAttribute(LoginHandler.SUBJECT_KEY, userSubject);
+
return true;
} catch (LoginException e) {
log.debug("User authentication failed", e);
import javax.xml.namespace.QName;
-import org.opensaml.xml.util.DatatypeHelper;
import org.opensaml.xml.util.XMLHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.w3c.dom.Element;
/** Schema type. */
public static final QName SCHEMA_TYPE = new QName(ProfileHandlerNamespaceHandler.NAMESPACE, "PreviousSession");
+ /** Class logger. */
+ private final Logger log = LoggerFactory.getLogger(PreviousSessionLoginHandlerBeanDefinitionParser.class);
+
/** {@inheritDoc} */
protected Class getBeanClass(Element arg0) {
return PreviousSessionLoginHandlerFactoryBean.class;
protected void doParse(Element config, BeanDefinitionBuilder builder) {
super.doParse(config, builder);
- builder.addPropertyValue("servletPath", DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null,
- "servletPath")));
-
+ if (config.hasAttributeNS(null, "servletPath")) {
+ log.warn("The 'servletPath' configuration option has been deprecated and is no longer supported.");
+ }
+
if (config.hasAttributeNS(null, "supportsPassiveAuthentication")) {
- builder.addPropertyValue("supportsPassiveAuth", XMLHelper.getAttributeValueAsBoolean(config
- .getAttributeNodeNS(null, "supportsPassiveAuthentication")));
- } else {
- builder.addPropertyValue("supportsPassiveAuth", false);
+ log.warn("The 'supportsPassiveAuthentication' configuration option has been deprecated and is no longer supported.");
}
if (config.hasAttributeNS(null, "reportPreviousSessionAuthnMethod")) {
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package edu.internet2.middleware.shibboleth.idp.session;
+import java.security.Principal;
+
import javax.security.auth.Subject;
import org.joda.time.DateTime;
-/**
- * Information about an authentication method employed by a user.
- */
+/** Information about an authentication method employed by a user. */
public interface AuthenticationMethodInformation {
-
+
/**
* Gets the Subject created by this authentication method.
*
* @return subject created by this authentication method
+ *
+ * @deprecated use {@link Session#getSubject()}
*/
public Subject getAuthenticationSubject();
/**
+ * Gets the principal, for the {@link Subject} of the session, created by this authentication method.
+ *
+ * @return principal created by this authentication method
+ */
+ public Principal getAuthenticationPrincipal();
+
+ /**
* Gets the unique identifier for the authentication method.
*
* @return unique identifier for the authentication method
+++ /dev/null
-/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.idp.session;
-
-import javax.servlet.http.HttpSession;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpSessionListener;
-
-import edu.internet2.middleware.shibboleth.common.session.SessionManager;
-
-/**
- * A listener that listens for the destruction of {@link HttpSession}s. This allows the {@link SessionManager} to
- * appropriately destroy a Shibboleth session whether the HTTP session is destroyed explicitly or through inactivity.
- */
-public class ContainerSessionListener implements HttpSessionListener {
-
- /** {@inheritDoc} */
- public void sessionCreated(HttpSessionEvent httpSessionEvent) {
- // we don't care about session creations
- }
-
- /** {@inheritDoc} */
- public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
- HttpSession httpSession = httpSessionEvent.getSession();
- SessionManager<Session> sessionManager = (SessionManager<Session>) httpSession.getServletContext()
- .getAttribute("sessionManager");
-
- sessionManager.destroySession((String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE));
- }
-}
\ No newline at end of file
log.trace("Updating IdP session activity time and adding session object to the request");
idpSession.setLastActivityInstant(new DateTime());
MDC.put("idpSessionId", idpSession.getSessionID());
- MDC.put("principalName", idpSession.getPrincipalName());
httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, idpSession);
}
}
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import org.joda.time.DateTime;
-/**
- * Information about a service a user has logged in to.
- */
+/** Information about a service a user has logged in to. */
public interface ServiceInformation {
/**
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package edu.internet2.middleware.shibboleth.idp.session.impl;
+import java.security.Principal;
+
import javax.security.auth.Subject;
import org.joda.time.DateTime;
/** Subject created by this authentication mechanism. */
private Subject authenticationSubject;
-
+
+ /** Principal created by the authentication method. */
+ private Principal authenticationPrincipal;
+
/** The authentication method (a URI). */
private String authenticationMethod;
private DateTime expirationInstant;
/**
- * Default constructor.
- *
- * @param method The unique identifier for the authentication method.
- * @param instant The time the user authenticated with this member.
- * @param duration The duration of this authentication method.
- */
- public AuthenticationMethodInformationImpl(String method, DateTime instant, long duration) {
-
- if (method == null || instant == null || duration < 0) {
- throw new IllegalArgumentException("Authentication method, instant, and duration may not be null");
- }
-
- authenticationMethod = method;
- authenticationInstant = instant;
- authenticationDuration = duration;
- expirationInstant = instant.plus(duration);
- }
-
- /**
- * Default constructor.
+ * Default constructor. This constructor does NOT add the given principal to the given subject.
*
- * @param subject Subject created by the authentication method
- * @param method The unique identifier for the authentication method.
- * @param instant The time the user authenticated with this member.
- * @param duration The duration of this authentication method.
+ * @param subject subject associated with the user's session
+ * @param principal principal created by the authentication method
+ * @param method The unique identifier for the authentication method
+ * @param instant The time the user authenticated with this member
+ * @param duration The duration of this authentication method
*/
- public AuthenticationMethodInformationImpl(Subject subject, String method, DateTime instant, long duration) {
+ public AuthenticationMethodInformationImpl(Subject subject, Principal principal, String method, DateTime instant,
+ long duration) {
if (method == null || instant == null || duration < 0) {
throw new IllegalArgumentException("Authentication method, instant, and duration may not be null");
}
authenticationSubject = subject;
+ authenticationPrincipal = principal;
authenticationMethod = method;
authenticationInstant = instant;
authenticationDuration = duration;
expirationInstant = instant.plus(duration);
}
-
+
/** {@inheritDoc} */
public Subject getAuthenticationSubject() {
return authenticationSubject;
}
/** {@inheritDoc} */
+ public Principal getAuthenticationPrincipal() {
+ return authenticationPrincipal;
+ }
+
+ /** {@inheritDoc} */
public String getAuthenticationMethod() {
return authenticationMethod;
}
public boolean isExpired() {
return expirationInstant.isBeforeNow();
}
-
+
/** {@inheritDoc} */
public int hashCode() {
return authenticationMethod.hashCode();
/** {@inheritDoc} */
public boolean equals(Object obj) {
- if(obj == this){
+ if (obj == this) {
return true;
}
-
+
if (!(obj instanceof AuthenticationMethodInformation)) {
return false;
}
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
-/**
- * Information about a service a user has logged in to.
- */
+/** Information about a service a user has logged in to. */
public class ServiceInformationImpl implements ServiceInformation {
/** Entity ID of the service. */
/** {@inheritDoc} */
public boolean equals(Object obj) {
- if(obj == this){
+ if (obj == this) {
return true;
}
-
+
if (!(obj instanceof ServiceInformation)) {
return false;
}
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
import edu.internet2.middleware.shibboleth.idp.session.Session;
-/**
- * Session information for user logged into the IdP.
- */
+/** Session information for user logged into the IdP. */
public class SessionImpl extends AbstractSession implements Session {
/** Serial version UID. */
private HashMap<String, ServiceInformation> servicesInformation;
/**
- * Default constructor.
+ * Constructor.
*
* @param sessionId ID of the session
- * @param principal principal ID of the user
* @param timeout inactivity timeout for the session in milliseconds
*/
- public SessionImpl(String sessionId, String principal, long timeout) {
- super(sessionId, principal, timeout);
+ public SessionImpl(String sessionId, long timeout) {
+ super(sessionId, timeout);
authnMethods = new HashMap<String, AuthenticationMethodInformation>();
servicesInformation = new HashMap<String, ServiceInformation>();
public Map<String, ServiceInformation> getServicesInformation() {
return servicesInformation;
}
-
+
/**
* Gets the service information for the given entity ID.
*
/*
- * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2007 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package edu.internet2.middleware.shibboleth.idp.session.impl;
import java.security.SecureRandom;
+import java.util.List;
+import java.util.Vector;
import org.apache.commons.ssl.util.Hex;
import org.joda.time.DateTime;
import edu.internet2.middleware.shibboleth.common.session.SessionManager;
import edu.internet2.middleware.shibboleth.idp.session.Session;
-/**
- * Manager of IdP sessions.
- */
+/** Manager of IdP sessions. */
public class SessionManagerImpl implements SessionManager<Session>, ApplicationContextAware {
/** Spring context used to publish login and logout events. */
}
/** {@inheritDoc} */
- public void setApplicationContext(ApplicationContext applicationContext) {
- appCtx = applicationContext;
+ public Session createSession() {
+ // generate a random session ID
+ byte[] sid = new byte[sessionIDSize];
+ prng.nextBytes(sid);
+ String sessionID = Hex.encode(sid);
+
+ Session session = new SessionImpl(sessionID, sessionLifetime);
+ SessionManagerEntry sessionEntry = new SessionManagerEntry(this, session, sessionLifetime);
+ sessionStore.put(partition, sessionID, sessionEntry);
+
+ MDC.put("idpSessionId", sessionID);
+
+ appCtx.publishEvent(new LoginEvent(session));
+ return session;
}
/** {@inheritDoc} */
String sessionID = Hex.encode(sid);
MDC.put("idpSessionId", sessionID);
- MDC.put("principalName", principal);
-
- Session session = new SessionImpl(sessionID, principal, sessionLifetime);
+
+ Session session = new SessionImpl(sessionID, sessionLifetime);
SessionManagerEntry sessionEntry = new SessionManagerEntry(this, session, sessionLifetime);
sessionStore.put(partition, sessionID, sessionEntry);
- sessionStore.put(partition, principal, sessionEntry);
appCtx.publishEvent(new LoginEvent(session));
return session;
}
}
/** {@inheritDoc} */
- public Session getSessionByPrincipalName(String name) {
+ public boolean indexSession(Session session, String index) {
+ if (sessionStore.contains(partition, index)) {
+ return false;
+ }
+
+ SessionManagerEntry sessionEntry = sessionStore.get(partition, session.getSessionID());
+ if (sessionEntry == null) {
+ return false;
+ }
+
+ if (sessionEntry.getSessionIndexes().contains(index)) {
+ return true;
+ }
+
+ sessionEntry.getSessionIndexes().add(index);
+ sessionStore.put(partition, index, sessionEntry);
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public void removeSessionIndex(String index) {
+ SessionManagerEntry sessionEntry = sessionStore.remove(partition, index);
+ if (sessionEntry != null) {
+ sessionEntry.getSessionIndexes().remove(index);
+ }
+ }
- // TODO
- return null;
+ /** {@inheritDoc} */
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ appCtx = applicationContext;
}
/**
/** User's session. */
private Session userSession;
+ /** Indexes for this session. */
+ private List<String> indexes;
+
/** Manager that owns the session. */
private SessionManager<Session> sessionManager;
sessionManager = manager;
userSession = session;
expirationTime = new DateTime().plus(lifetime);
+ indexes = new Vector<String>();
+ indexes.add(userSession.getSessionID());
+ }
+
+ /** {@inheritDoc} */
+ public DateTime getExpirationTime() {
+ return expirationTime;
}
/**
return userSession.getSessionID();
}
- /** {@inheritDoc} */
- public DateTime getExpirationTime() {
- return expirationTime;
+ /**
+ * Gets the list of indexes for this session.
+ *
+ * @return list of indexes for this session
+ */
+ public List<String> getSessionIndexes() {
+ return indexes;
}
/** {@inheritDoc} */
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
-
- <listener>
- <listener-class>edu.internet2.middleware.shibboleth.idp.session.ContainerSessionListener</listener-class>
- </listener>
<!-- Add IdP Session object to incoming profile requests -->
<filter>