fix authn classes, per chad's comments
authordmorr <dmorr@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 21 Feb 2007 16:31:08 +0000 (16:31 +0000)
committerdmorr <dmorr@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 21 Feb 2007 16:31:08 +0000 (16:31 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2154 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/idp/authn/AuthenticationHandler.java
src/edu/internet2/middleware/shibboleth/idp/authn/AuthenticationManager.java
src/edu/internet2/middleware/shibboleth/idp/authn/LoginContext.java
src/edu/internet2/middleware/shibboleth/idp/authn/Saml2LoginContext.java

index d440529..3238fb6 100644 (file)
 
 package edu.internet2.middleware.shibboleth.idp.authn;
 
-import javax.servlet.RequestDispatcher;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
 
-import org.opensaml.saml2.core.AuthnRequest;
 
 /**
  * Authentication handlers are responsible for authenticating a user using a 
@@ -38,7 +35,8 @@ import org.opensaml.saml2.core.AuthnRequest;
  * request attribute called <strong>principal</strong> with the principal name 
  * of the authenticated user. It must then forward the request/response to the 
  * provided return location by means of the
- * {@link RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
+ * {@link javax.servlet.RequestDispatcher.RequestDispatcher#forward(
+ *  javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
  * method.
  * 
  * When this handler is invoked to log a user out of the particular
@@ -50,30 +48,23 @@ import org.opensaml.saml2.core.AuthnRequest;
  * supporting such requests.
  * 
  * AuthentcationHandlers <strong>MUST NOT</strong> change or add any data to the 
- * user's {@link HttpSession} that persists past the process of authenticating 
- * the user, that is no additional session data may be added and no existing
- * session data may be changed when the handler redirects back to the return 
- * location.
+ * user's {@link javax.servlet.http.HttpSession} that persists past the process 
+ * of authenticating the user, that is no additional session data may be added
+ * and no existing session data may be changed when the handler redirects back 
+ * to the return location.
  */
 public interface AuthenticationHandler {
 
-    /** Key contiaing in login()'s request parameter */
-    public static final String AUTHN_REQUEST_KEY = "AuthnRequest";
-
-    /** Key containing principal name in login()'s response parameter */
-    public static final String PRINCIPAL_KEY = "principal";
-
     
     /**
      * Authenticates the user making the request.
      * 
      * @param request user request
      * @param response response to user
-     * @param passive whether the authentication must be passive
-     * @param force whether the handler must force an authentication
+     * @param loginCtx The {@link LoginContext} for the reqeust.
      */
     public void login(HttpServletRequest request, HttpServletResponse response,
-       boolean passive, boolean force);
+        LoginContext loginCtx);
 
 
     /**
@@ -85,7 +76,7 @@ public interface AuthenticationHandler {
      * @param principal principal named as returned during authentication
      */
     public void logout(HttpServletRequest request, HttpServletResponse response,
-       String principal);
+        String principal);
 
 
     /**
@@ -103,12 +94,4 @@ public interface AuthenticationHandler {
      * @return if this handler can force a user to (re-)authenticate.
      */
     public boolean supportsForceAuthentication();
-
-
-    /**
-     * Sets the location to return the user to once authenticated.
-     * 
-     * @param location location to return the user to once authenticated
-     */
-    public void setReturnLocation(String location);
 }
\ No newline at end of file
index 04c351c..3dbe8de 100644 (file)
 
 package edu.internet2.middleware.shibboleth.idp.authn;
 
-import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
-import edu.internet2.middleware.shibboleth.idp.session.impl.AuthenticationMethodInformationImpl;
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletContext;
@@ -28,26 +27,16 @@ import javax.servlet.http.HttpSession;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationHandler;
+import edu.internet2.middleware.shibboleth.idp.session.impl.AuthenticationMethodInformationImpl;
+
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
 import edu.internet2.middleware.shibboleth.idp.session.Session;
 import edu.internet2.middleware.shibboleth.idp.session.SessionManager;
 
-import javolution.util.FastList;
 import javolution.util.FastMap;
 
 import org.apache.log4j.Logger;
 
-import org.opensaml.saml2.core.AuthnContextClassRef;
-import org.opensaml.saml2.core.AuthnContextDeclRef;
-import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
-import org.opensaml.saml2.core.RequestedAuthnContext;
-
-import org.opensaml.saml2.core.AuthnContextClassRef;
-import org.opensaml.saml2.core.AuthnContextDeclRef;
-import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
-import org.opensaml.saml2.core.AuthnRequest;
-import org.opensaml.saml2.core.RequestedAuthnContext;
-
 import org.springframework.web.servlet.HttpServletBean;
 
 
