Permalink
Browse files

Added initial JASPIC test for request.authenticate

  • Loading branch information...
1 parent ae578b3 commit 654dcb70f639b2b9987c6b00a41ae8c60e75637f @arjantijms arjantijms committed Jul 7, 2016
View
@@ -25,6 +25,12 @@
in a Servlet via HttpServletRequest#getUserPrincipal
-->
<module>custom-principal</module>
+
+ <!-- Tests a simple authentication like basic-authentication, but uses
+ request.authenticate instead of the authentication prior to invoking
+ the resource.
+ -->
+ <module>programmatic-authentication</module>
<!-- Tests that the main methods of JASPIC artifacts like the SAM are called by the container at the right moment -->
<module>lifecycle</module>
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.javaee7</groupId>
+ <artifactId>jaspic</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>jaspic-programmatic-authentication</artifactId>
+ <packaging>war</packaging>
+
+ <name>Java EE 7 Sample: jaspic - programmatic-authentication</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.javaee7</groupId>
+ <artifactId>jaspic-common</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
@@ -0,0 +1,22 @@
+package org.javaee7.jaspic.programmaticauthentication.sam;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.annotation.WebListener;
+
+import org.javaee7.jaspic.common.BaseServletContextListener;
+import org.javaee7.jaspic.common.JaspicUtils;
+
+/**
+ *
+ * @author Arjan Tijms
+ *
+ */
+@WebListener
+public class SamAutoRegistrationListener extends BaseServletContextListener {
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ JaspicUtils.registerSAM(sce.getServletContext(), new TestServerAuthModule());
+ }
+
+}
@@ -0,0 +1,95 @@
+package org.javaee7.jaspic.programmaticauthentication.sam;
+
+import static javax.security.auth.message.AuthStatus.SEND_SUCCESS;
+import static javax.security.auth.message.AuthStatus.SUCCESS;
+
+import java.io.IOException;
+import java.security.Principal;
+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.callback.UnsupportedCallbackException;
+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;
+
+/**
+ * Very basic SAM that returns a single hardcoded user named "test" with role "architect" when the request *attribute*
+ * <code>doLogin</code> is present.
+ *
+ * @author Arjan Tijms
+ *
+ */
+public class TestServerAuthModule implements ServerAuthModule {
+
+ private CallbackHandler handler;
+ private Class<?>[] supportedMessageTypes = new Class[] { HttpServletRequest.class, HttpServletResponse.class };
+
+ @Override
+ public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler,
+ @SuppressWarnings("rawtypes") Map options) throws AuthException {
+ this.handler = handler;
+ }
+
+ @Override
+ public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
+ throws AuthException {
+
+ HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
+
+ Callback[] callbacks;
+
+ if (request.getAttribute("doLogin") != null) { // notice "getAttribute" here, this is set by the Servlet
+
+ // For the test perform a login by directly "returning" the details of the authenticated user.
+ // Normally credentials would be checked and the details fetched from some repository
+
+ callbacks = new Callback[] {
+ // The name of the authenticated user
+ new CallerPrincipalCallback(clientSubject, "test"),
+ // the roles of the authenticated user
+ new GroupPrincipalCallback(clientSubject, new String[] { "architect" })
+ };
+ } else {
+
+ // The JASPIC protocol for "do nothing"
+ callbacks = new Callback[] { new CallerPrincipalCallback(clientSubject, (Principal) null) };
+ }
+
+ try {
+
+ // Communicate the details of the authenticated user to the container. In many
+ // cases the handler will just store the details and the container will actually handle
+ // the login after we return from this method.
+ handler.handle(callbacks);
+
+ } catch (IOException | UnsupportedCallbackException e) {
+ throw (AuthException) new AuthException().initCause(e);
+ }
+
+ return SUCCESS;
+ }
+
+ @Override
+ public Class<?>[] getSupportedMessageTypes() {
+ return supportedMessageTypes;
+ }
+
+ @Override
+ public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException {
+ return SEND_SUCCESS;
+ }
+
+ @Override
+ public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException {
+
+ }
+}
@@ -0,0 +1,73 @@
+package org.javaee7.jaspic.programmaticauthentication.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ *
+ * @author Arjan Tijms
+ *
+ */
+@WebServlet(urlPatterns = "/public/authenticate")
+public class AuthenticateServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+
+ response.getWriter().write("This is a public servlet \n");
+
+ String webName = null;
+ if (request.getUserPrincipal() != null) {
+ webName = request.getUserPrincipal().getName();
+ }
+
+ response.getWriter().write("before web username: " + webName + "\n");
+ boolean webHasRole = request.isUserInRole("architect");
+ response.getWriter().write("before web user has role \"architect\": " + webHasRole + "\n");
+
+ // By *not* setting the "doLogin" request attribute the request.authenticate call
+ // would do nothing. request.authenticate is a mandatory authentication, so doing
+ // nothing is not allowed. But one or more initial failures should not prevent
+ // a later successful authentication.
+ if (request.getParameter("failFirst") != null) {
+ try {
+ request.authenticate(response);
+ } catch (IOException | ServletException e) {
+ // GlassFish returns false when either authentication is in progress or authentication
+ // failed (or was not done at all), but JBoss throws an exception.
+ // TODO: Get clarification what is actually expected, likely exception is most correct.
+ // Then test for the correct return value.
+ }
+ }
+
+ if ("2".equals(request.getParameter("failFirst"))) {
+ try {
+ request.authenticate(response);
+ } catch (IOException | ServletException e) {
+ }
+ }
+
+ // Programmatically trigger the authentication chain
+ request.setAttribute("doLogin", true);
+ boolean authenticateOutcome = request.authenticate(response);
+
+ if (request.getUserPrincipal() != null) {
+ webName = request.getUserPrincipal().getName();
+ }
+
+ response.getWriter().write("request.authenticate outcome: " + authenticateOutcome + "\n");
+
+ response.getWriter().write("after web username: " + webName + "\n");
+ webHasRole = request.isUserInRole("architect");
+ response.getWriter().write("after web user has role \"architect\": " + webHasRole + "\n");
+
+ }
+
+}
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
+<glassfish-web-app>
+
+ <security-role-mapping>
+ <role-name>architect</role-name>
+ <group-name>architect</group-name>
+ </security-role-mapping>
+
+ <parameter-encoding default-charset="UTF-8" />
+
+</glassfish-web-app>
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<application-bnd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-application-bnd_1_2.xsd"
+ xmlns="http://websphere.ibm.com/xml/ns/javaee"
+ version="1.2">
+
+ <security-role name="architect" id="architect" >
+ <group name="architect" access-id="architect" />
+ </security-role>
+
+</application-bnd>
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+
+<jboss-web>
+ <security-domain>jaspitest</security-domain>
+</jboss-web>
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Test</web-resource-name>
+ <url-pattern>/protected/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>architect</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <security-role>
+ <role-name>architect</role-name>
+ </security-role>
+
+</web-app>
@@ -0,0 +1,85 @@
+package org.javaee7.jaspic.programmaticauthentication;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.javaee7.jaspic.common.ArquillianBase;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.Archive;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xml.sax.SAXException;
+
+/**
+ * This tests that a call from a Servlet to HttpServletRequest#authenticate can result
+ * in a successful authentication.
+ *
+ * @author Arjan Tijms
+ *
+ */
+@RunWith(Arquillian.class)
+public class ProgrammaticAuthenticationTest extends ArquillianBase {
+
+ @Deployment(testable = false)
+ public static Archive<?> createDeployment() {
+ return defaultArchive();
+ }
+
+ @Test
+ public void testAuthenticate() throws IOException, SAXException {
+ assertAuthenticated(getFromServerPath("public/authenticate"));
+ }
+
+ @Test
+ public void testAuthenticateFailFirstOnce() throws IOException, SAXException {
+ // Before authenticating successfully, call request.authenticate which
+ // is known to fail (do nothing)
+ assertAuthenticated(getFromServerPath("public/authenticate?failFirst=1"));
+ }
+
+ @Test
+ public void testAuthenticateFailFirstTwice() throws IOException, SAXException {
+ // Before authenticating successfully, call request.authenticate twice which
+ // are both known to fail (do nothing)
+ assertAuthenticated(getFromServerPath("public/authenticate?failFirst=2"));
+ }
+
+ private void assertAuthenticated(String response) {
+
+ // Should not be authenticated in the "before" case, which is
+ // before request.authentiate is called
+ assertTrue(
+ "Should not be authenticated yet, but a username other than null was encountered. " +
+ "This is not correct.",
+ response.contains("before web username: null")
+ );
+ assertTrue(
+ "Should not be authenticated yet, but the user seems to have the role \"architect\". " +
+ "This is not correct.",
+ response.contains("before web user has role \"architect\": false")
+ );
+
+ // The main request.authenticate causes the SAM to be called which always authenticates
+ assertTrue(
+ "Calling request.authenticate should have returned true, but did not.",
+ response.contains("request.authenticate outcome: true")
+ );
+
+ // Should be authenticated in the "after" case, which is
+ // after request.authentiate is called
+ assertTrue(
+ "User should have been authenticated and given name \"test\", " +
+ " but does not appear to have this name",
+ response.contains("after web username: test")
+ );
+ assertTrue(
+ "User should have been authenticated and given role \"architect\", " +
+ " but does not appear to have this role",
+ response.contains("after web user has role \"architect\": true")
+ );
+ }
+
+
+}

0 comments on commit 654dcb7

Please sign in to comment.