add tomcat7 adapter test

This commit is contained in:
Bill Burke 2014-10-28 18:41:35 -04:00
parent 12e2a4698d
commit da27f43573
8 changed files with 239 additions and 244 deletions

View file

@ -211,6 +211,7 @@
<dependency> <dependency>
<groupId>org.picketlink</groupId> <groupId>org.picketlink</groupId>
<artifactId>picketlink-wildlfy-common</artifactId> <artifactId>picketlink-wildlfy-common</artifactId>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.wildfly</groupId> <groupId>org.wildfly</groupId>

View file

@ -36,6 +36,10 @@ public abstract class SamlKeycloakRule extends AbstractKeycloakRule {
resp.setContentType("text/plain"); resp.setContentType("text/plain");
OutputStream stream = resp.getOutputStream(); OutputStream stream = resp.getOutputStream();
Principal principal = req.getUserPrincipal(); Principal principal = req.getUserPrincipal();
if (principal == null) {
stream.write("null".getBytes());
return;
}
String name = principal.getName(); String name = principal.getName();
stream.write(name.getBytes()); stream.write(name.getBytes());
} }

View file

@ -116,7 +116,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>federation-properties-example</artifactId> <artifactId>keycloak-tomcat7-adapter</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@ -189,19 +189,6 @@
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId> <artifactId>selenium-chrome-driver</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.picketbox</groupId>
<artifactId>picketbox-ldap</artifactId>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.picketbox</groupId>
<artifactId>picketbox-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-wildlfy-common</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.wildfly</groupId> <groupId>org.wildfly</groupId>
<artifactId>wildfly-undertow</artifactId> <artifactId>wildfly-undertow</artifactId>

View file