@@ -55,31 +44,32 @@ import org.springframework.web.servlet.HttpServletBean;
  * Manager responsible for handling authentication requests.
  */
 public class AuthenticationManager extends HttpServletBean {
-    
+
+       /** log4j. */    
     private static final Logger log =
-           Logger.getLogger(AuthenticationManager.class.getName());
+        Logger.getLogger(AuthenticationManager.class.getName());
     
-    /** SessionManager to be used */
+    /** SessionManager to be used. */
     private SessionManager sessionMgr;
     
-    /** Map of URIs onto AuthenticationHandlerInfo */
+    /** Map of URIs onto AuthenticationHandlerInfo. */
     private FastMap<String, AuthenticationHandler> handlerMap
-           = new FastMap<String, AuthenticationHandler>();
+        = new FastMap<String, AuthenticationHandler>();
     
-    /** The default AuthenticationHandler */
+    /** The default AuthenticationHandler. */
     private AuthenticationHandler defaultHandler;
     
-    /* The URI for the default AuthenticationHandler */
+    /* The URI for the default AuthenticationHandler. */
     private String defaultHandlerURI;
 
-           
+        
     /**
-     * Gets the session manager to be used
+     * Gets the session manager to be used.
      *
      * @return session manager to be used
      */
     public SessionManager getSessionManager() {
-       return this.sessionMgr;
+        return sessionMgr;
     }
     
     
@@ -89,48 +79,70 @@ public class AuthenticationManager extends HttpServletBean {
      * @param manager session manager to be used.
      */
     public void setSessionManager(final SessionManager manager) {
-       this.sessionMgr = manager;
+        sessionMgr = manager;
+    }
+    
+    
+    /**
+     * Get the map of {@link AuthenticationHandlers}.
+     *
+     * @return The map of AuthenticationHandlers
+     */
+    public Map<String, AuthenticationHandler> getHandlerMap() {
+    
+        return new FastMap<String, AuthenticationHandler>(handlerMap);
     }
     
     
     /**
-     * Add a <String:AuthenticationHandler> mapping to the
+     * Set the {@link AuthenticationHandler} map.
+     *
+     * @param handlerMap The Map of URIs to AuthenticationHandlers
+     */
+    public void setHandlerMap(final Map<String, AuthenticationHandler> handlerMap) {
+    
+        for (String uri : handlerMap.keySet()) {
+            addHandlerMapping(uri, handlerMap.get(uri));
+        }
+    }
+    
+    
+    /**
+     * Add a <code>&lt;String:AuthenticationHandler&gr;</code> mapping to the
      * AuthenticationManager's table. If a mapping for the URI
      * already exists, it will be overwritten.
      *
      * The URI SHOULD be from the saml-autn-context-2.0-os
      *
      * @param uri A URI identifying the authentcation method.
-     * @param handlerInfo Informarmation about the handler.
-     *
-     * @throws IllegalArgumentExcetption if <code>handlerInfo.getUri()</code> returns </code>null</code>
+     * @param handler The AuthenticationHandler.
      */
     public void addHandlerMapping(String uri, AuthenticationHandler handler) {
-       
-       if (uri == null || handler == null) {
-           return;
-       }
-       
-       log.debug("registering " + handler.getClass().getName()
-           + " for " + uri);
-       
-       this.handlerMap.put(uri, handler);
+    
+        if (uri == null || handler == null) {
+            return;
+        }
+    
+        log.debug("registering " + handler.getClass().getName()
+            + " for " + uri);
+    
+        handlerMap.put(uri, handler);
     }
     
     
     /**
-     * Register the default {@link AuthenticationHandler}
+     * Register the default {@link AuthenticationHandler}.
      *
      * @param uri The URI of the default authentication handler (from saml-authn-context-2.0-os)
      * @param handler The default {@link AuthenticationHandler}.
      */
-    public void addDefaultHandler(String uri, AuthenticationHandler handler) {
-       
-       log.debug("Registering default handler "
-               + handler.getClass().getName());
-       
-       this.defaultHandler = handler;
-       this.defaultHandlerURI = uri;
+    public void setDefaultHandler(String uri, AuthenticationHandler handler) {
+    
+        log.debug("Registering default handler "
+            + handler.getClass().getName());
+    
+        defaultHandler = handler;
+        defaultHandlerURI = uri;
     }
     
     
@@ -143,92 +155,124 @@ public class AuthenticationManager extends HttpServletBean {
      * @param uri A URI identifying the authentcation method.
      */
     public void removeHandlerMapping(String uri) {
-       
-       if (uri == null) {
-           return;
-       }
-       
-       log.debug("unregistering handler for " + uri);
-       
-       this.handlerMap.remove(uri);
+    
+        if (uri == null) {
+            return;
+        }
+    
+        log.debug("unregistering handler for " + uri);
+    
+        handlerMap.remove(uri);
     }
     
     
     
     /**
-     * Primary entrypoint for the AuthnManager
+     * Primary entrypoint for the AuthnManager.
+     *
+     * @param req The ServletRequest.
+     * @param resp The ServletResponse.
      */
     public void doPost(HttpServletRequest req,
-           HttpServletResponse resp) throws ServletException, IOException {
-       
-       if (req == null || resp == null) {
-           log.error("Invalid parameters in AuthenticationManager's doPost().");
-           return;
-       }
-       
-       HttpSession httpSession = req.getSession();
-       if (httpSession == null) {
-           log.error("Unable to retrieve HttpSession from request.");
-           return;
-       }
-       Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
-       if (! (o instanceof LoginContext)) {
-           log.error("Invalid login context object -- object is not an instance of LoginContext.");
-           return;
-       }
-       LoginContext loginContext = (LoginContext)o;
-       
-       // If authentication has been attempted, don't try it again.
-       if (loginContext.getAuthenticationAttempted()) {
-           this.handleNewAuthnRequest(loginContext, req, resp);
-       } else {
-           this.finishAuthnRequest(loginContext, req, resp);
-       }
-       
-       
+        HttpServletResponse resp) throws ServletException, IOException {
+    
+        if (req == null || resp == null) {
+            log.error("Invalid parameters in AuthenticationManager's doPost().");
+            return;
+        }
+    
+        HttpSession httpSession = req.getSession();
+        Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+        if (! (o instanceof LoginContext)) {
+            log.error("Invalid login context object -- object is not an instance of LoginContext.");
+            return;
+        }
+        LoginContext loginContext = (LoginContext)o;
+    
+        // If authentication has been attempted, don't try it again.
+        if (loginContext.getAuthenticationAttempted()) {
+            handleNewAuthnRequest(loginContext, req, resp);
+        } else {
+            finishAuthnRequest(loginContext, req, resp);
+        }
     }
     
     
     
     /**
-     * Handle a new {@link AuthnRequest}s
+     * Handle a new authentication request.
      *
+     * @param loginContext The {@link LoginContext} for the new authentication request
+     * @param servletRequest The servlet request containing the authn request
+     * @param servletResponse The associated servlet response.
      */
     private void handleNewAuthnRequest(final LoginContext loginContext,
-           final HttpServletRequest servletRequest,
-           final HttpServletResponse servletResponse) throws ServletException, IOException {
-       
-       boolean forceAuthN = loginContext.getForceAuth();
-       boolean isPassive = loginContext.getPassiveAuth();
-       
-       RequestedAuthnContext authnCtx = null;
-       
-       // if loginContext is really a Saml2LoginContext, extract the
-       // (possibly null) RequestedAuthnContext.
-       if (loginContext instanceof Saml2LoginContext) {
-           Saml2LoginContext samlLoginContext = (Saml2LoginContext)loginContext;
-           authnCtx = samlLoginContext.getRequestedAuthnContext();
-       }
-       
-       // if no registered handler can evaluate the request, abort.
-       AuthenticationHandler handler =
-               this.getHandler(authnCtx, loginContext, forceAuthN, isPassive);
-       if (handler == null) {
-           loginContext.setAuthenticationAttempted();
-           loginContext.setAuthenticationOK(false);
-           loginContext.setAuthenticationFailureMessage("No installed AuthenticationHandlers can satisfy the authentication request.");
-           
-           RequestDispatcher dispatcher =
-                   servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
-           dispatcher.forward(servletRequest, servletResponse);
-       }
-       
-       // otherwise, forward control to the authenticationhandler
-       ServletContext servletContext = this.getServletContext();
-       loginContext.setAuthenticationManagerURL(servletRequest.getPathInfo());
-       handler.setReturnLocation(servletRequest.getPathInfo());
-       handler.login(servletRequest, servletResponse,
-               loginContext.getPassiveAuth(), loginContext.getForceAuth());
+        final HttpServletRequest servletRequest,
+        final HttpServletResponse servletResponse) throws ServletException, IOException {
+    
+        boolean forceAuthN = loginContext.getForceAuth();
+        boolean passiveAuthN = loginContext.getPassiveAuth();
+    
+        // set that authentication has been attempted, to prevent processing loops
+        loginContext.setAuthenticationAttempted();
+    
+        // if the profile handler set a list of requested authn methods,
+        // evaluate them. otherwise, evaluate the default handler.
+        String[] requestedAuthnMethods = loginContext.getRequestedAuthenticationMethods();
+        AuthenticationHandler handler = null;
+    
+        if (requestedAuthnMethods == null) {
+
+            // if no authn methods were specified, try the default handler
+            
+            if (evaluateHandler(defaultHandler, "default", forceAuthN, passiveAuthN)) {
+                handler = defaultHandler;
+                loginContext.setAuthenticationMethod(defaultHandlerURI);
+            }
+        
+        } else { 
+    
+            // evaluate all requested authn methods until we find a match.
+
+            for (String authnMethodURI : requestedAuthnMethods) {
+
+            AuthenticationHandler candidateHandler = handlerMap.get(authnMethodURI);
+            if (candidateHandler == null) {
+                log.debug("No registered authentication handlers can satisfy the "
+                    + " requested authentication method " + authnMethodURI);
+                continue;
+            }
+
+            if (evaluateHandler(candidateHandler, authnMethodURI, forceAuthN, passiveAuthN)) {
+
+                // we found a match. stop iterating.
+                handler = candidateHandler;
+                log.info("Using authentication handler " + handler.getClass().getName()
+                    + " for authentication method " + authnMethodURI);
+                loginContext.setAuthenticationMethod(authnMethodURI);
+                break;
+            }
+        }
+    }
+
+    // if no acceptable handler was found, abort.
+    if (handler == null) {
+        loginContext.setAuthenticationOK(false);
+        loginContext.setAuthenticationFailureMessage(
+            "No installed AuthenticationHandler can satisfy the authentication request.");
+        
+        log.error("No registered authentication handlers could satisify any requested "
+            + "authentication methods. Unable to process authentication request.");
+        
+        RequestDispatcher dispatcher =
+            servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
+        dispatcher.forward(servletRequest, servletResponse);
+    }
+    
+    // otherwise, forward control to the AuthenticationHandler
+    ServletContext servletContext = getServletContext();
+    loginContext.setAuthenticationManagerURL(servletRequest.getPathInfo());
+    handler.login(servletRequest, servletResponse, loginContext);
     }
     
     
@@ -238,135 +282,36 @@ public class AuthenticationManager extends HttpServletBean {
      *
      */
     private void finishAuthnRequest(final LoginContext loginContext,
-           final HttpServletRequest servletRequest,
-           final HttpServletResponse servletResponse) throws ServletException, IOException {
-       
-       // if authentication was successful, the authentication handler should
-       // have updated the LoginContext with additional information. Use that
-       // info to create a Session.
-       if (loginContext.getAuthenticationOK()) {
-           
-           AuthenticationMethodInformationImpl authMethodInfo =
-                   new AuthenticationMethodInformationImpl(loginContext.getAuthenticationMethod(),
-                       loginContext.getAuthenticationInstant(), loginContext.getAuthenticationDuration());
-           
-           Session shibSession = this.getSessionManager().createSession();
-           List<AuthenticationMethodInformation> authMethods = shibSession.getAuthenticationMethods();
-           authMethods.add(authMethodInfo);
-           loginContext.setSessionID(shibSession.getSessionID());
-       }
-       
-       RequestDispatcher dispatcher =
-               servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
-       dispatcher.forward(servletRequest, servletResponse);
-    }
+        final HttpServletRequest servletRequest,
+        final HttpServletResponse servletResponse) throws ServletException, IOException {
     
+        // if authentication was successful, the authentication handler should
+        // have updated the LoginContext with additional information. Use that
+        // info to create a Session.
+        if (loginContext.getAuthenticationOK()) {
+        
+            AuthenticationMethodInformationImpl authMethodInfo =
+                new AuthenticationMethodInformationImpl(loginContext.getAuthenticationMethod(),
+                    loginContext.getAuthenticationInstant(), loginContext.getAuthenticationDuration());
+        
+            Session shibSession = getSessionManager().createSession();
+            List<AuthenticationMethodInformation> authMethods = shibSession.getAuthenticationMethods();
+            authMethods.add(authMethodInfo);
+            loginContext.setSessionID(shibSession.getSessionID());
+        }
     
-    /**
-     * "Stub" method for handling LogoutRequest
-     */
-    private void handleLogoutRequest(final HttpServletRequest servletRequest,
-           final HttpServletResponse servletResponse) throws ServletException, IOException {
-       
+        RequestDispatcher dispatcher =
+            servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
+        dispatcher.forward(servletRequest, servletResponse);
     }
     
     
     /**
-     * Examine an {@link RequestedAuthnContext} against a list of installed
-     * {@link AuthenticationHandler}s. If an acceptable handler is found, return
-     * a reference to it. Otherwise return <code>null</code>. The {@link LoginContext}
-     * is updated to reflect the selected authentication method's URI.
-     *
-     * @param authnCtx A {@link RequestedAuthnContext}
-     * @param loginCtx The {@link LoginContext} 
-     * @param forceAuthN Should authentication be forced.
-     * @param passiveAuthN Must authentication happen without UI control.
-     *
-     * @return A reference to an {@link AuthenticationHandler} or <code>null</code>.
+     * "Stub" method for handling LogoutRequest.
      */
-    private AuthenticationHandler getHandler(final RequestedAuthnContext authnCtx,
-           final LoginContext loginCtx, boolean forceAuthN, boolean passiveAuthN) {
-       
-       // if no context is specified, evaluate the default handler
-       if (authnCtx == null) {
-           loginCtx.setAuthenticationMethod(this.defaultHandlerURI);
-           return this.evaluateHandler(this.defaultHandler, "default", 
-                   forceAuthN, passiveAuthN);
-       }
-       
-       
-       // For the immediate future, we only support the "exact" comparator.
-       AuthnContextComparisonTypeEnumeration comparator = authnCtx.getComparison();
-       if (comparator != null && comparator != AuthnContextComparisonTypeEnumeration.EXACT) {
-           log.error("Unsupported comparision operator ( " + comparator
-                   + ") in RequestedAuthnContext. Only exact comparisions are supported.");
-           return (null);
-       }
-       
-       // build a list of all requested authn classes and declrefs
-       List<String> requestedAuthnMethods = new FastList<String>();
-       List<AuthnContextClassRef> authnClasses = authnCtx.getAuthnContextClassRefs();
-       List<AuthnContextDeclRef> authnDeclRefs = authnCtx.getAuthnContextDeclRefs();
-       
-       if (authnClasses != null) {
-           for (AuthnContextClassRef classRef : authnClasses) {
-               if (classRef != null) {
-                   String s = classRef.getAuthnContextClassRef();
-                   if (s != null) {
-                       requestedAuthnMethods.add(s);
-                   }
-               }
-           }
-       }
-       
-       if (authnDeclRefs != null) {
-           for (AuthnContextDeclRef declRef : authnDeclRefs) {
-               if (declRef != null) {
-                   String s = declRef.getAuthnContextDeclRef();
-                   if (s != null) {
-                       requestedAuthnMethods.add(s);
-                   }
-               }
-           }
-       }
-       
-       
-       // if no AuthnContextClasses or AuthnContextDeclRefs were actually specified,
-       // evaluate the default handler
-       if (requestedAuthnMethods.size() == 0) {
-           return this.evaluateHandler(this.defaultHandler, "default",
-                   forceAuthN, passiveAuthN);
-       }
-       
-       
-       // evaluate all requested authn methods until we find a match.
-       AuthenticationHandler handler = null;
-       for (String s : requestedAuthnMethods) {
-           
-           AuthenticationHandler candidateHandler = this.handlerMap.get(s);
-           if (candidateHandler == null) {
-               log.debug("No registered authentication handlers can satisfy the "
-                       + " requested authentication method " + s);
-               continue;
-           }
-           
-           handler = this.evaluateHandler(candidateHandler, s, forceAuthN, passiveAuthN);
-           
-           if (handler != null) {
-               // we found a match. stop iterating.
-               log.info("Using authentication handler " + handler.getClass().getName()
-                   + " for authentication method " + s);
-               loginCtx.setAuthenticationMethod(s);
-               break;
-           }
-       }
-       
-       if (handler == null) {
-           log.error("No registered authentication handlers could satisify any requested "
-                   + "authentication methods. Unable to process authentication request.");
-       }
-       
-       return (handler);
+    private void handleLogoutRequest(final HttpServletRequest servletRequest,
+        final HttpServletResponse servletResponse) throws ServletException, IOException {
+    
     }
     
     
@@ -377,27 +322,28 @@ public class AuthenticationManager extends HttpServletBean {
      * @param description A description of the handler
      * @param forceAuthN Is (re)authentication forced?
      * @param passiveAuthN Can the AuthenticationHandler take control of the UI
-     * @return A reference to an {@link AuthenticationHandler} or <code>null</code>.
+     *
+     * @return <code>true</code> if handler meets the criteria, otherwise <code>false</code>
      */
-    private AuthenticationHandler evaluateHandler(final AuthenticationHandler handler,
-           String description, boolean forceAuthN, boolean passiveAuthN) {
-       
-       if (handler == null) {
-           return (null);
-       }
-       
-       if (forceAuthN && !handler.supportsForceAuthentication()) {
-           log.debug("The RequestedAuthnContext required forced authentication, "
-                   + "but the " + description + " handler does not support that feature.");
-           return (null);
-       }
-       
-       if (passiveAuthN && !handler.supportsPassive()) {
-           log.debug("The RequestedAuthnContext required passive authentication, "
-                   + "but the " + description + " handler does not support that feature.");
-           return (null);
-       }
-       
-       return handler;
+    private boolean evaluateHandler(final AuthenticationHandler handler,
+        String description, boolean forceAuthN, boolean passiveAuthN) {
+    
+        if (handler == null) {
+            return false;
+        }
+    
+        if (forceAuthN && !handler.supportsForceAuthentication()) {
+            log.debug("The RequestedAuthnContext required forced authentication, "
+                + "but the " + description + " handler does not support that feature.");
+            return false;
+        }
+    
+        if (passiveAuthN && !handler.supportsPassive()) {
+            log.debug("The RequestedAuthnContext required passive authentication, "
+                + "but the " + description + " handler does not support that feature.");
+            return false;
+        }
+    
+        return true;
     }
 }
index 54ff474..719a59b 100644 (file)
@@ -42,7 +42,8 @@ import org.joda.time.DateTime;
  *
  * The {@link AuthenticationManager} or an {@link AuthenticationHandler} should set the
  * {@link LoginContext#setAuthenticationAttempted()}, {@link LoginContext#setAuthnOK(boolean)},
- * {@link LoginContext#setAuthnFailure(String)} appropriately.
+ * {@link LoginContext#setAuthnFailure(String)}, {@link LoginContext#{setAuthenticationDuration(long)}
+ * {@link LoginContext#setAuthenticationInstant(DateTime)} appropriately.
  *
  */
 public class LoginContext {
@@ -52,43 +53,43 @@ public class LoginContext {
     
     
     /** Should user authentication be forced */
-    private boolean forceAuth = false;
+    protected boolean forceAuth = false;
     
     /** Must authentication not interact with the UI */
-    private boolean passiveAuth = false;
+    protected boolean passiveAuth = false;
     
     /** a catch-all map for other properties */
-    private Map<String, Object> propsMap = new FastMap<String, Object>();
+    protected Map<String, Object> propsMap = new FastMap<String, Object>();;
     
     /** The ProfileHandler URL */
-    private String profileHandlerURL;
+    protected String profileHandlerURL;
     
     /** The AuthenticationManager's URL */
-    private String authnManagerURL;
+    protected String authnManagerURL;
     
     /** has authentication been attempted yet */
-    private boolean authnAttempted = false;
+    protected boolean authnAttempted = false;
     
     /** The id of the authenticated user */ 
-    private String userID;
+    protected String userID;
 
     /** Did authentication succceed? */
-    private boolean authenticationOK;
+    protected boolean authenticationOK;
     
     /** Optional failure message  */
-    private String authnFailureMessage;
+    protected String authnFailureMessage;
 
     /** The instant of authentication */
-    private DateTime authnInstant;
+    protected DateTime authnInstant;
     
     /** The duration of authentication */
-    private long authnDuration;
+    protected long authnDuration;
     
     /** The method used to authenticate the user */
-    private String authnMethod;        
+    protected String authnMethod;        
     
     /** The session id */
-    private String sessionID;
+    protected String sessionID;
     
     
     /** Creates a new instance of LoginContext */
@@ -104,8 +105,8 @@ public class LoginContext {
      */
     public LoginContext(boolean forceAuth, boolean passiveAuth) {
         
-        this.forceAuth = forceAuth;
-        this.passiveAuth = passiveAuth;
+        forceAuth = forceAuth;
+        passiveAuth = passiveAuth;
     }    
     
     
@@ -115,7 +116,7 @@ public class LoginContext {
      * @return <code>true</code> if the authentication manager must reauth the user.
      */
     public boolean getForceAuth() {
-        return this.forceAuth;
+        return forceAuth;
     }
     
     
@@ -125,7 +126,7 @@ public class LoginContext {
      * @return <code>true</code> if the authentication manager must not interact with the users UI.
      */
     public boolean getPassiveAuth() {
-        return this.passiveAuth;
+        return passiveAuth;
     }
     
     
@@ -157,7 +158,7 @@ public class LoginContext {
      * @return The object, or <code>null</code> is no object exists for the key.
      */
     public Object getProperty(String key) {
-        return this.propsMap.get(key);
+        return propsMap.get(key);
     }
     
     
@@ -170,7 +171,7 @@ public class LoginContext {
      * @param obj The object to associate with key.
      */
     public void setProperty(String key, final Object obj) {
-        this.propsMap.put(key, obj);
+        propsMap.put(key, obj);
     }
  
     
@@ -179,7 +180,7 @@ public class LoginContext {
      * 
      * @param authnOK if authentication succeeded;
      */
-    public void setAuthnOK(boolean authnOK) {
+    public void setAuthenticationOK(boolean authnOK) {
         this.authenticationOK = authnOK;
     }
     
@@ -189,8 +190,8 @@ public class LoginContext {
      * 
      * @return <code>true</code> is the user was successfully authenticated.
      */
-    public boolean getAuthnOK() {
-        return this.authenticationOK;
+    public boolean getAuthenticationOK() {
+        return authenticationOK;
     }
     
     
@@ -198,8 +199,8 @@ public class LoginContext {
      * 
      * @param failureMessage A description of why authN failed.
      */ 
-    public void setAuthnFailureMessage(String failureMessage) {
-        this.authnFailureMessage = failureMessage;
+    public void setAuthenticationFailureMessage(String failureMessage) {
+        authnFailureMessage = failureMessage;
     }
     
     
@@ -208,8 +209,8 @@ public class LoginContext {
      * 
      * @return The failure message, or <code>null</code> is none was set.
      */
-    public String getAuthnFailureMessage() {
-        return this.authnFailureMessage;
+    public String getAuthenticationFailureMessage() {
+        return authnFailureMessage;
     }
     
     
@@ -220,7 +221,7 @@ public class LoginContext {
      * while processing a request.
      */
     public void setAuthenticationAttempted() {
-       this.authnAttempted = true;
+        authnAttempted = true;
     }
     
     
@@ -228,7 +229,7 @@ public class LoginContext {
      * Returns if authentication has been attempted for this user.
      */
     public boolean getAuthenticationAttempted() {
-       return this.authnAttempted;
+        return authnAttempted;
     }
     
     
@@ -248,7 +249,7 @@ public class LoginContext {
      * @return the ID of the user, or <code>null</code> if authentication failed.
      */
     public String getUserID() {
-        return this.userID;
+        return userID;
     }
     
     
@@ -258,7 +259,7 @@ public class LoginContext {
      * @return the URL of the profile handler that is invoking the Authentication Manager.
      */
     public String getProfileHandlerURL() {
-       return this.profileHandlerURL;
+        return profileHandlerURL;
     }
     
     
@@ -268,7 +269,7 @@ public class LoginContext {
      * @param profileHandlerURL The URL of the profile handler that invoked the AuthenticationManager/
      */
     public void setProfileHandlerURL(String profileHandlerURL) {
-       this.profileHandlerURL = profileHandlerURL;
+        this.profileHandlerURL = profileHandlerURL;
     }
     
     
@@ -277,8 +278,8 @@ public class LoginContext {
      *
      * @return the URL of the AuthenticationManager.
      */
-    public String getAuthnManagerURL() {
-       return this.authnManagerURL;
+    public String getAuthenticationManagerURL() {
+        return authnManagerURL;
     }
     
     
@@ -287,8 +288,8 @@ public class LoginContext {
      *
      * @param authnManagerURL the URL of the AuthenticationManager.
      */
-    public void setAuthnManagerURL(String authnManagerURL) {
-       this.authnManagerURL = authnManagerURL;
+    public void setAuthenticationManagerURL(String authnManagerURL) {
+        this.authnManagerURL = authnManagerURL;
     }
     
     
@@ -298,7 +299,7 @@ public class LoginContext {
      * @return The instant of authentication, or <code>null</code> if none was set.
      */
     public DateTime getAuthenticationInstant() {
-           return this.authnInstant;
+        return authnInstant;
     }
     
     
@@ -308,7 +309,7 @@ public class LoginContext {
      * @param authnInstant The instant of authentication.
      */
     public void setAuthenticationInstant(final DateTime authnInstant) {
-       this.authnInstant = authnInstant;
+        this.authnInstant = authnInstant;
     }
     
     
@@ -318,7 +319,7 @@ public class LoginContext {
      * @return The duration of authentication, or zero if none was set.
      */
     public long getAuthenticationDuration() {
-       return this.authnDuration;
+        return authnDuration;
     }
     
     
@@ -328,7 +329,7 @@ public class LoginContext {
      * @param authnDuration The duration of authentication.
      */
     public void setAuthenticationDuration(long authnDuration) {
-       this.authnDuration = authnDuration;
+        this.authnDuration = authnDuration;
     }
     
     
@@ -338,7 +339,7 @@ public class LoginContext {
      * @return The method used to authenticate the user.
      */
     public String getAuthenticationMethod() {
-       return this.authnMethod;
+        return authnMethod;
     }
     
     
@@ -348,7 +349,7 @@ public class LoginContext {
      * @param authnMethod The method used to authenticate the user.
      */
     public void setAuthenticationMethod(String authnMethod) {
-       this.authnMethod = authnMethod;
+        this.authnMethod = authnMethod;
     }
     
     
@@ -358,7 +359,7 @@ public class LoginContext {
      * @return the Session id.
      */
     public String getSessionID() {
-       return this.sessionID;
+        return sessionID;
     }
     
     
@@ -368,6 +369,20 @@ public class LoginContext {
      * @param sessionID the Session ID
      */
     public void setSessionID(String sessionID) {
-       this.sessionID = sessionID;
+        this.sessionID = sessionID;
+    }
+    
+    
+    /**
+     * Return the acceptable authentication handler URIs
+     * for authenticating this user. If no authentication
+     * methods are preferred, this method will return 
+     * <code>null</code>.
+     *
+     * @return an array of URIs, or <code>null</code>.
+     */
+    public String[] getRequestedAuthenticationMethods() {
+    
+        return null;
     }
 }
index 094aa9a..8c47253 100644 (file)
 package edu.internet2.middleware.shibboleth.idp.authn;
 
 
-import bsh.This;
-import java.util.Map;
-import javolution.util.FastMap;
+import java.util.List;
 
+import javolution.util.FastList;
+
+import org.apache.log4j.Logger;
+
+import org.opensaml.saml2.core.AuthnContextClassRef;
+import org.opensaml.saml2.core.AuthnContextDeclRef;
+import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
+import org.opensaml.saml2.core.AuthnRequest;
 import org.opensaml.saml2.core.RequestedAuthnContext;
 
+
 /**
- * Login context created by a profile handler and interpreted
- * by the authentication package.
+ * A SAML 2.0 {@link LoginContext}. 
  * 
- * Two properties are tracked by default:
- * 
- * <code>forceAuth</code> - Should user authentiation be forced.
- * <code>passiveAuth</code> - Should user authentication not control the UI.
- * 
- * A Map&lt;String, Object&gt; is provided to store other properties.
- * Alternatively, a profile handler may create a subclass of LoginContext with
- * extra fields.
+ * This class can interpret {@link RequestedAuthnContext} and act accordingly.
  */
 public class Saml2LoginContext extends LoginContext {
     
+    private static final Logger log = Logger.getLogger(Saml2LoginContext.class);
+    
     /** The {@link RequestedAuthnContext} */
     private RequestedAuthnContext ctx;
     
     
-    /** Creates a new instance of LoginContext */
-    public Saml2LoginContext() {
-    }
-    
-    
     /**
-     * Creates a new instance of LoginContext
+     * Creates a new instance of Saml2LoginContext.
      *
-     * @param forceAuth if the authentication manager must reauth the user.
-     * @param passiveAuth if the authentication manager must not interact with the users UI. 
-     * @param ctx The requested login context.
+     * @param authnRequest A SAML 2.0 Authentication Request.
      */
-    public Saml2LoginContext(boolean forceAuth, boolean passiveAuth, final RequestedAuthnContext ctx) {
+    public Saml2LoginContext(AuthnRequest authnRequest) {
         
-       super(forceAuth, passiveAuth);
-       
-       this.ctx = ctx;
-    }    
+        if (authnRequest != null) {
+            forceAuth = authnRequest.isForceAuthn();
+            passiveAuth = authnRequest.isPassive();
+            ctx = authnRequest.getRequestedAuthnContext();
+        }
+    }
     
     
-    /**
-     * Set the requested authentication context.
+    /** 
+     * This method evaluates a SAML2 {@link RequestedAuthnContext}
+     * and returns the list of requested authentication method URIs.
      *
-     * @param ctx The requested authN context.
+     * If the AuthnQuery did not contain a RequestedAuthnContext,
+     * this method will return <code>null</code>.
+     *
+     * @return An array of authentication method URIs, or <code>null</code>.
      */
-    public void setRequestedAuthnContext(RequestedAuthnContext ctx) {
-       this.ctx = ctx;
-    }
+    public String[] getRequestedAuthenticationMethods() {
+
+        if (ctx == null)
+            return null;
+
+        // For the immediate future, we only support the "exact" comparator.
+        // XXX: we should probably throw an exception or somehow indicate this 
+        // as an error to the caller.
+        AuthnContextComparisonTypeEnumeration comparator = ctx.getComparison();
+        if (comparator != null && comparator != AuthnContextComparisonTypeEnumeration.EXACT) {
+            log.error("Unsupported comparision operator ( " + comparator
+                + ") in RequestedAuthnContext. Only exact comparisions are supported.");
+            return null;
+        }
     
+        // build a list of all requested authn classes and declrefs
+        List<String> requestedAuthnMethods = new FastList<String>();
+        List<AuthnContextClassRef> authnClasses = ctx.getAuthnContextClassRefs();
+        List<AuthnContextDeclRef> authnDeclRefs = ctx.getAuthnContextDeclRefs();
     
-    /**
-     * Returns the requested authentication context.
-     *
-     * @return the RequestedAuthnContext, or <code>null</code> if none was set.
-     */
-    public RequestedAuthnContext getRequestedAuthnContext() {
-       return this.ctx;
-    }
+        if (authnClasses != null) {
+            for (AuthnContextClassRef classRef : authnClasses) {
+                if (classRef != null) {
+                    String s = classRef.getAuthnContextClassRef();
+                    if (s != null) {
+                        requestedAuthnMethods.add(s);
+                    }
+                }
+            }
+        }
+    
+        if (authnDeclRefs != null) {
+            for (AuthnContextDeclRef declRef : authnDeclRefs) {
+                if (declRef != null) {
+                    String s = declRef.getAuthnContextDeclRef();
+                    if (s != null) {
+                        requestedAuthnMethods.add(s);
+                    }
+                }
+            }
+        }
     
+        if (requestedAuthnMethods.size() == 0) {
+            return null;
+        } else {
+            String[] methods = new String[requestedAuthnMethods.size()];
+            return requestedAuthnMethods.toArray(methods);
+        }
+
+    }
 }