2 * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package edu.internet2.middleware.shibboleth.idp.authn;
19 import java.io.IOException;
20 import java.net.InetAddress;
21 import java.net.UnknownHostException;
22 import java.util.List;
24 import javax.servlet.RequestDispatcher;
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServlet;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29 import javax.servlet.http.HttpSession;
31 import org.apache.log4j.Logger;
32 import org.joda.time.DateTime;
33 import org.opensaml.xml.util.Pair;
35 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
36 import edu.internet2.middleware.shibboleth.common.session.SessionManager;
37 import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
38 import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
39 import edu.internet2.middleware.shibboleth.idp.session.Session;
40 import edu.internet2.middleware.shibboleth.idp.session.impl.AuthenticationMethodInformationImpl;
41 import edu.internet2.middleware.shibboleth.idp.session.impl.ServiceInformationImpl;
44 * Manager responsible for handling authentication requests.
46 public class AuthenticationEngine extends HttpServlet {
49 private static final Logger log = Logger.getLogger(AuthenticationEngine.class);
52 * Gets the manager used to retrieve handlers for requests.
54 * @return manager used to retrieve handlers for requests
56 public ProfileHandlerManager getProfileHandlerManager() {
57 return (ProfileHandlerManager) getServletContext().getAttribute("handlerManager");
61 * Gets the session manager to be used.
63 * @return session manager to be used
65 @SuppressWarnings("unchecked")
66 public SessionManager<Session> getSessionManager() {
67 return (SessionManager<Session>) getServletContext().getAttribute("sessionManager");
71 * Gets the authentication handler manager used by this engine.
73 * @return authentication handler manager used by this engine
75 public AuthenticationHandlerManager getAuthenticationHandlerManager() {
76 return (AuthenticationHandlerManager) getServletContext().getAttribute("authenticationHandlerManager");
81 * Returns control back to the authentication engine.
83 * @param httpRequest current http request
84 * @param httpResponse current http response
85 * @param loginContext user login context
87 public static void returnToAuthenticationEngine(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
88 HttpSession httpSession = httpRequest.getSession();
90 LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
93 RequestDispatcher distpather = httpRequest.getRequestDispatcher(loginContext.getAuthenticationManagerURL());
94 distpather.forward(httpRequest, httpResponse);
95 } catch (IOException e) {
96 log.fatal("Unable to return control back to authentication engine", e);
97 } catch (ServletException e) {
98 log.fatal("Unable to return control back to authentication engine", e);
103 protected void service(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException,
105 HttpSession httpSession = httpRequest.getSession();
107 LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
108 if (loginContext == null) {
112 // If authentication has been attempted, don't try it again.
113 if (loginContext.getAuthenticationAttempted()) {
114 handleNewAuthnRequest(loginContext, httpRequest, httpResponse);
116 finishAuthnRequest(loginContext, httpRequest, httpResponse);
121 * Handle a new authentication request.
123 * @param loginContext The {@link LoginContext} for the new authentication request
124 * @param httpRequest The servlet request containing the authn request
125 * @param httpResponse The associated servlet response.
127 * @throws IOException thrown if there is a problem reading/writting to the HTTP request/response
128 * @throws ServletException thrown if there is a problem transferring control to the authentication handler
130 protected void handleNewAuthnRequest(LoginContext loginContext, HttpServletRequest httpRequest,
131 HttpServletResponse httpResponse) throws ServletException, IOException {
133 HttpSession httpSession = httpRequest.getSession();
134 String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
135 Session shibSession = getSessionManager().getSession(shibSessionId);
137 AuthenticationMethodInformation authenticationMethod = getUsableExistingAuthenticationMethod(loginContext,
139 if (authenticationMethod != null) {
140 loginContext.setAuthenticationDuration(authenticationMethod.getAuthenticationDuration());
141 loginContext.setAuthenticationInstant(authenticationMethod.getAuthenticationInstant());
142 loginContext.setAuthenticationMethod(authenticationMethod.getAuthenticationMethod());
143 loginContext.setPrincipalAuthenticated(true);
144 loginContext.setPrincipalName(shibSession.getPrincipalName());
145 finishAuthnRequest(loginContext, httpRequest, httpResponse);
147 Pair<String, AuthenticationHandler> handler = getAuthenticationHandlerManager().getAuthenticationHandler(
150 if (handler == null) {
151 loginContext.setPassiveAuth(false);
153 .setAuthenticationFailureMessage("No installed AuthenticationHandler can satisfy the authentication request.");
154 log.error("No installed AuthenticationHandler can satisfy the authentication request.");
155 finishAuthnRequest(loginContext, httpRequest, httpResponse);
158 loginContext.setAuthenticationAttempted();
159 loginContext.setAuthenticationDuration(handler.getSecond().getAuthenticationDuration());
160 loginContext.setAuthenticationMethod(handler.getFirst());
161 loginContext.setAuthenticationManagerURL(httpRequest.getRequestURI());
163 httpSession.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
164 handler.getSecond().login(loginContext, httpRequest, httpResponse);
169 * Gets the authentication method, currently active for the user, that also meets the requirements expressed by the
170 * login context. If a method is returned the user does not need to authenticate again, if null is returned then the
171 * user must be authenticated.
173 * @param loginContext user login context
174 * @param shibSession user's shibboleth session
176 * @return active authentication method that meets authentication requirements or null
178 protected AuthenticationMethodInformation getUsableExistingAuthenticationMethod(LoginContext loginContext,
179 Session shibSession) {
180 if (loginContext.getForceAuth() || shibSession == null) {
184 List<String> preferredAuthnMethods = loginContext.getRequestedAuthenticationMethods();
186 if (preferredAuthnMethods == null || preferredAuthnMethods.size() == 0) {
187 for (AuthenticationMethodInformation authnMethod : shibSession.getAuthenticationMethods().values()) {
188 if (!authnMethod.isExpired()) {
193 for (String preferredAuthnMethod : preferredAuthnMethods) {
194 if (shibSession.getAuthenticationMethods().containsKey(preferredAuthnMethod)) {
195 AuthenticationMethodInformation authnMethodInfo = shibSession.getAuthenticationMethods().get(
196 preferredAuthnMethod);
197 if (!authnMethodInfo.isExpired()) {
198 return authnMethodInfo;
208 * Handle the "return leg" of an authentication request (i.e. clean up after an authentication handler has run).
210 * @param loginContext The {@link LoginContext} for the new authentication request
211 * @param httpRequest The servlet request containing the authn request
212 * @param httpResponse The associated servlet response.
214 * @throws IOException thrown if there is a problem reading/writting to the HTTP request/response
215 * @throws ServletException thrown if there is a problem transferring control to the authentication profile handler
217 protected void finishAuthnRequest(LoginContext loginContext, HttpServletRequest httpRequest,
218 HttpServletResponse httpResponse) throws ServletException, IOException {
220 HttpSession httpSession = httpRequest.getSession();
221 String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
222 Session shibSession = null;
223 AuthenticationMethodInformation authnMethodInfo = null;
224 ServiceInformation serviceInfo = null;
226 if (!loginContext.getAuthenticationAttempted()) {
227 // Authentication wasn't attempted so we're using a previously established authentication method
228 shibSession = getSessionManager().getSession(shibSessionId);
229 authnMethodInfo = shibSession.getAuthenticationMethods().get(loginContext.getAuthenticationMethod());
231 if (shibSessionId == null) {
234 addr = InetAddress.getByName(httpRequest.getRemoteAddr());
235 } catch (UnknownHostException ex) {
239 shibSession = (Session) getSessionManager().createSession(addr, loginContext.getPrincipalName());
240 httpSession.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, shibSession.getSessionID());
242 authnMethodInfo = new AuthenticationMethodInformationImpl(loginContext.getAuthenticationMethod(),
243 new DateTime(), loginContext.getAuthenticationDuration());
244 shibSession.getAuthenticationMethods().put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
248 loginContext.setSessionID(shibSession.getSessionID());
249 shibSession.setLastActivityInstant(new DateTime());
251 serviceInfo = shibSession.getServicesInformation().get(loginContext.getRelyingPartyId());
252 if (serviceInfo == null) {
253 serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(), authnMethodInfo);
256 RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
257 dispatcher.forward(httpRequest, httpResponse);
260 // TODO logout support