Merge pull request #2677 from stianst/KEYCLOAK-2850
KEYCLOAK-2850 Migrated ImpersonationTest and added builders
This commit is contained in:
commit
b6ed681402
10 changed files with 578 additions and 221 deletions
|
@ -129,7 +129,7 @@ public abstract class AbstractKeycloakTest {
|
||||||
private boolean resetTimeOffset;
|
private boolean resetTimeOffset;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeAbstractKeycloakTest() {
|
public void beforeAbstractKeycloakTest() throws Exception {
|
||||||
adminClient = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
|
adminClient = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
|
||||||
MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID);
|
MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID);
|
||||||
deleteMeOAuthClient = new DeleteMeOAuthClient(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth");
|
deleteMeOAuthClient = new DeleteMeOAuthClient(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth");
|
||||||
|
|
|
@ -64,7 +64,6 @@ public class AssertEvents {
|
||||||
private RealmRepresentation realmRep;
|
private RealmRepresentation realmRep;
|
||||||
private AbstractKeycloakTest context;
|
private AbstractKeycloakTest context;
|
||||||
private PublicKey realmPublicKey;
|
private PublicKey realmPublicKey;
|
||||||
private UserRepresentation defaultUser;
|
|
||||||
|
|
||||||
public AssertEvents(AbstractKeycloakTest ctx) throws Exception {
|
public AssertEvents(AbstractKeycloakTest ctx) throws Exception {
|
||||||
context = ctx;
|
context = ctx;
|
||||||
|
@ -74,11 +73,6 @@ public class AssertEvents {
|
||||||
String pubKeyString = realmRep.getPublicKey();
|
String pubKeyString = realmRep.getPublicKey();
|
||||||
realmPublicKey = PemUtils.decodePublicKey(pubKeyString);
|
realmPublicKey = PemUtils.decodePublicKey(pubKeyString);
|
||||||
|
|
||||||
defaultUser = getUser(DEFAULT_USERNAME);
|
|
||||||
if (defaultUser == null) {
|
|
||||||
throw new RuntimeException("Default user does not exist: " + DEFAULT_USERNAME + ". Make sure to add it to your test realm.");
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultEventsQueueUri = getAuthServerEventsQueueUri();
|
defaultEventsQueueUri = getAuthServerEventsQueueUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +186,7 @@ public class AssertEvents {
|
||||||
return new ExpectedEvent()
|
return new ExpectedEvent()
|
||||||
.realm(realmRep.getId())
|
.realm(realmRep.getId())
|
||||||
.client(DEFAULT_CLIENT_ID)
|
.client(DEFAULT_CLIENT_ID)
|
||||||
.user(defaultUser.getId())
|
.user(defaultUserId())
|
||||||
.ipAddress(DEFAULT_IP_ADDRESS)
|
.ipAddress(DEFAULT_IP_ADDRESS)
|
||||||
.session((String) null)
|
.session((String) null)
|
||||||
.event(event);
|
.event(event);
|
||||||
|
@ -357,6 +351,34 @@ public class AssertEvents {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Matcher<String> defaultUserId() {
|
||||||
|
return new TypeSafeMatcher<String>() {
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean matchesSafely(String item) {
|
||||||
|
return item.equals(getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText(getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUserId() {
|
||||||
|
if (userId == null) {
|
||||||
|
UserRepresentation user = getUser(DEFAULT_USERNAME);
|
||||||
|
if (user == null) {
|
||||||
|
throw new RuntimeException("Default user does not exist: " + DEFAULT_USERNAME + ". Make sure to add it to your test realm.");
|
||||||
|
}
|
||||||
|
userId = user.getId();
|
||||||
|
}
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private EventRepresentation fetchNextEvent() {
|
private EventRepresentation fetchNextEvent() {
|
||||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.testsuite.admin;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.admin.client.resource.RoleResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
|
@ -85,6 +86,10 @@ public class ApiUtil {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RoleResource findClientRoleByName(ClientResource client, String role) {
|
||||||
|
return client.roles().get(role);
|
||||||
|
}
|
||||||
|
|
||||||
public static UserRepresentation findUserByUsername(RealmResource realm, String username) {
|
public static UserRepresentation findUserByUsername(RealmResource realm, String username) {
|
||||||
UserRepresentation user = null;
|
UserRepresentation user = null;
|
||||||
List<UserRepresentation> ur = realm.users().search(username, null, null);
|
List<UserRepresentation> ur = realm.users().search(username, null, null);
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* 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.admin;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.models.AdminRoles;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.ImpersonationConstants;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
import org.keycloak.services.resources.admin.AdminRoot;
|
||||||
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
import org.keycloak.testsuite.util.ClientBuilder;
|
||||||
|
import org.keycloak.testsuite.util.CredentialBuilder;
|
||||||
|
import org.keycloak.testsuite.util.OAuthClient.AccessTokenResponse;
|
||||||
|
import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
|
||||||
|
import javax.ws.rs.client.Client;
|
||||||
|
import javax.ws.rs.client.ClientRequestContext;
|
||||||
|
import javax.ws.rs.client.ClientRequestFilter;
|
||||||
|
import javax.ws.rs.client.WebTarget;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests Undertow Adapter
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||||
|
*/
|
||||||
|
public class ImpersonationTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
|
private AssertEvents events;
|
||||||
|
|
||||||
|
private String impersonatedUserId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeAbstractKeycloakTest() throws Exception {
|
||||||
|
super.beforeAbstractKeycloakTest();
|
||||||
|
events = new AssertEvents(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||||
|
RealmBuilder realm = RealmBuilder.create().name("test").testEventListener();
|
||||||
|
|
||||||
|
realm.client(ClientBuilder.create().clientId("myclient").publicClient().directAccessGrants());
|
||||||
|
|
||||||
|
impersonatedUserId = KeycloakModelUtils.generateId();
|
||||||
|
|
||||||
|
realm.user(UserBuilder.create().id(impersonatedUserId).username("test-user@localhost"));
|
||||||
|
realm.user(UserBuilder.create().username("realm-admin").password("password").role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN));
|
||||||
|
realm.user(UserBuilder.create().username("impersonator").password("password").role(Constants.REALM_MANAGEMENT_CLIENT_ID, ImpersonationConstants.IMPERSONATION_ROLE).role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.VIEW_USERS));
|
||||||
|
realm.user(UserBuilder.create().username("bad-impersonator").password("password").role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.MANAGE_USERS));
|
||||||
|
|
||||||
|
testRealms.add(realm.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createAdminToken(String username, String realm) {
|
||||||
|
try {
|
||||||
|
String password = username.equals("admin") ? "admin" : "password";
|
||||||
|
String clientId = realm.equals("master") ? Constants.ADMIN_CLI_CLIENT_ID : "myclient";
|
||||||
|
AccessTokenResponse tokenResponse = oauthClient.doGrantAccessTokenRequest(realm, username, password, null, clientId, null);
|
||||||
|
if (tokenResponse.getStatusCode() != 200) {
|
||||||
|
throw new RuntimeException("Failed to get token: " + tokenResponse.getErrorDescription());
|
||||||
|
}
|
||||||
|
events.clear();
|
||||||
|
return tokenResponse.getAccessToken();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImpersonateByMasterAdmin() {
|
||||||
|
// test that composite is set up right for impersonation role
|
||||||
|
testSuccessfulImpersonation("admin", Config.getAdminRealm());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImpersonateByMasterImpersonator() {
|
||||||
|
Response response = adminClient.realm("master").users().create(UserBuilder.create().username("master-impersonator").build());
|
||||||
|
String userId = ApiUtil.getCreatedId(response);
|
||||||
|
response.close();
|
||||||
|
|
||||||
|
UserResource user = adminClient.realm("master").users().get(userId);
|
||||||
|
user.resetPassword(CredentialBuilder.create().password("password").build());
|
||||||
|
|
||||||
|
ClientResource testRealmClient = ApiUtil.findClientResourceByClientId(adminClient.realm("master"), "test-realm");
|
||||||
|
|
||||||
|
List<RoleRepresentation> roles = new LinkedList<>();
|
||||||
|
roles.add(ApiUtil.findClientRoleByName(testRealmClient, AdminRoles.VIEW_USERS).toRepresentation());
|
||||||
|
roles.add(ApiUtil.findClientRoleByName(testRealmClient, ImpersonationConstants.IMPERSONATION_ROLE).toRepresentation());
|
||||||
|
|
||||||
|
user.roles().clientLevel(testRealmClient.toRepresentation().getId()).add(roles);
|
||||||
|
|
||||||
|
testSuccessfulImpersonation("master-impersonator", Config.getAdminRealm());
|
||||||
|
|
||||||
|
adminClient.realm("master").users().get(userId).remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImpersonateByTestImpersonator() {
|
||||||
|
testSuccessfulImpersonation("impersonator", "test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImpersonateByTestAdmin() {
|
||||||
|
// test that composite is set up right for impersonation role
|
||||||
|
testSuccessfulImpersonation("realm-admin", "test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImpersonateByTestBadImpersonator() {
|
||||||
|
testForbiddenImpersonation("bad-impersonator", "test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImpersonateByMastertBadImpersonator() {
|
||||||
|
Response response = adminClient.realm("master").users().create(UserBuilder.create().username("master-bad-impersonator").build());
|
||||||
|
String userId = ApiUtil.getCreatedId(response);
|
||||||
|
response.close();
|
||||||
|
adminClient.realm("master").users().get(userId).resetPassword(CredentialBuilder.create().password("password").build());
|
||||||
|
|
||||||
|
testForbiddenImpersonation("master-bad-impersonator", Config.getAdminRealm());
|
||||||
|
|
||||||
|
adminClient.realm("master").users().get(userId).remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected void testSuccessfulImpersonation(String admin, String adminRealm) {
|
||||||
|
Client client = createClient(admin, adminRealm);
|
||||||
|
WebTarget impersonate = createImpersonateTarget(client);
|
||||||
|
Map data = impersonate.request().post(null, Map.class);
|
||||||
|
Assert.assertNotNull(data);
|
||||||
|
Assert.assertNotNull(data.get("redirect"));
|
||||||
|
|
||||||
|
// TODO Events not working
|
||||||
|
events.expect(EventType.IMPERSONATE)
|
||||||
|
.session(AssertEvents.isUUID())
|
||||||
|
.user(impersonatedUserId)
|
||||||
|
.detail(Details.IMPERSONATOR, admin)
|
||||||
|
.detail(Details.IMPERSONATOR_REALM, adminRealm)
|
||||||
|
.client((String) null).assertEvent();
|
||||||
|
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void testForbiddenImpersonation(String admin, String adminRealm) {
|
||||||
|
Client client = createClient(admin, adminRealm);
|
||||||
|
WebTarget impersonate = createImpersonateTarget(client);
|
||||||
|
Response response = impersonate.request().post(null);
|
||||||
|
response.close();
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected WebTarget createImpersonateTarget(Client client) {
|
||||||
|
UriBuilder authBase = UriBuilder.fromUri(getAuthServerRoot());
|
||||||
|
WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
|
||||||
|
WebTarget realmTarget = adminRealms.path("test");
|
||||||
|
return realmTarget.path("users").path(impersonatedUserId).path("impersonation");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Client createClient(String admin, String adminRealm) {
|
||||||
|
String token = createAdminToken(admin, adminRealm);
|
||||||
|
final String authHeader = "Bearer " + token;
|
||||||
|
ClientRequestFilter authFilter = new ClientRequestFilter() {
|
||||||
|
@Override
|
||||||
|
public void filter(ClientRequestContext requestContext) throws IOException {
|
||||||
|
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return javax.ws.rs.client.ClientBuilder.newBuilder().register(authFilter).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class ClientBuilder {
|
||||||
|
|
||||||
|
private ClientRepresentation rep = new ClientRepresentation();
|
||||||
|
|
||||||
|
public static ClientBuilder create() {
|
||||||
|
return new ClientBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientBuilder() {
|
||||||
|
rep.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientBuilder id(String id) {
|
||||||
|
rep.setId(id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientBuilder clientId(String clientId) {
|
||||||
|
rep.setClientId(clientId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientBuilder publicClient() {
|
||||||
|
rep.setPublicClient(true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientBuilder directAccessGrants() {
|
||||||
|
rep.setDirectAccessGrantsEnabled(true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientRepresentation build() {
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class CredentialBuilder {
|
||||||
|
|
||||||
|
private CredentialRepresentation rep = new CredentialRepresentation();
|
||||||
|
|
||||||
|
public static CredentialBuilder create() {
|
||||||
|
return new CredentialBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CredentialBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CredentialBuilder password(String password) {
|
||||||
|
rep.setType(CredentialRepresentation.PASSWORD);
|
||||||
|
rep.setValue(password);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CredentialRepresentation build() {
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class RealmBuilder {
|
||||||
|
|
||||||
|
private RealmRepresentation rep = new RealmRepresentation();
|
||||||
|
|
||||||
|
public static RealmBuilder create() {
|
||||||
|
return new RealmBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private RealmBuilder() {
|
||||||
|
rep.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmBuilder name(String name) {
|
||||||
|
rep.setRealm(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmBuilder testEventListener() {
|
||||||
|
if (rep.getEventsListeners() == null) {
|
||||||
|
rep.setEventsListeners(new LinkedList<String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
rep.getEventsListeners().add("event-queue");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmBuilder client(ClientBuilder client) {
|
||||||
|
return client(client.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmBuilder client(ClientRepresentation client) {
|
||||||
|
if (rep.getClients() == null) {
|
||||||
|
rep.setClients(new LinkedList<ClientRepresentation>());
|
||||||
|
}
|
||||||
|
rep.getClients().add(client);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmBuilder user(UserBuilder user) {
|
||||||
|
return user(user.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmBuilder user(UserRepresentation user) {
|
||||||
|
if (rep.getUsers() == null) {
|
||||||
|
rep.setUsers(new LinkedList<UserRepresentation>());
|
||||||
|
}
|
||||||
|
rep.getUsers().add(user);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RealmRepresentation build() {
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class RoleBuilder {
|
||||||
|
|
||||||
|
private RoleRepresentation rep = new RoleRepresentation();
|
||||||
|
|
||||||
|
public static RoleBuilder create() {
|
||||||
|
return new RoleBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private RoleBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleBuilder id(String id) {
|
||||||
|
rep.setId(id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleBuilder name(String name) {
|
||||||
|
rep.setName(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoleRepresentation build() {
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
|
public class UserBuilder {
|
||||||
|
|
||||||
|
private UserRepresentation rep = new UserRepresentation();
|
||||||
|
|
||||||
|
public static UserBuilder create() {
|
||||||
|
return new UserBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserBuilder() {
|
||||||
|
rep.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserBuilder id(String id) {
|
||||||
|
rep.setId(id);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserBuilder username(String username) {
|
||||||
|
rep.setUsername(username);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserBuilder password(String password) {
|
||||||
|
if (rep.getCredentials() == null) {
|
||||||
|
rep.setCredentials(new LinkedList<CredentialRepresentation>());
|
||||||
|
}
|
||||||
|
|
||||||
|
CredentialRepresentation credential = new CredentialRepresentation();
|
||||||
|
credential.setType(CredentialRepresentation.PASSWORD);
|
||||||
|
credential.setValue(password);
|
||||||
|
|
||||||
|
rep.getCredentials().add(credential);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserBuilder role(String role) {
|
||||||
|
if (rep.getRealmRoles() == null) {
|
||||||
|
rep.setRealmRoles(new LinkedList<String>());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserBuilder role(String client, String role) {
|
||||||
|
if (rep.getClientRoles() == null) {
|
||||||
|
rep.setClientRoles(new HashMap<String, List<String>>());
|
||||||
|
}
|
||||||
|
if (rep.getClientRoles().get(client) == null) {
|
||||||
|
rep.getClientRoles().put(client, new LinkedList<String>());
|
||||||
|
}
|
||||||
|
rep.getClientRoles().get(client).add(role);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRepresentation build() {
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,213 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.admin;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.ClassRule;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.keycloak.Config;
|
|
||||||
import org.keycloak.events.Details;
|
|
||||||
import org.keycloak.events.EventType;
|
|
||||||
import org.keycloak.models.AdminRoles;
|
|
||||||
import org.keycloak.models.ClientModel;
|
|
||||||
import org.keycloak.models.ClientSessionModel;
|
|
||||||
import org.keycloak.models.Constants;
|
|
||||||
import org.keycloak.models.ImpersonationConstants;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.RoleModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
|
||||||
import org.keycloak.models.UserSessionModel;
|
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
|
||||||
import org.keycloak.protocol.oidc.TokenManager;
|
|
||||||
import org.keycloak.representations.AccessToken;
|
|
||||||
import org.keycloak.services.managers.RealmManager;
|
|
||||||
import org.keycloak.services.resources.admin.AdminRoot;
|
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
|
||||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
|
||||||
|
|
||||||
import javax.ws.rs.client.Client;
|
|
||||||
import javax.ws.rs.client.ClientBuilder;
|
|
||||||
import javax.ws.rs.client.ClientRequestContext;
|
|
||||||
import javax.ws.rs.client.ClientRequestFilter;
|
|
||||||
import javax.ws.rs.client.WebTarget;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import javax.ws.rs.core.UriBuilder;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests Undertow Adapter
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
|
||||||
*/
|
|
||||||
public class ImpersonationTest {
|
|
||||||
|
|
||||||
static String impersonatedUserId;
|
|
||||||
|
|
||||||
@ClassRule
|
|
||||||
public static KeycloakRule keycloakRule = new KeycloakRule( new KeycloakRule.KeycloakSetup() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
|
||||||
impersonatedUserId = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm).getId();
|
|
||||||
{
|
|
||||||
UserModel masterImpersonator = manager.getSession().users().addUser(adminstrationRealm, "master-impersonator");
|
|
||||||
masterImpersonator.setEnabled(true);
|
|
||||||
ClientModel adminRealmClient = adminstrationRealm.getClientByClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(appRealm.getName()));
|
|
||||||
RoleModel masterManageUsersRole = adminRealmClient.getRole(AdminRoles.MANAGE_USERS);
|
|
||||||
masterImpersonator.grantRole(masterManageUsersRole);
|
|
||||||
|
|
||||||
RoleModel masterImpersonatorRole = adminRealmClient.getRole(ImpersonationConstants.IMPERSONATION_ROLE);
|
|
||||||
masterImpersonator.grantRole(masterImpersonatorRole);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
UserModel masterBadImpersonator = manager.getSession().users().addUser(adminstrationRealm, "master-bad-impersonator");
|
|
||||||
masterBadImpersonator.setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
UserModel impersonator = manager.getSession().users().addUser(appRealm, "impersonator");
|
|
||||||
impersonator.setEnabled(true);
|
|
||||||
ClientModel appRealmClient = appRealm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
|
||||||
|
|
||||||
RoleModel masterManageUsersRole = appRealmClient.getRole(AdminRoles.MANAGE_USERS);
|
|
||||||
impersonator.grantRole(masterManageUsersRole);
|
|
||||||
|
|
||||||
RoleModel realmImpersonatorRole = appRealmClient.getRole(ImpersonationConstants.IMPERSONATION_ROLE);
|
|
||||||
impersonator.grantRole(realmImpersonatorRole);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
UserModel impersonator = manager.getSession().users().addUser(appRealm, "realm-admin");
|
|
||||||
impersonator.setEnabled(true);
|
|
||||||
ClientModel appRealmClient = appRealm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
|
|
||||||
RoleModel realmImpersonatorRole = appRealmClient.getRole(AdminRoles.REALM_ADMIN);
|
|
||||||
impersonator.grantRole(realmImpersonatorRole);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
UserModel badimpersonator = manager.getSession().users().addUser(appRealm, "bad-impersonator");
|
|
||||||
badimpersonator.setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public AssertEvents events = new AssertEvents(keycloakRule);
|
|
||||||
|
|
||||||
|
|
||||||
private static String createAdminToken(String username, String realm) {
|
|
||||||
KeycloakSession session = keycloakRule.startSession();
|
|
||||||
try {
|
|
||||||
RealmManager manager = new RealmManager(session);
|
|
||||||
|
|
||||||
RealmModel adminRealm = manager.getRealm(realm);
|
|
||||||
ClientModel adminConsole = adminRealm.getClientByClientId(Constants.ADMIN_CLI_CLIENT_ID);
|
|
||||||
TokenManager tm = new TokenManager();
|
|
||||||
UserModel admin = session.users().getUserByUsername(username, adminRealm);
|
|
||||||
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
|
|
||||||
clientSession.setNote(OIDCLoginProtocol.ISSUER, "http://localhost:8081/auth/realms/" + realm);
|
|
||||||
UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
|
|
||||||
AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, true, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
|
|
||||||
return tm.encodeToken(adminRealm, token);
|
|
||||||
} finally {
|
|
||||||
keycloakRule.stopSession(session, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImpersonateByMasterAdmin() {
|
|
||||||
// test that composite is set up right for impersonation role
|
|
||||||
testSuccessfulImpersonation("admin", Config.getAdminRealm());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImpersonateByMasterImpersonator() {
|
|
||||||
testSuccessfulImpersonation("master-impersonator", Config.getAdminRealm());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImpersonateByTestImpersonator() {
|
|
||||||
testSuccessfulImpersonation("impersonator", "test");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImpersonateByTestAdmin() {
|
|
||||||
// test that composite is set up right for impersonation role
|
|
||||||
testSuccessfulImpersonation("realm-admin", "test");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImpersonateByTestBadImpersonator() {
|
|
||||||
testForbiddenImpersonation("bad-impersonator", "test");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImpersonateByMastertBadImpersonator() {
|
|
||||||
testForbiddenImpersonation("master-bad-impersonator", Config.getAdminRealm());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected void testSuccessfulImpersonation(String admin, String adminRealm) {
|
|
||||||
Client client = createClient(admin, adminRealm);
|
|
||||||
WebTarget impersonate = createImpersonateTarget(client);
|
|
||||||
Map data = impersonate.request().post(null, Map.class);
|
|
||||||
Assert.assertNotNull(data);
|
|
||||||
Assert.assertNotNull(data.get("redirect"));
|
|
||||||
|
|
||||||
events.expect(EventType.IMPERSONATE)
|
|
||||||
.session(AssertEvents.isUUID())
|
|
||||||
.user(impersonatedUserId)
|
|
||||||
.detail(Details.IMPERSONATOR, admin)
|
|
||||||
.detail(Details.IMPERSONATOR_REALM, adminRealm)
|
|
||||||
.client((String) null).assertEvent();
|
|
||||||
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void testForbiddenImpersonation(String admin, String adminRealm) {
|
|
||||||
Client client = createClient(admin, adminRealm);
|
|
||||||
WebTarget impersonate = createImpersonateTarget(client);
|
|
||||||
Response response = impersonate.request().post(null);
|
|
||||||
response.close();
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected WebTarget createImpersonateTarget(Client client) {
|
|
||||||
UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth");
|
|
||||||
WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
|
|
||||||
WebTarget realmTarget = adminRealms.path("test");
|
|
||||||
return realmTarget.path("users").path(impersonatedUserId).path("impersonation");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Client createClient(String admin, String adminRealm) {
|
|
||||||
String token = createAdminToken(admin, adminRealm);
|
|
||||||
final String authHeader = "Bearer " + token;
|
|
||||||
ClientRequestFilter authFilter = new ClientRequestFilter() {
|
|
||||||
@Override
|
|
||||||
public void filter(ClientRequestContext requestContext) throws IOException {
|
|
||||||
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return ClientBuilder.newBuilder().register(authFilter).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in a new issue