From fe444b28dd05d7f7c668335a7b68844ffc9047b9 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 12 Dec 2014 16:47:30 +0100 Subject: [PATCH] 455047 Update JASPI Added test harnesses. Corrected a few issues. --- jetty-jaspi/pom.xml | 13 ++ .../security/jaspi/JaspiAuthenticator.java | 12 +- .../jaspi/JaspiAuthenticatorFactory.java | 3 + .../jaspi/modules/BaseAuthModule.java | 1 - .../jaspi/modules/FormAuthModule.java | 3 +- .../security/jaspi/HttpHeaderAuthModule.java | 125 +++++++++++++ .../jetty/security/jaspi/JaspiTest.java | 167 ++++++++++++++++++ jetty-jaspi/src/test/resources/jaspi.xml | 39 ++++ 8 files changed, 359 insertions(+), 4 deletions(-) create mode 100644 jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/HttpHeaderAuthModule.java create mode 100644 jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java create mode 100644 jetty-jaspi/src/test/resources/jaspi.xml diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml index d76703db57a..ab5880ef36d 100644 --- a/jetty-jaspi/pom.xml +++ b/jetty-jaspi/pom.xml @@ -85,5 +85,18 @@ org.eclipse.jetty.orbit javax.security.auth.message + + + org.apache.geronimo.components + geronimo-jaspi + 2.0.0 + + + org.apache.geronimo.specs + geronimo-jaspic_1.0_spec + + + test + diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java index a6027487c69..c5228f44171 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.security.jaspi; +import java.io.IOException; import java.security.Principal; import java.util.Map; import java.util.Set; @@ -32,6 +33,7 @@ import javax.security.auth.message.config.ServerAuthContext; 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.security.IdentityService; @@ -192,10 +194,16 @@ public class JaspiAuthenticator extends LoginAuthenticator // we are processing a message in a secureResponse dialog. return Authentication.SEND_SUCCESS; } + if (authStatus == AuthStatus.FAILURE) + { + HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage(); + response.sendError(HttpServletResponse.SC_FORBIDDEN); + return Authentication.SEND_FAILURE; + } // should not happen - throw new NullPointerException("No AuthStatus returned"); + throw new IllegalStateException("No AuthStatus returned"); } - catch (AuthException e) + catch (IOException|AuthException e) { throw new ServerAuthException(e); } diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java index 6dbb00f8906..b31b0cafb4c 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticatorFactory.java @@ -102,6 +102,9 @@ public class JaspiAuthenticatorFactory extends DefaultAuthenticatorFactory Subject serviceSubject=findServiceSubject(server); String serverName=findServerName(server,serviceSubject); + String contextPath=context.getContextPath(); + if (contextPath==null || contextPath.length()==0) + contextPath="/"; String appContext = serverName + " " + context.getContextPath(); AuthConfigProvider authConfigProvider = authConfigFactory.getConfigProvider(MESSAGE_LAYER,appContext,listener); diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java index 7b691717c97..a5ce15855d8 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BaseAuthModule.java @@ -46,7 +46,6 @@ import org.eclipse.jetty.util.security.Credential; import org.eclipse.jetty.util.security.Password; /** - * @deprecated use *ServerAuthentication * @version $Rev: 4792 $ $Date: 2009-03-18 22:55:52 +0100 (Wed, 18 Mar 2009) $ */ public class BaseAuthModule implements ServerAuthModule, ServerAuthContext diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java index 9215eddaf00..f7f037eab64 100644 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java +++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java @@ -164,7 +164,8 @@ public class FormAuthModule extends BaseAuthModule HttpSession session = request.getSession(mandatory); // not mandatory or its the login or login error page don't authenticate - if (!mandatory || isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()))) return AuthStatus.SUCCESS; + if (!mandatory || isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()))) + return AuthStatus.SUCCESS; // TODO return null for do nothing? try { diff --git a/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/HttpHeaderAuthModule.java b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/HttpHeaderAuthModule.java new file mode 100644 index 00000000000..12cfad0c688 --- /dev/null +++ b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/HttpHeaderAuthModule.java @@ -0,0 +1,125 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.security.jaspi; + +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.message.AuthException; +import javax.security.auth.message.AuthStatus; +import javax.security.auth.message.MessageInfo; +import javax.security.auth.message.MessagePolicy; +import javax.security.auth.message.callback.CallerPrincipalCallback; +import javax.security.auth.message.callback.GroupPrincipalCallback; +import javax.security.auth.message.module.ServerAuthModule; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Example JASPI Auth Module based on http://www.trajano.net/2014/06/creating-a-simple-jaspic-auth-module/ + */ +public class HttpHeaderAuthModule implements ServerAuthModule +{ + + /** + * Supported message types. For our case we only need to deal with HTTP servlet request and responses. On Java EE 7 this will handle WebSockets as well. + */ + private static final Class[] SUPPORTED_MESSAGE_TYPES = new Class[] + { HttpServletRequest.class, HttpServletResponse.class }; + + /** + * Callback handler that is passed in initialize by the container. This processes the callbacks which are objects that populate the "subject". + */ + private CallbackHandler handler; + + /** + * Does nothing of note for what we need. + */ + @Override + public void cleanSubject(final MessageInfo messageInfo, final Subject subject) throws AuthException + { + } + + @SuppressWarnings("rawtypes") + @Override + public Class[] getSupportedMessageTypes() + { + return SUPPORTED_MESSAGE_TYPES; + } + + /** + * Initializes the module. Allows you to pass in options. + * + * @param requestPolicy + * request policy, ignored + * @param responsePolicy + * response policy, ignored + * @param h + * callback handler + * @param options + * options + */ + @Override + public void initialize(final MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler h, Map options) throws AuthException + { + handler = h; + } + + /** + * @return AuthStatus.SEND_SUCCESS + */ + @Override + public AuthStatus secureResponse(final MessageInfo paramMessageInfo, final Subject subject) throws AuthException + { + return AuthStatus.SEND_SUCCESS; + } + + /** + * Validation occurs here. + */ + @Override + public AuthStatus validateRequest(final MessageInfo messageInfo, final Subject client, final Subject serviceSubject) throws AuthException + { + + // Take the request from the messageInfo structure. + final HttpServletRequest req = (HttpServletRequest)messageInfo.getRequestMessage(); + try + { + // Get the user name from the header. If not there then fail authentication. + final String userName = req.getHeader("X-Forwarded-User"); + if (userName == null) + { + return AuthStatus.FAILURE; + } + + // Store the user name that was in the header and also set a group. + handler.handle(new Callback[] + { new CallerPrincipalCallback(client,userName), new GroupPrincipalCallback(client,new String[] + { "users" }) }); + return AuthStatus.SUCCESS; + } + catch (final Exception e) + { + throw new AuthException(e.getMessage()); + } + } + +} diff --git a/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java new file mode 100644 index 00000000000..771b15433be --- /dev/null +++ b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java @@ -0,0 +1,167 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.security.jaspi; + +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.util.B64Code; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Password; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class JaspiTest +{ + Server _server; + LocalConnector _connector; + + @Before + public void before() throws Exception + { + System.setProperty("org.apache.geronimo.jaspic.configurationFile","src/test/resources/jaspi.xml"); + _server = new Server(); + _connector = new LocalConnector(_server); + _server.addConnector(_connector); + + ContextHandlerCollection contexts = new ContextHandlerCollection(); + _server.setHandler(contexts); + + HashLoginService loginService = new HashLoginService("TestRealm"); + loginService.putUser("user",new Password("password"),new String[]{"users"}); + loginService.putUser("admin",new Password("secret"),new String[]{"users","admins"}); + _server.addBean(loginService); + + ContextHandler context = new ContextHandler(); + contexts.addHandler(context); + context.setContextPath("/ctx"); + + JaspiAuthenticatorFactory jaspiAuthFactory = new JaspiAuthenticatorFactory(); + + ConstraintSecurityHandler security = new ConstraintSecurityHandler(); + context.setHandler(security); + security.setAuthenticatorFactory(jaspiAuthFactory); + // security.setAuthenticator(new BasicAuthenticator()); + + Constraint constraint = new Constraint("All","users"); + constraint.setAuthenticate(true); + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setPathSpec("/jaspi/*"); + mapping.setConstraint(constraint); + security.addConstraintMapping(mapping); + + TestHandler handler = new TestHandler(); + security.setHandler(handler); + + ContextHandler other = new ContextHandler(); + contexts.addHandler(other); + other.setContextPath("/other"); + ConstraintSecurityHandler securityOther = new ConstraintSecurityHandler(); + other.setHandler(securityOther); + securityOther.setAuthenticatorFactory(jaspiAuthFactory); + securityOther.addConstraintMapping(mapping); + securityOther.setHandler(new TestHandler()); + + _server.start(); + } + + @After + public void after() throws Exception + { + _server.stop(); + } + + @Test + public void testNoConstraint() throws Exception + { + String response = _connector.getResponses("GET /ctx/test HTTP/1.0\n\n"); + assertThat(response,startsWith("HTTP/1.1 200 OK")); + } + + @Test + public void testConstraintNoAuth() throws Exception + { + String response = _connector.getResponses("GET /ctx/jaspi/test HTTP/1.0\n\n"); + assertThat(response,startsWith("HTTP/1.1 401 Unauthorized")); + assertThat(response,Matchers.containsString("WWW-Authenticate: basic realm=\"TestRealm\"")); + } + + @Test + public void testConstraintWrongAuth() throws Exception + { + String response = _connector.getResponses("GET /ctx/jaspi/test HTTP/1.0\n"+ + "Authorization: Basic " + B64Code.encode("user:wrong") + "\n\n"); + assertThat(response,startsWith("HTTP/1.1 401 Unauthorized")); + assertThat(response,Matchers.containsString("WWW-Authenticate: basic realm=\"TestRealm\"")); + } + + @Test + public void testConstraintAuth() throws Exception + { + String response = _connector.getResponses("GET /ctx/jaspi/test HTTP/1.0\n"+ + "Authorization: Basic " + B64Code.encode("user:password") + "\n\n"); + assertThat(response,startsWith("HTTP/1.1 200 OK")); + } + + @Test + public void testOtherNoAuth() throws Exception + { + String response = _connector.getResponses("GET /other/test HTTP/1.0\n\n"); + assertThat(response,startsWith("HTTP/1.1 403 Forbidden")); + } + + @Test + public void testOtherAuth() throws Exception + { + String response = _connector.getResponses("GET /other/test HTTP/1.0\n"+ + "X-Forwarded-User: user\n\n"); + assertThat(response,startsWith("HTTP/1.1 200 OK")); + } + + public class TestHandler extends AbstractHandler + { + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + baseRequest.setHandled(true); + response.setStatus(200); + response.setContentType("text/plain"); + response.getWriter().println("All OK"); + response.getWriter().println("requestURI="+request.getRequestURI()); + } + } +} diff --git a/jetty-jaspi/src/test/resources/jaspi.xml b/jetty-jaspi/src/test/resources/jaspi.xml new file mode 100644 index 00000000000..23a2ba5c7ed --- /dev/null +++ b/jetty-jaspi/src/test/resources/jaspi.xml @@ -0,0 +1,39 @@ + + + + + HTTP + server /ctx + description + + authenticationContextID1 + true + + + org.eclipse.jetty.security.jaspi.modules.BasicAuthModule + + org.eclipse.jetty.security.jaspi.modules.RealmName=TestRealm + + + + + true + + + HTTP + server /other + description + + authenticationContextID2 + true + + + org.eclipse.jetty.security.jaspi.HttpHeaderAuthModule + + + + + + true + +