3dbe8ded9856c87ec4e0fafa1510cb510d219eea
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / authn / AuthenticationManager.java
1 /*
2  * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package edu.internet2.middleware.shibboleth.idp.authn;
18
19 import java.io.IOException;
20 import java.util.List;
21 import java.util.Map;
22
23 import javax.servlet.RequestDispatcher;
24 import javax.servlet.ServletContext;
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpSession;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import edu.internet2.middleware.shibboleth.idp.session.impl.AuthenticationMethodInformationImpl;
31
32 import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
33 import edu.internet2.middleware.shibboleth.idp.session.Session;
34 import edu.internet2.middleware.shibboleth.idp.session.SessionManager;
35
36 import javolution.util.FastMap;
37
38 import org.apache.log4j.Logger;
39
40 import org.springframework.web.servlet.HttpServletBean;
41
42
43 /**
44  * Manager responsible for handling authentication requests.
45  */
46 public class AuthenticationManager extends HttpServletBean {
47
48         /** log4j. */    
49     private static final Logger log =
50         Logger.getLogger(AuthenticationManager.class.getName());
51     
52     /** SessionManager to be used. */
53     private SessionManager sessionMgr;
54     
55     /** Map of URIs onto AuthenticationHandlerInfo. */
56     private FastMap<String, AuthenticationHandler> handlerMap
57         = new FastMap<String, AuthenticationHandler>();
58     
59     /** The default AuthenticationHandler. */
60     private AuthenticationHandler defaultHandler;
61     
62     /* The URI for the default AuthenticationHandler. */
63     private String defaultHandlerURI;
64
65         
66     /**
67      * Gets the session manager to be used.
68      *
69      * @return session manager to be used
70      */
71     public SessionManager getSessionManager() {
72         return sessionMgr;
73     }
74     
75     
76     /**
77      * Sets the session manager to be used.
78      *
79      * @param manager session manager to be used.
80      */
81     public void setSessionManager(final SessionManager manager) {
82         sessionMgr = manager;
83     }
84     
85     
86     /**
87      * Get the map of {@link AuthenticationHandlers}.
88      *
89      * @return The map of AuthenticationHandlers
90      */
91     public Map<String, AuthenticationHandler> getHandlerMap() {
92     
93         return new FastMap<String, AuthenticationHandler>(handlerMap);
94     }
95     
96     
97     /**
98      * Set the {@link AuthenticationHandler} map.
99      *
100      * @param handlerMap The Map of URIs to AuthenticationHandlers
101      */
102     public void setHandlerMap(final Map<String, AuthenticationHandler> handlerMap) {
103     
104         for (String uri : handlerMap.keySet()) {
105             addHandlerMapping(uri, handlerMap.get(uri));
106         }
107     }
108     
109     
110     /**
111      * Add a <code>&lt;String:AuthenticationHandler&gr;</code> mapping to the
112      * AuthenticationManager's table. If a mapping for the URI
113      * already exists, it will be overwritten.
114      *
115      * The URI SHOULD be from the saml-autn-context-2.0-os
116      *
117      * @param uri A URI identifying the authentcation method.
118      * @param handler The AuthenticationHandler.
119      */
120     public void addHandlerMapping(String uri, AuthenticationHandler handler) {
121     
122         if (uri == null || handler == null) {
123             return;
124         }
125     
126         log.debug("registering " + handler.getClass().getName()
127             + " for " + uri);
128     
129         handlerMap.put(uri, handler);
130     }
131     
132     
133     /**
134      * Register the default {@link AuthenticationHandler}.
135      *
136      * @param uri The URI of the default authentication handler (from saml-authn-context-2.0-os)
137      * @param handler The default {@link AuthenticationHandler}.
138      */
139     public void setDefaultHandler(String uri, AuthenticationHandler handler) {
140     
141         log.debug("Registering default handler "
142             + handler.getClass().getName());
143     
144         defaultHandler = handler;
145         defaultHandlerURI = uri;
146     }
147     
148     
149     /**
150      * Remove a <String:AuthenticationHandler> mapping from the
151      * AuthenticationManager's table.
152      *
153      * The URI SHOULD be from the saml-authn-context-2.0-os
154      *
155      * @param uri A URI identifying the authentcation method.
156      */
157     public void removeHandlerMapping(String uri) {
158     
159         if (uri == null) {
160             return;
161         }
162     
163         log.debug("unregistering handler for " + uri);
164     
165         handlerMap.remove(uri);
166     }
167     
168     
169     
170     /**
171      * Primary entrypoint for the AuthnManager.
172      *
173      * @param req The ServletRequest.
174      * @param resp The ServletResponse.
175      */
176     public void doPost(HttpServletRequest req,
177         HttpServletResponse resp) throws ServletException, IOException {
178     
179         if (req == null || resp == null) {
180             log.error("Invalid parameters in AuthenticationManager's doPost().");
181             return;
182         }
183     
184         HttpSession httpSession = req.getSession();
185         Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
186         if (! (o instanceof LoginContext)) {
187             log.error("Invalid login context object -- object is not an instance of LoginContext.");
188             return;
189         }
190         LoginContext loginContext = (LoginContext)o;
191     
192         // If authentication has been attempted, don't try it again.
193         if (loginContext.getAuthenticationAttempted()) {
194             handleNewAuthnRequest(loginContext, req, resp);
195         } else {
196             finishAuthnRequest(loginContext, req, resp);
197         }
198     }
199     
200     
201     
202     /**
203      * Handle a new authentication request.
204      *
205      * @param loginContext The {@link LoginContext} for the new authentication request
206      * @param servletRequest The servlet request containing the authn request
207      * @param servletResponse The associated servlet response.
208      */
209     private void handleNewAuthnRequest(final LoginContext loginContext,
210         final HttpServletRequest servletRequest,
211         final HttpServletResponse servletResponse) throws ServletException, IOException {
212     
213         boolean forceAuthN = loginContext.getForceAuth();
214         boolean passiveAuthN = loginContext.getPassiveAuth();
215     
216         // set that authentication has been attempted, to prevent processing loops
217         loginContext.setAuthenticationAttempted();
218     
219         // if the profile handler set a list of requested authn methods,
220         // evaluate them. otherwise, evaluate the default handler.
221         String[] requestedAuthnMethods = loginContext.getRequestedAuthenticationMethods();
222         AuthenticationHandler handler = null;
223     
224         if (requestedAuthnMethods == null) {
225
226             // if no authn methods were specified, try the default handler
227             
228             if (evaluateHandler(defaultHandler, "default", forceAuthN, passiveAuthN)) {
229                 handler = defaultHandler;
230                 loginContext.setAuthenticationMethod(defaultHandlerURI);
231             }
232         
233         } else { 
234     
235             // evaluate all requested authn methods until we find a match.
236
237             for (String authnMethodURI : requestedAuthnMethods) {
238
239             AuthenticationHandler candidateHandler = handlerMap.get(authnMethodURI);
240             if (candidateHandler == null) {
241                 log.debug("No registered authentication handlers can satisfy the "
242                     + " requested authentication method " + authnMethodURI);
243                 continue;
244             }
245
246             if (evaluateHandler(candidateHandler, authnMethodURI, forceAuthN, passiveAuthN)) {
247
248                 // we found a match. stop iterating.
249                 handler = candidateHandler;
250                 log.info("Using authentication handler " + handler.getClass().getName()
251                     + " for authentication method " + authnMethodURI);
252                 loginContext.setAuthenticationMethod(authnMethodURI);
253                 break;
254             }
255         }
256     }
257
258     // if no acceptable handler was found, abort.
259     if (handler == null) {
260         loginContext.setAuthenticationOK(false);
261         loginContext.setAuthenticationFailureMessage(
262             "No installed AuthenticationHandler can satisfy the authentication request.");
263         
264         log.error("No registered authentication handlers could satisify any requested "
265             + "authentication methods. Unable to process authentication request.");
266         
267         RequestDispatcher dispatcher =
268             servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
269         dispatcher.forward(servletRequest, servletResponse);
270     }
271     
272     // otherwise, forward control to the AuthenticationHandler
273     ServletContext servletContext = getServletContext();
274     loginContext.setAuthenticationManagerURL(servletRequest.getPathInfo());
275     handler.login(servletRequest, servletResponse, loginContext);
276     }
277     
278     
279     /**
280      * Handle the "return leg" of an authentication request
281      * (i.e. clean up after an authentication handler has run).
282      *
283      */
284     private void finishAuthnRequest(final LoginContext loginContext,
285         final HttpServletRequest servletRequest,
286         final HttpServletResponse servletResponse) throws ServletException, IOException {
287     
288         // if authentication was successful, the authentication handler should
289         // have updated the LoginContext with additional information. Use that
290         // info to create a Session.
291         if (loginContext.getAuthenticationOK()) {
292         
293             AuthenticationMethodInformationImpl authMethodInfo =
294                 new AuthenticationMethodInformationImpl(loginContext.getAuthenticationMethod(),
295                     loginContext.getAuthenticationInstant(), loginContext.getAuthenticationDuration());
296         
297             Session shibSession = getSessionManager().createSession();
298             List<AuthenticationMethodInformation> authMethods = shibSession.getAuthenticationMethods();
299             authMethods.add(authMethodInfo);
300             loginContext.setSessionID(shibSession.getSessionID());
301         }
302     
303         RequestDispatcher dispatcher =
304             servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
305         dispatcher.forward(servletRequest, servletResponse);
306     }
307     
308     
309     /**
310      * "Stub" method for handling LogoutRequest.
311      */
312     private void handleLogoutRequest(final HttpServletRequest servletRequest,
313         final HttpServletResponse servletResponse) throws ServletException, IOException {
314     
315     }
316     
317     
318     /**
319      * Evaluate an authenticationhandler against a set of evaluation criteria.
320      *
321      * @param handler A candiate {@link AuthenticationHandler}
322      * @param description A description of the handler
323      * @param forceAuthN Is (re)authentication forced?
324      * @param passiveAuthN Can the AuthenticationHandler take control of the UI
325      *
326      * @return <code>true</code> if handler meets the criteria, otherwise <code>false</code>
327      */
328     private boolean evaluateHandler(final AuthenticationHandler handler,
329         String description, boolean forceAuthN, boolean passiveAuthN) {
330     
331         if (handler == null) {
332             return false;
333         }
334     
335         if (forceAuthN && !handler.supportsForceAuthentication()) {
336             log.debug("The RequestedAuthnContext required forced authentication, "
337                 + "but the " + description + " handler does not support that feature.");
338             return false;
339         }
340     
341         if (passiveAuthN && !handler.supportsPassive()) {
342             log.debug("The RequestedAuthnContext required passive authentication, "
343                 + "but the " + description + " handler does not support that feature.");
344             return false;
345         }
346     
347         return true;
348     }
349 }