]> WPIA git - gigi.git/blobdiff - lib/jetty/org/eclipse/jetty/security/SecurityHandler.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / security / SecurityHandler.java
diff --git a/lib/jetty/org/eclipse/jetty/security/SecurityHandler.java b/lib/jetty/org/eclipse/jetty/security/SecurityHandler.java
new file mode 100644 (file)
index 0000000..a6e108e
--- /dev/null
@@ -0,0 +1,708 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.security;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.eclipse.jetty.security.authentication.DeferredAuthentication;
+import org.eclipse.jetty.server.Authentication;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.server.UserIdentity;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.server.session.AbstractSession;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Abstract SecurityHandler.
+ * Select and apply an {@link Authenticator} to a request.
+ * <p>
+ * The Authenticator may either be directly set on the handler
+ * or will be create during {@link #start()} with a call to
+ * either the default or set AuthenticatorFactory.
+ * <p>
+ * 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 = Log.getLogger(SecurityHandler.class);
+
+    /* ------------------------------------------------------------ */
+    private boolean _checkWelcomeFiles = false;
+    private Authenticator _authenticator;
+    private Authenticator.Factory _authenticatorFactory=new DefaultAuthenticatorFactory();
+    private String _realmName;
+    private String _authMethod;
+    private final Map<String,String> _initParameters=new HashMap<String,String>();
+    private LoginService _loginService;
+    private IdentityService _identityService;
+    private boolean _renewSession=true;
+
+    /* ------------------------------------------------------------ */
+    protected SecurityHandler()
+    {
+        addBean(_authenticatorFactory);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 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
+     * @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 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<String> getInitParameterNames()
+    {
+        return _initParameters.keySet();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set an initialization parameter.
+     * @param key
+     * @param value
+     * @return previous value
+     * @throws IllegalStateException if the SecurityHandler is running
+     */
+    public String setInitParameter(String key, String value)
+    {
+        if (isRunning())
+            throw new IllegalStateException("running");
+        return _initParameters.put(key,value);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected LoginService findLoginService() throws Exception
+    {
+        Collection<LoginService> 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
+        ContextHandler.Context context =ContextHandler.getCurrentContext();
+        if (context!=null)
+        {
+            Enumeration<String> 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));
+            }
+            
+            //register a session listener to handle securing sessions when authentication is performed
+            context.getContextHandler().addEventListener(new HttpSessionListener()
+            {
+                @Override
+                public void sessionDestroyed(HttpSessionEvent se)
+                {
+                }
+
+                @Override
+                public void sessionCreated(HttpSessionEvent se)
+                {                    
+                    //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
+                    HttpChannel<?> channel = HttpChannel.getCurrentHttpChannel();              
+                    
+                    if (channel == null)
+                        return;
+                    Request request = channel.getRequest();
+                    if (request == null)
+                        return;
+                    
+                    if (request.isSecure())
+                    {
+                        se.getSession().setAttribute(AbstractSession.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
+                    }
+                }
+            });
+        }
+
+        // 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)
+            {
+                if (_realmName!=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);
+        }
+
+        Authenticator.Factory authenticatorFactory = getAuthenticatorFactory();
+        if (_authenticator==null && authenticatorFactory!=null && _identityService!=null)
+            setAuthenticator(authenticatorFactory.getAuthenticator(getServer(),ContextHandler.getCurrentContext(),this, _identityService, _loginService));
+
+        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;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.security.Authenticator.AuthConfiguration#isSessionRenewedOnAuthentication()
+     */
+    @Override
+    public boolean isSessionRenewedOnAuthentication()
+    {
+        return _renewSession;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set renew the session on Authentication.
+     * <p>
+     * If set to true, then on authentication, the session associated with a reqeuest is invalidated and replaced with a new session.
+     * @see org.eclipse.jetty.security.Authenticator.AuthConfiguration#isSessionRenewedOnAuthentication()
+     */
+    public void setSessionRenewedOnAuthentication(boolean renew)
+    {
+        _renewSession=renew;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Handler#handle(java.lang.String,
+     *      javax.servlet.http.HttpServletRequest,
+     *      javax.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
+            Object previousIdentity = 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)
+                        previousIdentity = _identityService.associate(userAuth.getUserIdentity());
+
+                    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
+                    {
+                        previousIdentity = deferred.getPreviousAssociation();
+                    }
+
+                    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
+                {
+                    baseRequest.setAuthentication(authentication);
+                    if (_identityService!=null)
+                        previousIdentity = _identityService.associate(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
+            {
+                if (_identityService!=null)
+                    _identityService.disassociate(previousIdentity);
+            }
+        }
+        else
+            handler.handle(pathInContext, baseRequest, request, response);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public static SecurityHandler getCurrentSecurityHandler()
+    {
+        Context context = ContextHandler.getCurrentContext();
+        if (context==null)
+            return null;
+
+        return context.getContextHandler().getChildHandlerByClass(SecurityHandler.class);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void logout(Authentication.User user)
+    {
+        LOG.debug("logout {}",user);
+        LoginService login_service=getLoginService();
+        if (login_service!=null)
+        {
+            login_service.logout(user.getUserIdentity());
+        }
+
+        IdentityService identity_service=getIdentityService();
+        if (identity_service!=null)
+        {
+            // TODO recover previous from threadlocal (or similar)
+            Object previous=null;
+            identity_service.disassociate(previous);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    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 base_response, 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();
+        }
+    };
+
+}