All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.ee8.security.SecurityHandler Maven / Gradle / Ivy

There is a newer version: 2.0.31
Show newest version
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee8.security;

import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.ee8.nested.Authentication;
import org.eclipse.jetty.ee8.nested.ContextHandler;
import org.eclipse.jetty.ee8.nested.ContextHandler.APIContext;
import org.eclipse.jetty.ee8.nested.Handler;
import org.eclipse.jetty.ee8.nested.HandlerWrapper;
import org.eclipse.jetty.ee8.nested.Request;
import org.eclipse.jetty.ee8.nested.Response;
import org.eclipse.jetty.ee8.security.authentication.DeferredAuthentication;
import org.eclipse.jetty.security.DefaultIdentityService;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.UserIdentity;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Select and apply an {@link Authenticator} to a request.
 * 

* The Authenticator may either be directly set on the handler * or will be created during {@link #start()} with a call to * either the default or set AuthenticatorFactory. *

* SecurityHandler has a set of initparameters that are used by the * Authentication.Configuration. At startup, any context init parameters * that start with "org.eclipse.jetty.security." that do not have * values in the SecurityHandler init parameters, are copied. */ public abstract class SecurityHandler extends HandlerWrapper implements Authenticator.AuthConfiguration { private static final Logger LOG = LoggerFactory.getLogger(SecurityHandler.class); private static final List __knownAuthenticatorFactories = new ArrayList<>(); private boolean _checkWelcomeFiles = false; private Authenticator _authenticator; private Authenticator.Factory _authenticatorFactory; private String _realmName; private String _authMethod; private final Map _initParameters = new HashMap<>(); private LoginService _loginService; private IdentityService _identityService; private boolean _renewSessionOnAuthentication = true; private int _sessionMaxInactiveIntervalOnAuthentication = 0; static { TypeUtil.serviceStream(ServiceLoader.load(Authenticator.Factory.class)).forEach(__knownAuthenticatorFactories::add); __knownAuthenticatorFactories.add(new DefaultAuthenticatorFactory()); } protected SecurityHandler() { installBean(new DumpableCollection("knownAuthenticatorFactories", __knownAuthenticatorFactories)); } /** * Get the identityService. * * @return the identityService */ @Override public IdentityService getIdentityService() { return _identityService; } /** * Set the identityService. * * @param identityService the identityService to set */ public void setIdentityService(IdentityService identityService) { if (isStarted()) throw new IllegalStateException("Started"); updateBean(_identityService, identityService); _identityService = identityService; } /** * Get the loginService. * * @return the loginService */ @Override public LoginService getLoginService() { return _loginService; } /** * Set the loginService. * * @param loginService the loginService to set */ public void setLoginService(LoginService loginService) { if (isStarted()) throw new IllegalStateException("Started"); updateBean(_loginService, loginService); _loginService = loginService; } public Authenticator getAuthenticator() { return _authenticator; } /** * Set the authenticator. * * @param authenticator the authenticator * @throws IllegalStateException if the SecurityHandler is running */ public void setAuthenticator(Authenticator authenticator) { if (isStarted()) throw new IllegalStateException("Started"); updateBean(_authenticator, authenticator); _authenticator = authenticator; if (_authenticator != null) _authMethod = _authenticator.getAuthMethod(); } /** * @return the authenticatorFactory */ public Authenticator.Factory getAuthenticatorFactory() { return _authenticatorFactory; } /** * @param authenticatorFactory the authenticatorFactory to set * @throws IllegalStateException if the SecurityHandler is running */ public void setAuthenticatorFactory(Authenticator.Factory authenticatorFactory) { if (isRunning()) throw new IllegalStateException("running"); updateBean(_authenticatorFactory, authenticatorFactory); _authenticatorFactory = authenticatorFactory; } /** * @return the list of discovered authenticatorFactories */ public List getKnownAuthenticatorFactories() { return __knownAuthenticatorFactories; } /** * @return the realmName */ @Override public String getRealmName() { return _realmName; } /** * @param realmName the realmName to set * @throws IllegalStateException if the SecurityHandler is running */ public void setRealmName(String realmName) { if (isRunning()) throw new IllegalStateException("running"); _realmName = realmName; } /** * @return the authMethod */ @Override public String getAuthMethod() { return _authMethod; } /** * @param authMethod the authMethod to set * @throws IllegalStateException if the SecurityHandler is running */ public void setAuthMethod(String authMethod) { if (isRunning()) throw new IllegalStateException("running"); _authMethod = authMethod; } /** * @return True if forwards to welcome files are authenticated */ public boolean isCheckWelcomeFiles() { return _checkWelcomeFiles; } /** * @param authenticateWelcomeFiles True if forwards to welcome files are * authenticated * @throws IllegalStateException if the SecurityHandler is running */ public void setCheckWelcomeFiles(boolean authenticateWelcomeFiles) { if (isRunning()) throw new IllegalStateException("running"); _checkWelcomeFiles = authenticateWelcomeFiles; } @Override public String getInitParameter(String key) { return _initParameters.get(key); } @Override public Set getInitParameterNames() { return _initParameters.keySet(); } /** * Set an initialization parameter. * * @param key the init key * @param value the init value * @return previous value * @throws IllegalStateException if the SecurityHandler is started */ public String setInitParameter(String key, String value) { if (isStarted()) throw new IllegalStateException("started"); return _initParameters.put(key, value); } protected LoginService findLoginService() throws Exception { Collection list = getServer().getBeans(LoginService.class); LoginService service = null; String realm = getRealmName(); if (realm != null) { for (LoginService s : list) { if (s.getName() != null && s.getName().equals(realm)) { service = s; break; } } } else if (list.size() == 1) service = list.iterator().next(); return service; } protected IdentityService findIdentityService() { return getServer().getBean(IdentityService.class); } @Override protected void doStart() throws Exception { // copy security init parameters APIContext context = ContextHandler.getCurrentContext(); if (context != null) { Enumeration names = context.getInitParameterNames(); while (names != null && names.hasMoreElements()) { String name = names.nextElement(); if (name.startsWith("org.eclipse.jetty.security.") && getInitParameter(name) == null) setInitParameter(name, context.getInitParameter(name)); } } // complicated resolution of login and identity service to handle // many different ways these can be constructed and injected. if (_loginService == null) { setLoginService(findLoginService()); if (_loginService != null) unmanage(_loginService); } if (_identityService == null) { if (_loginService != null) setIdentityService(_loginService.getIdentityService()); if (_identityService == null) setIdentityService(findIdentityService()); if (_identityService == null) { setIdentityService(new DefaultIdentityService()); manage(_identityService); } else unmanage(_identityService); } if (_loginService != null) { if (_loginService.getIdentityService() == null) _loginService.setIdentityService(_identityService); else if (_loginService.getIdentityService() != _identityService) throw new IllegalStateException("LoginService has different IdentityService to " + this); } if (_authenticator == null) { // If someone has set an authenticator factory only use that, otherwise try the list of discovered factories. if (_authenticatorFactory != null) { Authenticator authenticator = _authenticatorFactory.getAuthenticator(getServer(), ContextHandler.getCurrentContext(), this, _identityService, _loginService); if (authenticator != null) { if (LOG.isDebugEnabled()) LOG.debug("Created authenticator {} with {}", authenticator, _authenticatorFactory); setAuthenticator(authenticator); } } else { for (Authenticator.Factory factory : getKnownAuthenticatorFactories()) { Authenticator authenticator = factory.getAuthenticator(getServer(), ContextHandler.getCurrentContext(), this, _identityService, _loginService); if (authenticator != null) { if (LOG.isDebugEnabled()) LOG.debug("Created authenticator {} with {}", authenticator, factory); setAuthenticator(authenticator); break; } } } } if (_authenticator != null) _authenticator.setConfiguration(this); else if (_realmName != null) { LOG.warn("No Authenticator for {}", this); throw new IllegalStateException("No Authenticator"); } super.doStart(); } @Override protected void doStop() throws Exception { //if we discovered the services (rather than had them explicitly configured), remove them. if (!isManaged(_identityService)) { removeBean(_identityService); _identityService = null; } if (!isManaged(_loginService)) { removeBean(_loginService); _loginService = null; } super.doStop(); } protected boolean checkSecurity(Request request) { switch(request.getDispatcherType()) { case REQUEST: case ASYNC: return true; case FORWARD: if (isCheckWelcomeFiles() && request.getAttribute("org.eclipse.jetty.server.welcome") != null) { request.removeAttribute("org.eclipse.jetty.server.welcome"); return true; } return false; default: return false; } } @Override public boolean isSessionRenewedOnAuthentication() { return _renewSessionOnAuthentication; } /** * Set renew the session on Authentication. *

* If set to true, then on authentication, the session associated with a reqeuest is invalidated and replaced with a new session. * * @param renew true to renew the authentication on session * @see Authenticator.AuthConfiguration#isSessionRenewedOnAuthentication() */ public void setSessionRenewedOnAuthentication(boolean renew) { _renewSessionOnAuthentication = renew; } @Override public int getSessionMaxInactiveIntervalOnAuthentication() { return _sessionMaxInactiveIntervalOnAuthentication; } /** * Set the interval in seconds, which if non-zero, will be set with * {@link javax.servlet.http.HttpSession#setMaxInactiveInterval(int)} * when a session is newly authenticated. * @param seconds An interval in seconds; or 0 to not set the interval * on authentication; or a negative number to make the * session never timeout after authentication. */ public void setSessionMaxInactiveIntervalOnAuthentication(int seconds) { _sessionMaxInactiveIntervalOnAuthentication = seconds; } /* * @see org.eclipse.jetty.server.Handler#handle(java.lang.String, * jakarta.servlet.http.HttpServletRequest, * jakarta.servlet.http.HttpServletResponse, int) */ @Override public void handle(String pathInContext, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { final Response base_response = baseRequest.getResponse(); final Handler handler = getHandler(); if (handler == null) return; final Authenticator authenticator = _authenticator; if (checkSecurity(baseRequest)) { //See Servlet Spec 3.1 sec 13.6.3 if (authenticator != null) authenticator.prepareRequest(baseRequest); RoleInfo roleInfo = prepareConstraintInfo(pathInContext, baseRequest); // Check data constraints if (!checkUserDataPermissions(pathInContext, baseRequest, base_response, roleInfo)) { if (!baseRequest.isHandled()) { response.sendError(HttpServletResponse.SC_FORBIDDEN); baseRequest.setHandled(true); } return; } // is Auth mandatory? boolean isAuthMandatory = isAuthMandatory(baseRequest, base_response, roleInfo); if (isAuthMandatory && authenticator == null) { LOG.warn("No authenticator for: {}", roleInfo); if (!baseRequest.isHandled()) { response.sendError(HttpServletResponse.SC_FORBIDDEN); baseRequest.setHandled(true); } return; } // check authentication IdentityService.Association identityAssociation = null; try { Authentication authentication = baseRequest.getAuthentication(); if (authentication == null || authentication == Authentication.NOT_CHECKED) authentication = authenticator == null ? Authentication.UNAUTHENTICATED : authenticator.validateRequest(request, response, isAuthMandatory); if (authentication instanceof Authentication.Wrapped) { request = ((Authentication.Wrapped) authentication).getHttpServletRequest(); response = ((Authentication.Wrapped) authentication).getHttpServletResponse(); } if (authentication instanceof Authentication.ResponseSent) { baseRequest.setHandled(true); } else if (authentication instanceof Authentication.User) { Authentication.User userAuth = (Authentication.User) authentication; baseRequest.setAuthentication(authentication); if (_identityService != null) { UserIdentity user = userAuth.getUserIdentity(); identityAssociation = _identityService.associate(user, null); } if (isAuthMandatory) { boolean authorized = checkWebResourcePermissions(pathInContext, baseRequest, base_response, roleInfo, userAuth.getUserIdentity()); if (!authorized) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "!role"); baseRequest.setHandled(true); return; } } handler.handle(pathInContext, baseRequest, request, response); if (authenticator != null) authenticator.secureResponse(request, response, isAuthMandatory, userAuth); } else if (authentication instanceof Authentication.Deferred) { DeferredAuthentication deferred = (DeferredAuthentication) authentication; baseRequest.setAuthentication(authentication); try { handler.handle(pathInContext, baseRequest, request, response); } finally { identityAssociation = deferred.getAssociation(); } if (authenticator != null) { Authentication auth = baseRequest.getAuthentication(); if (auth instanceof Authentication.User) { Authentication.User userAuth = (Authentication.User) auth; authenticator.secureResponse(request, response, isAuthMandatory, userAuth); } else authenticator.secureResponse(request, response, isAuthMandatory, null); } } else if (isAuthMandatory) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "unauthenticated"); baseRequest.setHandled(true); } else { baseRequest.setAuthentication(authentication); if (_identityService != null) identityAssociation = _identityService.associate(null, null); handler.handle(pathInContext, baseRequest, request, response); if (authenticator != null) authenticator.secureResponse(request, response, isAuthMandatory, null); } } catch (ServerAuthException e) { // jaspi 3.8.3 send HTTP 500 internal server error, with message // from AuthException response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } finally { IO.close(identityAssociation); } } else handler.handle(pathInContext, baseRequest, request, response); } public static SecurityHandler getCurrentSecurityHandler() { APIContext context = ContextHandler.getCurrentContext(); if (context == null) return null; return context.getContextHandler().getChildHandlerByClass(SecurityHandler.class); } public void logout(Authentication.User user) { LOG.debug("logout {}", user); if (user == null) return; LoginService loginService = getLoginService(); if (loginService != null) { loginService.logout(user.getUserIdentity()); } IdentityService identityService = getIdentityService(); if (identityService != null) identityService.onLogout(user.getUserIdentity()); } protected abstract RoleInfo prepareConstraintInfo(String pathInContext, Request request); protected abstract boolean checkUserDataPermissions(String pathInContext, Request request, Response response, RoleInfo constraintInfo) throws IOException; protected abstract boolean isAuthMandatory(Request baseRequest, Response baseResponse, Object constraintInfo); protected abstract boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity) throws IOException; public class NotChecked implements Principal { @Override public String getName() { return null; } @Override public String toString() { return "NOT CHECKED"; } public SecurityHandler getSecurityHandler() { return SecurityHandler.this; } } public static final Principal __NO_USER = new Principal() { @Override public String getName() { return null; } @Override public String toString() { return "No User"; } }; /** * Nobody user. The Nobody UserPrincipal is used to indicate a partial state * of authentication. A request with a Nobody UserPrincipal will be allowed * past all authentication constraints - but will not be considered an * authenticated request. It can be used by Authenticators such as * FormAuthenticator to allow access to logon and error pages within an * authenticated URI tree. */ public static final Principal __NOBODY = new Principal() { @Override public String getName() { return "Nobody"; } @Override public String toString() { return getName(); } }; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy