/*
* 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.Before;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.Config;
import org.keycloak.admin.client.Keycloak;
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.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.CredentialBuilder;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.core.Response;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.junit.Assume;
import org.junit.BeforeClass;
/**
* Tests Undertow Adapter
*
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
*/
public class ImpersonationTest extends AbstractKeycloakTest {
@Rule
public AssertEvents events = new AssertEvents(this);
private String impersonatedUserId;
@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());
}
@BeforeClass
public static void enabled() {
Assume.assumeFalse("impersonation".equals(System.getProperty("feature.name"))
&& "disabled".equals(System.getProperty("feature.value")));
}
@Before
public void beforeTest() {
impersonatedUserId = ApiUtil.findUserByUsername(adminClient.realm("test"), "test-user@localhost").getId();
}
@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) {
Keycloak client = login(admin, adminRealm);
try {
Map data = client.realms().realm("test").users().get(impersonatedUserId).impersonate();
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();
} finally {
client.close();
}
}
protected void testForbiddenImpersonation(String admin, String adminRealm) {
Keycloak client = createAdminClient(adminRealm, establishClientId(adminRealm), admin);
try {
client.realms().realm("test").users().get(impersonatedUserId).impersonate();
} catch (ClientErrorException e) {
Assert.assertTrue(e.getMessage().indexOf("403 Forbidden") != -1);
} finally {
client.close();
}
}
Keycloak createAdminClient(String realm, String clientId, String username) {
return createAdminClient(realm, clientId, username, null);
}
String establishClientId(String realm) {
return realm.equals("master") ? Constants.ADMIN_CLI_CLIENT_ID : "myclient";
}
Keycloak createAdminClient(String realm, String clientId, String username, String password) {
if (password == null) {
password = username.equals("admin") ? "admin" : "password";
}
return Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
realm, username, password, clientId);
}
private Keycloak login(String username, String realm) {
String clientId = establishClientId(realm);
Keycloak client = createAdminClient(realm, clientId, username);
client.tokenManager().grantToken();
// only poll for LOGIN event if realm is not master
// - since for master testing event listener is not installed
if (!AuthRealm.MASTER.equals(realm)) {
EventRepresentation e = events.poll();
Assert.assertEquals("Event type", EventType.LOGIN.toString(), e.getType());
Assert.assertEquals("Client ID", clientId, e.getClientId());
Assert.assertEquals("Username", username, e.getDetails().get("username"));
}
return client;
}
}