diff options
Diffstat (limited to 'jetty-security/src/main/java/org')
34 files changed, 3981 insertions, 0 deletions
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/Authentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/Authentication.java new file mode 100644 index 0000000000..51dd8ce815 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/Authentication.java @@ -0,0 +1,66 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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 org.eclipse.jetty.server.UserIdentity; + +/** + * Authentication state of a user. + * + * @version $Rev: 4701 $ $Date: 2009-03-03 13:01:26 +0100 (Tue, 03 Mar 2009) $ + */ +public interface Authentication +{ + public enum Status + { + SEND_FAILURE(false), SEND_SUCCESS(true), SEND_CONTINUE(false), SUCCESS(true); + boolean _success; + Status(boolean success) {_success=success; } + public boolean isSuccess(){ return _success;} + } + + Status getAuthStatus(); + + String getAuthMethod(); + + UserIdentity getUserIdentity(); + + boolean isSuccess(); + + + public static final Authentication SUCCESS_UNAUTH_RESULTS = new Authentication() + { + public String getAuthMethod() {return null;} + public Status getAuthStatus() {return Authentication.Status.SUCCESS;} + public UserIdentity getUserIdentity() {return UserIdentity.UNAUTHENTICATED_IDENTITY;} + public boolean isSuccess() {return true;} + }; + + public static final Authentication SEND_CONTINUE_RESULTS = new Authentication() + { + public String getAuthMethod() {return null;} + public Status getAuthStatus() {return Authentication.Status.SEND_CONTINUE;} + public UserIdentity getUserIdentity() {return UserIdentity.UNAUTHENTICATED_IDENTITY;} + public boolean isSuccess() {return false;} + }; + + public static final Authentication SEND_FAILURE_RESULTS = new Authentication() + { + public String getAuthMethod() {return null;} + public Status getAuthStatus() {return Authentication.Status.SEND_FAILURE;} + public UserIdentity getUserIdentity() {return UserIdentity.UNAUTHENTICATED_IDENTITY;} + public boolean isSuccess() {return false;} + }; + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java new file mode 100644 index 0000000000..f3e37aa697 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/Authenticator.java @@ -0,0 +1,53 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.util.Set; + +import javax.servlet.ServletContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.eclipse.jetty.server.Server; + +/** + * This is like the JASPI ServerAuthContext but is intended to be easier to use + * and allow lazy auth. + * + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public interface Authenticator +{ + void setConfiguration(Configuration configuration); + String getAuthMethod(); + + Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatory) throws ServerAuthException; + Authentication.Status secureResponse(ServletRequest request, ServletResponse response, boolean mandatory, Authentication validatedUser) throws ServerAuthException; + + interface Configuration + { + String getAuthMethod(); + String getRealmName(); + boolean isLazy(); + String getInitParameter(String key); + Set<String> getInitParameterNames(); + LoginService getLoginService(); + IdentityService getIdentityService(); + } + + interface Factory + { + Authenticator getAuthenticator(Server server, ServletContext context, Configuration configuration); + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintAware.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintAware.java new file mode 100644 index 0000000000..1135aa7591 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintAware.java @@ -0,0 +1,28 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.util.Set; + +/** + * @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $ + */ +public interface ConstraintAware +{ + ConstraintMapping[] getConstraintMappings(); + + Set<String> getRoles(); + + void setConstraintMappings(ConstraintMapping[] constraintMappings, Set<String> roles); +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java new file mode 100644 index 0000000000..378ff14d57 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java @@ -0,0 +1,79 @@ +// ======================================================================== +// Copyright (c) 2004-2009 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 org.eclipse.jetty.http.security.Constraint; + +public class ConstraintMapping +{ + String _method; + + String _pathSpec; + + Constraint _constraint; + + /* ------------------------------------------------------------ */ + /** + * @return Returns the constraint. + */ + public Constraint getConstraint() + { + return _constraint; + } + + /* ------------------------------------------------------------ */ + /** + * @param constraint The constraint to set. + */ + public void setConstraint(Constraint constraint) + { + this._constraint = constraint; + } + + /* ------------------------------------------------------------ */ + /** + * @return Returns the method. + */ + public String getMethod() + { + return _method; + } + + /* ------------------------------------------------------------ */ + /** + * @param method The method to set. + */ + public void setMethod(String method) + { + this._method = method; + } + + /* ------------------------------------------------------------ */ + /** + * @return Returns the pathSpec. + */ + public String getPathSpec() + { + return _pathSpec; + } + + /* ------------------------------------------------------------ */ + /** + * @param pathSpec The pathSpec to set. + */ + public void setPathSpec(String pathSpec) + { + this._pathSpec = pathSpec; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java new file mode 100644 index 0000000000..361e45e441 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java @@ -0,0 +1,316 @@ +// ======================================================================== +// Copyright (c) 1999-2009 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.util.Map; +import java.util.Set; + +import org.eclipse.jetty.http.PathMap; +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.StringMap; + +/* ------------------------------------------------------------ */ +/** + * Handler to enforce SecurityConstraints. This implementation is servlet spec + * 2.4 compliant and precomputes the constraint combinations for runtime + * efficiency. + * + */ +public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware +{ + private ConstraintMapping[] _constraintMappings; + private Set<String> _roles; + private PathMap _constraintMap = new PathMap(); + private boolean _strict = true; + + + /* ------------------------------------------------------------ */ + /** Get the strict mode. + * @return true if the security handler is running in strict mode. + */ + public boolean isStrict() + { + return _strict; + } + + /* ------------------------------------------------------------ */ + /** Set the strict mode of the security handler. + * <p> + * When in strict mode (the default), the full servlet specification + * will be implemented. + * If not in strict mode, some additional flexibility in configuration + * is allowed:<ul> + * <li>All users do not need to have a role defined in the deployment descriptor + * <li>The * role in a constraint applies to ANY role rather than all roles defined in + * the deployment descriptor. + * </ul> + * + * @param strict the strict to set + */ + public void setStrict(boolean strict) + { + _strict = strict; + } + + /* ------------------------------------------------------------ */ + /** + * @return Returns the contraintMappings. + */ + public ConstraintMapping[] getConstraintMappings() + { + return _constraintMappings; + } + + /* ------------------------------------------------------------ */ + public Set<String> getRoles() + { + return _roles; + } + + /* ------------------------------------------------------------ */ + /** + * Process the constraints following the combining rules in Servlet 3.0 EA + * spec section 13.7.1 Note that much of the logic is in the RoleInfo class. + * + * @param constraintMappings + * The contraintMappings to set. + * @param roles + */ + public void setConstraintMappings(ConstraintMapping[] constraintMappings, Set<String> roles) + { + if (isStarted()) + throw new IllegalStateException("Started"); + _constraintMappings = constraintMappings; + this._roles = roles; + + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.security.SecurityHandler#doStart() + */ + @Override + protected void doStart() throws Exception + { + _constraintMap.clear(); + if (_constraintMappings != null) + { + for (ConstraintMapping mapping : _constraintMappings) + { + Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.get(mapping.getPathSpec()); + if (mappings == null) + { + mappings = new StringMap(); + _constraintMap.put(mapping.getPathSpec(),mappings); + } + RoleInfo allMethodsRoleInfo = mappings.get(null); + if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden()) + { + continue; + } + String httpMethod = mapping.getMethod(); + RoleInfo roleInfo = mappings.get(httpMethod); + if (roleInfo == null) + { + roleInfo = new RoleInfo(); + mappings.put(httpMethod,roleInfo); + if (allMethodsRoleInfo != null) + { + roleInfo.combine(allMethodsRoleInfo); + } + } + if (roleInfo.isForbidden()) + { + continue; + } + Constraint constraint = mapping.getConstraint(); + boolean forbidden = constraint.isForbidden(); + roleInfo.setForbidden(forbidden); + if (forbidden) + { + if (httpMethod == null) + { + mappings.clear(); + mappings.put(null,roleInfo); + } + } + else + { + UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint()); + roleInfo.setUserDataConstraint(userDataConstraint); + + boolean unchecked = !constraint.getAuthenticate(); + roleInfo.setUnchecked(unchecked); + if (!roleInfo.isUnchecked()) + { + if (constraint.isAnyRole()) + { + if (_strict) + { + // * means "all defined roles" + for (String role : _roles) + roleInfo.addRole(role); + } + else + // * means any role + roleInfo.setAnyRole(true); + } + else + { + String[] newRoles = constraint.getRoles(); + for (String role : newRoles) + { + if (_strict &&!_roles.contains(role)) + throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles); + roleInfo.addRole(role); + } + } + } + if (httpMethod == null) + { + for (Map.Entry<String, RoleInfo> entry : mappings.entrySet()) + { + if (entry.getKey() != null) + { + RoleInfo specific = entry.getValue(); + specific.combine(roleInfo); + } + } + } + } + } + } + super.doStart(); + } + + protected Object prepareConstraintInfo(String pathInContext, Request request) + { + Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext); + + if (mappings != null) + { + String httpMethod = request.getMethod(); + RoleInfo roleInfo = mappings.get(httpMethod); + if (roleInfo == null) + { + roleInfo = mappings.get(null); + if (roleInfo != null) + { + return roleInfo; + } + } + } + return null; + } + + protected boolean checkUserDataPermissions(String pathInContext, Request request, Response response, Object constraintInfo) throws IOException + { + if (constraintInfo == null) + { + return true; + } + RoleInfo roleInfo = (RoleInfo)constraintInfo; + if (roleInfo.isForbidden()) + { + return false; + } + UserDataConstraint dataConstraint = roleInfo.getUserDataConstraint(); + if (dataConstraint == null || dataConstraint == UserDataConstraint.None) + { + return true; + } + HttpConnection connection = HttpConnection.getCurrentConnection(); + Connector connector = connection.getConnector(); + + if (dataConstraint == UserDataConstraint.Integral) + { + if (connector.isIntegral(request)) + return true; + if (connector.getConfidentialPort() > 0) + { + String url = connector.getIntegralScheme() + "://" + request.getServerName() + ":" + connector.getIntegralPort() + request.getRequestURI(); + if (request.getQueryString() != null) + url += "?" + request.getQueryString(); + response.setContentLength(0); + response.sendRedirect(url); + request.setHandled(true); + } + return false; + } + else if (dataConstraint == UserDataConstraint.Confidential) + { + if (connector.isConfidential(request)) + return true; + + if (connector.getConfidentialPort() > 0) + { + String url = connector.getConfidentialScheme() + "://" + request.getServerName() + ":" + connector.getConfidentialPort() + + request.getRequestURI(); + if (request.getQueryString() != null) + url += "?" + request.getQueryString(); + + response.setContentLength(0); + response.sendRedirect(url); + request.setHandled(true); + } + return false; + } + else + { + throw new IllegalArgumentException("Invalid dataConstraint value: " + dataConstraint); + } + + } + + protected boolean isAuthMandatory(Request base_request, Response base_response, Object constraintInfo) + { + if (constraintInfo == null) + { + return false; + } + return !((RoleInfo)constraintInfo).isUnchecked(); + } + + protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity) + throws IOException + { + if (constraintInfo == null) + { + return true; + } + RoleInfo roleInfo = (RoleInfo)constraintInfo; + + if (roleInfo.isUnchecked()) + { + return true; + } + + if (roleInfo.isAnyRole() && request.getAuthType()!=null) + return true; + + String[] roles = roleInfo.getRoles(); + for (String role : roles) + { + if (userIdentity.isUserInRole(role)) + return true; + } + return false; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/CrossContextPsuedoSession.java b/jetty-security/src/main/java/org/eclipse/jetty/security/CrossContextPsuedoSession.java new file mode 100644 index 0000000000..9e202beb9a --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/CrossContextPsuedoSession.java @@ -0,0 +1,31 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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 javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $ + */ +public interface CrossContextPsuedoSession<T> +{ + + T fetch(HttpServletRequest request); + + void store(T data, HttpServletResponse response); + + void clear(HttpServletRequest request); + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthentication.java new file mode 100644 index 0000000000..58b41007d5 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthentication.java @@ -0,0 +1,59 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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 org.eclipse.jetty.server.UserIdentity; + + +/** + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class DefaultAuthentication implements Authentication +{ + private final Authentication.Status _authStatus; + private final String _authMethod; + private final UserIdentity _userIdentity; + + public DefaultAuthentication(Authentication.Status authStatus, String authMethod, UserIdentity userIdentity) + { + _authStatus = authStatus; + _authMethod = authMethod; + _userIdentity=userIdentity; + } + + public String getAuthMethod() + { + return _authMethod; + } + + public Authentication.Status getAuthStatus() + { + return _authStatus; + } + + public UserIdentity getUserIdentity() + { + return _userIdentity; + } + + public boolean isSuccess() + { + return _authStatus.isSuccess(); + } + + public String toString() + { + return "{Auth,"+_authMethod+","+_authStatus+","+","+_userIdentity+"}"; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthenticatorFactory.java b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthenticatorFactory.java new file mode 100644 index 0000000000..75fa6ac8c8 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultAuthenticatorFactory.java @@ -0,0 +1,88 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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 javax.servlet.ServletContext; + +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.security.Authenticator.Configuration; +import org.eclipse.jetty.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.security.authentication.ClientCertAuthenticator; +import org.eclipse.jetty.security.authentication.DigestAuthenticator; +import org.eclipse.jetty.security.authentication.FormAuthenticator; +import org.eclipse.jetty.security.authentication.LazyAuthenticator; +import org.eclipse.jetty.security.authentication.SessionCachingAuthenticator; +import org.eclipse.jetty.server.Server; + +/* ------------------------------------------------------------ */ +/** + * The Default Authenticator Factory. + * Uses the {@link Configuration#getAuthMethod()} to select an {@link Authenticator} from: <ul> + * <li>{@link BasicAuthenticator}</li> + * <li>{@link DigestAuthenticator}</li> + * <li>{@link FormAuthenticator}</li> + * <li>{@link ClientCertAuthenticator}</li> + * </ul> + * If {@link Configuration#isLazy()} is true, the Authenticator is wrapped with a {@link LazyAuthenticator} + * instance. The FormAuthenticator is always wrapped in a {@link SessionCachingAuthenticator}. + * <p> + * If a {@link LoginService} has not been set on this factory, then + * the service is selected by searching the {@link Server#getBeans(Class)} results for + * a service that matches the realm name, else the first LoginService found is used. + * + */ +public class DefaultAuthenticatorFactory implements Authenticator.Factory +{ + LoginService _loginService; + + public Authenticator getAuthenticator(Server server, ServletContext context, Configuration configuration) + { + String auth=configuration.getAuthMethod(); + Authenticator authenticator=null; + + if (auth==null || Constraint.__BASIC_AUTH.equalsIgnoreCase(auth)) + authenticator=new BasicAuthenticator(); + else if (Constraint.__DIGEST_AUTH.equalsIgnoreCase(auth)) + authenticator=new DigestAuthenticator(); + else if (Constraint.__FORM_AUTH.equalsIgnoreCase(auth)) + authenticator=new SessionCachingAuthenticator(new FormAuthenticator()); + if (Constraint.__CERT_AUTH.equalsIgnoreCase(auth)||Constraint.__CERT_AUTH2.equalsIgnoreCase(auth)) + authenticator=new ClientCertAuthenticator(); + + if (configuration.isLazy() && authenticator!=null) + authenticator=new LazyAuthenticator(authenticator); + + return authenticator; + } + + + /* ------------------------------------------------------------ */ + /** + * @return the loginService + */ + public LoginService getLoginService() + { + return _loginService; + } + + /* ------------------------------------------------------------ */ + /** + * @param loginService the loginService to set + */ + public void setLoginService(LoginService loginService) + { + _loginService = loginService; + } + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultIdentityService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultIdentityService.java new file mode 100644 index 0000000000..ada4b36d4d --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultIdentityService.java @@ -0,0 +1,119 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.security.Principal; +import java.util.Map; + +import javax.security.auth.Subject; + +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.server.UserIdentity.Scope; + + +/* ------------------------------------------------------------ */ +/** + * Default Identity Service implementation. + * This service handles only role reference maps passed in an + * associated {@link UserIdentity.Scope}. If there are roles + * refs present, then associate will wrap the UserIdentity with one + * that uses the role references in the {@link UserIdentity#isUserInRole(String)} + * implementation. All other operations are effectively noops. + * + */ +public class DefaultIdentityService implements IdentityService<UserIdentity, RoleRunAsToken> +{ + public DefaultIdentityService() + { + } + + /* ------------------------------------------------------------ */ + /** + * If there are roles refs present in the scope, then wrap the UserIdentity + * with one that uses the role references in the {@link UserIdentity#isUserInRole(String)} + */ + public UserIdentity associate(UserIdentity user, Scope scope) + { + Map<String,String> roleRefMap=scope.getRoleRefMap(); + if (roleRefMap!=null && roleRefMap.size()>0) + return new RoleRefUserIdentity(user,roleRefMap); + return user; + } + + public void disassociate(UserIdentity scoped) + { + } + + public RoleRunAsToken associateRunAs(RunAsToken token) + { + return null; + } + + public void disassociateRunAs(RoleRunAsToken lastToken) + { + } + + public RunAsToken newRunAsToken(String runAsName) + { + return new RoleRunAsToken(runAsName); + } + + public UserIdentity newSystemUserIdentity() + { + return null; + } + + public UserIdentity newUserIdentity(final Subject subject, final Principal userPrincipal, final String[] roles) + { + return new DefaultUserIdentity(subject,userPrincipal,roles); + } + + /* ------------------------------------------------------------ */ + /** + * Wrapper UserIdentity used to apply RoleRef map. + * + */ + public static class RoleRefUserIdentity implements UserIdentity + { + final private UserIdentity _delegate; + final private Map<String,String> _roleRefMap; + + public RoleRefUserIdentity(final UserIdentity user, final Map<String, String> roleRefMap) + { + _delegate=user; + _roleRefMap=roleRefMap; + } + + public String[] getRoles() + { + return _delegate.getRoles(); + } + + public Subject getSubject() + { + return _delegate.getSubject(); + } + + public Principal getUserPrincipal() + { + return _delegate.getUserPrincipal(); + } + + public boolean isUserInRole(String role) + { + String link=_roleRefMap.get(role); + return _delegate.isUserInRole(link==null?role:link); + } + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java new file mode 100644 index 0000000000..07580f00e3 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/DefaultUserIdentity.java @@ -0,0 +1,71 @@ +// ======================================================================== +// Copyright (c) 2009-2009 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.security.Principal; + +import javax.security.auth.Subject; + +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.security.Authentication.Status; +import org.eclipse.jetty.server.UserIdentity; + + +/* ------------------------------------------------------------ */ +/** + * The default implementation of UserIdentity. + * + */ +public class DefaultUserIdentity implements UserIdentity +{ + /* Cache successful authentications for BASIC and DIGEST to avoid creation on every request */ + public final Authentication SUCCESSFUL_BASIC = new DefaultAuthentication(Status.SUCCESS,Constraint.__BASIC_AUTH,this); + public final Authentication SUCCESSFUL_DIGEST = new DefaultAuthentication(Status.SUCCESS,Constraint.__BASIC_AUTH,this); + + private final Subject _subject; + private final Principal _userPrincipal; + private final String[] _roles; + + public DefaultUserIdentity(Subject subject, Principal userPrincipal, String[] roles) + { + _subject=subject; + _userPrincipal=userPrincipal; + _roles=roles; + } + + public String[] getRoles() + { + return _roles; + } + + public Subject getSubject() + { + return _subject; + } + + public Principal getUserPrincipal() + { + return _userPrincipal; + } + + public boolean isUserInRole(String role) + { + for (String r :_roles) + if (r.equals(role)) + return true; + return false; + } + + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/HashCrossContextPsuedoSession.java b/jetty-security/src/main/java/org/eclipse/jetty/security/HashCrossContextPsuedoSession.java new file mode 100644 index 0000000000..06c589b03a --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/HashCrossContextPsuedoSession.java @@ -0,0 +1,90 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @version $Rev: 4660 $ $Date: 2009-02-25 17:29:53 +0100 (Wed, 25 Feb 2009) $ + */ +public class HashCrossContextPsuedoSession<T> implements CrossContextPsuedoSession<T> +{ + private final String _cookieName; + + private final String _cookiePath; + + private final Random _random = new SecureRandom(); + + private final Map<String, T> _data = new HashMap<String, T>(); + + public HashCrossContextPsuedoSession(String cookieName, String cookiePath) + { + this._cookieName = cookieName; + this._cookiePath = cookiePath == null ? "/" : cookiePath; + } + + public T fetch(HttpServletRequest request) + { + for (Cookie cookie : request.getCookies()) + { + if (_cookieName.equals(cookie.getName())) + { + String key = cookie.getValue(); + return _data.get(key); + } + } + return null; + } + + public void store(T datum, HttpServletResponse response) + { + String key; + + synchronized (_data) + { + // Create new ID + while (true) + { + key = Long.toString(Math.abs(_random.nextLong()), 30 + (int) (System.currentTimeMillis() % 7)); + if (!_data.containsKey(key)) break; + } + + _data.put(key, datum); + } + + Cookie cookie = new Cookie(_cookieName, key); + cookie.setPath(_cookiePath); + response.addCookie(cookie); + } + + public void clear(HttpServletRequest request) + { + for (Cookie cookie : request.getCookies()) + { + if (_cookieName.equals(cookie.getName())) + { + String key = cookie.getValue(); + _data.remove(key); + break; + } + } + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java new file mode 100644 index 0000000000..f82590390b --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java @@ -0,0 +1,247 @@ +// ======================================================================== +// Copyright (c) 1996-2009 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.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.Scanner; +import org.eclipse.jetty.util.Scanner.BulkListener; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.Resource; + +/* ------------------------------------------------------------ */ +/** + * Properties User Realm. + * + * An implementation of UserRealm that stores users and roles in-memory in + * HashMaps. + * <P> + * Typically these maps are populated by calling the load() method or passing a + * properties resource to the constructor. The format of the properties file is: + * + * <PRE> + * username: password [,rolename ...] + * </PRE> + * + * Passwords may be clear text, obfuscated or checksummed. The class + * com.eclipse.Util.Password should be used to generate obfuscated passwords or + * password checksums. + * + * If DIGEST Authentication is used, the password must be in a recoverable + * format, either plain text or OBF:. + * + * @see org.eclipse.jetty.security.Password + * + */ +public class HashLoginService extends MappedLoginService +{ + private String _config; + private Resource _configResource; + private Scanner _scanner; + private int _refreshInterval = 0;// default is not to reload + + /* ------------------------------------------------------------ */ + public HashLoginService() + { + } + + /* ------------------------------------------------------------ */ + public HashLoginService(String name) + { + setName(name); + } + + /* ------------------------------------------------------------ */ + public HashLoginService(String name, String config) + { + setName(name); + setConfig(config); + } + + /* ------------------------------------------------------------ */ + public String getConfig() + { + return _config; + } + + /* ------------------------------------------------------------ */ + public void getConfig(String config) + { + _config=config; + } + + /* ------------------------------------------------------------ */ + public Resource getConfigResource() + { + return _configResource; + } + + /* ------------------------------------------------------------ */ + /** + * Load realm users from properties file. The property file maps usernames + * to password specs followed by an optional comma separated list of role + * names. + * + * @param config Filename or url of user properties file. + * @exception java.io.IOException if user properties file could not be + * loaded + */ + public void setConfig(String config) + { + _config = config; + } + + /* ------------------------------------------------------------ */ + public void setRefreshInterval(int msec) + { + _refreshInterval = msec; + } + + /* ------------------------------------------------------------ */ + public int getRefreshInterval() + { + return _refreshInterval; + } + + /* ------------------------------------------------------------ */ + @Override + protected UserIdentity loadUser(String username) + { + // TODO Auto-generated method stub + return null; + } + + /* ------------------------------------------------------------ */ + @Override + public void loadUsers() throws IOException + { + if (_config==null) + return; + _configResource = Resource.newResource(_config); + + if (Log.isDebugEnabled()) Log.debug("Load " + this + " from " + _config); + Properties properties = new Properties(); + properties.load(_configResource.getInputStream()); + Set<String> known = new HashSet<String>(); + + for (Map.Entry<Object, Object> entry : properties.entrySet()) + { + String username = ((String) entry.getKey()).trim(); + String credentials = ((String) entry.getValue()).trim(); + String roles = null; + int c = credentials.indexOf(','); + if (c > 0) + { + roles = credentials.substring(c + 1).trim(); + credentials = credentials.substring(0, c).trim(); + } + + if (username != null && username.length() > 0 && credentials != null && credentials.length() > 0) + { + String[] roleArray = UserIdentity.NO_ROLES; + if (roles != null && roles.length() > 0) + roleArray = roles.split(","); + known.add(username); + putUser(username,Credential.getCredential(credentials),roleArray); + } + } + + Iterator<String> users = _users.keySet().iterator(); + while(users.hasNext()) + { + String user=users.next(); + if (!known.contains(user)) + users.remove(); + } + } + + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() + */ + protected void doStart() throws Exception + { + super.doStart(); + + if (getRefreshInterval() > 0) + { + _scanner = new Scanner(); + _scanner.setScanInterval(getRefreshInterval()); + List<File> dirList = new ArrayList<File>(1); + dirList.add(_configResource.getFile()); + _scanner.setScanDirs(dirList); + _scanner.setFilenameFilter(new FilenameFilter() + { + public boolean accept(File dir, String name) + { + File f = new File(dir, name); + try + { + if (f.compareTo(_configResource.getFile()) == 0) return true; + } + catch (IOException e) + { + return false; + } + + return false; + } + + }); + _scanner.addListener(new BulkListener() + { + public void filesChanged(List filenames) throws Exception + { + if (filenames == null) return; + if (filenames.isEmpty()) return; + if (filenames.size() == 1 && filenames.get(0).equals(_config)) loadUsers(); + } + + public String toString() + { + return "HashLoginService$Scanner"; + } + + }); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.setRecursive(false); + _scanner.start(); + } + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop() + */ + protected void doStop() throws Exception + { + super.doStop(); + if (_scanner != null) _scanner.stop(); + _scanner = null; + } + + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java new file mode 100644 index 0000000000..84e7071b8d --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/IdentityService.java @@ -0,0 +1,99 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.security.Principal; + +import javax.security.auth.Subject; + +import org.eclipse.jetty.server.UserIdentity; + +/* ------------------------------------------------------------ */ +/** + * Associates UserIdentities from with threads and UserIdentity.Contexts. + * + */ +public interface IdentityService <SCOPED extends UserIdentity, RUNAS> +{ + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /** A scoped UserIdentity. + * + * An interface used to ob + * + */ + interface Scoped + { + UserIdentity getScopedUserIdentity(); + } + + /* ------------------------------------------------------------ */ + /** + * Associate the {@link UserIdentity} and {@link UserIdentity.Scope} + * with the current thread. + * @param user The current user. + * @param context The new scope. + * @return A scoped {@link UserIdentity}. + */ + SCOPED associate(UserIdentity user, UserIdentity.Scope context); + + /* ------------------------------------------------------------ */ + /** + * Disassociate the current UserIdentity and reinstate the + * previousUser identity. + * TODO this might not be necessary. Both existing implementations are no-ops + * @param scoped SCOPED returned from previous associate call + */ + void disassociate(SCOPED scoped); + + /* ------------------------------------------------------------ */ + /** + * Associate a runas Token with the current thread. + * @param token The runAsToken to associate. + * @return The previous runAsToken or null. + */ + RUNAS associateRunAs(RunAsToken token); + + /* ------------------------------------------------------------ */ + /** + * Disassociate the current runAsToken from the thread + * and reassociate the previous token. + * @param token RUNAS returned from previous associateRunAs call + */ + void disassociateRunAs(RUNAS token); + + /* ------------------------------------------------------------ */ + /** + * Create a new UserIdentity for use with this identity service. + * The UserIdentity should be immutable and able to be cached. + * + * @param subject Subject to include in UserIdentity + * @param userPrincipal Principal to include in UserIdentity. This will be returned from getUserPrincipal calls + * @param roles set of roles to include in UserIdentity. + * @return A new immutable UserIdententity + */ + UserIdentity newUserIdentity(Subject subject, Principal userPrincipal, String[] roles); + + /* ------------------------------------------------------------ */ + /** + * Create a new RunAsToken from a runAsName (normally a role). + * @param runAsName Normally a role name + * @return A new immutable RunAsToken + */ + RunAsToken newRunAsToken(String runAsName); + + UserIdentity newSystemUserIdentity(); +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java new file mode 100644 index 0000000000..a994c45b5c --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java @@ -0,0 +1,263 @@ +// ======================================================================== +// Copyright (c) 2003-2009 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.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.eclipse.jetty.http.security.Password; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.Resource; + +/* ------------------------------------------------------------ */ +/** + * HashMapped User Realm with JDBC as data source. JDBCLoginService extends + * HashULoginService and adds a method to fetch user information from database. + * The login() method checks the inherited Map for the user. If the user is not + * found, it will fetch details from the database and populate the inherited + * Map. It then calls the superclass login() method to perform the actual + * authentication. Periodically (controlled by configuration parameter), + * internal hashes are cleared. Caching can be disabled by setting cache refresh + * interval to zero. Uses one database connection that is initialized at + * startup. Reconnect on failures. authenticate() is 'synchronized'. + * + * An example properties file for configuration is in + * $JETTY_HOME/etc/jdbcRealm.properties + * + * @version $Id: JDBCLoginService.java 4792 2009-03-18 21:55:52Z gregw $ + * + * + * + * + */ + +public class JDBCLoginService extends MappedLoginService +{ + private String _config; + private String _jdbcDriver; + private String _url; + private String _userName; + private String _password; + private String _userTableKey; + private String _userTablePasswordField; + private String _roleTableRoleField; + private int _cacheTime; + private long _lastHashPurge; + private Connection _con; + private String _userSql; + private String _roleSql; + + + /* ------------------------------------------------------------ */ + public JDBCLoginService() + throws IOException + { + } + + /* ------------------------------------------------------------ */ + public JDBCLoginService(String name) + throws IOException + { + setName(name); + } + + /* ------------------------------------------------------------ */ + public JDBCLoginService(String name, String config) + throws IOException + { + setName(name); + setConfig(config); + } + + /* ------------------------------------------------------------ */ + public JDBCLoginService(String name, IdentityService identityService, String config) + throws IOException + { + setName(name); + setIdentityService(identityService); + setConfig(config); + } + + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.security.MappedLoginService#doStart() + */ + @Override + protected void doStart() throws Exception + { + Properties properties = new Properties(); + Resource resource = Resource.newResource(_config); + properties.load(resource.getInputStream()); + + _jdbcDriver = properties.getProperty("jdbcdriver"); + _url = properties.getProperty("url"); + _userName = properties.getProperty("username"); + _password = properties.getProperty("password"); + String _userTable = properties.getProperty("usertable"); + _userTableKey = properties.getProperty("usertablekey"); + String _userTableUserField = properties.getProperty("usertableuserfield"); + _userTablePasswordField = properties.getProperty("usertablepasswordfield"); + String _roleTable = properties.getProperty("roletable"); + String _roleTableKey = properties.getProperty("roletablekey"); + _roleTableRoleField = properties.getProperty("roletablerolefield"); + String _userRoleTable = properties.getProperty("userroletable"); + String _userRoleTableUserKey = properties.getProperty("userroletableuserkey"); + String _userRoleTableRoleKey = properties.getProperty("userroletablerolekey"); + _cacheTime = new Integer(properties.getProperty("cachetime")); + + if (_jdbcDriver == null || _jdbcDriver.equals("") + || _url == null + || _url.equals("") + || _userName == null + || _userName.equals("") + || _password == null + || _cacheTime < 0) + { + if (Log.isDebugEnabled()) Log.debug("UserRealm " + getName() + " has not been properly configured"); + } + _cacheTime *= 1000; + _lastHashPurge = 0; + _userSql = "select " + _userTableKey + "," + _userTablePasswordField + " from " + _userTable + " where " + _userTableUserField + " = ?"; + _roleSql = "select r." + _roleTableRoleField + + " from " + + _roleTable + + " r, " + + _userRoleTable + + " u where u." + + _userRoleTableUserKey + + " = ?" + + " and r." + + _roleTableKey + + " = u." + + _userRoleTableRoleKey; + + Loader.loadClass(this.getClass(), _jdbcDriver).newInstance(); + connectDatabase(); + super.doStart(); + } + + + /* ------------------------------------------------------------ */ + public String getConfig() + { + return _config; + } + + /* ------------------------------------------------------------ */ + /** + * Load JDBC connection configuration from properties file. + * + * @param config Filename or url of user properties file. + * @exception java.io.IOException + */ + public void setConfig(String config) + { + if (isRunning()) + throw new IllegalStateException("Running"); + _config=config; + } + + /* ------------------------------------------------------------ */ + /** + * (re)Connect to database with parameters setup by loadConfig() + */ + public void connectDatabase() + { + try + { + Class.forName(_jdbcDriver); + _con = DriverManager.getConnection(_url, _userName, _password); + } + catch (SQLException e) + { + Log.warn("UserRealm " + getName() + " could not connect to database; will try later", e); + } + catch (ClassNotFoundException e) + { + Log.warn("UserRealm " + getName() + " could not connect to database; will try later", e); + } + } + + /* ------------------------------------------------------------ */ + @Override + public UserIdentity login(String username, Object credentials) + { + long now = System.currentTimeMillis(); + if (now - _lastHashPurge > _cacheTime || _cacheTime == 0) + { + _users.clear(); + _lastHashPurge = now; + } + + return super.login(username,credentials); + } + + + /* ------------------------------------------------------------ */ + @Override + protected void loadUsers() + { + } + + /* ------------------------------------------------------------ */ + @Override + protected UserIdentity loadUser(String username) + { + try + { + if (null == _con) + connectDatabase(); + + if (null == _con) + throw new SQLException("Can't connect to database"); + + PreparedStatement stat = _con.prepareStatement(_userSql); + stat.setObject(1, username); + ResultSet rs = stat.executeQuery(); + + if (rs.next()) + { + int key = rs.getInt(_userTableKey); + String credentials = rs.getString(_userTablePasswordField); + stat.close(); + + stat = _con.prepareStatement(_roleSql); + stat.setInt(1, key); + rs = stat.executeQuery(); + List<String> roles = new ArrayList<String>(); + while (rs.next()) + roles.add(rs.getString(_roleTableRoleField)); + + stat.close(); + return putUser(username, new Password(credentials),roles.toArray(new String[roles.size()])); + } + } + catch (SQLException e) + { + Log.warn("UserRealm " + getName() + " could not load user information from database", e); + connectDatabase(); + } + return null; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/LazyAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/LazyAuthentication.java new file mode 100644 index 0000000000..99a8df7a3f --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/LazyAuthentication.java @@ -0,0 +1,85 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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 javax.security.auth.Subject; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.eclipse.jetty.server.UserIdentity; + + +/** + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class LazyAuthentication implements Authentication +{ + private static final Subject unauthenticatedSubject = new Subject(); + + private final Authenticator _serverAuthentication; + private final ServletRequest _request; + private final ServletResponse _response; + + private Authentication _delegate; + + public LazyAuthentication(Authenticator serverAuthentication, ServletRequest request, ServletResponse response) + { + if (serverAuthentication == null) throw new NullPointerException("No ServerAuthentication"); + this._serverAuthentication = serverAuthentication; + this._request=request; + this._response=response; + } + + private Authentication getDelegate() + { + if (_delegate == null) + { + try + { + _delegate = _serverAuthentication.validateRequest(_request, _response, false); + } + catch (ServerAuthException e) + { + _delegate = DefaultAuthentication.SEND_FAILURE_RESULTS; + } + } + return _delegate; + } + + public Authentication.Status getAuthStatus() + { + return getDelegate().getAuthStatus(); + } + + public boolean isSuccess() + { + return getDelegate().isSuccess(); + } + + // for cleaning in secureResponse + public UserIdentity getUserIdentity() + { + return _delegate == null ? UserIdentity.UNAUTHENTICATED_IDENTITY: _delegate.getUserIdentity(); + } + + public String getAuthMethod() + { + return getDelegate().getAuthMethod(); + } + + public String toString() + { + return "{Lazy,"+_delegate+"}"; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/LoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/LoginService.java new file mode 100644 index 0000000000..2b2cc6d44a --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/LoginService.java @@ -0,0 +1,28 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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 org.eclipse.jetty.server.UserIdentity; + +/** + * @version $Rev: 4734 $ $Date: 2009-03-07 18:46:18 +0100 (Sat, 07 Mar 2009) $ + */ +public interface LoginService +{ + String getName(); + UserIdentity login(String username,Object credentials); + + IdentityService<UserIdentity,?> getIdentityService(); + void setIdentityService(IdentityService<UserIdentity,?> service); +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java new file mode 100644 index 0000000000..d4e88b3814 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/MappedLoginService.java @@ -0,0 +1,292 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.security.auth.Subject; + +import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.component.AbstractLifeCycle; + + + +/* ------------------------------------------------------------ */ +/** + * A login service that keeps UserIdentities in a concurrent map + * either as the source or a cache of the users. + * + */ +public abstract class MappedLoginService extends AbstractLifeCycle implements LoginService +{ + protected IdentityService _identityService=new DefaultIdentityService(); + protected String _name; + protected final ConcurrentMap<String, UserIdentity> _users=new ConcurrentHashMap<String, UserIdentity>(); + + /* ------------------------------------------------------------ */ + protected MappedLoginService() + { + } + + /* ------------------------------------------------------------ */ + /** Get the name. + * @return the name + */ + public String getName() + { + return _name; + } + + /* ------------------------------------------------------------ */ + /** Get the identityService. + * @return the identityService + */ + public IdentityService getIdentityService() + { + return _identityService; + } + + /* ------------------------------------------------------------ */ + /** Get the users. + * @return the users + */ + public ConcurrentMap<String, UserIdentity> getUsers() + { + return _users; + } + + /* ------------------------------------------------------------ */ + /** Set the identityService. + * @param identityService the identityService to set + */ + public void setIdentityService(IdentityService identityService) + { + if (isRunning()) + throw new IllegalStateException("Running"); + _identityService = identityService; + } + + /* ------------------------------------------------------------ */ + /** Set the name. + * @param name the name to set + */ + public void setName(String name) + { + if (isRunning()) + throw new IllegalStateException("Running"); + _name = name; + } + + /* ------------------------------------------------------------ */ + /** Set the users. + * @param users the users to set + */ + public void setUsers(Map<String, UserIdentity> users) + { + if (isRunning()) + throw new IllegalStateException("Running"); + _users.clear(); + _users.putAll(users); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() + */ + protected void doStart() throws Exception + { + loadUsers(); + super.doStart(); + } + + /* ------------------------------------------------------------ */ + protected void doStop() throws Exception + { + super.doStop(); + } + + /* ------------------------------------------------------------ */ + public String toString() + { + return this.getClass().getSimpleName()+"["+_name+"]"; + } + + /* ------------------------------------------------------------ */ + /** Put user into realm. + * Called by implementations to put the user data loaded from + * file/db etc into the user structure. + * @param userName User name + * @param info a UserIdentity instance, or a String password or Credential instance + * @return User instance + */ + protected synchronized UserIdentity putUser(String userName, Object info) + { + final UserIdentity identity; + if (info instanceof UserIdentity) + identity=(UserIdentity)info; + else + { + Credential credential = (info instanceof Credential)?(Credential)info:Credential.getCredential(info.toString()); + + Principal userPrincipal = new KnownUser(userName,credential); + Subject subject = new Subject(); + subject.getPrincipals().add(userPrincipal); + subject.getPrivateCredentials().add(credential); + subject.setReadOnly(); + identity=_identityService.newUserIdentity(subject,userPrincipal,UserIdentity.NO_ROLES); + } + + _users.put(userName,identity); + return identity; + } + + /* ------------------------------------------------------------ */ + /** Put user into realm. + * @param userName + * @param credential + * @param roles + * @return UserIdentity + */ + protected synchronized UserIdentity putUser(String userName, Credential credential, String[] roles) + { + Principal userPrincipal = new KnownUser(userName,credential); + Subject subject = new Subject(); + subject.getPrincipals().add(userPrincipal); + subject.getPrivateCredentials().add(credential); + + if (roles!=null) + for (String role : roles) + subject.getPrincipals().add(new RolePrincipal(role)); + + subject.setReadOnly(); + UserIdentity identity=_identityService.newUserIdentity(subject,userPrincipal,roles); + _users.put(userName,identity); + return identity; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.security.LoginService#login(java.lang.String, java.lang.Object) + */ + public UserIdentity login(String username, Object credentials) + { + UserIdentity user = _users.get(username); + + if (user==null) + user = loadUser(username); + + if (user!=null) + { + UserPrincipal principal = (UserPrincipal)user.getUserPrincipal(); + if (principal.authenticate(credentials)) + return user; + } + return null; + } + + /* ------------------------------------------------------------ */ + protected abstract UserIdentity loadUser(String username); + + /* ------------------------------------------------------------ */ + protected abstract void loadUsers() throws IOException; + + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + public interface UserPrincipal extends Principal + { + boolean authenticate(Object credentials); + public boolean isAuthenticated(); + } + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + public class RolePrincipal implements Principal + { + private final String _name; + public RolePrincipal(String name) + { + _name=name; + } + public String getName() + { + return _name; + } + } + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + public static class Anonymous implements UserPrincipal + { + public boolean isAuthenticated() + { + return false; + } + + public String getName() + { + return "Anonymous"; + } + + public boolean authenticate(Object credentials) + { + return false; + } + + } + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + public static class KnownUser implements UserPrincipal + { + private final String _name; + private final Credential _credential; + + /* -------------------------------------------------------- */ + public KnownUser(String name,Credential credential) + { + _name=name; + _credential=credential; + } + + /* -------------------------------------------------------- */ + public boolean authenticate(Object credentials) + { + return _credential!=null && _credential.check(credentials); + } + + /* ------------------------------------------------------------ */ + public String getName() + { + return _name; + } + + /* -------------------------------------------------------- */ + public boolean isAuthenticated() + { + return true; + } + } +} + diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/RoleInfo.java b/jetty-security/src/main/java/org/eclipse/jetty/security/RoleInfo.java new file mode 100644 index 0000000000..d4d687f51c --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/RoleInfo.java @@ -0,0 +1,135 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.util.Arrays; + +import org.eclipse.jetty.util.LazyList; + +/** + * + * Badly named class that holds the role and user data constraint info for a + * path/http method combination, extracted and combined from security + * constraints. + * + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class RoleInfo +{ + private final static String[] NO_ROLES={}; + private boolean _isAnyRole; + private boolean _unchecked; + private boolean _forbidden; + private UserDataConstraint _userDataConstraint; + + private String[] _roles = NO_ROLES; + + public boolean isUnchecked() + { + return _unchecked; + } + + public void setUnchecked(boolean unchecked) + { + this._unchecked = unchecked; + if (unchecked) + { + _forbidden=false; + _roles=NO_ROLES; + _isAnyRole=false; + } + } + + public boolean isForbidden() + { + return _forbidden; + } + + public void setForbidden(boolean forbidden) + { + this._forbidden = forbidden; + if (forbidden) + { + _unchecked = false; + _userDataConstraint = null; + _isAnyRole=false; + _roles=NO_ROLES; + } + } + + public boolean isAnyRole() + { + return _isAnyRole; + } + + public void setAnyRole(boolean anyRole) + { + this._isAnyRole=anyRole; + if (anyRole) + { + _unchecked = false; + _roles=NO_ROLES; + } + } + + public UserDataConstraint getUserDataConstraint() + { + return _userDataConstraint; + } + + public void setUserDataConstraint(UserDataConstraint userDataConstraint) + { + if (userDataConstraint == null) throw new NullPointerException("Null UserDataConstraint"); + if (this._userDataConstraint == null) + { + this._userDataConstraint = userDataConstraint; + } + else + { + this._userDataConstraint = this._userDataConstraint.combine(userDataConstraint); + } + } + + public String[] getRoles() + { + return _roles; + } + + public void addRole(String role) + { + _roles=(String[])LazyList.addToArray(_roles,role,String.class); + } + + public void combine(RoleInfo other) + { + if (other._forbidden) + setForbidden(true); + else if (other._unchecked) + setUnchecked(true); + else if (other._isAnyRole) + setAnyRole(true); + else if (!_isAnyRole) + { + for (String r : other._roles) + _roles=(String[])LazyList.addToArray(_roles,r,String.class); + } + + setUserDataConstraint(other._userDataConstraint); + } + + public String toString() + { + return "{RoleInfo"+(_forbidden?",F":"")+(_unchecked?",U":"")+(_isAnyRole?",*":Arrays.asList(_roles).toString())+"}"; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/RoleRunAsToken.java b/jetty-security/src/main/java/org/eclipse/jetty/security/RoleRunAsToken.java new file mode 100644 index 0000000000..02ee2d20e0 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/RoleRunAsToken.java @@ -0,0 +1,39 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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; + + + +/** + * @version $Rev: 4701 $ $Date: 2009-03-03 13:01:26 +0100 (Tue, 03 Mar 2009) $ + */ +public class RoleRunAsToken implements RunAsToken +{ + private final String _runAsRole; + + public RoleRunAsToken(String runAsRole) + { + this._runAsRole = runAsRole; + } + + public String getRunAsRole() + { + return _runAsRole; + } + + public String toString() + { + return "RoleRunAsToken("+_runAsRole+")"; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/RunAsToken.java b/jetty-security/src/main/java/org/eclipse/jetty/security/RunAsToken.java new file mode 100644 index 0000000000..7bf84ccd87 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/RunAsToken.java @@ -0,0 +1,22 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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; + +/** + * marker interface for run-as-role tokens + * @version $Rev: 4701 $ $Date: 2009-03-03 13:01:26 +0100 (Tue, 03 Mar 2009) $ + */ +public interface RunAsToken +{ +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java new file mode 100644 index 0000000000..f550d23563 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java @@ -0,0 +1,524 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConnection; +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.HandlerWrapper; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.log.Log; + +/** + * 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. + */ +public abstract class SecurityHandler extends HandlerWrapper implements Authenticator.Configuration +{ + /* ------------------------------------------------------------ */ + private boolean _checkWelcomeFiles = false; + private Authenticator _authenticator; + private Authenticator.Factory _authenticatorFactory=new DefaultAuthenticatorFactory(); + private boolean _isLazy=true; + private String _realmName; + private String _authMethod; + private final Map<String,String> _initParameters=new HashMap<String,String>(); + private LoginService _loginService; + private boolean _loginServiceShared; + private IdentityService<UserIdentity,?> _identityService; + + /* ------------------------------------------------------------ */ + protected SecurityHandler() + { + } + + /* ------------------------------------------------------------ */ + /** Get the identityService. + * @return the identityService + */ + public IdentityService<UserIdentity,?> getIdentityService() + { + return _identityService; + } + + /* ------------------------------------------------------------ */ + /** Set the identityService. + * @param identityService the identityService to set + */ + public void setIdentityService(IdentityService<UserIdentity,?> identityService) + { + if (isStarted()) + throw new IllegalStateException("Started"); + _identityService = identityService; + } + + /* ------------------------------------------------------------ */ + /** Get the loginService. + * @return the loginService + */ + 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"); + _loginService = loginService; + _loginServiceShared=false; + } + + + /* ------------------------------------------------------------ */ + 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"); + _authenticator = authenticator; + } + + /* ------------------------------------------------------------ */ + /** + * @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"); + _authenticatorFactory = authenticatorFactory; + } + + /* ------------------------------------------------------------ */ + /** + * @return the isLazy + */ + public boolean isLazy() + { + return _isLazy; + } + + /* ------------------------------------------------------------ */ + /** + * @param isLazy the isLazy to set + * @throws IllegalStateException if the SecurityHandler is running + */ + public void setLazy(boolean isLazy) + { + if (isRunning()) + throw new IllegalStateException("running"); + _isLazy = isLazy; + } + + /* ------------------------------------------------------------ */ + /** + * @return the realmName + */ + 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 + */ + 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; + } + + /* ------------------------------------------------------------ */ + public String getInitParameter(String key) + { + return _initParameters.get(key); + } + + /* ------------------------------------------------------------ */ + 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() + { + List<LoginService> list = getServer().getBeans(LoginService.class); + + for (LoginService service : list) + if (service.getName().equals(getRealmName())) + return service; + if (list.size()>0) + return list.get(0); + return null; + } + + /* ------------------------------------------------------------ */ + protected IdentityService<UserIdentity,?> findIdentityService() + { + List<IdentityService> services = getServer().getBeans(IdentityService.class); + if (services!=null && services.size()>0) + return services.get(0); + return null; + } + + /* ------------------------------------------------------------ */ + /** + */ + protected void doStart() + throws Exception + { + // complicated resolution of login and identity service to handle + // many different ways these can be constructed and injected. + + if (_loginService==null) + { + _loginService=findLoginService(); + if (_loginService!=null) + _loginServiceShared=true; + } + + if (_identityService==null) + { + if (_loginService!=null) + _identityService=_loginService.getIdentityService(); + + if (_identityService==null) + _identityService=findIdentityService(); + + if (_identityService==null) + _identityService=new DefaultIdentityService(); + } + + 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 (!_loginServiceShared && _loginService instanceof LifeCycle) + ((LifeCycle)_loginService).start(); + + if (_authenticator==null && _authenticatorFactory!=null) + { + _authenticator=_authenticatorFactory.getAuthenticator(getServer(),ContextHandler.getCurrentContext(),this); + if (_authenticator!=null) + _authMethod=_authenticator.getAuthMethod(); + } + + if (_authenticator==null) + { + Log.warn("No ServerAuthentication for "+this); + throw new IllegalStateException("No ServerAuthentication"); + } + + _authenticator.setConfiguration(this); + if (_authenticator instanceof LifeCycle) + ((LifeCycle)_authenticator).start(); + + + super.doStart(); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.server.handler.HandlerWrapper#doStop() + */ + @Override + protected void doStop() throws Exception + { + super.doStop(); + + if (!_loginServiceShared && _loginService instanceof LifeCycle) + ((LifeCycle)_loginService).stop(); + + } + + protected boolean checkSecurity(Request request) + { + switch(request.getDispatcherType()) + { + case REQUEST: + case ASYNC: + return true; + case FORWARD: + if (_checkWelcomeFiles && 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.server.Handler#handle(java.lang.String, + * javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse, int) + */ + public void handle(String pathInContext, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + final Request base_request = (request instanceof Request) ? (Request) request : HttpConnection.getCurrentConnection().getRequest(); + final Response base_response = (response instanceof Response) ? (Response) response : HttpConnection.getCurrentConnection().getResponse(); + final Handler handler=getHandler(); + + if (handler==null) + return; + + if (checkSecurity(base_request)) + { + Object constraintInfo = prepareConstraintInfo(pathInContext, base_request); + + // Check data constraints + if (!checkUserDataPermissions(pathInContext, base_request, base_response, constraintInfo)) + { + if (!base_request.isHandled()) + { + response.sendError(Response.SC_FORBIDDEN); + base_request.setHandled(true); + } + return; + } + + // is Auth mandatory? + boolean isAuthMandatory = isAuthMandatory(base_request, base_response, constraintInfo); + + // check authentication + UserIdentity old_user_identity=base_request.getUserIdentity(); + try + { + final Authenticator authenticator = _authenticator; + Authentication authentication = authenticator.validateRequest(request, response, isAuthMandatory); + + if (authentication.getAuthStatus() == Authentication.Status.SUCCESS) + { + final UserIdentity user_identity=authentication.getUserIdentity(); + base_request.setAuthType(authentication.getAuthMethod()); + base_request.setUserIdentity(user_identity); + + if (isAuthMandatory && !checkWebResourcePermissions(pathInContext, base_request, base_response, constraintInfo, user_identity)) + { + response.sendError(Response.SC_FORBIDDEN, "User not in required role"); + base_request.setHandled(true); + return; + } + + handler.handle(pathInContext, request, response); + + authenticator.secureResponse(request, response, isAuthMandatory, authentication); + } + else + { + base_request.setHandled(true); + } + } + catch (ServerAuthException e) + { + // jaspi 3.8.3 send HTTP 500 internal server error, with message + // from AuthException + response.sendError(Response.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + finally + { + base_request.setUserIdentity(old_user_identity); + } + } + else + handler.handle(pathInContext, request, response); + } + + + /* ------------------------------------------------------------ */ + protected abstract Object prepareConstraintInfo(String pathInContext, Request request); + + /* ------------------------------------------------------------ */ + protected abstract boolean checkUserDataPermissions(String pathInContext, Request request, Response response, Object constraintInfo) throws IOException; + + /* ------------------------------------------------------------ */ + protected abstract boolean isAuthMandatory(Request base_request, 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 + { + public String getName() + { + return null; + } + + public String toString() + { + return "NOT CHECKED"; + } + + public SecurityHandler getSecurityHandler() + { + return SecurityHandler.this; + } + } + + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + public static Principal __NO_USER = new Principal() + { + public String getName() + { + return null; + } + + 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 Principal __NOBODY = new Principal() + { + public String getName() + { + return "Nobody"; + } + + public String toString() + { + return getName(); + } + }; + + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ServerAuthException.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ServerAuthException.java new file mode 100644 index 0000000000..d0f26943bf --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ServerAuthException.java @@ -0,0 +1,42 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.security.GeneralSecurityException; + +/** + * @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $ + */ +public class ServerAuthException extends GeneralSecurityException +{ + + public ServerAuthException() + { + } + + public ServerAuthException(String s) + { + super(s); + } + + public ServerAuthException(String s, Throwable throwable) + { + super(s, throwable); + } + + public ServerAuthException(Throwable throwable) + { + super(throwable); + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/UserDataConstraint.java b/jetty-security/src/main/java/org/eclipse/jetty/security/UserDataConstraint.java new file mode 100644 index 0000000000..e1d4369242 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/UserDataConstraint.java @@ -0,0 +1,35 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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; + +/** + * @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $ + */ +public enum UserDataConstraint +{ + None, Integral, Confidential; + + public static UserDataConstraint get(int dataConstraint) + { + if (dataConstraint < -1 || dataConstraint > 2) throw new IllegalArgumentException("Expected -1, 0, 1, or 2, not: " + dataConstraint); + if (dataConstraint == -1) return None; + return values()[dataConstraint]; + } + + public UserDataConstraint combine(UserDataConstraint other) + { + if (this.compareTo(other) < 0) return this; + return other; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java new file mode 100644 index 0000000000..d91251e651 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java @@ -0,0 +1,106 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import java.io.IOException; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpHeaders; +import org.eclipse.jetty.http.security.B64Code; +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.security.Authentication; +import org.eclipse.jetty.security.DefaultAuthentication; +import org.eclipse.jetty.security.DefaultUserIdentity; +import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.StringUtil; + +/** + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class BasicAuthenticator extends LoginAuthenticator +{ + /* ------------------------------------------------------------ */ + /** + * @param loginService + */ + public BasicAuthenticator() + { + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.security.Authenticator#getAuthMethod() + */ + public String getAuthMethod() + { + return Constraint.__BASIC_AUTH; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.security.Authenticator#validateRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse, boolean) + */ + public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException + { + HttpServletRequest request = (HttpServletRequest)req; + HttpServletResponse response = (HttpServletResponse)res; + String credentials = request.getHeader(HttpHeaders.AUTHORIZATION); + + try + { + if (credentials != null) + { + credentials = credentials.substring(credentials.indexOf(' ')+1); + credentials = B64Code.decode(credentials,StringUtil.__ISO_8859_1); + int i = credentials.indexOf(':'); + String username = credentials.substring(0,i); + String password = credentials.substring(i+1); + + UserIdentity user = _loginService.login(username,password); + if (user!=null) + { + if (user instanceof DefaultUserIdentity) + return ((DefaultUserIdentity)user).SUCCESSFUL_BASIC; + return new DefaultAuthentication(Authentication.Status.SUCCESS,Constraint.__BASIC_AUTH,user); + } + } + + if (!mandatory) + { + return DefaultAuthentication.SUCCESS_UNAUTH_RESULTS; + } + response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "basic realm=\"" + _loginService.getName() + '"'); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return DefaultAuthentication.SEND_CONTINUE_RESULTS; + } + catch (IOException e) + { + throw new ServerAuthException(e); + } + } + + // most likely validatedUser is not needed here. + + // corrct? + public Authentication.Status secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, Authentication validatedUser) throws ServerAuthException + { + return Authentication.Status.SUCCESS; + } + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java new file mode 100644 index 0000000000..4b2cd51997 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java @@ -0,0 +1,98 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import java.io.IOException; +import java.security.Principal; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.security.B64Code; +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.security.Authentication; +import org.eclipse.jetty.security.DefaultAuthentication; +import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.server.UserIdentity; + +/** + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class ClientCertAuthenticator extends LoginAuthenticator +{ + public ClientCertAuthenticator() + { + super(); + } + + public String getAuthMethod() + { + return Constraint.__CERT_AUTH; + } + + /** + * TODO what should happen if an insecure page is accessed without a client + * cert? Current code requires a client cert always but allows access to + * insecure pages if it is not recognized. + * + * @return + * @throws ServerAuthException + */ + public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException + { + HttpServletRequest request = (HttpServletRequest)req; + HttpServletResponse response = (HttpServletResponse)res; + java.security.cert.X509Certificate[] certs = (java.security.cert.X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); + + try + { + // Need certificates. + if (certs == null || certs.length == 0 || certs[0] == null) + { + response.sendError(HttpServletResponse.SC_FORBIDDEN, + "A client certificate is required for accessing this web application but the server's listener is not configured for mutual authentication (or the client did not provide a certificate)."); + return DefaultAuthentication.SEND_FAILURE_RESULTS; + } + + Principal principal = certs[0].getSubjectDN(); + if (principal == null) principal = certs[0].getIssuerDN(); + final String username = principal == null ? "clientcert" : principal.getName(); + + // TODO no idea if this is correct + final char[] credential = B64Code.encode(certs[0].getSignature()); + + UserIdentity user = _loginService.login(username,credential); + if (user!=null) + return new DefaultAuthentication(Authentication.Status.SUCCESS,Constraint.__CERT_AUTH2,user); + + if (!mandatory) + { + return DefaultAuthentication.SUCCESS_UNAUTH_RESULTS; + } + response.sendError(HttpServletResponse.SC_FORBIDDEN, "The provided client certificate does not correspond to a trusted user."); + return DefaultAuthentication.SEND_FAILURE_RESULTS; + } + catch (IOException e) + { + throw new ServerAuthException(e.getMessage()); + } + } + + public Authentication.Status secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, Authentication validatedUser) throws ServerAuthException + { + return Authentication.Status.SUCCESS; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DelegateAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DelegateAuthenticator.java new file mode 100644 index 0000000000..e7343cc7d2 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DelegateAuthenticator.java @@ -0,0 +1,57 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.eclipse.jetty.security.Authentication; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.ServerAuthException; + +public class DelegateAuthenticator implements Authenticator +{ + protected final Authenticator _delegate; + + public void setConfiguration(Configuration configuration) + { + _delegate.setConfiguration(configuration); + } + + public String getAuthMethod() + { + return _delegate.getAuthMethod(); + } + + public DelegateAuthenticator(Authenticator delegate) + { + _delegate=delegate; + } + + public Authenticator getDelegate() + { + return _delegate; + } + + public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean manditory) throws ServerAuthException + { + return _delegate.validateRequest(request, response, manditory); + } + + public Authentication.Status secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, Authentication validatedUser) throws ServerAuthException + { + return _delegate.secureResponse(req,res, mandatory, validatedUser); + } + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java new file mode 100644 index 0000000000..dad736a197 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java @@ -0,0 +1,333 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import java.io.IOException; +import java.security.MessageDigest; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpHeaders; +import org.eclipse.jetty.http.security.B64Code; +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.http.security.Credential; +import org.eclipse.jetty.security.Authentication; +import org.eclipse.jetty.security.DefaultAuthentication; +import org.eclipse.jetty.security.DefaultUserIdentity; +import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.QuotedStringTokenizer; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.log.Log; + +/** + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class DigestAuthenticator extends LoginAuthenticator +{ + protected long _maxNonceAge = 0; + protected long _nonceSecret = this.hashCode() ^ System.currentTimeMillis(); + protected boolean _useStale = false; + + public DigestAuthenticator() + { + super(); + } + + public String getAuthMethod() + { + return Constraint.__DIGEST_AUTH; + } + + public Authentication.Status secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, Authentication validatedUser) throws ServerAuthException + { + return Authentication.Status.SUCCESS; + } + + public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException + { + HttpServletRequest request = (HttpServletRequest)req; + HttpServletResponse response = (HttpServletResponse)res; + String credentials = request.getHeader(HttpHeaders.AUTHORIZATION); + + try + { + boolean stale = false; + if (credentials != null) + { + if (Log.isDebugEnabled()) Log.debug("Credentials: " + credentials); + QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(credentials, "=, ", true, false); + final Digest digest = new Digest(request.getMethod()); + String last = null; + String name = null; + + while (tokenizer.hasMoreTokens()) + { + String tok = tokenizer.nextToken(); + char c = (tok.length() == 1) ? tok.charAt(0) : '\0'; + + switch (c) + { + case '=': + name = last; + last = tok; + break; + case ',': + name = null; + case ' ': + break; + + default: + last = tok; + if (name != null) + { + if ("username".equalsIgnoreCase(name)) + digest.username = tok; + else if ("realm".equalsIgnoreCase(name)) + digest.realm = tok; + else if ("nonce".equalsIgnoreCase(name)) + digest.nonce = tok; + else if ("nc".equalsIgnoreCase(name)) + digest.nc = tok; + else if ("cnonce".equalsIgnoreCase(name)) + digest.cnonce = tok; + else if ("qop".equalsIgnoreCase(name)) + digest.qop = tok; + else if ("uri".equalsIgnoreCase(name)) + digest.uri = tok; + else if ("response".equalsIgnoreCase(name)) digest.response = tok; + break; + } + } + } + + int n = checkNonce(digest.nonce, (Request)request); + + if (n > 0) + { + UserIdentity user = _loginService.login(digest.username,digest); + if (user!=null) + { + if (user instanceof DefaultUserIdentity) + return ((DefaultUserIdentity)user).SUCCESSFUL_BASIC; + return new DefaultAuthentication(Authentication.Status.SUCCESS,Constraint.__DIGEST_AUTH,user); + } + } + else if (n == 0) stale = true; + + } + + if (!mandatory) { return DefaultAuthentication.SUCCESS_UNAUTH_RESULTS; } + String domain = request.getContextPath(); + if (domain == null) domain = "/"; + response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"" + _loginService.getName() + + "\", domain=\"" + + domain + + "\", nonce=\"" + + newNonce((Request)request) + + "\", algorithm=MD5, qop=\"auth\"" + + (_useStale ? (" stale=" + stale) : "")); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return DefaultAuthentication.SEND_CONTINUE_RESULTS; + } + catch (IOException e) + { + throw new ServerAuthException(e); + } + + } + + public String newNonce(Request request) + { + long ts=request.getTimeStamp(); + long sk = _nonceSecret; + + byte[] nounce = new byte[24]; + for (int i = 0; i < 8; i++) + { + nounce[i] = (byte) (ts & 0xff); + ts = ts >> 8; + nounce[8 + i] = (byte) (sk & 0xff); + sk = sk >> 8; + } + + byte[] hash = null; + try + { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.reset(); + md.update(nounce, 0, 16); + hash = md.digest(); + } + catch (Exception e) + { + Log.warn(e); + } + + for (int i = 0; i < hash.length; i++) + { + nounce[8 + i] = hash[i]; + if (i == 23) break; + } + + return new String(B64Code.encode(nounce)); + } + + /** + * @param nonce nonce to check + * @param request + * @return -1 for a bad nonce, 0 for a stale none, 1 for a good nonce + */ + /* ------------------------------------------------------------ */ + private int checkNonce(String nonce, Request request) + { + try + { + byte[] n = B64Code.decode(nonce.toCharArray()); + if (n.length != 24) return -1; + + long ts = 0; + long sk = _nonceSecret; + byte[] n2 = new byte[16]; + System.arraycopy(n, 0, n2, 0, 8); + for (int i = 0; i < 8; i++) + { + n2[8 + i] = (byte) (sk & 0xff); + sk = sk >> 8; + ts = (ts << 8) + (0xff & (long) n[7 - i]); + } + + long age = request.getTimeStamp() - ts; + if (Log.isDebugEnabled()) Log.debug("age=" + age); + + byte[] hash = null; + try + { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.reset(); + md.update(n2, 0, 16); + hash = md.digest(); + } + catch (Exception e) + { + Log.warn(e); + } + + for (int i = 0; i < 16; i++) + if (n[i + 8] != hash[i]) return -1; + + if (_maxNonceAge > 0 && (age < 0 || age > _maxNonceAge)) return 0; // stale + + return 1; + } + catch (Exception e) + { + Log.ignore(e); + } + return -1; + } + + private static class Digest extends Credential + { + String method = null; + String username = null; + String realm = null; + String nonce = null; + String nc = null; + String cnonce = null; + String qop = null; + String uri = null; + String response = null; + + /* ------------------------------------------------------------ */ + Digest(String m) + { + method = m; + } + + /* ------------------------------------------------------------ */ + public boolean check(Object credentials) + { + String password = (credentials instanceof String) ? (String) credentials : credentials.toString(); + + try + { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] ha1; + if (credentials instanceof Credential.MD5) + { + // Credentials are already a MD5 digest - assume it's in + // form user:realm:password (we have no way to know since + // it's a digest, alright?) + ha1 = ((Credential.MD5) credentials).getDigest(); + } + else + { + // calc A1 digest + md.update(username.getBytes(StringUtil.__ISO_8859_1)); + md.update((byte) ':'); + md.update(realm.getBytes(StringUtil.__ISO_8859_1)); + md.update((byte) ':'); + md.update(password.getBytes(StringUtil.__ISO_8859_1)); + ha1 = md.digest(); + } + // calc A2 digest + md.reset(); + md.update(method.getBytes(StringUtil.__ISO_8859_1)); + md.update((byte) ':'); + md.update(uri.getBytes(StringUtil.__ISO_8859_1)); + byte[] ha2 = md.digest(); + + // calc digest + // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" + // nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) ) + // <"> + // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) + // ) > <"> + + md.update(TypeUtil.toString(ha1, 16).getBytes(StringUtil.__ISO_8859_1)); + md.update((byte) ':'); + md.update(nonce.getBytes(StringUtil.__ISO_8859_1)); + md.update((byte) ':'); + md.update(nc.getBytes(StringUtil.__ISO_8859_1)); + md.update((byte) ':'); + md.update(cnonce.getBytes(StringUtil.__ISO_8859_1)); + md.update((byte) ':'); + md.update(qop.getBytes(StringUtil.__ISO_8859_1)); + md.update((byte) ':'); + md.update(TypeUtil.toString(ha2, 16).getBytes(StringUtil.__ISO_8859_1)); + byte[] digest = md.digest(); + + // check digest + return (TypeUtil.toString(digest, 16).equalsIgnoreCase(response)); + } + catch (Exception e) + { + Log.warn(e); + } + + return false; + } + + public String toString() + { + return username + "," + response; + } + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java new file mode 100644 index 0000000000..c01fe7258f --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java @@ -0,0 +1,219 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import java.io.IOException; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.eclipse.jetty.http.security.Constraint; +import org.eclipse.jetty.security.Authentication; +import org.eclipse.jetty.security.DefaultAuthentication; +import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.log.Log; + +/** + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class FormAuthenticator extends LoginAuthenticator +{ + public final static String __FORM_LOGIN_PAGE="org.eclipse.jetty.security.form_login_page"; + public final static String __FORM_ERROR_PAGE="org.eclipse.jetty.security.form_error_page"; + public final static String __J_URI = "org.eclipse.jetty.util.URI"; + public final static String __J_AUTHENTICATED = "org.eclipse.jetty.server.Auth"; + public final static String __J_SECURITY_CHECK = "/j_security_check"; + public final static String __J_USERNAME = "j_username"; + public final static String __J_PASSWORD = "j_password"; + private String _formErrorPage; + private String _formErrorPath; + private String _formLoginPage; + private String _formLoginPath; + + public FormAuthenticator() + { + } + + /* ------------------------------------------------------------ */ + public FormAuthenticator(String login,String error) + { + if (login!=null) + setLoginPage(login); + if (error!=null) + setErrorPage(error); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.security.authentication.LoginAuthenticator#setConfiguration(org.eclipse.jetty.security.Authenticator.Configuration) + */ + @Override + public void setConfiguration(Configuration configuration) + { + super.setConfiguration(configuration); + String login=configuration.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE); + if (login!=null) + setLoginPage(login); + String error=configuration.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE); + if (error!=null) + setErrorPage(error); + } + + + + public String getAuthMethod() + { + return Constraint.__FORM_AUTH; + } + + private void setLoginPage(String path) + { + if (!path.startsWith("/")) + { + Log.warn("form-login-page must start with /"); + path = "/" + path; + } + _formLoginPage = path; + _formLoginPath = path; + if (_formLoginPath.indexOf('?') > 0) + _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?')); + } + + /* ------------------------------------------------------------ */ + private void setErrorPage(String path) + { + if (path == null || path.trim().length() == 0) + { + _formErrorPath = null; + _formErrorPage = null; + } + else + { + if (!path.startsWith("/")) + { + Log.warn("form-error-page must start with /"); + path = "/" + path; + } + _formErrorPage = path; + _formErrorPath = path; + + if (_formErrorPath.indexOf('?') > 0) + _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?')); + } + } + + public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException + { + HttpServletRequest request = (HttpServletRequest)req; + HttpServletResponse response = (HttpServletResponse)res; + HttpSession session = request.getSession(mandatory); + String uri = request.getPathInfo(); + // not mandatory and not authenticated + if (session == null || isLoginOrErrorPage(uri)) + { + return DefaultAuthentication.SUCCESS_UNAUTH_RESULTS; + } + + + try + { + // Handle a request for authentication. + // TODO perhaps j_securitycheck can be uri suffix? + if (uri.endsWith(__J_SECURITY_CHECK)) + { + final String username = request.getParameter(__J_USERNAME); + final char[] password = request.getParameter(__J_PASSWORD).toCharArray(); + + UserIdentity user = _loginService.login(username,password); + if (user!=null) + { + // Redirect to original request + String nuri = (String) session.getAttribute(__J_URI); + if (nuri == null || nuri.length() == 0) + { + nuri = request.getContextPath(); + if (nuri.length() == 0) nuri = URIUtil.SLASH; + } + // TODO shouldn't we forward to original URI instead? + session.removeAttribute(__J_URI); // Remove popped return URI. + response.setContentLength(0); + response.sendRedirect(response.encodeRedirectURL(nuri)); + return new DefaultAuthentication(Authentication.Status.SEND_SUCCESS,Constraint.__FORM_AUTH,user); + } + + // not authenticated + if (Log.isDebugEnabled()) Log.debug("Form authentication FAILED for " + StringUtil.printable(username)); + if (_formErrorPage == null) + { + if (response != null) + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + else + { +// response.setContentLength(0); //???? + RequestDispatcher dispatcher = request.getRequestDispatcher(_formErrorPage); + dispatcher.forward(request, response); + } + // TODO is this correct response if isMandatory false??? Can + // that occur? + return DefaultAuthentication.SEND_FAILURE_RESULTS; + } + // Check if the session is already authenticated. + + // Don't authenticate authform or errorpage + if (!mandatory) + // TODO verify this is correct action + return DefaultAuthentication.SUCCESS_UNAUTH_RESULTS; + + // redirect to login page + if (request.getQueryString() != null) + uri += "?" + request.getQueryString(); + //TODO is this safe if the client is sending several requests concurrently in the same session to secured resources? + session.setAttribute(__J_URI, request.getScheme() + "://" + + request.getServerName() + + ":" + + request.getServerPort() + + URIUtil.addPaths(request.getContextPath(), uri)); + RequestDispatcher dispatcher = request.getRequestDispatcher(_formLoginPage); + dispatcher.forward(request, response); + return DefaultAuthentication.SEND_CONTINUE_RESULTS; + } + catch (IOException e) + { + throw new ServerAuthException(e); + } + catch (ServletException e) + { + throw new ServerAuthException(e); + } + } + + public boolean isLoginOrErrorPage(String pathInContext) + { + return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath)); + } + + public Authentication.Status secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, Authentication validatedUser) throws ServerAuthException + { + return Authentication.Status.SUCCESS; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LazyAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LazyAuthenticator.java new file mode 100644 index 0000000000..65c237bb35 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LazyAuthenticator.java @@ -0,0 +1,45 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.eclipse.jetty.security.Authentication; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.LazyAuthentication; +import org.eclipse.jetty.security.ServerAuthException; + +/** + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class LazyAuthenticator extends DelegateAuthenticator +{ + public LazyAuthenticator(Authenticator delegate) + { + super(delegate); + } + + /** + * @see org.eclipse.jetty.security.Authenticator#validateRequest(ServletRequest, ServletResponse, boolean) + */ + public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatory) throws ServerAuthException + { + if (!mandatory) + { + return new LazyAuthentication(_delegate,request,response); + } + return _delegate.validateRequest(request, response, mandatory); + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java new file mode 100644 index 0000000000..444d249853 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java @@ -0,0 +1,44 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.IdentityService; +import org.eclipse.jetty.security.LoginService; + +public abstract class LoginAuthenticator implements Authenticator +{ + protected LoginService _loginService; + protected IdentityService _identityService; + + protected LoginAuthenticator() + { + } + + public void setConfiguration(Configuration configuration) + { + _loginService=configuration.getLoginService(); + if (_loginService==null) + throw new IllegalStateException("No LoginService for "+this); + _identityService=configuration.getIdentityService(); + if (_identityService==null) + throw new IllegalStateException("No IdentityService for "+this); + } + + public LoginService getLoginService() + { + return _loginService; + } + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginCallback.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginCallback.java new file mode 100644 index 0000000000..06a285c298 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginCallback.java @@ -0,0 +1,50 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import java.security.Principal; + +import javax.security.auth.Subject; + + +/** + * This is similar to the jaspi PasswordValidationCallback but includes user + * principal and group info as well. + * + * @version $Rev: 4792 $ $Date: 2009-03-18 22:55:52 +0100 (Wed, 18 Mar 2009) $ + */ +public interface LoginCallback +{ + public Subject getSubject(); + + public String getUserName(); + + public Object getCredential(); + + public boolean isSuccess(); + + public void setSuccess(boolean success); + + public Principal getUserPrincipal(); + + public void setUserPrincipal(Principal userPrincipal); + + public String[] getRoles(); + + public void setRoles(String[] roles); + + public void clearPassword(); + + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginCallbackImpl.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginCallbackImpl.java new file mode 100644 index 0000000000..bb1ee36898 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginCallbackImpl.java @@ -0,0 +1,104 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import java.security.Principal; + +import javax.security.auth.Subject; + +import org.eclipse.jetty.server.UserIdentity; + +/** + * This is similar to the jaspi PasswordValidationCallback but includes user + * principal and group info as well. + * + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class LoginCallbackImpl implements LoginCallback +{ + // initial data + private final Subject subject; + + private final String userName; + + private Object credential; + + private boolean success; + + private Principal userPrincipal; + + private String[] roles = UserIdentity.NO_ROLES; + + //TODO could use Credential instance instead of Object if Basic/Form create a Password object + public LoginCallbackImpl (Subject subject, String userName, Object credential) + { + this.subject = subject; + this.userName = userName; + this.credential = credential; + } + + public Subject getSubject() + { + return subject; + } + + public String getUserName() + { + return userName; + } + + public Object getCredential() + { + return credential; + } + + public boolean isSuccess() + { + return success; + } + + public void setSuccess(boolean success) + { + this.success = success; + } + + public Principal getUserPrincipal() + { + return userPrincipal; + } + + public void setUserPrincipal(Principal userPrincipal) + { + this.userPrincipal = userPrincipal; + } + + public String[] getRoles() + { + return roles; + } + + public void setRoles(String[] groups) + { + this.roles = groups; + } + + public void clearPassword() + { + if (credential != null) + { + credential = null; + } + } + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionCachingAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionCachingAuthenticator.java new file mode 100644 index 0000000000..b862fd8efe --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionCachingAuthenticator.java @@ -0,0 +1,59 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.eclipse.jetty.security.Authentication; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.DefaultAuthentication; +import org.eclipse.jetty.security.ServerAuthException; + +/** + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class SessionCachingAuthenticator extends DelegateAuthenticator +{ + public final static String __J_AUTHENTICATED = "org.eclipse.jetty.server.Auth"; + + + public SessionCachingAuthenticator(Authenticator delegate) + { + super(delegate); + } + + public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatory) throws ServerAuthException + { + HttpSession session = ((HttpServletRequest)request).getSession(mandatory); + // not mandatory and not authenticated + if (session == null) + return DefaultAuthentication.SUCCESS_UNAUTH_RESULTS; + + Authentication authentication = (Authentication) session.getAttribute(__J_AUTHENTICATED); + if (authentication != null) + return authentication; + + authentication = _delegate.validateRequest(request, response, mandatory); + if (authentication != null && authentication.getUserIdentity().getSubject() != null) + { + Authentication next=new DefaultAuthentication(Authentication.Status.SUCCESS,authentication.getAuthMethod(),authentication.getUserIdentity()); + session.setAttribute(__J_AUTHENTICATED, next); + } + return authentication; + } + +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/XCPSCachingAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/XCPSCachingAuthenticator.java new file mode 100644 index 0000000000..c139bb45f1 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/XCPSCachingAuthenticator.java @@ -0,0 +1,55 @@ +// ======================================================================== +// Copyright (c) 2008-2009 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.authentication; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.security.Authentication; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.CrossContextPsuedoSession; +import org.eclipse.jetty.security.ServerAuthException; + +/** + * Cross-context psuedo-session caching ServerAuthentication + * + * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $ + */ +public class XCPSCachingAuthenticator extends DelegateAuthenticator +{ + public final static String __J_AUTHENTICATED = "org.eclipse.jetty.server.Auth"; + + private final CrossContextPsuedoSession<Authentication> _xcps; + + public XCPSCachingAuthenticator(Authenticator delegate, CrossContextPsuedoSession<Authentication> xcps) + { + super(delegate); + this._xcps = xcps; + } + + public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean manditory) throws ServerAuthException + { + + Authentication serverAuthResult = _xcps.fetch((HttpServletRequest)request); + if (serverAuthResult != null) return serverAuthResult; + + serverAuthResult = _delegate.validateRequest(request, response, manditory); + if (serverAuthResult != null) _xcps.store(serverAuthResult, (HttpServletResponse)response); + + return serverAuthResult; + } + +} |