import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
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 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;
/** Class logger. */
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);
}
/**
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");
+ 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) {
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");
}
- 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()) {
startUserAuthentication(loginContext, httpRequest, httpResponse);
} else {
- completeAuthenticationWithoutActiveMethod(loginContext, httpRequest, httpResponse);
+ completeAuthentication(loginContext, httpRequest, httpResponse);
}
}
HttpServletResponse httpResponse) {
LOG.debug("Beginning user authentication process");
try {
- Map<String, LoginHandler> possibleLoginHandlers = determinePossibleLoginHandlers(loginContext);
- ArrayList<AuthenticationMethodInformation> activeAuthnMethods = new ArrayList<AuthenticationMethodInformation>();
-
- Session userSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
- if (userSession != null) {
- activeAuthnMethods.addAll(userSession.getAuthenticationMethods().values());
+ 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);
+ // Filter out possible candidate login handlers by forced and passive authentication requirements
if (loginContext.isForceAuthRequired()) {
- LOG.debug("Forced authentication is required, filtering possible login handlers accordingly");
- filterByForceAuthentication(loginContext, activeAuthnMethods, possibleLoginHandlers);
- } else {
- if (activeAuthnMethods != null) {
- LOG.debug("Forced authentication not required, using existing authentication method");
- for (AuthenticationMethodInformation activeAuthnMethod : activeAuthnMethods) {
- if (possibleLoginHandlers.containsKey(activeAuthnMethod.getAuthenticationMethod())) {
- completeAuthenticationWithActiveMethod(activeAuthnMethod, httpRequest, httpResponse);
- return;
- }
- }
- }
+ filterByForceAuthentication(idpSession, loginContext, possibleLoginHandlers);
}
if (loginContext.isPassiveAuthRequired()) {
- LOG.debug("Passive authentication is required, filtering poassibl login handlers accordingly.");
filterByPassiveAuthentication(loginContext, possibleLoginHandlers);
}
- // Since we made it this far, just pick the first remaining login handler from the list
- Entry<String, LoginHandler> chosenLoginHandler = possibleLoginHandlers.entrySet().iterator().next();
- LOG.debug("Authenticating user with login handler of type {}", chosenLoginHandler.getValue().getClass()
- .getName());
- authenticateUser(chosenLoginHandler.getKey(), chosenLoginHandler.getValue(), loginContext, httpRequest,
- httpResponse);
+ // If the user already has a session and its usage is acceptable than use it
+ // otherwise just use the first candidate login handler
+ 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);
}
-
}
/**
*/
protected Map<String, LoginHandler> determinePossibleLoginHandlers(LoginContext loginContext)
throws AuthenticationException {
- Map<String, LoginHandler> supportedLoginHandlers = new HashMap<String, LoginHandler>(getProfileHandlerManager()
+ Map<String, LoginHandler> supportedLoginHandlers = new HashMap<String, LoginHandler>(handlerManager
.getLoginHandlers());
LOG.trace("Supported login handlers: {}", supportedLoginHandlers);
LOG.trace("Requested authentication methods: {}", loginContext.getRequestedAuthenticationMethods());
Entry<String, LoginHandler> supportedLoginHandler;
while (supportedLoginHandlerItr.hasNext()) {
supportedLoginHandler = supportedLoginHandlerItr.next();
- if (!loginContext.getRequestedAuthenticationMethods().contains(supportedLoginHandler.getKey())) {
+ if (supportedLoginHandler.getKey().equals(PreviousSessionLoginHandler.PREVIOUS_SESSION_AUTHN_METHOD)
+ || !loginContext.getRequestedAuthenticationMethods().contains(supportedLoginHandler.getKey())) {
supportedLoginHandlerItr.remove();
continue;
}
/**
* Filters out any login handler based on the requirement for forced authentication.
*
- * During forced authentication any handler that has not previously been used to authenticate the the user or any
+ * 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 activeAuthnMethods currently active authentication methods
* @param loginHandlers login handlers to filter
*
* @throws ForceAuthenticationException thrown if no handlers remain after filtering
*/
- protected void filterByForceAuthentication(LoginContext loginContext,
- Collection<AuthenticationMethodInformation> activeAuthnMethods, Map<String, LoginHandler> loginHandlers)
- throws ForceAuthenticationException {
+ protected void filterByForceAuthentication(Session idpSession, LoginContext loginContext,
+ Map<String, LoginHandler> loginHandlers) throws ForceAuthenticationException {
+ LOG.debug("Forced authentication is required, filtering possible login handlers accordingly");
- LoginHandler loginHandler;
+ ArrayList<AuthenticationMethodInformation> activeMethods = new ArrayList<AuthenticationMethodInformation>();
+ if (idpSession != null) {
+ activeMethods.addAll(idpSession.getAuthenticationMethods().values());
+ }
- if (activeAuthnMethods != null) {
- for (AuthenticationMethodInformation activeAuthnMethod : activeAuthnMethods) {
- loginHandler = loginHandlers.get(activeAuthnMethod.getAuthenticationMethod());
- if (loginHandler != null && !loginHandler.supportsForceAuthentication()) {
- for (String handlerSupportedMethods : loginHandler.getSupportedAuthenticationMethods()) {
- loginHandlers.remove(handlerSupportedMethods);
- }
+ 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 (loginHandlers.isEmpty()) {
LOG.error("Force authentication required but no login handlers available to support it");
*/
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()) {
authnMethodItr.remove();
}
}
+
+ LOG.debug("Authentication handlers remaining after passive authentication requirement filtering: {}",
+ loginHandlers);
if (loginHandlers.isEmpty()) {
LOG.error("Passive authentication required but no login handlers available to support it");
}
/**
- * Authenticates the user with the given authentication method provided by the given login handler.
+ * Completes the authentication request using an existing, active, authentication method for the current user.
*
- * @param authnMethod the authentication method that will be used to authenticate the user
- * @param logingHandler login handler that will authenticate 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 authenticateUser(String authnMethod, LoginHandler logingHandler, LoginContext loginContext,
- HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
+ protected void authenticateUserWithPreviousSession(LoginContext loginContext,
+ Map<String, LoginHandler> possibleLoginHandlers, HttpServletRequest httpRequest,
+ HttpServletResponse httpResponse) {
+ LOG.debug("Authenticating user by way of existing session.");
- loginContext.setAuthenticationAttempted();
- loginContext.setAuthenticationDuration(logingHandler.getAuthenticationDuration());
- loginContext.setAuthenticationMethod(authnMethod);
- loginContext.setAuthenticationEngineURL(HttpHelper.getRequestUriWithoutContext(httpRequest));
- logingHandler.login(httpRequest, httpResponse);
+ 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;
+ }
+ }
+
+ 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());
+
+ httpRequest.getSession().setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
+ loginHandler.login(httpRequest, httpResponse);
}
/**
- * Completes the authentication request using an existing, active, authentication method for the current user.
+ * Authenticates the user with the given authentication method provided by the given login handler.
*
- * @param authenticationMethod authentication method to use to complete the request
+ * @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 completeAuthenticationWithActiveMethod(AuthenticationMethodInformation authenticationMethod,
+ protected void authenticateUser(String authnMethod, LoginHandler loginHandler, LoginContext loginContext,
HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
- HttpSession httpSession = httpRequest.getSession();
-
- Session shibSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
-
- LOG.debug("Populating login context with existing session and authentication method information.");
- 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());
+ LOG.debug("Authenticating user with login handler of type {}", loginHandler.getClass().getName());
- ServiceInformation serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(),
- authenticationMethod);
- shibSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
-
- returnToProfileHandler(loginContext, httpRequest, httpResponse);
+ 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);
}
/**
- * Completes the authentication process when and already active authentication mechanism wasn't used, that is, when
- * the user was really authenticated.
+ * 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
* @param httpRequest current HTTP request
* @param httpResponse current HTTP response
*/
- protected void completeAuthenticationWithoutActiveMethod(LoginContext loginContext, HttpServletRequest httpRequest,
+ protected void completeAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
+ LOG.debug("Completing user authentication process");
- String principalName = DatatypeHelper.safeTrimOrNullString((String) httpRequest
- .getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
+ // 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) {
- 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;
+ 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);
- loginContext.setPrincipalName(principalName);
- loginContext.setAuthenticationInstant(new DateTime());
// We allow a login handler to override the authentication method in the event that it supports multiple methods
String actualAuthnMethod = DatatypeHelper.safeTrimOrNullString((String) httpRequest
loginContext.setAuthenticationMethod(actualAuthnMethod);
}
- updateUserSession(loginContext, httpRequest, httpResponse);
-
- LOG.debug("User {} authentication with authentication method {}", loginContext.getPrincipalName(), loginContext
+ LOG.debug("User {} authenticated with method {}", loginContext.getPrincipalName(), loginContext
.getAuthenticationMethod());
-
+ updateUserSession(loginContext, httpRequest, httpResponse);
returnToProfileHandler(loginContext, httpRequest, httpResponse);
}
*/
protected void updateUserSession(LoginContext loginContext, HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
- Session shibSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
- if (shibSession == null) {
+ Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
+ if (idpSession == null) {
LOG.debug("Creating shibboleth session for principal {}", loginContext.getPrincipalName());
- shibSession = (Session) getSessionManager().createSession(loginContext.getPrincipalName());
- loginContext.setSessionID(shibSession.getSessionID());
- addSessionCookie(httpRequest, httpResponse, shibSession);
+ 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: {}",
}
AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(subject, authnMethod,
- new DateTime(), loginContext.getAuthenticationDuration());
+ 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);
+ idpSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
}
/**
Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, userSession.getSessionID());
sessionCookie.setPath(httpRequest.getContextPath());
sessionCookie.setSecure(false);
-
- int maxAge = (int) (userSession.getInactivityTimeout() / 1000);
- sessionCookie.setMaxAge(maxAge);
+ sessionCookie.setMaxAge(-1);
httpResponse.addCookie(sessionCookie);
}