KEYCLOAK-17655 - Can't impersonate

This commit is contained in:
Mathieu CLAUDEL 2021-04-06 09:00:58 +02:00 committed by Marek Posolda
parent 71dcbec642
commit df714506cc
2 changed files with 122 additions and 25 deletions

View file

@ -298,9 +298,10 @@ public class UserResource {
RealmModel authenticatedRealm = auth.adminAuth().getRealm();
// if same realm logout before impersonation
boolean sameRealm = false;
if (authenticatedRealm.getId().equals(realm.getId())) {
String sessionState = auth.adminAuth().getToken().getSessionState();
if (authenticatedRealm.getId().equals(realm.getId()) && sessionState != null) {
sameRealm = true;
UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, auth.adminAuth().getToken().getSessionState());
UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, sessionState);
AuthenticationManager.expireIdentityCookie(realm, session.getContext().getUri(), clientConnection);
AuthenticationManager.expireRememberMeCookie(realm, session.getContext().getUri(), clientConnection);
AuthenticationManager.backchannelLogout(session, authenticatedRealm, userSession, session.getContext().getUri(), clientConnection, headers, true);

View file

@ -34,6 +34,7 @@ import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.Config;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.ClientResource;
@ -49,9 +50,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
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.representations.idm.*;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
@ -60,13 +59,7 @@ import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.A
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.CredentialBuilder;
import org.keycloak.testsuite.util.DroneUtils;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.testsuite.util.*;
import org.openqa.selenium.Cookie;
import javax.ws.rs.ClientErrorException;
@ -74,17 +67,10 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.*;
import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT;
import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot;
@ -141,7 +127,7 @@ public class ImpersonationTest extends AbstractKeycloakTest {
testRealms.add(realm.build());
}
@BeforeClass
public static void enabled() {
Assume.assumeFalse("impersonation".equals(System.getProperty("feature.name"))
@ -240,6 +226,57 @@ public class ImpersonationTest extends AbstractKeycloakTest {
ApiUtil.findClientByClientId(realm, "test-app").remove();
}
// KEYCLOAK-17655
@Test
public void testImpersonationBySameRealmServiceAccount() throws Exception {
// Create test client service account
RealmResource realm = adminClient.realms().realm("test");
ClientRepresentation clientApp = ClientBuilder.create()
.id(KeycloakModelUtils.generateId())
.clientId("service-account-cl")
.secret("password")
.serviceAccountsEnabled(true)
.build();
clientApp.setServiceAccountsEnabled(true);
realm.clients().create(clientApp);
UserRepresentation user = ClientManager.realm(adminClient.realm("test")).clientId("service-account-cl").getServiceAccountUser();
user.setServiceAccountClientId("service-account-cl");
// add impersonation roles
ApiUtil.assignClientRoles(realm, user.getId(), Constants.REALM_MANAGEMENT_CLIENT_ID, ImpersonationConstants.IMPERSONATION_ROLE);
// Impersonation
testSuccessfulServiceAccountImpersonation(user, "test");
// Remove test client
ApiUtil.findClientByClientId(realm, "service-account-cl").remove();
}
@Test
public void testImpersonationByMasterRealmServiceAccount() throws Exception {
// Create test client service account
RealmResource realm = adminClient.realms().realm("master");
ClientRepresentation clientApp = ClientBuilder.create()
.id(KeycloakModelUtils.generateId())
.clientId("service-account-cl")
.secret("password")
.serviceAccountsEnabled(true)
.build();
clientApp.setServiceAccountsEnabled(true);
realm.clients().create(clientApp);
UserRepresentation user = ClientManager.realm(adminClient.realm("master")).clientId("service-account-cl").getServiceAccountUser();
user.setServiceAccountClientId("service-account-cl");
// add impersonation roles
ApiUtil.assignRealmRoles(realm, user.getId(), "admin");
// Impersonation
testSuccessfulServiceAccountImpersonation(user, "master");
// Remove test client
ApiUtil.findClientByClientId(realm, "service-account-cl").remove();
}
// Return the SSO cookie from the impersonated session
protected Set<Cookie> testSuccessfulImpersonation(String admin, String adminRealm) {
@ -293,15 +330,14 @@ public class ImpersonationTest extends AbstractKeycloakTest {
Set<Cookie> cookies = cookieStore.getCookies().stream()
.filter(c -> c.getName().startsWith(AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE))
.map(c -> new Cookie(c.getName(), c.getValue(), c.getDomain(), c.getPath(), c.getExpiryDate(), c.isSecure(), true) )
.map(c -> new Cookie(c.getName(), c.getValue(), c.getDomain(), c.getPath(), c.getExpiryDate(), c.isSecure(), true))
.collect(Collectors.toSet());
Assert.assertNotNull(cookies);
Assert.assertThat(cookies, is(not(empty())));
return cookies;
}
catch (IOException e) {
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@ -352,4 +388,64 @@ public class ImpersonationTest extends AbstractKeycloakTest {
}
return client;
}
// Return the SSO cookie from the impersonated session
protected Set<Cookie> testSuccessfulServiceAccountImpersonation(UserRepresentation serviceAccount, String serviceAccountRealm) {
ResteasyClientBuilder resteasyClientBuilder = new ResteasyClientBuilder();
resteasyClientBuilder.connectionPoolSize(10);
resteasyClientBuilder.httpEngine(AdminClientUtil.getCustomClientHttpEngine(resteasyClientBuilder, 10, null));
ResteasyClient resteasyClient = resteasyClientBuilder.build();
// Login adminClient
try (Keycloak client = loginServiceAccount(serviceAccount, serviceAccountRealm, resteasyClient)) {
// Impersonate test-user with service account
return impersonateServiceAccount(client);
}
}
private Keycloak loginServiceAccount(UserRepresentation serviceAccount, String serviceAccountRealm, ResteasyClient resteasyClient) {
Keycloak client = createServiceAccountClient(serviceAccountRealm, serviceAccount, resteasyClient);
// get token
client.tokenManager().getAccessToken();
return client;
}
Keycloak createServiceAccountClient(String serviceAccountRealm, UserRepresentation serviceAccount, ResteasyClient resteasyClient) {
return KeycloakBuilder.builder().serverUrl(getAuthServerContextRoot() + "/auth")
.realm(serviceAccountRealm)
.clientId(serviceAccount.getServiceAccountClientId())
.clientSecret("password")
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.resteasyClient(resteasyClient)
.build();
}
private Set<Cookie> impersonateServiceAccount(Keycloak adminClient) {
BasicCookieStore cookieStore = new BasicCookieStore();
try (CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build()) {
HttpUriRequest req = RequestBuilder.post()
.setUri(AUTH_SERVER_ROOT + "/admin/realms/test/users/" + impersonatedUserId + "/impersonation")
.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + adminClient.tokenManager().getAccessTokenString())
.build();
HttpResponse res = httpClient.execute(req);
String resBody = EntityUtils.toString(res.getEntity());
Assert.assertNotNull(resBody);
Assert.assertTrue(resBody.contains("redirect"));
Set<Cookie> cookies = cookieStore.getCookies().stream()
.filter(c -> c.getName().startsWith(AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE))
.map(c -> new Cookie(c.getName(), c.getValue(), c.getDomain(), c.getPath(), c.getExpiryDate(), c.isSecure(), true))
.collect(Collectors.toSet());
Assert.assertNotNull(cookies);
Assert.assertThat(cookies, is(not(empty())));
return cookies;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}