/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.jaxrs;
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.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.keycloak.OAuth2Constants;
import org.keycloak.TokenIdGenerator;
import org.keycloak.adapters.CorsHeaders;
import org.keycloak.adapters.HttpClientBuilder;
import org.keycloak.common.util.Time;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.Constants;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.WebDriver;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JaxrsFilterTest {
private static final String JAXRS_APP_URL = Constants.SERVER_ROOT + "/jaxrs-simple/res";
private static final String JAXRS_APP_PUSN_NOT_BEFORE_URL = Constants.SERVER_ROOT + "/jaxrs-simple/" + AdapterConstants.K_PUSH_NOT_BEFORE;
public static final String CONFIG_FILE_INIT_PARAM = "config-file";
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
ClientModel app = appRealm.addClient("jaxrs-app");
app.setEnabled(true);
RoleModel role = app.addRole("jaxrs-app-user");
UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
user.grantRole(role);
JaxrsFilterTest.appRealm = appRealm;
}
});
@ClassRule
public static ExternalResource clientRule = new ExternalResource() {
@Override
protected void before() throws Throwable {
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
client = new ResteasyClientBuilder().httpEngine(engine).build();
}
@Override
protected void after() {
client.close();
}
};
private static ResteasyClient client;
@Rule
public WebRule webRule = new WebRule(this);
@WebResource
protected WebDriver driver;
// Used for signing admin action
protected static RealmModel appRealm;
@Test
public void testBasic() {
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
Map<String,String> initParams = new TreeMap<String,String>();
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak.json");
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
}
});
// Send GET request without token, it should fail
Response getResp = client.target(JAXRS_APP_URL).request().get();
Assert.assertEquals(getResp.getStatus(), 401);
getResp.close();
// Send POST request without token, it should fail
Response postResp = client.target(JAXRS_APP_URL).request().post(Entity.form(new Form()));
Assert.assertEquals(postResp.getStatus(), 401);
postResp.close();
// Retrieve token
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
// Send GET request with token and assert it's passing
JaxrsTestResource.SimpleRepresentation getRep = client.target(JAXRS_APP_URL).request()
.header(HttpHeaders.AUTHORIZATION, authHeader)
.get(JaxrsTestResource.SimpleRepresentation.class);
Assert.assertEquals("get", getRep.getMethod());
Assert.assertTrue(getRep.getHasUserRole());
Assert.assertFalse(getRep.getHasAdminRole());
Assert.assertFalse(getRep.getHasJaxrsAppRole());
// Assert that principal is ID of user (should be in UUID format)
UUID.fromString(getRep.getPrincipal());
// Send POST request with token and assert it's passing
JaxrsTestResource.SimpleRepresentation postRep = client.target(JAXRS_APP_URL).request()
.header(HttpHeaders.AUTHORIZATION, authHeader)
.post(Entity.form(new Form()), JaxrsTestResource.SimpleRepresentation.class);
Assert.assertEquals("post", postRep.getMethod());
Assert.assertEquals(getRep.getPrincipal(), postRep.getPrincipal());
}
@Test
public void testRelativeUriAndPublicKey() {
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
Map<String,String> initParams = new TreeMap<String,String>();
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak-relative.json");
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
}
});
// Send GET request without token, it should fail
Response getResp = client.target(JAXRS_APP_URL).request().get();
Assert.assertEquals(getResp.getStatus(), 401);
getResp.close();
// Retrieve token
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
// Send GET request with token and assert it's passing
JaxrsTestResource.SimpleRepresentation getRep = client.target(JAXRS_APP_URL).request()
.header(HttpHeaders.AUTHORIZATION, authHeader)
.get(JaxrsTestResource.SimpleRepresentation.class);
Assert.assertEquals("get", getRep.getMethod());
Assert.assertTrue(getRep.getHasUserRole());
Assert.assertFalse(getRep.getHasAdminRole());
Assert.assertFalse(getRep.getHasJaxrsAppRole());
// Assert that principal is ID of user (should be in UUID format)
UUID.fromString(getRep.getPrincipal());
}
@Test
public void testSslRequired() {
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
Map<String, String> initParams = new TreeMap<String, String>();
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak-ssl.json");
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
}
});
// Retrieve token
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
// Fail due to non-https
Response getResp = client.target(JAXRS_APP_URL).request()
.header(HttpHeaders.AUTHORIZATION, authHeader)
.get();
Assert.assertEquals(getResp.getStatus(), 403);
getResp.close();
}
@Test
public void testResourceRoleMappings() {
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
Map<String, String> initParams = new TreeMap<String, String>();
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak-resource-mappings.json");
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
}
});
// Retrieve token
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
// Send GET request with token and assert it's passing
JaxrsTestResource.SimpleRepresentation getRep = client.target(JAXRS_APP_URL).request()
.header(HttpHeaders.AUTHORIZATION, authHeader)
.get(JaxrsTestResource.SimpleRepresentation.class);
Assert.assertEquals("get", getRep.getMethod());
// principal is username
Assert.assertEquals("test-user@localhost", getRep.getPrincipal());
// User is in jaxrs-app-user role thanks to use-resource-role-mappings
Assert.assertFalse(getRep.getHasUserRole());
Assert.assertFalse(getRep.getHasAdminRole());
Assert.assertTrue(getRep.getHasJaxrsAppRole());
}
@Test
public void testCors() {
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
Map<String,String> initParams = new TreeMap<String,String>();
initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak.json");
keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
}
});
// Send OPTIONS request
Response optionsResp = client.target(JAXRS_APP_URL).request()
.header(CorsHeaders.ORIGIN, "http://localhost:8081")
.options();
Assert.assertEquals("true", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
Assert.assertEquals("http://localhost:8081", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
optionsResp.close();
// Retrieve token
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
// Send GET request with token but bad origin
Response badOriginResp = client.target(JAXRS_APP_URL).request()
.header(HttpHeaders.AUTHORIZATION, authHeader)
.header(CorsHeaders.ORIGIN, "http://evil.org")
.get();
Assert.assertEquals(403, badOriginResp.getStatus());
badOriginResp.close();
// Send GET request with token and good origin
Response goodResp = client.target(JAXRS_APP_URL).request()
.header(HttpHeaders.AUTHORIZATION, authHeader)
.header(CorsHeaders.ORIGIN, "http://localhost:8081")
.get();
Assert.assertEquals(200, goodResp.getStatus());
Assert.assertEquals("true", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
Assert.assertEquals("http://localhost:8081", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
JaxrsTestResource.SimpleRepresentation getRep = goodResp.readEntity(JaxrsTestResource.SimpleRepresentation.class);
Assert.assertEquals("get", getRep.getMethod());
goodResp.close();
}
// @Test
public void testCxfExample() {
//String uri = "http://localhost:9000/customerservice/customers/123";
String uri = "http://localhost:8080/jax_rs_basic_servlet/services/service1/customerservice/customers/123";
Response resp = client.target(uri).request()
.get();
Assert.assertEquals(resp.getStatus(), 401);
resp.close();
// Retrieve token
OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
String authHeader = "Bearer " + accessTokenResp.getAccessToken();
String resp2 = client.target(uri).request()
.header(HttpHeaders.AUTHORIZATION, authHeader)
.get(String.class);
System.out.println(resp2);
}
private OAuthClient.AccessTokenResponse retrieveAccessToken() {
OAuthClient oauth = new OAuthClient(driver);
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
Assert.assertEquals(200, response.getStatusCode());
return response;
}
}