first pass at authnmgr
authordmorr <dmorr@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 31 Jan 2007 17:24:21 +0000 (17:24 +0000)
committerdmorr <dmorr@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 31 Jan 2007 17:24:21 +0000 (17:24 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2138 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 [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/authn/Saml2LoginContext.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/authn/impl/AuthenticationHandlerInfo.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequest.java
src/edu/internet2/middleware/shibboleth/idp/session/AuthenticationMethodInformation.java
src/edu/internet2/middleware/shibboleth/idp/session/impl/AuthenticationMethodInformationImpl.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/session/impl/ServiceInformationImpl.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/session/impl/SessionImpl.java [new file with mode: 0644]

index bf76d2c..d440529 100644 (file)
@@ -24,30 +24,46 @@ import javax.servlet.http.HttpSession;
 import org.opensaml.saml2.core.AuthnRequest;
 
 /**
- * Authentication handlers are responsible for authenticating a user using a particular authentication context class and
- * logging users out for that same mechanism.
+ * Authentication handlers are responsible for authenticating a user using a 
+ * particular authentication context class and logging users out for that same
+ * mechanism.
  * 
- * When this handler is invoked to log a user in the incomg will contain a {@link AuthnRequest} attribute registered
- * under the name <strong>AuthnRequest</strong>. If the authentication request coming into the IdP is not a SAML 2
- * request the receiving profile handler will translate the incoming details into a {@link AuthnRequest}.
+ * When this handler is invoked to log a user in the incoming request will contain a 
+ * {@link AuthnRequest} attribute registered under the name 
+ * <strong>AuthnRequest</strong>. If the authentication request coming into the
+ * IdP is not a SAML 2 request the receiving profile handler will translate the
+ * incoming details into a {@link AuthnRequest}.
  * 
- * Upon successfull authentication the handler <strong>must</strong> set a 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)} method.
+ * Upon successfull authentication the handler <strong>must</strong> set a 
+ * 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)}
+ * method.
  * 
- * When this handler is invoked to log a user out of the particular authentication source the handler may perform any
- * operation necessary to log a user out. When finished 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)} method. This call will
- * occur only after SAML log out requests have been sent to all services supporting such requests.
+ * When this handler is invoked to log a user out of the particular
+ * authentication source the handler may perform any operation necessary to 
+ * log a user out. When finished 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)}
+ * method. This call will occur before SAML logout requests have been sent to all services 
+ * 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.
+ * 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.
  */
 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.
      * 
@@ -56,16 +72,21 @@ public interface AuthenticationHandler {
      * @param passive whether the authentication must be passive
      * @param force whether the handler must force an authentication
      */
-    public void login(HttpServletRequest request, HttpServletResponse response, boolean passive, boolean force);
+    public void login(HttpServletRequest request, HttpServletResponse response,
+       boolean passive, boolean force);
+
 
     /**
-     * Logs out the given user from the authentication mechansim represented by this handler.
+     * Logs out the given user from the authentication
+     * mechanism represented by this handler.
      * 
      * @param request user request
      * @param response response to user
      * @param principal principal named as returned during authentication
      */
-    public void logout(HttpServletRequest request, HttpServletResponse response, String principal);
+    public void logout(HttpServletRequest request, HttpServletResponse response,
+       String principal);
+
 
     /**
      * Gets whether this handler supports passive authentication.
@@ -74,13 +95,16 @@ public interface AuthenticationHandler {
      */
     public boolean supportsPassive();
 
+
     /**
-     * Gets whether this handler supports the ability to force a user's to authenticate.
+     * Returns if this handler supports the ability
+     * to force a user to (re-)authenticate.
      * 
-     * @return whether this handler supports the ability to force a user's to authenticate
+     * @return if this handler can force a user to (re-)authenticate.
      */
     public boolean supportsForceAuthentication();
 
+
     /**
      * Sets the location to return the user to once authenticated.
      * 
index 1136b36..1010c61 100644 (file)
 
 package edu.internet2.middleware.shibboleth.idp.authn;
 
-import org.springframework.web.servlet.HttpServletBean;
+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 javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+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.authn.impl.AuthenticationHandlerInfo;
+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;
+
+
 /**
- * Manager responsible for handling an authentication requests.
+ * Manager responsible for handling authentication requests.
  */
 public class AuthenticationManager extends HttpServletBean {
-
-    /** Session manager to be used by this servlet */
-       private SessionManager sessionMgr;
+    
+    private static final Logger log =
+           Logger.getLogger(AuthenticationManager.class.getName());
+    
+    /** SessionManager to be used */
+    private SessionManager sessionMgr;
+    
+    /** Map of URIs onto AuthenticationHandlerInfo */
+    private FastMap<String, AuthenticationHandlerInfo> handlerMap
+           = new FastMap<String, AuthenticationHandlerInfo>();
+    
+    /** The default AuthenticationHandler */
+    private AuthenticationHandlerInfo defaultHandlerInfo;
+    
+    
+    /**
+     * Gets the session manager to be used
+     *
+     * @return session manager to be used
+     */
+    public SessionManager getSessionManager() {
+       return this.sessionMgr;
+    }
+    
+    
+    /**
+     * Sets the session manager to be used.
+     *
+     * @param manager session manager to be used.
+     */
+    public void setSessionManager(final SessionManager manager) {
+       this.sessionMgr = manager;
+    }
+    
+    
+    /**
+     * Add a <String:AuthenticationHandler> 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>
+     */
+    public void addHandlerMapping(String uri, AuthenticationHandlerInfo handlerInfo) {
+       
+       if (uri == null || handlerInfo == null) {
+           return;
+       }
+       
+       log.debug("registering " + handlerInfo.getHandler().getClass().getName()
+       + " for " + uri);
+       
+       this.handlerMap.put(uri, handlerInfo);
+    }
+    
     
     /**
-     * Gets the session manager to be used by this servlet.
-     * 
-     * @return session manager to be used by this servlet
+     * Register the default {@link AuthenticationHandler}
+     *
+     * @param handlerInfo Information about the handler.
      */
-    public SessionManager getSessionManager(){
-        return sessionMgr;
+    public void addDefaultHandler(AuthenticationHandlerInfo handlerInfo) {
+       
+       log.debug("Registering default handler "
+               + handlerInfo.getHandler().getClass().getName());
+       
+       this.defaultHandlerInfo = handlerInfo;
     }
     
+    
+    /**
+     * Remove a <String:AuthenticationHandler> mapping from the
+     * AuthenticationManager's table.
+     *
+     * The URI SHOULD be from the saml-authn-context-2.0-os
+     *
+     * @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);
+    }
+    
+    
+    
+    /**
+     * Primary entrypoint for the AuthnManager
+     */
+    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);
+       }
+       
+       
+    }
+    
+    
+    
+    /**
+     * Handle a new {@link AuthnRequest}s
+     *
+     */
+    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();
+       }
+       
+       AuthenticationHandler handler = this.getHandler(authnCtx, forceAuthN, isPassive);
+       if (handler == null) {
+           loginContext.setAuthenticationAttempted();
+           loginContext.setAuthnOK(false);
+           loginContext.setAuthnFailureMessage("No installed AuthenticationHandlers can satisfy the authentication request.");
+           
+           RequestDispatcher dispatcher =
+                   servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
+           dispatcher.forward(servletRequest, servletResponse);
+       }
+       
+       // forward control to the authenticationhandler
+       ServletContext servletContext = this.getServletContext();
+       String saml2handlerPath = servletContext.getRealPath(servletRequest.getServletPath());
+       loginContext.setAuthnManagerURL(servletRequest.getPathInfo());
+    }
+    
+    
+    /**
+     * Handle the "return leg" of an authentication request
+     * (i.e. clean up after an authentication handler has run).
+     *
+     */
+    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.getAuthnOK()) {
+           
+           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);
+    }
+    
+    
+    /**
+     * "Stub" method for handling LogoutRequest
+     */
+    private void handleLogoutRequest(final HttpServletRequest servletRequest,
+           final HttpServletResponse servletResponse) throws ServletException, IOException {
+       
+    }
+    
+    
+    /**
+     * 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>
+     *
+     * @param ctx A {@link RequestedAuthnContext}
+     * @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>.
+     */
+    private AuthenticationHandler getHandler(final RequestedAuthnContext ctx,
+           boolean forceAuthN, boolean passiveAuthN) {
+       
+       // if no context is specified, evaluate the default handler
+       if (ctx == null) {
+           return this.evaluateHandler(this.defaultHandlerInfo, "default", forceAuthN, passiveAuthN);
+       }
+       
+       
+       // For the immediate future, we only support the "exact" comparator.
+       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();
+       
+       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.defaultHandlerInfo, "default", forceAuthN, passiveAuthN);
+       }
+       
+       
+       // evaluate all requested authn methods until we find a match.
+       AuthenticationHandler handler = null;
+       for (String s : requestedAuthnMethods) {
+           
+           AuthenticationHandlerInfo handlerInfo = this.handlerMap.get(s);
+           if (handlerInfo == null) {
+               log.debug("No registered authentication handlers can satisfy the "
+                       + " requested authentication method " + s);
+               continue;
+           }
+           
+           handler = this.evaluateHandler(handlerInfo, s, forceAuthN, passiveAuthN);
+           
+           if (handler != null) {
+               // we found a match. stop iterating.
+               log.info("Using authentication handler " + handlerInfo.getHandler().getClass().getName()
+               + " for authentication method " + s);
+               break;
+           }
+       }
+       
+       if (handler == null) {
+           log.error("No registered authentication handlers could satisify any requested "
+                   + "authentication methods. Unable to process authentication request.");
+       }
+       
+       return (handler);
+    }
+    
+    
     /**
-     * Sets the session manager to be used by this servlet.
-     * 
-     * @param manager session manager to be used by this servlet
+     * Evaluate an authenticationhandler against a set of evaluation criteria.
+     *
+     * @param handlerInfo Handler metadata
+     * @param forceAuthN Is (re)authentication forced?
+     * @param passiveAuthN Can the AuthenticationHandler take control of the UI
+     * @param description A description of the handler
+     *
+     * @return A reference to an {@link AuthenticationHandler} or <code>null</code>.
      */
