Skip to content

Commit 93b0df1

Browse files
authored
Merge pull request javaee-samples#353 from arjantijms/master
Added initial JASPIC test for request.authenticate
2 parents a96abca + 654dcb7 commit 93b0df1

File tree

10 files changed

+352
-0
lines changed

10 files changed

+352
-0
lines changed

jaspic/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
in a Servlet via HttpServletRequest#getUserPrincipal
2626
-->
2727
<module>custom-principal</module>
28+
29+
<!-- Tests a simple authentication like basic-authentication, but uses
30+
request.authenticate instead of the authentication prior to invoking
31+
the resource.
32+
-->
33+
<module>programmatic-authentication</module>
2834

2935
<!-- Tests that the main methods of JASPIC artifacts like the SAM are called by the container at the right moment -->
3036
<module>lifecycle</module>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>org.javaee7</groupId>
7+
<artifactId>jaspic</artifactId>
8+
<version>1.0-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>jaspic-programmatic-authentication</artifactId>
12+
<packaging>war</packaging>
13+
14+
<name>Java EE 7 Sample: jaspic - programmatic-authentication</name>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>org.javaee7</groupId>
19+
<artifactId>jaspic-common</artifactId>
20+
<version>1.0-SNAPSHOT</version>
21+
</dependency>
22+
</dependencies>
23+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.javaee7.jaspic.programmaticauthentication.sam;
2+
3+
import javax.servlet.ServletContextEvent;
4+
import javax.servlet.annotation.WebListener;
5+
6+
import org.javaee7.jaspic.common.BaseServletContextListener;
7+
import org.javaee7.jaspic.common.JaspicUtils;
8+
9+
/**
10+
*
11+
* @author Arjan Tijms
12+
*
13+
*/
14+
@WebListener
15+
public class SamAutoRegistrationListener extends BaseServletContextListener {
16+
17+
@Override
18+
public void contextInitialized(ServletContextEvent sce) {
19+
JaspicUtils.registerSAM(sce.getServletContext(), new TestServerAuthModule());
20+
}
21+
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package org.javaee7.jaspic.programmaticauthentication.sam;
2+
3+
import static javax.security.auth.message.AuthStatus.SEND_SUCCESS;
4+
import static javax.security.auth.message.AuthStatus.SUCCESS;
5+
6+
import java.io.IOException;
7+
import java.security.Principal;
8+
import java.util.Map;
9+
10+
import javax.security.auth.Subject;
11+
import javax.security.auth.callback.Callback;
12+
import javax.security.auth.callback.CallbackHandler;
13+
import javax.security.auth.callback.UnsupportedCallbackException;
14+
import javax.security.auth.message.AuthException;
15+
import javax.security.auth.message.AuthStatus;
16+
import javax.security.auth.message.MessageInfo;
17+
import javax.security.auth.message.MessagePolicy;
18+
import javax.security.auth.message.callback.CallerPrincipalCallback;
19+
import javax.security.auth.message.callback.GroupPrincipalCallback;
20+
import javax.security.auth.message.module.ServerAuthModule;
21+
import javax.servlet.http.HttpServletRequest;
22+
import javax.servlet.http.HttpServletResponse;
23+
24+
/**
25+
* Very basic SAM that returns a single hardcoded user named "test" with role "architect" when the request *attribute*
26+
* <code>doLogin</code> is present.
27+
*
28+
* @author Arjan Tijms
29+
*
30+
*/
31+
public class TestServerAuthModule implements ServerAuthModule {
32+
33+
private CallbackHandler handler;
34+
private Class<?>[] supportedMessageTypes = new Class[] { HttpServletRequest.class, HttpServletResponse.class };
35+
36+
@Override
37+
public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler,
38+
@SuppressWarnings("rawtypes") Map options) throws AuthException {
39+
this.handler = handler;
40+
}
41+
42+
@Override
43+
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject)
44+
throws AuthException {
45+
46+
HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
47+
48+
Callback[] callbacks;
49+
50+
if (request.getAttribute("doLogin") != null) { // notice "getAttribute" here, this is set by the Servlet
51+
52+
// For the test perform a login by directly "returning" the details of the authenticated user.
53+
// Normally credentials would be checked and the details fetched from some repository
54+
55+
callbacks = new Callback[] {
56+
// The name of the authenticated user
57+
new CallerPrincipalCallback(clientSubject, "test"),
58+
// the roles of the authenticated user
59+
new GroupPrincipalCallback(clientSubject, new String[] { "architect" })
60+
};
61+
} else {
62+
63+
// The JASPIC protocol for "do nothing"
64+
callbacks = new Callback[] { new CallerPrincipalCallback(clientSubject, (Principal) null) };
65+
}
66+
67+
try {
68+
69+
// Communicate the details of the authenticated user to the container. In many
70+
// cases the handler will just store the details and the container will actually handle
71+
// the login after we return from this method.
72+
handler.handle(callbacks);
73+
74+
} catch (IOException | UnsupportedCallbackException e) {
75+
throw (AuthException) new AuthException().initCause(e);
76+
}
77+
78+
return SUCCESS;
79+
}
80+
81+
@Override
82+
public Class<?>[] getSupportedMessageTypes() {
83+
return supportedMessageTypes;
84+
}
85+
86+
@Override
87+
public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException {
88+
return SEND_SUCCESS;
89+
}
90+
91+
@Override
92+
public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException {
93+
94+
}
95+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.javaee7.jaspic.programmaticauthentication.servlet;
2+
3+
import java.io.IOException;
4+
5+
import javax.servlet.ServletException;
6+
import javax.servlet.annotation.WebServlet;
7+
import javax.servlet.http.HttpServlet;
8+
import javax.servlet.http.HttpServletRequest;
9+
import javax.servlet.http.HttpServletResponse;
10+
11+
/**
12+
*
13+
* @author Arjan Tijms
14+
*
15+
*/
16+
@WebServlet(urlPatterns = "/public/authenticate")
17+
public class AuthenticateServlet extends HttpServlet {
18+
19+
private static final long serialVersionUID = 1L;
20+
21+
@Override
22+
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
23+
24+
response.getWriter().write("This is a public servlet \n");
25+
26+
String webName = null;
27+
if (request.getUserPrincipal() != null) {
28+
webName = request.getUserPrincipal().getName();
29+
}
30+
31+
response.getWriter().write("before web username: " + webName + "\n");
32+
boolean webHasRole = request.isUserInRole("architect");
33+
response.getWriter().write("before web user has role \"architect\": " + webHasRole + "\n");
34+
35+
// By *not* setting the "doLogin" request attribute the request.authenticate call
36+
// would do nothing. request.authenticate is a mandatory authentication, so doing
37+
// nothing is not allowed. But one or more initial failures should not prevent
38+
// a later successful authentication.
39+
if (request.getParameter("failFirst") != null) {
40+
try {
41+
request.authenticate(response);
42+
} catch (IOException | ServletException e) {
43+
// GlassFish returns false when either authentication is in progress or authentication
44+
// failed (or was not done at all), but JBoss throws an exception.
45+
// TODO: Get clarification what is actually expected, likely exception is most correct.
46+
// Then test for the correct return value.
47+
}
48+
}
49+
50+
if ("2".equals(request.getParameter("failFirst"))) {
51+
try {
52+
request.authenticate(response);
53+
} catch (IOException | ServletException e) {
54+
}
55+
}
56+
57+
// Programmatically trigger the authentication chain
58+
request.setAttribute("doLogin", true);
59+
boolean authenticateOutcome = request.authenticate(response);
60+
61+
if (request.getUserPrincipal() != null) {
62+
webName = request.getUserPrincipal().getName();
63+
}
64+
65+
response.getWriter().write("request.authenticate outcome: " + authenticateOutcome + "\n");
66+
67+
response.getWriter().write("after web username: " + webName + "\n");
68+
webHasRole = request.isUserInRole("architect");
69+
response.getWriter().write("after web user has role \"architect\": " + webHasRole + "\n");
70+
71+
}
72+
73+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!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">
3+
<glassfish-web-app>
4+
5+
<security-role-mapping>
6+
<role-name>architect</role-name>
7+
<group-name>architect</group-name>
8+
</security-role-mapping>
9+
10+
<parameter-encoding default-charset="UTF-8" />
11+
12+
</glassfish-web-app>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<application-bnd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-application-bnd_1_2.xsd"
4+
xmlns="http://websphere.ibm.com/xml/ns/javaee"
5+
version="1.2">
6+
7+
<security-role name="architect" id="architect" >
8+
<group name="architect" access-id="architect" />
9+
</security-role>
10+
11+
</application-bnd>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0"?>
2+
3+
<jboss-web>
4+
<security-domain>jaspitest</security-domain>
5+
</jboss-web>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
3+
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"
4+
version="3.0">
5+
6+
<security-constraint>
7+
<web-resource-collection>
8+
<web-resource-name>Test</web-resource-name>
9+
<url-pattern>/protected/*</url-pattern>
10+
</web-resource-collection>
11+
<auth-constraint>
12+
<role-name>architect</role-name>
13+
</auth-constraint>
14+
</security-constraint>
15+
16+
<security-role>
17+
<role-name>architect</role-name>
18+
</security-role>
19+
20+
</web-app>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.javaee7.jaspic.programmaticauthentication;
2+
3+
import static org.junit.Assert.assertTrue;
4+
5+
import java.io.IOException;
6+
7+
import org.javaee7.jaspic.common.ArquillianBase;
8+
import org.jboss.arquillian.container.test.api.Deployment;
9+
import org.jboss.arquillian.junit.Arquillian;
10+
import org.jboss.shrinkwrap.api.Archive;
11+
import org.junit.Test;
12+
import org.junit.runner.RunWith;
13+
import org.xml.sax.SAXException;
14+
15+
/**
16+
* This tests that a call from a Servlet to HttpServletRequest#authenticate can result
17+
* in a successful authentication.
18+
*
19+
* @author Arjan Tijms
20+
*
21+
*/
22+
@RunWith(Arquillian.class)
23+
public class ProgrammaticAuthenticationTest extends ArquillianBase {
24+
25+
@Deployment(testable = false)
26+
public static Archive<?> createDeployment() {
27+
return defaultArchive();
28+
}
29+
30+
@Test
31+
public void testAuthenticate() throws IOException, SAXException {
32+
assertAuthenticated(getFromServerPath("public/authenticate"));
33+
}
34+
35+
@Test
36+
public void testAuthenticateFailFirstOnce() throws IOException, SAXException {
37+
// Before authenticating successfully, call request.authenticate which
38+
// is known to fail (do nothing)
39+
assertAuthenticated(getFromServerPath("public/authenticate?failFirst=1"));
40+
}
41+
42+
@Test
43+
public void testAuthenticateFailFirstTwice() throws IOException, SAXException {
44+
// Before authenticating successfully, call request.authenticate twice which
45+
// are both known to fail (do nothing)
46+
assertAuthenticated(getFromServerPath("public/authenticate?failFirst=2"));
47+
}
48+
49+
private void assertAuthenticated(String response) {
50+
51+
// Should not be authenticated in the "before" case, which is
52+
// before request.authentiate is called
53+
assertTrue(
54+
"Should not be authenticated yet, but a username other than null was encountered. " +
55+
"This is not correct.",
56+
response.contains("before web username: null")
57+
);
58+
assertTrue(
59+
"Should not be authenticated yet, but the user seems to have the role \"architect\". " +
60+
"This is not correct.",
61+
response.contains("before web user has role \"architect\": false")
62+
);
63+
64+
// The main request.authenticate causes the SAM to be called which always authenticates
65+
assertTrue(
66+
"Calling request.authenticate should have returned true, but did not.",
67+
response.contains("request.authenticate outcome: true")
68+
);
69+
70+
// Should be authenticated in the "after" case, which is
71+
// after request.authentiate is called
72+
assertTrue(
73+
"User should have been authenticated and given name \"test\", " +
74+
" but does not appear to have this name",
75+
response.contains("after web username: test")
76+
);
77+
assertTrue(
78+
"User should have been authenticated and given role \"architect\", " +
79+
" but does not appear to have this role",
80+
response.contains("after web user has role \"architect\": true")
81+
);
82+
}
83+
84+
85+
}

0 commit comments

Comments
 (0)