@ -21,263 +21,155 @@
*/ */
package org.keycloak.testsuite; package org.keycloak.testsuite;
import org.apache.http.NameValuePair; import org.apache.catalina.startup.Tomcat;
import org.apache.http.client.CookieStore; import org.junit.AfterClass;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.adapters.HttpClientBuilder; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OpenIDConnectService; import org.keycloak.protocol.oidc.OpenIDConnectService;
import org.keycloak.services.resources.LoginActionsService; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.Constants; import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.OAuthClient; import org.keycloak.services.resources.admin.AdminRoot;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.AbstractKeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule; import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.util.BasicAuthHelper; import org.keycloak.testutils.KeycloakServer;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import javax.ws.rs.client.Entity; import javax.servlet.ServletException;
import javax.ws.rs.core.Form; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import java.net.URI; import java.io.File;
import java.util.Collections; import java.io.IOException;
import java.util.Date; import java.io.OutputStream;
import java.util.HashMap; import java.net.URL;
import java.util.List; import java.security.Principal;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class Tomcat7Test { public class Tomcat7Test {
static String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth"))
.queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8080/customer-portal").build("demo").toString();
@ClassRule @ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(); public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
public static class BrowserLogin implements Runnable
{
private WebDriver driver;
public BrowserLogin() {
driver = WebRule.createWebDriver();
}
@Override @Override
public void run() { protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
driver.manage().deleteAllCookies(); RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/tomcat-test/demorealm.json"), RealmRepresentation.class);
OAuthClient oauth = new OAuthClient(driver); RealmModel realm = manager.importRealm(representation);
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
count.incrementAndGet();
}
} }
};
public static AtomicLong count = new AtomicLong(0); public static class SendUsernameServlet extends HttpServlet {
public static class JaxrsClientLogin implements Runnable
{
ResteasyClient client;
private String baseUrl = Constants.AUTH_SERVER_ROOT;
private String realm = "test";
private String responseType = OAuth2Constants.CODE;
private String grantType = "authorization_code";
private String clientId = "test-app";
private String redirectUri = "http://localhost:8081/app/auth";
public JaxrsClientLogin() {
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
httpClient.setCookieStore(new CookieStore() {
@Override @Override
public void addCookie(Cookie cookie) { protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
//To change body of implemented methods use File | Settings | File Templates. resp.setContentType("text/plain");
OutputStream stream = resp.getOutputStream();
Principal principal = req.getUserPrincipal();
if (principal == null) {
stream.write("null".getBytes());
return;
} }
String name = principal.getName();
stream.write(name.getBytes());
stream.write("\n".getBytes());
KeycloakSecurityContext context = (KeycloakSecurityContext)req.getAttribute(KeycloakSecurityContext.class.getName());
stream.write(context.getIdToken().getName().getBytes());
stream.write("\n".getBytes());
stream.write(logoutUri.getBytes());
}
@Override @Override
public List<Cookie> getCookies() { protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
return Collections.emptyList(); doGet(req, resp);
}
} }
@Override static Tomcat tomcat = null;
public boolean clearExpired(Date date) {
return false; //To change body of implemented methods use File | Settings | File Templates. @BeforeClass
public static void initTomcat() throws Exception {
String webappDirLocation = "src/test/resources/tomcat-test/webapp/";
URL dir = Tomcat7Test.class.getResource("/tomcat-test/webapp/META-INF/context.xml");
File webappDir = new File(dir.getFile()).getParentFile().getParentFile();
tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.addWebapp("/customer-portal", webappDir.toString());
System.out.println("configuring app with basedir: " + webappDir.toString());
tomcat.start();
//tomcat.getServer().await();
} }
@Override @AfterClass
public void clear() { public static void shutdownTomcat() throws Exception {
//To change body of implemented methods use File | Settings | File Templates. tomcat.stop();
} tomcat.destroy();
});
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
this.client = new ResteasyClientBuilder().httpEngine(engine).build();
} }
public String getLoginFormUrl(String state) { @Rule
UriBuilder b = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri(baseUrl)); public WebRule webRule = new WebRule(this);
if (responseType != null) { @WebResource
b.queryParam(OAuth2Constants.RESPONSE_TYPE, responseType); protected WebDriver driver;
} @WebResource
if (clientId != null) { protected LoginPage loginPage;
b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
}
if (redirectUri != null) {
b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
}
if (state != null) {
b.queryParam(OAuth2Constants.STATE, state);
}
return b.build(realm).toString();
}
public String getProcessLoginUrl(String state) { public static final String LOGIN_URL = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri("http://localhost:8081/auth")).build("demo").toString();
UriBuilder b = LoginActionsService.processLoginUrl(UriBuilder.fromUri(baseUrl)); @Test
if (clientId != null) { public void testLoginSSOAndLogout() throws Exception {
b.queryParam(OAuth2Constants.CLIENT_ID, clientId); driver.navigate().to("http://localhost:8080/customer-portal");
} System.out.println("Current url: " + driver.getCurrentUrl());
if (redirectUri != null) { Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri); loginPage.login("bburke@redhat.com", "password");
} System.out.println("Current url: " + driver.getCurrentUrl());
if (state != null) { Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8080/customer-portal");
b.queryParam(OAuth2Constants.STATE, state); String pageSource = driver.getPageSource();
} System.out.println(pageSource);
return b.build(realm).toString(); Assert.assertTrue(pageSource.contains("Bill Burke"));
}
static Pattern actionParser = Pattern.compile("action=\"([^\"]+)\""); // test logout
public void run() { String logoutUri = OpenIDConnectService.logoutUrl(UriBuilder.fromUri("http://localhost:8081/auth"))
//this.client = new ResteasyClientBuilder().build(); .queryParam(OAuth2Constants.REDIRECT_URI, "http://localhost:8080/customer-portal").build("demo").toString();
String state = "42"; driver.navigate().to(logoutUri);
String loginFormUrl = getLoginFormUrl(state); Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
String html = client.target(loginFormUrl).request().get(String.class); driver.navigate().to("http://localhost:8080/customer-portal");
Matcher matcher = actionParser.matcher(html); String currentUrl = driver.getCurrentUrl();
matcher.find(); Assert.assertTrue(currentUrl.startsWith(LOGIN_URL));
String actionUrl = matcher.group(1);
if (!actionUrl.startsWith("http")) {
actionUrl = UriBuilder.fromUri(actionUrl).scheme("http").host("localhost").port(8081).build().toString();
}
Form form = new Form();
form.param("username", "test-user@localhost");
form.param("password", "password");
Response response = client.target(actionUrl).request().post(Entity.form(form));
URI uri = null;
Assert.assertEquals(302, response.getStatus());
uri = response.getLocation();
for (String header : response.getHeaders().keySet()) {
for (Object value : response.getHeaders().get(header)) {
System.out.println(header + ": " + value);
}
}
response.close();
Assert.assertNotNull(uri);
String code = getCode(uri);
Assert.assertNotNull(code);
form = new Form();
form.param(OAuth2Constants.GRANT_TYPE, grantType)
.param(OAuth2Constants.CODE, code)
.param(OAuth2Constants.REDIRECT_URI, redirectUri);
String authorization = BasicAuthHelper.createHeader(clientId, "password");
String res = client.target(OpenIDConnectService.accessCodeToTokenUrl(UriBuilder.fromUri(baseUrl)).build(realm)).request()
.header(HttpHeaders.AUTHORIZATION, authorization)
.post(Entity.form(form), String.class);
count.incrementAndGet();
//client.close();
}
public String getCode(URI uri) {
Map<String, String> m = new HashMap<String, String>();
List<NameValuePair> pairs = URLEncodedUtils.parse(uri, "UTF-8");
for (NameValuePair p : pairs) {
if (p.getName().equals("code")) return p.getValue();
m.put(p.getName(), p.getValue());
}
return null;
}
public void close()
{
client.close();
}
} }
@Test @Test
public void perfJaxrsClientLogin() @Ignore
{ public void runit() throws Exception {
long ITERATIONS = 3; Thread.sleep(10000000);
JaxrsClientLogin login = new JaxrsClientLogin();
long start = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
//System.out.println("*************************");
login.run();
}
long end = System.currentTimeMillis() - start;
System.out.println("took: " + end);
} }
@Test
public void perfBrowserLogin()
{
long ITERATIONS = 3;
long start = System.currentTimeMillis();
BrowserLogin login = new BrowserLogin();
for (int i = 0; i < ITERATIONS; i++) {
//System.out.println("----------------------------------");
login.run();
}
long end = System.currentTimeMillis() - start;
System.out.println("took: " + end);
}
@Test
public void multiThread() throws Exception {
int num_threads = 20;
Thread[] threads = new Thread[num_threads];
for (int i = 0; i < num_threads; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
perfJaxrsClientLogin();
}
});
}
long start = System.currentTimeMillis();
for (int i = 0; i < num_threads; i++) {
threads[i].start();
}
for (int i = 0; i < num_threads; i++) {
threads[i].join();
}
long end = System.currentTimeMillis() - start;
System.out.println(count.toString() + " took: " + end);
System.out.println(count.floatValue() / ((float)end) * 1000+ " logins/s");
}
} }

