Skip to content

Commit

Permalink
KEYCLOAK-5981 Test Impersonation works when authenticationSession exists
Browse files Browse the repository at this point in the history
  • Loading branch information
mposolda committed Dec 12, 2017
1 parent 63efee6 commit b8416df
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel;
import org.keycloak.events.Event;
Expand Down Expand Up @@ -48,6 +49,7 @@
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.scheduled.ClearExpiredUserSessions;
Expand Down Expand Up @@ -78,6 +80,8 @@
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.Response;
import java.text.ParseException;
import java.text.SimpleDateFormat;
Expand All @@ -96,6 +100,9 @@ public class TestingResourceProvider implements RealmResourceProvider {
private final KeycloakSession session;
private final Map<String, TimerProvider.TimerTaskContext> suspendedTimerTasks;

@Context
private HttpRequest request;

@Override
public Object getResource() {
return this;
Expand Down Expand Up @@ -549,6 +556,15 @@ private AuthDetails repToModel(AuthDetailsRepresentation rep) {
return details;
}

@GET
@Path("/get-sso-cookie")
@Produces(MediaType.APPLICATION_JSON)
public String getSSOCookieValue() {
Map<String, Cookie> cookies = request.getHttpHeaders().getCookies();
return cookies.get(AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE).getValue();
}


@Path("/cache/{cache}")
public TestCacheResource getCacheResource(@PathParam("cache") String cacheName) {
return new TestCacheResource(session, cacheName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config.Scope;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
Expand All @@ -36,7 +37,9 @@ public class TestingResourceProviderFactory implements RealmResourceProviderFact

@Override
public RealmResourceProvider create(KeycloakSession session) {
return new TestingResourceProvider(session, suspendedTimerTasks);
TestingResourceProvider testProvider = new TestingResourceProvider(session, suspendedTimerTasks);
ResteasyProviderFactory.getInstance().injectProperties(testProvider);
return testProvider;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public static KeycloakTestingClient getInstance(String serverUrl) {
return new KeycloakTestingClient(serverUrl, null);
}

public static KeycloakTestingClient getInstance(String serverUrl, ResteasyClient resteasyClient) {
return new KeycloakTestingClient(serverUrl, resteasyClient);
}

public TestingResource testing() {
return target.path("/realms/master").proxy(TestingResource.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ public List<AdminEventRepresentation> getAdminEvents(@QueryParam("realmId") Stri
@Consumes(MediaType.APPLICATION_JSON)
void onAdminEvent(final AdminEventRepresentation rep, @QueryParam("includeRepresentation") boolean includeRepresentation);

@GET
@Path("/get-sso-cookie")
@Produces(MediaType.APPLICATION_JSON)
String getSSOCookieValue();

@POST
@Path("/remove-user-session")
@Produces(MediaType.APPLICATION_JSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@

package org.keycloak.testsuite.admin;

import org.jboss.arquillian.graphene.page.Page;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
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.KeycloakBuilder;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
Expand All @@ -34,12 +39,17 @@
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
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.client.KeycloakTestingClient;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.CredentialBuilder;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;

Expand All @@ -50,6 +60,7 @@
import java.util.Map;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.openqa.selenium.Cookie;

/**
* Tests Undertow Adapter
Expand All @@ -61,6 +72,12 @@ public class ImpersonationTest extends AbstractKeycloakTest {
@Rule
public AssertEvents events = new AssertEvents(this);

@Page
protected AppPage appPage;

@Page
protected LoginPage loginPage;

private String impersonatedUserId;

@Override
Expand Down Expand Up @@ -147,25 +164,66 @@ public void testImpersonateByMastertBadImpersonator() {
}


protected void testSuccessfulImpersonation(String admin, String adminRealm) {
// KEYCLOAK-5981
@Test
public void testImpersonationWorksWhenAuthenticationSessionExists() throws Exception {
// Create test client
RealmResource realm = adminClient.realms().realm("test");
Response resp = realm.clients().create(ClientBuilder.create().clientId("test-app").addRedirectUri(OAuthClient.APP_ROOT + "/*").build());
resp.close();

// Open the URL for the client (will redirect to Keycloak server AuthorizationEndpoint and create authenticationSession)
String loginFormUrl = oauth.getLoginFormUrl();
driver.navigate().to(loginFormUrl);
loginPage.assertCurrent();

// Impersonate and get SSO cookie. Setup that cookie for webDriver
String ssoCookie = testSuccessfulImpersonation("realm-admin", "test");
driver.manage().addCookie(new Cookie(AuthenticationManager.KEYCLOAK_IDENTITY_COOKIE, ssoCookie));

// Open the URL again - should be directly redirected to the app due the SSO login
driver.navigate().to(loginFormUrl);
appPage.assertCurrent();

// Remove test client
ApiUtil.findClientByClientId(realm, "test-app").remove();
}


// Return the SSO cookie from the impersonated session
protected String testSuccessfulImpersonation(String admin, String adminRealm) {
ResteasyClient resteasyClient = new ResteasyClientBuilder().connectionPoolSize(10).build();

Keycloak client = login(admin, adminRealm);
// Login adminClient
Keycloak client = login(admin, adminRealm, resteasyClient);
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();
// Impersonate
impersonate(client, admin, adminRealm);

// Get the SSO cookie. Needs to use same RestEasyClient used by adminClient to be able to see the cookies
KeycloakTestingClient testingClient = KeycloakTestingClient.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth", resteasyClient);
String kcIdentity = testingClient.testing("test").getSSOCookieValue();
Assert.assertNotNull(kcIdentity);

return kcIdentity;
} finally {
client.close();
resteasyClient.close();
}
}

private void impersonate(Keycloak adminClient, String admin, String adminRealm) {
Map data = adminClient.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();
}

protected void testForbiddenImpersonation(String admin, String adminRealm) {
Keycloak client = createAdminClient(adminRealm, establishClientId(adminRealm), admin);
try {
Expand All @@ -178,24 +236,30 @@ protected void testForbiddenImpersonation(String admin, String adminRealm) {
}

Keycloak createAdminClient(String realm, String clientId, String username) {
return createAdminClient(realm, clientId, username, null);
return createAdminClient(realm, clientId, username, null, 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) {
Keycloak createAdminClient(String realm, String clientId, String username, String password, ResteasyClient resteasyClient) {
if (password == null) {
password = username.equals("admin") ? "admin" : "password";
}
return Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
realm, username, password, clientId);

return KeycloakBuilder.builder().serverUrl(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth")
.realm(realm)
.username(username)
.password(password)
.clientId(clientId)
.resteasyClient(resteasyClient)
.build();
}

private Keycloak login(String username, String realm) {
private Keycloak login(String username, String realm, ResteasyClient resteasyClient) {
String clientId = establishClientId(realm);
Keycloak client = createAdminClient(realm, clientId, username);
Keycloak client = createAdminClient(realm, clientId, username, null, resteasyClient);

client.tokenManager().grantToken();
// only poll for LOGIN event if realm is not master
Expand Down

0 comments on commit b8416df

Please sign in to comment.