package edu.internet2.middleware.shibboleth.idp.authn;
import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
import javax.security.auth.Subject;
import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
-import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.opensaml.xml.util.DatatypeHelper;
-import org.opensaml.xml.util.Pair;
+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;
*/
public class AuthenticationEngine extends HttpServlet {
+ /** Name of the IdP Cookie containing the IdP session ID. */
+ public static final String IDP_SESSION_COOKIE_NAME = "_idp_session";
+
/** Serial version UID. */
private static final long serialVersionUID = 8494202791991613148L;
/** Class logger. */
- private static final Logger LOG = Logger.getLogger(AuthenticationEngine.class);
+ private static final Logger LOG = LoggerFactory.getLogger(AuthenticationEngine.class);
- /**
- * Gets the manager used to retrieve handlers for requests.
- *
- * @return manager used to retrieve handlers for requests
- */
- public IdPProfileHandlerManager getProfileHandlerManager() {
- return (IdPProfileHandlerManager) getServletContext().getAttribute("handlerManager");
- }
+ /** Profile handler manager. */
+ private IdPProfileHandlerManager handlerManager;
- /**
- * Gets the session manager to be used.
- *
- * @return session manager to be used
- */
- @SuppressWarnings("unchecked")
- public SessionManager<Session> getSessionManager() {
- return (SessionManager<Session>) getServletContext().getAttribute("sessionManager");
+ /** Session manager. */
+ private SessionManager<Session> sessionManager;
+
+ /** {@inheritDoc} */
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+
+ String handlerManagerId = config.getInitParameter("handlerManagerId");
+ if (DatatypeHelper.isEmpty(handlerManagerId)) {
+ handlerManagerId = "shibboleth.HandlerManager";
+ }
+ handlerManager = (IdPProfileHandlerManager) getServletContext().getAttribute(handlerManagerId);
+
+ String sessionManagerId = config.getInitParameter("sessionManagedId");
+ if (DatatypeHelper.isEmpty(sessionManagerId)) {
+ sessionManagerId = "shibboleth.SessionManager";
+ }
+
+ sessionManager = (SessionManager<Session>) getServletContext().getAttribute(sessionManagerId);
}
/**
*
* @param httpRequest current http request
* @param httpResponse current http response
- *
- * @throws ServletException thrown if unable to return to authentication engine
*/
- public static void returnToAuthenticationEngine(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
- throws ServletException {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Returning control to authentication engine");
- }
+ 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);
if (loginContext == null) {
LOG.error("User HttpSession did not contain a login context. Unable to return to authentication engine");
- throw new ServletException(
- "User HttpSession did not contain a login context. Unable to return to authentication engine");
+ forwardRequest("/idp-error.jsp", httpRequest, httpResponse);
+ } else {
+ forwardRequest(loginContext.getAuthenticationEngineURL(), httpRequest, httpResponse);
}
- forwardRequest(loginContext.getAuthenticationEngineURL(), httpRequest, httpResponse);
}
/**
*/
public static void returnToProfileHandler(LoginContext loginContext, HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Returning control to profile handler at: " + loginContext.getProfileHandlerURL());
- }
+ 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);
}
dispatcher.forward(httpRequest, httpResponse);
return;
} catch (IOException e) {
- LOG.fatal("Unable to return control back to authentication engine", e);
+ LOG.error("Unable to return control back to authentication engine", e);
} catch (ServletException e) {
- LOG.fatal("Unable to return control back to authentication engine", e);
+ LOG.error("Unable to return control back to authentication engine", e);
}
}
@SuppressWarnings("unchecked")
protected void service(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException,
IOException {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Processing incoming request");
- }
+ LOG.debug("Processing incoming request");
if (httpResponse.isCommitted()) {
LOG.error("HTTP Response already committed");
}
- HttpSession httpSession = httpRequest.getSession();
- LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+ 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);
+ }
+
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 (!loginContext.getAuthenticationAttempted()) {
- String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
- Session shibSession = getSessionManager().getSession(shibSessionId);
-
- if (shibSession != null) {
- AuthenticationMethodInformation authenticationMethod = getUsableExistingAuthenticationMethod(
- loginContext, shibSession);
- if (authenticationMethod != null) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("An active authentication method is applicable for relying party. "
- + "Using authentication method " + authenticationMethod.getAuthenticationMethod()
- + " as authentication method to relying party without re-authenticating user.");
- }
- authenticateUserWithActiveMethod(httpRequest, httpResponse, authenticationMethod);
- }
- }
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("No active authentication method is applicable for relying party. "
- + "Authenticating user with to be determined method.");
- }
- authenticateUserWithoutActiveMethod1(httpRequest, httpResponse);
+ startUserAuthentication(loginContext, httpRequest, httpResponse);
} else {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Request returned from authentication handler, completing authentication process.");
- }
- authenticateUserWithoutActiveMethod2(httpRequest, httpResponse);
+ completeAuthentication(loginContext, httpRequest, httpResponse);
}
-
- return;
}
/**
- * Completes the authentication request using an existing, active, authentication method for the current user.
+ * Begins the authentication process. Determines if forced re-authentication is required or if an existing, active,
+ * authentication method is sufficient. Also determines, when authentication is required, which handler to use
+ * depending on whether passive authentication is required.
*
+ * @param loginContext current login context
* @param httpRequest current HTTP request
* @param httpResponse current HTTP response
- * @param authenticationMethod authentication method to use to complete the request
*/
- protected void authenticateUserWithActiveMethod(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
- AuthenticationMethodInformation authenticationMethod) {
- HttpSession httpSession = httpRequest.getSession();
+ protected void startUserAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
+ HttpServletResponse httpResponse) {
+ LOG.debug("Beginning user authentication process");
+ try {
+ Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
+ if(idpSession != null){
+ LOG.debug("Existing IdP session available for principal {}", idpSession.getPrincipalName());
+ }
+
+ Map<String, LoginHandler> possibleLoginHandlers = determinePossibleLoginHandlers(loginContext);
+ LOG.debug("Possible authentication handlers for this request: {}", possibleLoginHandlers);
- String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
- Session shibSession = getSessionManager().getSession(shibSessionId);
+ // Filter out possible candidate login handlers by forced and passive authentication requirements
+ if (loginContext.isForceAuthRequired()) {
+ filterByForceAuthentication(idpSession, loginContext, possibleLoginHandlers);
+ }
- if (LOG.isDebugEnabled()) {
- LOG.debug("Populating login context with existing session and authentication method information.");
+ if (loginContext.isPassiveAuthRequired()) {
+ filterByPassiveAuthentication(loginContext, possibleLoginHandlers);
+ }
+
+ // 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);
+ if (idpSession != null
+ && possibleLoginHandlers.containsKey(PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD)) {
+ authenticateUserWithPreviousSession(loginContext, possibleLoginHandlers, httpRequest, httpResponse);
+ } else {
+ Entry<String, LoginHandler> chosenLoginHandler = possibleLoginHandlers.entrySet().iterator().next();
+ authenticateUser(chosenLoginHandler.getKey(), chosenLoginHandler.getValue(), loginContext, httpRequest,
+ httpResponse);
+ }
+ } catch (AuthenticationException e) {
+ loginContext.setAuthenticationFailure(e);
+ returnToProfileHandler(loginContext, httpRequest, httpResponse);
}
- LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
- loginContext.setAuthenticationDuration(authenticationMethod.getAuthenticationDuration());
- loginContext.setAuthenticationInstant(authenticationMethod.getAuthenticationInstant());
- loginContext.setAuthenticationMethod(authenticationMethod.getAuthenticationMethod());
- loginContext.setPrincipalAuthenticated(true);
- loginContext.setPrincipalName(shibSession.getPrincipalName());
+ }
- ServiceInformation serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(),
- authenticationMethod);
- shibSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
+ /**
+ * Determines which configured login handlers will support the requested authentication methods.
+ *
+ * @param loginContext current login context
+ *
+ * @return login methods that may be used to authenticate the user
+ *
+ * @throws AuthenticationException thrown if no login handler meets the given requirements
+ */
+ protected Map<String, LoginHandler> determinePossibleLoginHandlers(LoginContext loginContext)
+ throws AuthenticationException {
+ Map<String, LoginHandler> supportedLoginHandlers = new HashMap<String, LoginHandler>(handlerManager
+ .getLoginHandlers());
+ LOG.trace("Supported login handlers: {}", supportedLoginHandlers);
+ LOG.trace("Requested authentication methods: {}", loginContext.getRequestedAuthenticationMethods());
+
+ Iterator<Entry<String, LoginHandler>> supportedLoginHandlerItr = supportedLoginHandlers.entrySet().iterator();
+ Entry<String, LoginHandler> supportedLoginHandler;
+ while (supportedLoginHandlerItr.hasNext()) {
+ supportedLoginHandler = supportedLoginHandlerItr.next();
+ if (!(supportedLoginHandler.getKey().equals(PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD))
+ && !loginContext.getRequestedAuthenticationMethods().contains(supportedLoginHandler.getKey())) {
+ supportedLoginHandlerItr.remove();
+ continue;
+ }
+ }
- returnToProfileHandler(loginContext, httpRequest, httpResponse);
+ if (supportedLoginHandlers.isEmpty()) {
+ LOG.error("No authentication method, requested by the service provider, is supported");
+ throw new AuthenticationException(
+ "No authentication method, requested by the service provider, is supported");
+ }
+
+ return supportedLoginHandlers;
}
/**
- * Performs the first part of user authentication. An authentication handler is determined, the login context is
- * populated with some initial information, and control is forward to the selected handler so that it may
- * authenticate the user.
+ * Filters out any login handler based on the requirement for forced authentication.
*
- * @param httpRequest current HTTP request
- * @param httpResponse current HTTP response
+ * During forced authentication any handler that has not previously been used to authenticate the user or any
+ * handlers that have been and support force re-authentication may be used. Filter out any of the other ones.
+ *
+ * @param idpSession user's current IdP session
+ * @param loginContext current login context
+ * @param loginHandlers login handlers to filter
+ *
+ * @throws ForceAuthenticationException thrown if no handlers remain after filtering
*/
- protected void authenticateUserWithoutActiveMethod1(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
- HttpSession httpSession = httpRequest.getSession();
- LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+ protected void filterByForceAuthentication(Session idpSession, LoginContext loginContext,
+ Map<String, LoginHandler> loginHandlers) throws ForceAuthenticationException {
+ LOG.debug("Forced authentication is required, filtering possible login handlers accordingly");
- if (LOG.isDebugEnabled()) {
- LOG.debug("Selecting appropriate authentication method for request.");
+ ArrayList<AuthenticationMethodInformation> activeMethods = new ArrayList<AuthenticationMethodInformation>();
+ if (idpSession != null) {
+ activeMethods.addAll(idpSession.getAuthenticationMethods().values());
}
- Pair<String, LoginHandler> handler = getProfileHandlerManager().getAuthenticationHandler(loginContext);
-
- if (handler == null) {
- loginContext.setPrincipalAuthenticated(false);
- loginContext.setAuthenticationFailureMessage("No AuthenticationHandler satisfys the request from: "
- + loginContext.getRelyingPartyId());
- LOG.error("No AuthenticationHandler satisfies the request from relying party: "
- + loginContext.getRelyingPartyId());
- returnToProfileHandler(loginContext, httpRequest, httpResponse);
- return;
+
+ LoginHandler loginHandler;
+ for (AuthenticationMethodInformation activeMethod : activeMethods) {
+ loginHandler = loginHandlers.get(activeMethod.getAuthenticationMethod());
+ if (loginHandler != null && !loginHandler.supportsForceAuthentication()) {
+ for (String handlerSupportedMethods : loginHandler.getSupportedAuthenticationMethods()) {
+ loginHandlers.remove(handlerSupportedMethods);
+ }
+ }
}
+
+ LOG.debug("Authentication handlers remaining after forced authentication requirement filtering: {}",
+ loginHandlers);
- if (LOG.isDebugEnabled()) {
- LOG.debug("Authentication method " + handler.getFirst() + " will be used to authenticate user.");
+ if (loginHandlers.isEmpty()) {
+ LOG.error("Force authentication required but no login handlers available to support it");
+ throw new ForceAuthenticationException();
}
- loginContext.setAuthenticationAttempted();
- loginContext.setAuthenticationDuration(handler.getSecond().getAuthenticationDuration());
- loginContext.setAuthenticationMethod(handler.getFirst());
- loginContext.setAuthenticationEngineURL(HttpHelper.getRequestUriWithoutContext(httpRequest));
+ }
+
+ /**
+ * Filters out any login handler that doesn't support passive authentication if the login context indicates passive
+ * authentication is required.
+ *
+ * @param loginContext current login context
+ * @param loginHandlers login handlers to filter
+ *
+ * @throws PassiveAuthenticationException thrown if no handlers remain after filtering
+ */
+ protected void filterByPassiveAuthentication(LoginContext loginContext, Map<String, LoginHandler> loginHandlers)
+ throws PassiveAuthenticationException {
+ LOG.debug("Passive authentication is required, filtering poassible login handlers accordingly.");
+
+ LoginHandler loginHandler;
+ Iterator<Entry<String, LoginHandler>> authnMethodItr = loginHandlers.entrySet().iterator();
+ while (authnMethodItr.hasNext()) {
+ loginHandler = authnMethodItr.next().getValue();
+ if (!loginHandler.supportsPassive()) {
+ authnMethodItr.remove();
+ }
+ }
+
+ LOG.debug("Authentication handlers remaining after passive authentication requirement filtering: {}",
+ loginHandlers);
- if (LOG.isDebugEnabled()) {
- LOG.debug("Transferring control to authentication handler of type: "
- + handler.getSecond().getClass().getName());
+ if (loginHandlers.isEmpty()) {
+ LOG.error("Passive authentication required but no login handlers available to support it");
+ throw new PassiveAuthenticationException();
}
- handler.getSecond().login(httpRequest, httpResponse);
}
/**
- * Performs the second part of user authentication. The principal name set by the authentication handler is
- * retrieved and pushed in to the login context, a Shibboleth session is created if needed, information indicating
- * that the user has logged into the service is recorded and finally control is returned back to the profile
- * handler.
+ * Completes the authentication request using an existing, active, authentication method for the current user.
*
+ * @param loginContext current login context
+ * @param possibleLoginHandlers login handlers that meet the peers authentication requirements
* @param httpRequest current HTTP request
* @param httpResponse current HTTP response
*/
- protected void authenticateUserWithoutActiveMethod2(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
- HttpSession httpSession = httpRequest.getSession();
+ protected void authenticateUserWithPreviousSession(LoginContext loginContext,
+ Map<String, LoginHandler> possibleLoginHandlers, HttpServletRequest httpRequest,
+ HttpServletResponse httpResponse) {
+ LOG.debug("Authenticating user by way of existing session.");
- String principalName = (String) httpRequest.getAttribute(LoginHandler.PRINCIPAL_NAME_KEY);
- LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
- if (DatatypeHelper.isEmpty(principalName)) {
- loginContext.setPrincipalAuthenticated(false);
- loginContext.setAuthenticationFailureMessage("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);
+ PreviousSessionLoginHandler loginHandler = (PreviousSessionLoginHandler) handlerManager.getLoginHandlers().get(
+ PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD);
+
+ AuthenticationMethodInformation authenticationMethod = null;
+ for (String possibleAuthnMethod : possibleLoginHandlers.keySet()) {
+ authenticationMethod = idpSession.getAuthenticationMethods().get(possibleAuthnMethod);
+ if (authenticationMethod != null) {
+ break;
+ }
}
- loginContext.setPrincipalName(principalName);
- loginContext.setAuthenticationInstant(new DateTime());
- String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
- Session shibSession = getSessionManager().getSession(shibSessionId);
+ if (loginHandler.reportPreviousSessionAuthnMethod()) {
+ loginContext.setAuthenticationDuration(loginHandler.getAuthenticationDuration());
+ loginContext.setAuthenticationInstant(new DateTime());
+ loginContext.setAuthenticationMethod(PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD);
+ } else {
+ loginContext.setAuthenticationDuration(authenticationMethod.getAuthenticationDuration());
+ loginContext.setAuthenticationInstant(authenticationMethod.getAuthenticationInstant());
+ loginContext.setAuthenticationMethod(authenticationMethod.getAuthenticationMethod());
+ }
+ loginContext.setPrincipalName(idpSession.getPrincipalName());
- if (shibSession == null) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Creating shibboleth session for principal " + principalName);
- }
+ 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);
+ }
- InetAddress addr;
- try {
- addr = InetAddress.getByName(httpRequest.getRemoteAddr());
- } catch (UnknownHostException ex) {
- addr = null;
+ /**
+ * Completes the authentication process.
+ *
+ * The principal name set by the authentication handler is retrieved and pushed in to the login context, a
+ * Shibboleth session is created if needed, information indicating that the user has logged into the service is
+ * recorded and finally control is returned back to the profile handler.
+ *
+ * @param loginContext current login context
+ * @param httpRequest current HTTP request
+ * @param httpResponse current HTTP response
+ */
+ protected void completeAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
+ 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;
}
+ }
+ loginContext.setPrincipalAuthenticated(true);
- shibSession = (Session) getSessionManager().createSession(addr, loginContext.getPrincipalName());
- loginContext.setSessionID(shibSession.getSessionID());
- httpSession.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, shibSession.getSessionID());
+ // 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);
}
- if (LOG.isDebugEnabled()) {
- LOG.debug("Recording authentication and service information in Shibboleth session for principal: "
- + principalName);
+ LOG.debug("User {} authenticated with method {}", loginContext.getPrincipalName(), loginContext
+ .getAuthenticationMethod());
+ updateUserSession(loginContext, httpRequest, httpResponse);
+ returnToProfileHandler(loginContext, httpRequest, httpResponse);
+ }
+
+ /**
+ * 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 httpRequest current HTTP request
+ * @param httpResponse current HTTP response
+ */
+ protected void updateUserSession(LoginContext loginContext, HttpServletRequest httpRequest,
+ HttpServletResponse httpResponse) {
+ 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());
+ 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);
- AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(subject, loginContext
- .getAuthenticationMethod(), new DateTime(), loginContext.getAuthenticationDuration());
+ String authnMethod = (String) httpRequest.getAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY);
+ if (DatatypeHelper.isEmpty(authnMethod)) {
+ authnMethod = loginContext.getAuthenticationMethod();
+ }
+
+ AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(subject, authnMethod,
+ loginContext.getAuthenticationInstant(), loginContext.getAuthenticationDuration());
- shibSession.getAuthenticationMethods().put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
+ idpSession.getAuthenticationMethods().put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
ServiceInformation serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(),
authnMethodInfo);
- shibSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
-
- shibSession.setLastActivityInstant(new DateTime());
-
- returnToProfileHandler(loginContext, httpRequest, httpResponse);
+ idpSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
}
/**
- * Gets the authentication method, currently active for the user, that also meets the requirements expressed by the
- * login context. If a method is returned the user does not need to authenticate again, if null is returned then the
- * user must be authenticated.
- *
- * @param loginContext user login context
- * @param shibSession user's shibboleth session
+ * Adds an IdP session cookie to the outbound response.
*
- * @return active authentication method that meets authentication requirements or null
+ * @param httpRequest current request
+ * @param httpResponse current response
+ * @param userSession user's session
*/
- protected AuthenticationMethodInformation getUsableExistingAuthenticationMethod(LoginContext loginContext,
- Session shibSession) {
- if (loginContext.getForceAuth() || shibSession == null) {
- return null;
- }
-
- List<String> preferredAuthnMethods = loginContext.getRequestedAuthenticationMethods();
+ protected void addSessionCookie(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
+ Session userSession) {
+ httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, userSession);
- if (preferredAuthnMethods == null || preferredAuthnMethods.size() == 0) {
- for (AuthenticationMethodInformation authnMethod : shibSession.getAuthenticationMethods().values()) {
- if (!authnMethod.isExpired()) {
- return authnMethod;
- }
- }
- } else {
- for (String preferredAuthnMethod : preferredAuthnMethods) {
- if (shibSession.getAuthenticationMethods().containsKey(preferredAuthnMethod)) {
- AuthenticationMethodInformation authnMethodInfo = shibSession.getAuthenticationMethods().get(
- preferredAuthnMethod);
- if (!authnMethodInfo.isExpired()) {
- return authnMethodInfo;
- }
- }
- }
- }
+ LOG.debug("Adding IdP session cookie to HTTP response");
+ Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, userSession.getSessionID());
+ sessionCookie.setPath(httpRequest.getContextPath());
+ sessionCookie.setSecure(false);
+ sessionCookie.setMaxAge(-1);
- return null;
+ httpResponse.addCookie(sessionCookie);
}
}
\ No newline at end of file