View file

@ -0,0 +1,58 @@
{
"id": "demo",
"realm": "demo",
"enabled": true,
"accessTokenLifespan": 3000,
"accessCodeLifespan": 10,
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
"social": false,
"passwordCredentialGrantAllowed": true,
"updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
"users" : [
{
"username" : "bburke@redhat.com",
"enabled": true,
"email" : "bburke@redhat.com",
"firstName": "Bill",
"lastName": "Burke",
"credentials" : [
{ "type" : "password",
"value" : "password" }
],
"realmRoles": [ "user", "admin" ],
"applicationRoles": {
"account": [ "manage-account" ]
}
}
],
"roles" : {
"realm" : [
{
"name": "user",
"description": "User privileges"
},
{
"name": "admin",
"description": "Administrator privileges"
}
]
},
"applications": [
{
"name": "customer-portal",
"enabled": true,
"fullScopeAllowed": true,
"adminUrl": "http://localhost:8080/customer-portal",
"baseUrl": "http://localhost:8080/customer-portal",
"redirectUris": [
"http://localhost:8080/customer-portal/*"
],
"secret": "password"
}
]
}

View file

@ -0,0 +1,3 @@
<Context path="/customer-portal">
<Valve className="org.keycloak.adapters.tomcat7.KeycloakAuthenticatorValve"/>
</Context>

View file

@ -0,0 +1,10 @@
{
"realm": "demo",
"resource": "customer-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8081/auth",
"ssl-required" : "external",
"credentials": {
"secret": "password"
}
}

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<module-name>adapter-test</module-name>
<servlet>
<servlet-name>SendUsername</servlet-name>
<servlet-class>org.keycloak.testsuite.Tomcat7Test$SendUsernameServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SendUsername</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admins</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>demo</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
</web-app>