2 // ========================================================================
3 // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.security;
21 import java.io.IOException;
22 import java.security.Principal;
23 import java.util.Collection;
24 import java.util.Enumeration;
25 import java.util.HashMap;
29 import javax.servlet.ServletException;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32 import javax.servlet.http.HttpSessionEvent;
33 import javax.servlet.http.HttpSessionListener;
35 import org.eclipse.jetty.security.authentication.DeferredAuthentication;
36 import org.eclipse.jetty.server.Authentication;
37 import org.eclipse.jetty.server.Handler;
38 import org.eclipse.jetty.server.HttpChannel;
39 import org.eclipse.jetty.server.Request;
40 import org.eclipse.jetty.server.Response;
41 import org.eclipse.jetty.server.UserIdentity;
42 import org.eclipse.jetty.server.handler.ContextHandler;
43 import org.eclipse.jetty.server.handler.ContextHandler.Context;
44 import org.eclipse.jetty.server.handler.HandlerWrapper;
45 import org.eclipse.jetty.server.session.AbstractSession;
46 import org.eclipse.jetty.util.log.Log;
47 import org.eclipse.jetty.util.log.Logger;
50 * Abstract SecurityHandler.
51 * Select and apply an {@link Authenticator} to a request.
53 * The Authenticator may either be directly set on the handler
54 * or will be create during {@link #start()} with a call to
55 * either the default or set AuthenticatorFactory.
57 * SecurityHandler has a set of initparameters that are used by the
58 * Authentication.Configuration. At startup, any context init parameters
59 * that start with "org.eclipse.jetty.security." that do not have
60 * values in the SecurityHandler init parameters, are copied.
63 public abstract class SecurityHandler extends HandlerWrapper implements Authenticator.AuthConfiguration
65 private static final Logger LOG = Log.getLogger(SecurityHandler.class);
67 /* ------------------------------------------------------------ */
68 private boolean _checkWelcomeFiles = false;
69 private Authenticator _authenticator;
70 private Authenticator.Factory _authenticatorFactory=new DefaultAuthenticatorFactory();
71 private String _realmName;
72 private String _authMethod;
73 private final Map<String,String> _initParameters=new HashMap<String,String>();
74 private LoginService _loginService;
75 private IdentityService _identityService;
76 private boolean _renewSession=true;
78 /* ------------------------------------------------------------ */
79 protected SecurityHandler()
81 addBean(_authenticatorFactory);
84 /* ------------------------------------------------------------ */
85 /** Get the identityService.
86 * @return the identityService
89 public IdentityService getIdentityService()
91 return _identityService;
94 /* ------------------------------------------------------------ */
95 /** Set the identityService.
96 * @param identityService the identityService to set
98 public void setIdentityService(IdentityService identityService)
101 throw new IllegalStateException("Started");
102 updateBean(_identityService,identityService);
103 _identityService = identityService;
106 /* ------------------------------------------------------------ */
107 /** Get the loginService.
108 * @return the loginService
111 public LoginService getLoginService()
113 return _loginService;
116 /* ------------------------------------------------------------ */
117 /** Set the loginService.
118 * @param loginService the loginService to set
120 public void setLoginService(LoginService loginService)
123 throw new IllegalStateException("Started");
124 updateBean(_loginService,loginService);
125 _loginService = loginService;
129 /* ------------------------------------------------------------ */
130 public Authenticator getAuthenticator()
132 return _authenticator;
135 /* ------------------------------------------------------------ */
136 /** Set the authenticator.
137 * @param authenticator
138 * @throws IllegalStateException if the SecurityHandler is running
140 public void setAuthenticator(Authenticator authenticator)
143 throw new IllegalStateException("Started");
144 updateBean(_authenticator,authenticator);
145 _authenticator = authenticator;
146 if (_authenticator!=null)
147 _authMethod=_authenticator.getAuthMethod();
150 /* ------------------------------------------------------------ */
152 * @return the authenticatorFactory
154 public Authenticator.Factory getAuthenticatorFactory()
156 return _authenticatorFactory;
159 /* ------------------------------------------------------------ */
161 * @param authenticatorFactory the authenticatorFactory to set
162 * @throws IllegalStateException if the SecurityHandler is running
164 public void setAuthenticatorFactory(Authenticator.Factory authenticatorFactory)
167 throw new IllegalStateException("running");
168 updateBean(_authenticatorFactory,authenticatorFactory);
169 _authenticatorFactory = authenticatorFactory;
172 /* ------------------------------------------------------------ */
174 * @return the realmName
177 public String getRealmName()
182 /* ------------------------------------------------------------ */
184 * @param realmName the realmName to set
185 * @throws IllegalStateException if the SecurityHandler is running
187 public void setRealmName(String realmName)
190 throw new IllegalStateException("running");
191 _realmName = realmName;
194 /* ------------------------------------------------------------ */
196 * @return the authMethod
199 public String getAuthMethod()
204 /* ------------------------------------------------------------ */
206 * @param authMethod the authMethod to set
207 * @throws IllegalStateException if the SecurityHandler is running
209 public void setAuthMethod(String authMethod)
212 throw new IllegalStateException("running");
213 _authMethod = authMethod;
216 /* ------------------------------------------------------------ */
218 * @return True if forwards to welcome files are authenticated
220 public boolean isCheckWelcomeFiles()
222 return _checkWelcomeFiles;
225 /* ------------------------------------------------------------ */
227 * @param authenticateWelcomeFiles True if forwards to welcome files are
229 * @throws IllegalStateException if the SecurityHandler is running
231 public void setCheckWelcomeFiles(boolean authenticateWelcomeFiles)
234 throw new IllegalStateException("running");
235 _checkWelcomeFiles = authenticateWelcomeFiles;
238 /* ------------------------------------------------------------ */
240 public String getInitParameter(String key)
242 return _initParameters.get(key);
245 /* ------------------------------------------------------------ */
247 public Set<String> getInitParameterNames()
249 return _initParameters.keySet();
252 /* ------------------------------------------------------------ */
253 /** Set an initialization parameter.
256 * @return previous value
257 * @throws IllegalStateException if the SecurityHandler is running
259 public String setInitParameter(String key, String value)
262 throw new IllegalStateException("running");
263 return _initParameters.put(key,value);
266 /* ------------------------------------------------------------ */
267 protected LoginService findLoginService() throws Exception
269 Collection<LoginService> list = getServer().getBeans(LoginService.class);
270 LoginService service = null;
271 String realm=getRealmName();
274 for (LoginService s : list)
275 if (s.getName()!=null && s.getName().equals(realm))
281 else if (list.size()==1)
282 service = list.iterator().next();
287 /* ------------------------------------------------------------ */
288 protected IdentityService findIdentityService()
290 return getServer().getBean(IdentityService.class);
293 /* ------------------------------------------------------------ */
297 protected void doStart()
300 // copy security init parameters
301 ContextHandler.Context context =ContextHandler.getCurrentContext();
304 Enumeration<String> names=context.getInitParameterNames();
305 while (names!=null && names.hasMoreElements())
307 String name =names.nextElement();
308 if (name.startsWith("org.eclipse.jetty.security.") &&
309 getInitParameter(name)==null)
310 setInitParameter(name,context.getInitParameter(name));
313 //register a session listener to handle securing sessions when authentication is performed
314 context.getContextHandler().addEventListener(new HttpSessionListener()
317 public void sessionDestroyed(HttpSessionEvent se)
322 public void sessionCreated(HttpSessionEvent se)
324 //if current request is authenticated, then as we have just created the session, mark it as secure, as it has not yet been returned to a user
325 HttpChannel<?> channel = HttpChannel.getCurrentHttpChannel();
329 Request request = channel.getRequest();
333 if (request.isSecure())
335 se.getSession().setAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
341 // complicated resolution of login and identity service to handle
342 // many different ways these can be constructed and injected.
344 if (_loginService==null)
346 setLoginService(findLoginService());
347 if (_loginService!=null)
348 unmanage(_loginService);
351 if (_identityService==null)
353 if (_loginService!=null)
354 setIdentityService(_loginService.getIdentityService());
356 if (_identityService==null)
357 setIdentityService(findIdentityService());
359 if (_identityService==null)
361 if (_realmName!=null)
363 setIdentityService(new DefaultIdentityService());
364 manage(_identityService);
368 unmanage(_identityService);
371 if (_loginService!=null)
373 if (_loginService.getIdentityService()==null)
374 _loginService.setIdentityService(_identityService);
375 else if (_loginService.getIdentityService()!=_identityService)
376 throw new IllegalStateException("LoginService has different IdentityService to "+this);
379 Authenticator.Factory authenticatorFactory = getAuthenticatorFactory();
380 if (_authenticator==null && authenticatorFactory!=null && _identityService!=null)
381 setAuthenticator(authenticatorFactory.getAuthenticator(getServer(),ContextHandler.getCurrentContext(),this, _identityService, _loginService));
383 if (_authenticator!=null)
384 _authenticator.setConfiguration(this);
385 else if (_realmName!=null)
387 LOG.warn("No Authenticator for "+this);
388 throw new IllegalStateException("No Authenticator");
395 /* ------------------------------------------------------------ */
396 protected void doStop() throws Exception
398 //if we discovered the services (rather than had them explicitly configured), remove them.
399 if (!isManaged(_identityService))
401 removeBean(_identityService);
402 _identityService = null;
405 if (!isManaged(_loginService))
407 removeBean(_loginService);
414 /* ------------------------------------------------------------ */
415 protected boolean checkSecurity(Request request)
417 switch(request.getDispatcherType())
423 if (isCheckWelcomeFiles() && request.getAttribute("org.eclipse.jetty.server.welcome") != null)
425 request.removeAttribute("org.eclipse.jetty.server.welcome");
434 /* ------------------------------------------------------------ */
436 * @see org.eclipse.jetty.security.Authenticator.AuthConfiguration#isSessionRenewedOnAuthentication()
439 public boolean isSessionRenewedOnAuthentication()
441 return _renewSession;
444 /* ------------------------------------------------------------ */
445 /** Set renew the session on Authentication.
447 * If set to true, then on authentication, the session associated with a reqeuest is invalidated and replaced with a new session.
448 * @see org.eclipse.jetty.security.Authenticator.AuthConfiguration#isSessionRenewedOnAuthentication()
450 public void setSessionRenewedOnAuthentication(boolean renew)
455 /* ------------------------------------------------------------ */
457 * @see org.eclipse.jetty.server.Handler#handle(java.lang.String,
458 * javax.servlet.http.HttpServletRequest,
459 * javax.servlet.http.HttpServletResponse, int)
462 public void handle(String pathInContext, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
464 final Response base_response = baseRequest.getResponse();
465 final Handler handler=getHandler();
470 final Authenticator authenticator = _authenticator;
472 if (checkSecurity(baseRequest))
474 //See Servlet Spec 3.1 sec 13.6.3
475 if (authenticator != null)
476 authenticator.prepareRequest(baseRequest);
478 RoleInfo roleInfo = prepareConstraintInfo(pathInContext, baseRequest);
480 // Check data constraints
481 if (!checkUserDataPermissions(pathInContext, baseRequest, base_response, roleInfo))
483 if (!baseRequest.isHandled())
485 response.sendError(HttpServletResponse.SC_FORBIDDEN);
486 baseRequest.setHandled(true);
491 // is Auth mandatory?
492 boolean isAuthMandatory =
493 isAuthMandatory(baseRequest, base_response, roleInfo);
495 if (isAuthMandatory && authenticator==null)
497 LOG.warn("No authenticator for: "+roleInfo);
498 if (!baseRequest.isHandled())
500 response.sendError(HttpServletResponse.SC_FORBIDDEN);
501 baseRequest.setHandled(true);
506 // check authentication
507 Object previousIdentity = null;
510 Authentication authentication = baseRequest.getAuthentication();
511 if (authentication==null || authentication==Authentication.NOT_CHECKED)
512 authentication=authenticator==null?Authentication.UNAUTHENTICATED:authenticator.validateRequest(request, response, isAuthMandatory);
514 if (authentication instanceof Authentication.Wrapped)
516 request=((Authentication.Wrapped)authentication).getHttpServletRequest();
517 response=((Authentication.Wrapped)authentication).getHttpServletResponse();
520 if (authentication instanceof Authentication.ResponseSent)
522 baseRequest.setHandled(true);
524 else if (authentication instanceof Authentication.User)
526 Authentication.User userAuth = (Authentication.User)authentication;
527 baseRequest.setAuthentication(authentication);
528 if (_identityService!=null)
529 previousIdentity = _identityService.associate(userAuth.getUserIdentity());
533 boolean authorized=checkWebResourcePermissions(pathInContext, baseRequest, base_response, roleInfo, userAuth.getUserIdentity());
536 response.sendError(HttpServletResponse.SC_FORBIDDEN, "!role");
537 baseRequest.setHandled(true);
542 handler.handle(pathInContext, baseRequest, request, response);
543 if (authenticator!=null)
544 authenticator.secureResponse(request, response, isAuthMandatory, userAuth);
546 else if (authentication instanceof Authentication.Deferred)
548 DeferredAuthentication deferred= (DeferredAuthentication)authentication;
549 baseRequest.setAuthentication(authentication);
553 handler.handle(pathInContext, baseRequest, request, response);
557 previousIdentity = deferred.getPreviousAssociation();
560 if (authenticator!=null)
562 Authentication auth=baseRequest.getAuthentication();
563 if (auth instanceof Authentication.User)
565 Authentication.User userAuth = (Authentication.User)auth;
566 authenticator.secureResponse(request, response, isAuthMandatory, userAuth);
569 authenticator.secureResponse(request, response, isAuthMandatory, null);
574 baseRequest.setAuthentication(authentication);
575 if (_identityService!=null)
576 previousIdentity = _identityService.associate(null);
577 handler.handle(pathInContext, baseRequest, request, response);
578 if (authenticator!=null)
579 authenticator.secureResponse(request, response, isAuthMandatory, null);
582 catch (ServerAuthException e)
584 // jaspi 3.8.3 send HTTP 500 internal server error, with message
585 // from AuthException
586 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
590 if (_identityService!=null)
591 _identityService.disassociate(previousIdentity);
595 handler.handle(pathInContext, baseRequest, request, response);
599 /* ------------------------------------------------------------ */
600 public static SecurityHandler getCurrentSecurityHandler()
602 Context context = ContextHandler.getCurrentContext();
606 return context.getContextHandler().getChildHandlerByClass(SecurityHandler.class);
609 /* ------------------------------------------------------------ */
610 public void logout(Authentication.User user)
612 LOG.debug("logout {}",user);
613 LoginService login_service=getLoginService();
614 if (login_service!=null)
616 login_service.logout(user.getUserIdentity());
619 IdentityService identity_service=getIdentityService();
620 if (identity_service!=null)
622 // TODO recover previous from threadlocal (or similar)
623 Object previous=null;
624 identity_service.disassociate(previous);
628 /* ------------------------------------------------------------ */
629 protected abstract RoleInfo prepareConstraintInfo(String pathInContext, Request request);
631 /* ------------------------------------------------------------ */
632 protected abstract boolean checkUserDataPermissions(String pathInContext, Request request, Response response, RoleInfo constraintInfo) throws IOException;
634 /* ------------------------------------------------------------ */
635 protected abstract boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo);
637 /* ------------------------------------------------------------ */
638 protected abstract boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo,
639 UserIdentity userIdentity) throws IOException;
642 /* ------------------------------------------------------------ */
643 /* ------------------------------------------------------------ */
644 public class NotChecked implements Principal
647 public String getName()
653 public String toString()
655 return "NOT CHECKED";
658 public SecurityHandler getSecurityHandler()
660 return SecurityHandler.this;
665 /* ------------------------------------------------------------ */
666 /* ------------------------------------------------------------ */
667 public static final Principal __NO_USER = new Principal()
670 public String getName()
676 public String toString()
682 /* ------------------------------------------------------------ */
683 /* ------------------------------------------------------------ */
685 * Nobody user. The Nobody UserPrincipal is used to indicate a partial state
686 * of authentication. A request with a Nobody UserPrincipal will be allowed
687 * past all authentication constraints - but will not be considered an
688 * authenticated request. It can be used by Authenticators such as
689 * FormAuthenticator to allow access to logon and error pages within an
690 * authenticated URI tree.
692 public static final Principal __NOBODY = new Principal()
695 public String getName()
701 public String toString()