Permalink
Browse files

Added initial JASPIC test for request.authenticate

  • Loading branch information...
arjantijms committed Jul 7, 2016
1 parent ae578b3 commit 654dcb70f639b2b9987c6b00a41ae8c60e75637f
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.