-    public void setSessionManager(SessionManager manager){
-        sessionMgr = manager;
+    private AuthenticationHandler evaluateHandler(final AuthenticationHandlerInfo handlerInfo,
+           String description, boolean forceAuthN, boolean passiveAuthN) {
+       
+       if (handlerInfo == null) {
+           return (null);
+       }
+       
+       if (forceAuthN && !handlerInfo.supportsForce()) {
+           log.debug("The RequestedAuthnContext required forced authentication, "
+                   + "but the " + description + " handler does not support that feature.");
+           return (null);
+       }
+       
+       if (passiveAuthN && !handlerInfo.supportsPassive()) {
+           log.debug("The RequestedAuthnContext required passive authentication, "
+                   + "but the " + description + " handler does not support that feature.");
+           return (null);
+       }
+       
+       return handlerInfo.getHandler();
     }
-}
\ No newline at end of file
+}
diff --git a/src/edu/internet2/middleware/shibboleth/idp/authn/LoginContext.java b/src/edu/internet2/middleware/shibboleth/idp/authn/LoginContext.java
new file mode 100644 (file)
index 0000000..54ff474
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.authn;
+
+
+import java.util.Map;
+import javolution.util.FastMap;
+
+import org.joda.time.DateTime;
+
+
+/**
+ * Login context created by a profile handler and interpreted
+ * by the authentication package.
+ * 
+ * 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.
+ *
+ * LoginContexts should be created by a profile handler when authentication is needed.
+ * Once control has returned to the profile handler, it should remove the LoginContext
+ * from the HttpSession.
+ *
+ * The {@link AuthenticationManager} or an {@link AuthenticationHandler} should set the
+ * {@link LoginContext#setAuthenticationAttempted()}, {@link LoginContext#setAuthnOK(boolean)},
+ * {@link LoginContext#setAuthnFailure(String)} appropriately.
+ *
+ */
+public class LoginContext {
+    
+    /** the key in a HttpSession where login contexts are stored */
+    public static final String LOGIN_CONTEXT_KEY = "shib2.logincontext";
+    
+    
+    /** Should user authentication be forced */
+    private boolean forceAuth = false;
+    
+    /** Must authentication not interact with the UI */
+    private boolean passiveAuth = false;
+    
+    /** a catch-all map for other properties */
+    private Map<String, Object> propsMap = new FastMap<String, Object>();
+    
+    /** The ProfileHandler URL */
+    private String profileHandlerURL;
+    
+    /** The AuthenticationManager's URL */
+    private String authnManagerURL;
+    
+    /** has authentication been attempted yet */
+    private boolean authnAttempted = false;
+    
+    /** The id of the authenticated user */ 
+    private String userID;
+
+    /** Did authentication succceed? */
+    private boolean authenticationOK;
+    
+    /** Optional failure message  */
+    private String authnFailureMessage;
+
+    /** The instant of authentication */
+    private DateTime authnInstant;
+    
+    /** The duration of authentication */
+    private long authnDuration;
+    
+    /** The method used to authenticate the user */
+    private String authnMethod;        
+    
+    /** The session id */
+    private String sessionID;
+    
+    
+    /** Creates a new instance of LoginContext */
+    public LoginContext() {
+    }
+    
+    
+    /**
+     * Creates a new instance of LoginContext
+     *
+     * @param forceAuth if the authentication manager must reauth the user.
+     * @param passiveAuth if the authentication manager must not interact with the users UI. 
+     */
+    public LoginContext(boolean forceAuth, boolean passiveAuth) {
+        
+        this.forceAuth = forceAuth;
+        this.passiveAuth = passiveAuth;
+    }    
+    
+    
+    /**
+     * Returns if authentication must be forced.
+     *
+     * @return <code>true</code> if the authentication manager must reauth the user.
+     */
+    public boolean getForceAuth() {
+        return this.forceAuth;
+    }
+    
+    
+    /**
+     * Returns if authentication must be passive.
+     * 
+     * @return <code>true</code> if the authentication manager must not interact with the users UI.
+     */
+    public boolean getPassiveAuth() {
+        return this.passiveAuth;
+    }
+    
+    
+    /**
+     * Sets if authentication must be forced.
+     *
+     * @param forceAuth if the authentication manager must reauth the user.
+     */
+    public void setForceAuth(boolean forceAuth) {
+        this.forceAuth = forceAuth;
+    }
+    
+    
+    /**
+     * Sets if authentication must be passive.
+     * 
+     * @param passiveAuth if the authentication manager must not interact with the users UI.
+     */
+    public void setPassiveAuth(boolean passiveAuth) {
+        this.passiveAuth = passiveAuth;
+    }
+    
+    
+    /**
+     * Get an optional property object.
+     * 
+     * @param key The key in the properites Map.
+     * 
+     * @return The object, or <code>null</code> is no object exists for the key.
+     */
+    public Object getProperty(String key) {
+        return this.propsMap.get(key);
+    }
+    
+    
+    /**
+     * Sets an optional property object.
+     * 
+     * If an object is already associated with key, it will be overwritten.
+     * 
+     * @param key The key to set.
+     * @param obj The object to associate with key.
+     */
+    public void setProperty(String key, final Object obj) {
+        this.propsMap.put(key, obj);
+    }
+    
+    /**
+     * Sets if authentication succeeded.
+     * 
+     * @param authnOK if authentication succeeded;
+     */
+    public void setAuthnOK(boolean authnOK) {
+        this.authenticationOK = authnOK;
+    }
+    
+    
+    /**
+     * Returns if authentication succeeded.
+     * 
+     * @return <code>true</code> is the user was successfully authenticated.
+     */
+    public boolean getAuthnOK() {
+        return this.authenticationOK;
+    }
+    
+    
+    /** Sets the optional authentication failure message.
+     * 
+     * @param failureMessage A description of why authN failed.
+     */ 
+    public void setAuthnFailureMessage(String failureMessage) {
+        this.authnFailureMessage = failureMessage;
+    }
+    
+    
+    /**
+     * Returns the optional authentication failure message.
+     * 
+     * @return The failure message, or <code>null</code> is none was set.
+     */
+    public String getAuthnFailureMessage() {
+        return this.authnFailureMessage;
+    }
+    
+    
+    /**
+     * Set if authentication has been attempted.
+     *
+     * This method should be called by an {@link AuthenticationHandler} 
+     * while processing a request.
+     */
+    public void setAuthenticationAttempted() {
+       this.authnAttempted = true;
+    }
+    
+    
+    /**
+     * Returns if authentication has been attempted for this user.
+     */
+    public boolean getAuthenticationAttempted() {
+       return this.authnAttempted;
+    }
+    
+    
+    /**
+     * Sets the ID of the authenticated user.
+     * 
+     * @param userID The userid.
+     */
+    public void setUserID(String userID) {
+        this.userID = userID;
+    }
+    
+    
+    /**
+     * Returns the ID of the authenticated user.
+     * 
+     * @return the ID of the user, or <code>null</code> if authentication failed.
+     */
+    public String getUserID() {
+        return this.userID;
+    }
+    
+    
+    /**
+     * Gets the ProfileHandler URL.
+     *
+     * @return the URL of the profile handler that is invoking the Authentication Manager.
+     */
+    public String getProfileHandlerURL() {
+       return this.profileHandlerURL;
+    }
+    
+    
+    /**
+     * Sets the ProfileHandler URL.
+     *
+     * @param profileHandlerURL The URL of the profile handler that invoked the AuthenticationManager/
+     */
+    public void setProfileHandlerURL(String profileHandlerURL) {
+       this.profileHandlerURL = profileHandlerURL;
+    }
+    
+    
+    /**
+     * Gets the AuthenticationManager URL.
+     *
+     * @return the URL of the AuthenticationManager.
+     */
+    public String getAuthnManagerURL() {
+       return this.authnManagerURL;
+    }
+    
+    
+    /**
+     * Sets the AuthenticationManager's URL.
+     *
+     * @param authnManagerURL the URL of the AuthenticationManager.
+     */
+    public void setAuthnManagerURL(String authnManagerURL) {
+       this.authnManagerURL = authnManagerURL;
+    }
+    
+    
+    /**
+     * Gets the authentication instant.
+     *
+     * @return The instant of authentication, or <code>null</code> if none was set.
+     */
+    public DateTime getAuthenticationInstant() {
+           return this.authnInstant;
+    }
+    
+    
+    /**
+     * Sets the authentication instant.
+     *
+     * @param authnInstant The instant of authentication.
+     */
+    public void setAuthenticationInstant(final DateTime authnInstant) {
+       this.authnInstant = authnInstant;
+    }
+    
+    
+    /**
+     * Gets the duration of authentication.
+     *
+     * @return The duration of authentication, or zero if none was set.
+     */
+    public long getAuthenticationDuration() {
+       return this.authnDuration;
+    }
+    
+    
+    /**
+     * Sets the duration of authentication.
+     *
+     * @param authnDuration The duration of authentication.
+     */
+    public void setAuthenticationDuration(long authnDuration) {
+       this.authnDuration = authnDuration;
+    }
+    
+    
+    /**
+     * Gets the method used to authenticate the user.
+     *
+     * @return The method used to authenticate the user.
+     */
+    public String getAuthenticationMethod() {
+       return this.authnMethod;
+    }
+    
+    
+    /**
+     * Sets the method used to authenticate the user.
+     *
+     * @param authnMethod The method used to authenticate the user.
+     */
+    public void setAuthenticationMethod(String authnMethod) {
+       this.authnMethod = authnMethod;
+    }
+    
+    
+    /**
+     * Gets the {@link Session} ID
+     *
+     * @return the Session id.
+     */
+    public String getSessionID() {
+       return this.sessionID;
+    }
+    
+    
+    /**
+     * Sets the {@link Session} ID
+     *
+     * @param sessionID the Session ID
+     */
+    public void setSessionID(String sessionID) {
+       this.sessionID = sessionID;
+    }
+}
diff --git a/src/edu/internet2/middleware/shibboleth/idp/authn/Saml2LoginContext.java b/src/edu/internet2/middleware/shibboleth/idp/authn/Saml2LoginContext.java
new file mode 100644 (file)
index 0000000..094aa9a
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.authn;
+
+
+import bsh.This;
+import java.util.Map;
+import javolution.util.FastMap;
+
+import org.opensaml.saml2.core.RequestedAuthnContext;
+
+/**
+ * Login context created by a profile handler and interpreted
+ * by the authentication package.
+ * 
+ * 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.
+ */
+public class Saml2LoginContext extends LoginContext {
+    
+    /** The {@link RequestedAuthnContext} */
+    private RequestedAuthnContext ctx;
+    
+    
+    /** Creates a new instance of LoginContext */
+    public Saml2LoginContext() {
+    }
+    
+    
+    /**
+     * Creates a new instance of LoginContext
+     *
+     * @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.
+     */
+    public Saml2LoginContext(boolean forceAuth, boolean passiveAuth, final RequestedAuthnContext ctx) {
+        
+       super(forceAuth, passiveAuth);
+       
+       this.ctx = ctx;
+    }    
+    
+    
+    /**
+     * Set the requested authentication context.
+     *
+     * @param ctx The requested authN context.
+     */
+    public void setRequestedAuthnContext(RequestedAuthnContext ctx) {
+       this.ctx = ctx;
+    }
+    
+    
+    /**
+     * Returns the requested authentication context.
+     *
+     * @return the RequestedAuthnContext, or <code>null</code> if none was set.
+     */
+    public RequestedAuthnContext getRequestedAuthnContext() {
+       return this.ctx;
+    }
+    
+}
diff --git a/src/edu/internet2/middleware/shibboleth/idp/authn/impl/AuthenticationHandlerInfo.java b/src/edu/internet2/middleware/shibboleth/idp/authn/impl/AuthenticationHandlerInfo.java
new file mode 100644 (file)
index 0000000..5647537
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.authn.impl;
+
+
+import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationHandler;
+
+
+/**
+ * Wrapper class around {@link AuthenticationHandler} metadata.
+ *
+ * This class wraps three pieces of metadata:
+ * <code>supportsPassive</code>, a boolean indicating if the handler supports passive authN
+ * <code>supportsForce</code>, a boolean indicating if the handler supports forced authN
+ * <code>handler</code>, a reference to an {@link AuthenticationHandler} servlet
+ */
+public class AuthenticationHandlerInfo {
+    
+    /** does the handler support passive authN */
+    private boolean supportsPassive;
+    
+    /** does the handler support forced authN */
+    private boolean supportsForce;
+    
+    /** the {@link AuthenticationHandler itself */
+    private AuthenticationHandler handler;
+    
+    
+    /**
+     * constructor.
+     *
+     * @param handler The {@link AuthenticationHandler} reference.
+     * @param supportsPassive does the handler supports passive authN
+     * @param supportsForce does the handler supports forced authN
+     *
+     * @throws IllegalArgumentException if handler is <code>null</code>
+     */
+    public AuthenticationHandlerInfo(AuthenticationHandler handler,
+            boolean supportsPassive, boolean supportsForce) throws IllegalArgumentException {
+        
+        if (handler == null) {
+            throw new IllegalArgumentException("handler is null");
+        }
+        
+        
+        this.supportsPassive = supportsPassive;
+        this.supportsForce = supportsForce;
+    }
+    
+    
+    /**
+     * Getter for the {@link AuthenticationHandler} itself.
+     *
+     * @return the AuthenticationHandler reference.
+     */
+    public AuthenticationHandler getHandler() {
+        return this.handler;
+    }
+    
+    
+    /**
+     * Getter for supportsPassive.
+     *
+     * @return if the handler supports passive authN.
+     */
+    public boolean supportsPassive() {
+        return this.supportsPassive;
+    }
+    
+    
+    /**
+     * Getter for supportsForce
+     *
+     * @return if the handler supports forced authN.
+     */
+    public boolean supportsForce() {
+        return this.supportsForce;
+    }
+}
\ No newline at end of file
index e189392..1b302d9 100644 (file)
  */
 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
 
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.List;
+
+
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
+import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationManager;
+
+import javolution.util.FastList;
+
+import org.apache.log4j.Logger;
+
+import org.opensaml.Configuration;
+import org.opensaml.DefaultBootstrap;
+import org.opensaml.common.xml.ParserPoolManager;
+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.GetComplete;
+import org.opensaml.saml2.core.IDPEntry;
+import org.opensaml.saml2.core.IDPList;
+import org.opensaml.saml2.core.LogoutRequest;
+import org.opensaml.saml2.core.RequestedAuthnContext;
+import org.opensaml.saml2.core.Scoping;
+import org.opensaml.xml.ConfigurationException;
+import org.opensaml.xml.io.Unmarshaller;
+import org.opensaml.xml.io.UnmarshallingException;
+import org.opensaml.xml.parse.XMLParserException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.xml.sax.InputSource;
+
 
 /**
  * SAML 2.0 Authentication Request profile handler
  */
 public class AuthenticationRequest implements ProfileHandler {
-
+    
+    private static final Logger log =
+            Logger.getLogger(AuthenticationRequest.class.getName());
+    
+    
+    /** AuthenticationManager to be used */
+    private AuthenticationManager authnMgr;
+    
+    
     /** {@inheritDoc} */
     public boolean processRequest(ServletRequest request, ServletResponse response) throws ServletException {
-        // TODO Auto-generated method stub
+        
+    
+//        // Check if we are in scope to handle this AuthnRequest
+//        // XXX: How do we get the current IdP's relying party uri (if we're in multiple feds?)
+//        boolean scopeOK = this.checkScope(authnRequest, "");
+//        if (!scopeOK) {
+//            log.error("AuthnRequest contains a Scoping element which "
+//                    + "does not contain a providerID registered with this IdP.");
+//        }
+       
+       
         return false;
     }
-}
\ No newline at end of file
+
+    
+    /**
+     * Check if an {@link AuthnRequest} contains a {@link Scoping} element.
+     * If so, check if the specified IdP is in the {@link IDPList} element.
+     * If no Scoping element is present, this method returns <code>true</code>.
+     *
+     * @param authnRequest The {@link AuthnRequest} element to check.
+     * @param providerId The IdP's ProviderID
+     *
+     * @return <code>true</code>if idp is in the IDPList, otherwise <code>false</code>
+     */
+    private boolean checkScope(final AuthnRequest authnRequest, String providerId) {
+        
+        List<String> idpEntries = new FastList<String>();
+        
+        if (authnRequest == null) {
+            return (false);
+        }
+        
+        if (providerId == null) {
+            return (false);
+        }
+        
+        Scoping scoping = authnRequest.getScoping();
+        if (scoping == null) {
+            return (true);
+        }
+        
+        // process all of the explicitly listed idp provider ids
+        IDPList idpList = scoping.getIDPList();
+        if (idpList == null) {
+            return (true);
+        }
+        
+        List<IDPEntry> explicitIDPEntries = idpList.getIDPEntrys();
+        if (explicitIDPEntries != null) {
+            for (IDPEntry entry : explicitIDPEntries) {
+                String s = entry.getProviderID();
+                if (s != null) {
+                    idpEntries.add(s);
+                }
+            }
+        }
+        
+        
+        // If the IDPList is incomplete, retrieve the complete list
+        // and add the entries to idpEntries.
+        GetComplete getComplete = idpList.getGetComplete();
+        IDPList referencedIdPs = this.getCompleteIDPList(getComplete);
+        if (referencedIdPs != null) {
+            List<IDPEntry> referencedIDPEntries = referencedIdPs.getIDPEntrys();
+            if (referencedIDPEntries != null) {
+                for (IDPEntry entry : referencedIDPEntries) {
+                    String s = entry.getProviderID();
+                    if (s != null) {
+                        idpEntries.add(s);
+                    }
+                }
+            }
+        }
+        
+        
+        // iterate over all the IDPEntries we've gathered, 
+        // and check if we're in scope.
+        boolean found = false;
+        for (String requestProviderId : idpEntries) {
+            if (providerId.equals(requestProviderId)) {
+                found = true;
+                log.debug("Found Scoping match for IdP: (" 
+                            + providerId + ")");
+                break;
+            }
+        }
+        
+        return (found);
+    }
+
+    
+    /**
+     * Retrieve an incomplete IDPlist.
+     *
+     * This only handles URL-based <GetComplete/> references.
+     *
+     * @param getComplete The (possibly <code>null</code>) &lt;GetComplete/&gt; element
+     *
+     * @return an {@link IDPList} or <code>null</code> if the uri can't be dereferenced.
+     */
+    private IDPList getCompleteIDPList(GetComplete getComplete) {
+        
+        // XXX: enhance this method to cache the url and last-modified-header
+        
+        if (getComplete == null) {
+            return (null);
+        }
+        
+        String uri = getComplete.getGetComplete();
+        if (uri != null) {
+            return (null);
+        }
+        
+        
+        IDPList idpList = null;
+        InputStream istream = null;
+        
+        try {
+            URL url = new URL(uri);
+            URLConnection conn = url.openConnection();
+            istream = conn.getInputStream();
+            
+            // convert the raw data into an XML object
+            DefaultBootstrap.bootstrap();
+            ParserPoolManager parserMgr = ParserPoolManager.getInstance();
+            Document doc = parserMgr.parse(new InputSource(istream));
+            Element docElement = doc.getDocumentElement();
+            Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(docElement);
+            idpList = (IDPList) unmarshaller.unmarshall(docElement);
+            
+        } catch (MalformedURLException ex) {
+            log.error("Unable to retrieve GetComplete IDPList. Unsupported URI: " + uri);
+        } catch (IOException ex) {
+            log.error("IO Error while retreieving GetComplete IDPList from " + uri, ex);
+        } catch (ConfigurationException ex) {
+            log.error("Internal OpenSAML error while parsing GetComplete IDPList from " + uri, ex);
+        } catch (XMLParserException ex) {
+            log.error("Internal OpenSAML error while parsing GetComplete IDPList from " + uri, ex);
+        } catch (UnmarshallingException ex) {
+            log.error("Internal OpenSAML error while unmarshalling GetComplete IDPList from " + uri, ex);
+        } finally {
+            if (istream != null) {
+                try {
+                    istream.close();
+                } catch (IOException ex) {
+                    // nothing to do here.
+                }
+            }
+        }
+        
+        return idpList;
+    }
+}
index dfc2c69..0439444 100644 (file)
@@ -35,7 +35,7 @@ public interface AuthenticationMethodInformation {
      * 
      * @return time the user authenticated with this member
      */
-    public DateTime getAuthenticationInstance();
+    public DateTime getAuthenticationInstant();
 
     /**
      * Gets the duration of this authentication method.
diff --git a/src/edu/internet2/middleware/shibboleth/idp/session/impl/AuthenticationMethodInformationImpl.java b/src/edu/internet2/middleware/shibboleth/idp/session/impl/AuthenticationMethodInformationImpl.java
new file mode 100644 (file)
index 0000000..136593b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.session.impl;
+
+import org.joda.time.DateTime;
+
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
+
+
+/**
+ * Information about an authentication method employed by a user.
+ */
+public class AuthenticationMethodInformationImpl implements AuthenticationMethodInformation {
+    
+    /** The authentication method (a URI) */
+    private String authenticationMethod;
+    
+    /** The timestamp at which authentication occurred */
+    private DateTime authenticationInstant;
+    
+    /** The lifetime of the authentication method */
+    private long authenticationDuration;
+    
+    
+    /**
+     * Default constructor
+     *
+     * @param authenticationMethod The unique identifier for the authentication method.
+     * @param authenticationInstant The time the user authenticated with this member.
+     * @param authenticationDuration The duration of this authentication method.
+     */
+    public AuthenticationMethodInformationImpl(final String authenticationMethod,
+           final DateTime authenticationInstant, long authenticationDuration) {
+       
+       if (authenticationMethod == null || authenticationInstant == null
+               || authenticationDuration < 0) {
+           return;
+       }
+       
+       this.authenticationMethod = authenticationMethod;
+       this.authenticationInstant = authenticationInstant;
+       this.authenticationDuration = authenticationDuration;
+    }
+    
+    
+    /**
+     * "Cloning" constructor.
+     *
+     * @param methodInfo The {@link AuthenticationMethodInfo} to duplicate.
+     */
+    public AuthenticationMethodInformationImpl(final AuthenticationMethodInformation methodInfo) {
+       
+       if (methodInfo == null) {
+           return;
+       }
+       
+       this.authenticationMethod = methodInfo.getAuthenticationMethod();
+       this.authenticationInstant = methodInfo.getAuthenticationInstant();
+       this.authenticationDuration = methodInfo.getAuthenticationDuration();
+    }
+    
+    
+    /** {@inheritDoc} */
+    public String getAuthenticationMethod() {
+       return this.authenticationMethod;
+    }
+    
+    
+    /** {@inheritDoc} */
+    public DateTime getAuthenticationInstant() {
+       return this.authenticationInstant;
+    }
+    
+    
+    /** {@inheritDoc} */
+    public long getAuthenticationDuration() {
+       return this.authenticationDuration;
+    }
+    
+    
+    /** {@inheritDoc} */
+    public boolean equals(Object obj) {
+       
+       if (!(obj instanceof AuthenticationMethodInformation)) {
+           return false;
+       }
+       
+       AuthenticationMethodInformation amInfo = (AuthenticationMethodInformation)obj;
+       
+       if (this.getAuthenticationMethod().equals(amInfo.getAuthenticationMethod())
+           && this.getAuthenticationInstant().equals(amInfo.getAuthenticationInstant())
+           && this.getAuthenticationDuration() == amInfo.getAuthenticationDuration()) {
+           
+           return true;
+       } else {
+           return false;
+       }
+    }
+}
diff --git a/src/edu/internet2/middleware/shibboleth/idp/session/impl/ServiceInformationImpl.java b/src/edu/internet2/middleware/shibboleth/idp/session/impl/ServiceInformationImpl.java
new file mode 100644 (file)
index 0000000..2a278e0
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.session.impl;
+
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
+import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
+
+import org.joda.time.DateTime;
+import org.opensaml.saml2.core.NameID;
+
+
+/**
+ * Information about a service a user has logged in to.
+ */
+public class ServiceInformationImpl implements ServiceInformation {
+
+    private String entityID;
+
+    private DateTime authenticationInstant;
+
+    private AuthenticationMethodInformation methodInfo;
+    
+    private NameID nameId;
+
+    
+    /**
+     * Default constructor.
+     * 
+     * @param entityID The unique identifier for the service.
+     * @param authenticationInstant The time the user authenticated to the service. 
+     * @param methodInfo The authentication method used to log into the service.
+     * @param nameId The {@link NameID} used for the subject/user with this service.
+     * 
+     */
+    public ServiceInformationImpl(String entityID, DateTime authenticationInstant,
+        AuthenticationMethodInfo methodInfo, final NameID nameId) {
+        
+        if (entityID == null || authenticationInstant == null
+                       || methodInfo == null || nameId == null) {
+                
+               return;
+        }
+        
+        this.entityID = entityID;
+        this.authenticationInstant = authenticationInstant;
+        this.methodInfo = methodInfo;
+        this.nameId = nameId;  // XXX: Don't we need to defensively clone nameId? It's mutable.
+    }
+
+
+    /** 
+     * Cloning constructor.
+     * 
+     * @param serviceInfo The ServiceInformation instance to duplicate.
+     */
+    public ServiceInformationImpl(final ServiceInformation serviceInfo) {
+    
+        if (serviceInfo == null) {
+                return;
+        }
+        
+        this.entityID = serviceInfo.getEntityID();
+        this.authenticationInstant = serviceInfo.getAuthenticationInstant();
+        this.methodInfo = serviceInfo.getAuthenticationMethod();
+        this.nameId = serviceInfo.getSubjectNameID();
+    }
+
+
+    /** {@inheritDoc} */
+    public String getEntityID() {
+        return this.entityID;
+    }
+
+       
+    /** {@inheritDoc} */
+    public DateTime getAuthenticationInstant() {
+        return this.authenticationInstant;
+    }
+
+       
+    /** {@inheritDoc} */
+    public AuthenticationMethodInformation getAuthenticationMethod() {
+        return this.methodInfo;
+    }
+       
+       
+    /** {@inheritDoc} */
+    public NameID getSubjectNameID() {
+        return this.nameId;
+    }
+    
+    
+    /** {@inheritDoc} */
+    public boolean equals(Object obj) {
+       
+       if (! obj instanceof ServiceInformation) {
+               return false;
+       }
+       
+       ServiceInformation si = (ServiceInformation)obj;
+       
+       if (this.getEntityID().equals(si.getEntityID())
+                       && this.getAuthenticationInstant().equals(si.getAuthenticationInstant())
+                       && this.getSubjectNameID().equals(si.getSubjecTNameID())) {
+               
+               return true;
+       } else {
+               return false;
+       }
+    }
+}
\ No newline at end of file
diff --git a/src/edu/internet2/middleware/shibboleth/idp/session/impl/SessionImpl.java b/src/edu/internet2/middleware/shibboleth/idp/session/impl/SessionImpl.java
new file mode 100644 (file)
index 0000000..f3437f4
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.session.impl;
+
+import java.util.List;
+
+import javolution.util.FastList;
+
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
+import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
+
+// implementation note: 
+// pay attention to package names in this file!
+//
+// this class is shib.idp.session.impl.SessionImpl. It implements the shib.idp.session.Session
+// interface. that interface, in turn, extends shib.common.session.Session, which is implemented
+// in shib.common.session.impl.SessionImpl.
+
+
+/**
+ * Session information for user logged into the IdP.
+ */
+public class SessionImpl
+       extends edu.internet2.middleware.shibboleth.common.session.impl.SessionImpl
+       implements Session {
+    
+    /** The list of methods used to authentictate the user */
+    private List<AuthenticationMethodInformation> authnMethods =
+           new FastList<AuthenticationMethodInformation>();
+    
+    /** The list of services to which the user has logged in */
+    private List<ServiceInformation> servicesInformation =
+           new FastList<ServiceInformation>();
+
+    
+    /**
+     * Default constructor.
+     * 
+     * @param principalID The principal ID of the user
+     */
+    public SessionImpl(String principalID) {
+       
+       super(principalID);
+    }
+    
+    
+    /** {@inheritDoc} */
+    public List<AuthenticationMethodInformation> getAuthenticationMethods() {
+    
+       // XXX : This is suspect. One should not return
+       // a reference to a private mutable object. The Session
+       // interface should have methods for adding and removing
+       // AuthenticationMethodInformation and ServicesInformation
+       // entries. Further, the Session interface assumes that 
+       // the implementation will return a thread-safe List. Not
+       // all List implementations are thread-safe.
+           
+       return this.authnMethods;
+    }
+
+
+    /** {@inheritDoc} */
+    public List<ServiceInformation> getServicesInformation() {
+       
+       // XXX: warning: Potentially dangerous. see above note.
+       
+       return this.servicesInformation;
+    }
+    
+}