From fc9dbcf6cb1daa5e19bb3214012ed44154104cb0 Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 10 May 2016 12:07:11 +0200 Subject: [PATCH] KEYCLOAK-2881 Admin events testing --- .../events/EventsListenerProvider.java | 32 +- .../events/EventsListenerProviderFactory.java | 4 +- .../rest/TestingResourceProvider.java | 28 +- .../client/resources/TestingResource.java | 13 +- .../testsuite/AbstractKeycloakTest.java | 6 +- .../org/keycloak/testsuite/AssertEvents.java | 5 +- .../testsuite/admin/AbstractAdminTest.java | 16 + .../org/keycloak/testsuite/admin/ApiUtil.java | 2 +- .../admin/AttackDetectionResourceTest.java | 2 + .../keycloak/testsuite/admin/ClientTest.java | 132 ++++--- .../testsuite/admin/IdentityProviderTest.java | 131 +++---- .../admin/InitialAccessTokenResourceTest.java | 9 +- .../testsuite/admin/RoleByIdResourceTest.java | 13 + .../admin/UserFederationMapperTest.java | 36 +- .../testsuite/admin/UserFederationTest.java | 36 +- .../keycloak/testsuite/admin/UserTest.java | 155 +++++--- .../admin/client/AbstractClientTest.java | 43 ++- .../client/AbstractProtocolMapperTest.java | 30 +- .../client/ClientProtocolMapperTest.java | 60 ++- .../admin/client/ClientRolesTest.java | 19 +- .../ClientTemplateProtocolMapperTest.java | 80 ++-- .../admin/client/ClientTemplateTest.java | 66 ++-- .../testsuite/admin/client/ClientTest.java | 20 +- .../admin/client/CredentialsTest.java | 17 + .../admin/client/InstallationTest.java | 11 +- .../testsuite/admin/client/SessionTest.java | 9 +- .../event/AdminEventAuthDetailsTest.java | 146 +++++++ .../admin/group/AbstractGroupTest.java | 4 + .../testsuite/admin/group/GroupTest.java | 93 +++-- .../partialimport/PartialImportTest.java | 46 ++- .../testsuite/admin/realm/RealmRolesTest.java | 32 +- .../testsuite/admin/realm/RealmTest.java | 54 ++- .../testsuite/util/AdminEventPaths.java | 356 ++++++++++++++++++ .../testsuite/util/AssertAdminEvents.java | 245 ++++++++++++ .../keycloak/testsuite/util/RealmBuilder.java | 14 +- 35 files changed, 1649 insertions(+), 316 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AdminEventAuthDetailsTest.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AssertAdminEvents.java diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java index a4a1a400511f..87606ca984be 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/events/EventsListenerProvider.java @@ -30,6 +30,7 @@ public class EventsListenerProvider implements EventListenerProvider { private static final BlockingQueue events = new LinkedBlockingQueue(); + private static final BlockingQueue adminEvents = new LinkedBlockingQueue<>(); @Override public void onEvent(Event event) { @@ -38,7 +39,8 @@ public void onEvent(Event event) { @Override public void onEvent(AdminEvent event, boolean includeRepresentation) { - // TODO: implement if needed + // Save the copy for case when same AdminEventBuilder is used more times during same transaction to avoid overwriting previously referenced event + adminEvents.add(copy(event)); } @Override @@ -46,7 +48,31 @@ public void close() { } - public static BlockingQueue getInstance() { - return events; + public static Event poll() { + return events.poll(); + } + + public static AdminEvent pollAdminEvent() { + return adminEvents.poll(); + } + + public static void clear() { + events.clear(); + } + + public static void clearAdminEvents() { + adminEvents.clear(); + } + + private AdminEvent copy(AdminEvent adminEvent) { + AdminEvent newEvent = new AdminEvent(); + newEvent.setAuthDetails(adminEvent.getAuthDetails()); + newEvent.setError(adminEvent.getError()); + newEvent.setOperationType(adminEvent.getOperationType()); + newEvent.setRealmId(adminEvent.getRealmId()); + newEvent.setRepresentation(adminEvent.getRepresentation()); + newEvent.setResourcePath(adminEvent.getResourcePath()); + newEvent.setTime(adminEvent.getTime()); + return newEvent; } } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java index 6578269babde..af2e2414e981 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/events/EventsListenerProviderFactory.java @@ -28,6 +28,8 @@ */ public class EventsListenerProviderFactory implements EventListenerProviderFactory { + public static final String PROVIDER_ID = "event-queue"; + private static final EventsListenerProvider INSTANCE = new EventsListenerProvider(); @Override @@ -49,6 +51,6 @@ public void close() { @Override public String getId() { - return "event-queue"; + return PROVIDER_ID; } } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java index 546d92caeec8..ee6fc92703f9 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java @@ -21,10 +21,12 @@ import org.keycloak.common.util.Time; import org.keycloak.connections.infinispan.InfinispanConnectionProvider; import org.keycloak.events.Event; +import org.keycloak.events.admin.AdminEvent; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.RealmManager; @@ -139,7 +141,7 @@ public Map setTimeOffset(Map time) { @Path("/poll-event-queue") @Produces(MediaType.APPLICATION_JSON) public EventRepresentation getEvent() { - Event event = EventsListenerProvider.getInstance().poll(); + Event event = EventsListenerProvider.poll(); if (event != null) { return ModelToRepresentation.toRepresentation(event); } else { @@ -147,11 +149,31 @@ public EventRepresentation getEvent() { } } + @POST + @Path("/poll-admin-event-queue") + @Produces(MediaType.APPLICATION_JSON) + public AdminEventRepresentation getAdminEvent() { + AdminEvent adminEvent = EventsListenerProvider.pollAdminEvent(); + if (adminEvent != null) { + return ModelToRepresentation.toRepresentation(adminEvent); + } else { + return null; + } + } + @POST @Path("/clear-event-queue") @Produces(MediaType.APPLICATION_JSON) - public Response clearQueue() { - EventsListenerProvider.getInstance().clear(); + public Response clearEventQueue() { + EventsListenerProvider.clear(); + return Response.ok().build(); + } + + @POST + @Path("/clear-admin-event-queue") + @Produces(MediaType.APPLICATION_JSON) + public Response clearAdminEventQueue() { + EventsListenerProvider.clearAdminEvents(); return Response.ok().build(); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java index 89c755ab9fcd..5f04c30abadf 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java @@ -17,6 +17,7 @@ package org.keycloak.testsuite.client.resources; +import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.testsuite.rest.representation.AuthenticatorState; @@ -56,10 +57,20 @@ public interface TestingResource { @Produces(MediaType.APPLICATION_JSON) EventRepresentation pollEvent(); + @POST + @Path("/poll-admin-event-queue") + @Produces(MediaType.APPLICATION_JSON) + AdminEventRepresentation pollAdminEvent(); + @POST @Path("/clear-event-queue") @Produces(MediaType.APPLICATION_JSON) - Response clearQueue(); + Response clearEventQueue(); + + @POST + @Path("/clear-admin-event-queue") + @Produces(MediaType.APPLICATION_JSON) + Response clearAdminEventQueue(); @POST @Path("/remove-user-session") diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java index 65254da4ce8a..a14abf342192 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java @@ -184,13 +184,17 @@ public void setDefaultPageUriParameters() { loginPage.setAuthRealm(MASTER); } - protected KeycloakTestingClient getTestingClient() { + public KeycloakTestingClient getTestingClient() { if (testingClient == null) { testingClient = KeycloakTestingClient.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth"); } return testingClient; } + public Keycloak getAdminClient() { + return adminClient; + } + public abstract void addTestRealms(List testRealms); private void addTestRealms() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java index 187cc375f762..d9a0291aa9a5 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java @@ -63,7 +63,8 @@ public Statement apply(final Statement base, org.junit.runner.Description descri return new Statement() { @Override public void evaluate() throws Throwable { - context.getTestingClient().testing().clearQueue(); + // TODO: Ideally clear the queue just before testClass rather then before each method + context.getTestingClient().testing().clearEventQueue(); base.evaluate(); // TODO Test should fail if there are leftover events } @@ -83,7 +84,7 @@ public void assertEmpty() { } public void clear() { - Response res = context.testingClient.testing().clearQueue(); + Response res = context.testingClient.testing().clearEventQueue(); try { Assert.assertEquals("clear-event-queue success", res.getStatus(), 200); } finally { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AbstractAdminTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AbstractAdminTest.java index 9c2b2e1b6ba6..e538dcc5e5b4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AbstractAdminTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AbstractAdminTest.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -27,11 +28,15 @@ import java.util.Map; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.events.log.JBossLoggingEventListenerProviderFactory; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.TestRealmKeycloakTest; +import org.keycloak.testsuite.events.EventsListenerProviderFactory; +import org.keycloak.testsuite.util.AssertAdminEvents; import org.keycloak.util.JsonSerialization; import static org.junit.Assert.assertArrayEquals; @@ -46,6 +51,10 @@ public abstract class AbstractAdminTest extends TestRealmKeycloakTest { protected static final String REALM_NAME = "admin-client-test"; protected RealmResource realm; + protected String realmId; + + @Rule + public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this); @Override public void configureTestRealm(RealmRepresentation testRealm) { @@ -64,12 +73,19 @@ public void addTestRealms(List testRealms) { config.put("host", "localhost"); config.put("port", "3025"); adminRealmRep.setSmtpServer(config); + + List eventListeners = new ArrayList<>(); + eventListeners.add(JBossLoggingEventListenerProviderFactory.ID); + eventListeners.add(EventsListenerProviderFactory.PROVIDER_ID); + adminRealmRep.setEventsListeners(eventListeners); + testRealms.add(adminRealmRep); } @Before public void setRealm() { realm = adminClient.realm(REALM_NAME); + realmId = realm.toRepresentation().getId(); } // old testsuite expects this realm to be removed at the end of the test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java index 4f1a74d24e07..f0a37adfd689 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java @@ -162,7 +162,7 @@ public static void assignClientRoles(RealmResource realm, String userId, String + clientName + "\" in realm: \"" + realmName + "\""); userResource.roles().clientLevel(clientId).add(roleRepresentations); } else { - log.warn("client with name " + clientName + "doesn't exist in realm " + realmName); + log.warn("client with name " + clientName + " doesn't exist in realm " + realmName); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AttackDetectionResourceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AttackDetectionResourceTest.java index be405a4a1308..40bc43e89a80 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AttackDetectionResourceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AttackDetectionResourceTest.java @@ -33,6 +33,8 @@ import static org.junit.Assert.assertTrue; /** + * TODO adminEvents: Add adminEvents once resourcePath is added in AttackDetectionResource (server-side) events + * * @author Stian Thorgersen */ public class AttackDetectionResourceTest extends AbstractAdminTest { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java index e094aabd6ac7..03f9ceffeb03 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java @@ -17,12 +17,14 @@ package org.keycloak.testsuite.admin; +import org.hamcrest.Matchers; import org.junit.Test; import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ProtocolMappersResource; import org.keycloak.admin.client.resource.RoleMappingResource; import org.keycloak.common.util.Time; +import org.keycloak.events.admin.OperationType; import org.keycloak.models.AccountRoles; import org.keycloak.models.Constants; import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; @@ -43,6 +45,8 @@ import org.keycloak.services.resources.admin.ScopeMappedResource; import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.util.AdminEventPaths; +import org.keycloak.testsuite.util.AssertAdminEvents; import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.CredentialBuilder; import org.keycloak.testsuite.util.OAuthClient; @@ -75,7 +79,11 @@ private ClientRepresentation createClient() { Response response = realm.clients().create(rep); response.close(); String id = ApiUtil.getCreatedId(response); + + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientResourcePath(id), rep); + rep.setId(id); + return rep; } @@ -92,6 +100,8 @@ public void removeClient() { String id = createClient().getId(); realm.clients().get(id).remove(); + + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.clientResourcePath(id)); } @Test @@ -138,33 +148,35 @@ public void getClientSessions() throws Exception { @Test // KEYCLOAK-1110 public void deleteDefaultRole() { - ClientRepresentation rep = new ClientRepresentation(); - rep.setClientId("my-app"); - rep.setEnabled(true); - Response response = realm.clients().create(rep); - response.close(); - String id = ApiUtil.getCreatedId(response); + ClientRepresentation rep = createClient(); + String id = rep.getId(); RoleRepresentation role = new RoleRepresentation("test", "test", false); realm.clients().get(id).roles().create(role); - rep = realm.clients().get(id).toRepresentation(); - rep.setDefaultRoles(new String[] { "test" }); - realm.clients().get(id).update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, Matchers.startsWith(AdminEventPaths.clientRolesResourcePath(id)), role); + + ClientRepresentation foundClientRep = realm.clients().get(id).toRepresentation(); + foundClientRep.setDefaultRoles(new String[]{"test"}); + realm.clients().get(id).update(foundClientRep); + + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientResourcePath(id), rep); - assertArrayEquals(new String[] { "test" }, realm.clients().get(id).toRepresentation().getDefaultRoles()); + assertArrayEquals(new String[]{"test"}, realm.clients().get(id).toRepresentation().getDefaultRoles()); realm.clients().get(id).roles().deleteRole("test"); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.clientRoleResourcePath(id, "test")); + assertNull(realm.clients().get(id).toRepresentation().getDefaultRoles()); } @Test public void testProtocolMappers() { - createClient(); + String clientDbId = createClient().getId(); ProtocolMappersResource mappersResource = ApiUtil.findClientByClientId(realm, "my-app").getProtocolMappers(); - protocolMappersTest(mappersResource); + protocolMappersTest(clientDbId, mappersResource); } @Test @@ -178,6 +190,8 @@ public void updateClient() { realm.clients().get(client.getId()).update(newClient); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientResourcePath(client.getId()), newClient); + ClientRepresentation storedClient = realm.clients().get(client.getId()).toRepresentation(); assertClient(client, storedClient); @@ -186,6 +200,8 @@ public void updateClient() { realm.clients().get(client.getId()).update(newClient); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientResourcePath(client.getId()), newClient); + storedClient = realm.clients().get(client.getId()).toRepresentation(); assertClient(client, storedClient); } @@ -202,6 +218,18 @@ public void serviceAccount() { public void pushRevocation() { testingClient.testApp().clearAdminActions(); + ClientRepresentation client = createAppClient(); + String id = client.getId(); + + realm.clients().get(id).pushRevocation(); + + PushNotBeforeAction pushNotBefore = testingClient.testApp().getAdminPushNotBefore(); + assertEquals(client.getNotBefore().intValue(), pushNotBefore.getNotBefore()); + + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.clientPushRevocationPath(id)); + } + + private ClientRepresentation createAppClient() { String redirectUri = oauth.getRedirectUri().replace("/master/", "/" + REALM_NAME + "/"); ClientRepresentation client = new ClientRepresentation(); @@ -211,41 +239,38 @@ public void pushRevocation() { client.setSecret("secret"); int notBefore = Time.currentTime() - 60; - client.setNotBefore(notBefore); + Response response = realm.clients().create(client); String id = ApiUtil.getCreatedId(response); response.close(); - realm.clients().get(id).pushRevocation(); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientResourcePath(id), client); - PushNotBeforeAction pushNotBefore = testingClient.testApp().getAdminPushNotBefore(); - assertEquals(notBefore, pushNotBefore.getNotBefore()); + client.setId(id); + return client; } @Test public void nodes() { testingClient.testApp().clearAdminActions(); - String redirectUri = oauth.getRedirectUri().replace("/master/", "/" + REALM_NAME + "/"); - - ClientRepresentation client = new ClientRepresentation(); - client.setClientId("test-app"); - client.setAdminUrl(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/app/admin"); - client.setRedirectUris(Collections.singletonList(redirectUri)); - client.setSecret("secret"); - - Response response = realm.clients().create(client); - String id = ApiUtil.getCreatedId(response); - response.close(); + ClientRepresentation client = createAppClient(); + String id = client.getId(); realm.clients().get(id).registerNode(Collections.singletonMap("node", suiteContext.getAuthServerInfo().getContextRoot().getHost())); realm.clients().get(id).registerNode(Collections.singletonMap("node", "invalid")); + // TODO adminEvents: should be rather CREATE and include nodePath like in DELETE event + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.clientNodesPath(id)); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.clientNodesPath(id)); + GlobalRequestResult result = realm.clients().get(id).testNodesAvailable(); assertEquals(1, result.getSuccessRequests().size()); assertEquals(1, result.getFailedRequests().size()); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.clientTestNodesAvailablePath(id)); + TestAvailabilityAction testAvailable = testingClient.testApp().getTestAvailable(); assertEquals("test-app", testAvailable.getResource()); @@ -253,24 +278,17 @@ public void nodes() { realm.clients().get(id).unregisterNode("invalid"); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.clientNodePath(id, "invalid")); + assertEquals(1, realm.clients().get(id).toRepresentation().getRegisteredNodes().size()); } @Test public void offlineUserSessions() throws IOException { - String redirectUri = oauth.getRedirectUri().replace("/master/", "/" + REALM_NAME + "/"); + ClientRepresentation client = createAppClient(); + String id = client.getId(); - ClientRepresentation client = new ClientRepresentation(); - client.setClientId("test-app"); - client.setAdminUrl(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/app/admin"); - client.setRedirectUris(Collections.singletonList(redirectUri)); - client.setSecret("secret"); - - Response response = realm.clients().create(client); - String id = ApiUtil.getCreatedId(response); - response.close(); - - response = realm.users().create(UserBuilder.create().username("testuser").build()); + Response response = realm.users().create(UserBuilder.create().username("testuser").build()); String userId = ApiUtil.getCreatedId(response); response.close(); @@ -280,7 +298,7 @@ public void offlineUserSessions() throws IOException { assertEquals(new Long(0), offlineSessionCount.get("count")); oauth.realm(REALM_NAME); - oauth.redirectUri(redirectUri); + oauth.redirectUri(client.getRedirectUris().get(0)); oauth.scope(OAuth2Constants.OFFLINE_ACCESS); oauth.doLogin("testuser", "password"); AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get("code"), "secret"); @@ -300,21 +318,37 @@ public void scopes() { String id = ApiUtil.getCreatedId(response); response.close(); + assertAdminEvents.poll(); + RoleMappingResource scopesResource = realm.clients().get(id).getScopeMappings(); - realm.roles().create(RoleBuilder.create().name("role1").build()); - realm.roles().create(RoleBuilder.create().name("role2").build()); + RoleRepresentation roleRep1 = RoleBuilder.create().name("role1").build(); + RoleRepresentation roleRep2 = RoleBuilder.create().name("role2").build(); + realm.roles().create(roleRep1); + realm.roles().create(roleRep2); + + AssertAdminEvents.ExpectedAdminEvent adminEvent = assertAdminEvents.expect() + .realmId(realmId) + .operationType(OperationType.CREATE) + .resourcePath(Matchers.startsWith(AdminEventPaths.rolesResourcePath())); + adminEvent.representation(roleRep1).assertEvent(); + adminEvent.representation(roleRep2).assertEvent(); - RoleRepresentation roleRep1 = realm.roles().get("role1").toRepresentation(); - RoleRepresentation roleRep2 = realm.roles().get("role2").toRepresentation(); + roleRep1 = realm.roles().get("role1").toRepresentation(); + roleRep2 = realm.roles().get("role2").toRepresentation(); realm.roles().get("role1").addComposites(Collections.singletonList(roleRep2)); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, Matchers.startsWith(AdminEventPaths.roleResourceCompositesPath("role1"))); + String accountMgmtId = realm.clients().findByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).get(0).getId(); RoleRepresentation viewAccountRoleRep = realm.clients().get(accountMgmtId).roles().get(AccountRoles.VIEW_PROFILE).toRepresentation(); scopesResource.realmLevel().add(Collections.singletonList(roleRep1)); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientScopeMappingsRealmLevelPath(id) + "/" + roleRep1.getId()); + scopesResource.clientLevel(accountMgmtId).add(Collections.singletonList(viewAccountRoleRep)); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientScopeMappingsClientLevelPath(id, accountMgmtId) + "/" + viewAccountRoleRep.getId()); Assert.assertNames(scopesResource.realmLevel().listAll(), "role1"); Assert.assertNames(scopesResource.realmLevel().listEffective(), "role1", "role2"); @@ -328,7 +362,10 @@ public void scopes() { Assert.assertNames(scopesResource.getAll().getClientMappings().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings(), AccountRoles.VIEW_PROFILE); scopesResource.realmLevel().remove(Collections.singletonList(roleRep1)); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.clientScopeMappingsRealmLevelPath(id) + "/" + roleRep1.getId()); + scopesResource.clientLevel(accountMgmtId).remove(Collections.singletonList(viewAccountRoleRep)); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.clientScopeMappingsClientLevelPath(id, accountMgmtId) + "/" + viewAccountRoleRep.getId()); Assert.assertNames(scopesResource.realmLevel().listAll()); Assert.assertNames(scopesResource.realmLevel().listEffective()); @@ -338,7 +375,7 @@ public void scopes() { Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective()); } - public static void protocolMappersTest(ProtocolMappersResource mappersResource) { + public void protocolMappersTest(String clientDbId, ProtocolMappersResource mappersResource) { // assert default mappers found List protocolMappers = mappersResource.getMappers(); @@ -370,6 +407,8 @@ public static void protocolMappersTest(ProtocolMappersResource mappersResource) fooMapperId = location.substring(location.lastIndexOf("/") + 1); response.close(); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientProtocolMapperPath(clientDbId, fooMapperId), fooMapper); + fooMapper = mappersResource.getMapperById(fooMapperId); assertEquals(fooMapper.getName(), "foo"); @@ -377,11 +416,14 @@ public static void protocolMappersTest(ProtocolMappersResource mappersResource) fooMapper.setProtocolMapper("foo-mapper-updated"); mappersResource.update(fooMapperId, fooMapper); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientProtocolMapperPath(clientDbId, fooMapperId), fooMapper); + fooMapper = mappersResource.getMapperById(fooMapperId); assertEquals(fooMapper.getProtocolMapper(), "foo-mapper-updated"); // Remove foo mapper mappersResource.delete(fooMapperId); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.clientProtocolMapperPath(clientDbId, fooMapperId)); try { mappersResource.getMapperById(fooMapperId); fail("Not expected to find deleted mapper"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java index 955e12fc85e7..c346b68010f4 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java @@ -17,6 +17,7 @@ package org.keycloak.testsuite.admin; +import org.hamcrest.Matchers; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput; import org.junit.Test; import org.keycloak.admin.client.resource.IdentityProviderResource; @@ -25,6 +26,7 @@ import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType; import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; @@ -32,6 +34,7 @@ import org.keycloak.saml.common.util.StaxParserUtil; import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLEntityDescriptorParser; import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.util.AdminEventPaths; import javax.ws.rs.NotFoundException; import javax.ws.rs.core.MediaType; @@ -61,27 +64,21 @@ public class IdentityProviderTest extends AbstractAdminTest { @Test public void testFindAll() { - Response response = realm.identityProviders().create(create("google", "google")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); + create(createRep("google", "google")); - response = realm.identityProviders().create(create("facebook", "facebook")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); + create(createRep("facebook", "facebook")); Assert.assertNames(realm.identityProviders().findAll(), "google", "facebook"); } @Test public void testCreate() { - IdentityProviderRepresentation newIdentityProvider = create("new-identity-provider", "oidc"); + IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc"); newIdentityProvider.getConfig().put("clientId", "clientId"); newIdentityProvider.getConfig().put("clientSecret", "clientSecret"); - Response response = realm.identityProviders().create(newIdentityProvider); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); + create(newIdentityProvider); IdentityProviderResource identityProviderResource = realm.identityProviders().get("new-identity-provider"); @@ -103,14 +100,12 @@ public void testCreate() { @Test public void testUpdate() { - IdentityProviderRepresentation newIdentityProvider = create("update-identity-provider", "oidc"); + IdentityProviderRepresentation newIdentityProvider = createRep("update-identity-provider", "oidc"); newIdentityProvider.getConfig().put("clientId", "clientId"); newIdentityProvider.getConfig().put("clientSecret", "clientSecret"); - Response response = realm.identityProviders().create(newIdentityProvider); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); + create(newIdentityProvider); IdentityProviderResource identityProviderResource = realm.identityProviders().get("update-identity-provider"); @@ -128,6 +123,7 @@ public void testUpdate() { representation.getConfig().put("clientId", "changedClientId"); identityProviderResource.update(representation); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.identityProviderPath("update-identity-provider"), representation); identityProviderResource = realm.identityProviders().get(representation.getInternalId()); @@ -140,15 +136,13 @@ public void testUpdate() { assertEquals("changedClientId", representation.getConfig().get("clientId")); } - @Test(expected = NotFoundException.class) + @Test public void testRemove() { - IdentityProviderRepresentation newIdentityProvider = create("remove-identity-provider", "saml"); + IdentityProviderRepresentation newIdentityProvider = createRep("remove-identity-provider", "saml"); - Response response = realm.identityProviders().create(newIdentityProvider); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); + create(newIdentityProvider); - IdentityProviderResource identityProviderResource = realm.identityProviders().get("update-identity-provider"); + IdentityProviderResource identityProviderResource = realm.identityProviders().get("remove-identity-provider"); assertNotNull(identityProviderResource); @@ -157,16 +151,30 @@ public void testRemove() { assertNotNull(representation); identityProviderResource.remove(); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.identityProviderPath("remove-identity-provider")); - realm.identityProviders().get("update-identity-provider"); + try { + realm.identityProviders().get("remove-identity-provider").toRepresentation(); + Assert.fail("Not expected to found"); + } catch (NotFoundException nfe) { + // Expected + } } + private void create(IdentityProviderRepresentation idpRep) { + Response response = realm.identityProviders().create(idpRep); + Assert.assertNotNull(ApiUtil.getCreatedId(response)); + response.close(); + + // TODO adminEvents: should rather use alias instead of internalId (same issue like for roles) + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, Matchers.startsWith(AdminEventPaths.identityProviderCreatePath()), idpRep); + } - private IdentityProviderRepresentation create(String id, String providerId) { - return create(id, providerId, null); + private IdentityProviderRepresentation createRep(String id, String providerId) { + return createRep(id, providerId, null); } - private IdentityProviderRepresentation create(String id, String providerId, Map config) { + private IdentityProviderRepresentation createRep(String id, String providerId, Map config) { IdentityProviderRepresentation idp = new IdentityProviderRepresentation(); idp.setAlias(id); @@ -184,82 +192,52 @@ public void testMapperTypes() { IdentityProviderResource provider; Map mapperTypes; - Response response = realm.identityProviders().create(create("google", "google")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("google", "google")); provider = realm.identityProviders().get("google"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "google-user-attribute-mapper"); - response = realm.identityProviders().create(create("facebook", "facebook")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("facebook", "facebook")); provider = realm.identityProviders().get("facebook"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "facebook-user-attribute-mapper"); - response = realm.identityProviders().create(create("github", "github")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("github", "github")); provider = realm.identityProviders().get("github"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "github-user-attribute-mapper"); - response = realm.identityProviders().create(create("twitter", "twitter")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("twitter", "twitter")); provider = realm.identityProviders().get("twitter"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes); - response = realm.identityProviders().create(create("linkedin", "linkedin")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("linkedin", "linkedin")); provider = realm.identityProviders().get("linkedin"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "linkedin-user-attribute-mapper"); - response = realm.identityProviders().create(create("microsoft", "microsoft")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("microsoft", "microsoft")); provider = realm.identityProviders().get("microsoft"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "microsoft-user-attribute-mapper"); - response = realm.identityProviders().create(create("stackoverflow", "stackoverflow")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("stackoverflow", "stackoverflow")); provider = realm.identityProviders().get("stackoverflow"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "stackoverflow-user-attribute-mapper"); - response = realm.identityProviders().create(create("keycloak-oidc", "keycloak-oidc")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("keycloak-oidc", "keycloak-oidc")); provider = realm.identityProviders().get("keycloak-oidc"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "keycloak-oidc-role-to-role-idp-mapper", "oidc-user-attribute-idp-mapper", "oidc-role-idp-mapper", "oidc-username-idp-mapper"); - response = realm.identityProviders().create(create("oidc", "oidc")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("oidc", "oidc")); provider = realm.identityProviders().get("oidc"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "oidc-user-attribute-idp-mapper", "oidc-role-idp-mapper", "oidc-username-idp-mapper"); - response = realm.identityProviders().create(create("saml", "saml")); - Assert.assertNotNull(ApiUtil.getCreatedId(response)); - response.close(); - + create(createRep("saml", "saml")); provider = realm.identityProviders().get("saml"); mapperTypes = provider.getMapperTypes(); assertMapperTypes(mapperTypes, "saml-user-attribute-idp-mapper", "saml-role-idp-mapper", "saml-username-idp-mapper"); @@ -278,11 +256,9 @@ private void assertMapperTypes(Map mappers = provider.getMappers(); @@ -366,16 +339,20 @@ public void testMappers() { // update mapper mapper.getConfig().put("role", "master-realm.manage-realm"); provider.update(id, mapper); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.identityProviderMapperPath("google", id), mapper); + mapper = provider.getMapperById(id); Assert.assertNotNull("mapperById not null", mapper); Assert.assertEquals("config changed", "master-realm.manage-realm", mapper.getConfig().get("role")); // delete mapper provider.delete(id); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.identityProviderMapperPath("google", id)); try { provider.getMapperById(id); Assert.fail("Should fail with NotFoundException"); } catch (NotFoundException e) { + // Expected } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/InitialAccessTokenResourceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/InitialAccessTokenResourceTest.java index b8c956068585..2c56db8c8424 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/InitialAccessTokenResourceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/InitialAccessTokenResourceTest.java @@ -21,8 +21,10 @@ import org.junit.Test; import org.keycloak.admin.client.resource.ClientInitialAccessResource; import org.keycloak.common.util.Time; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation; import org.keycloak.representations.idm.ClientInitialAccessPresentation; +import org.keycloak.testsuite.util.AdminEventPaths; import java.util.List; @@ -52,6 +54,7 @@ public void testInitialAccessTokens() { int time = Time.currentTime(); ClientInitialAccessPresentation response = resource.create(rep); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientInitialAccessPath(response.getId()), rep); assertNotNull(response.getId()); assertEquals(new Integer(2), response.getCount()); @@ -61,10 +64,12 @@ public void testInitialAccessTokens() { assertNotNull(response.getToken()); rep.setCount(3); - resource.create(rep); + response = resource.create(rep); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientInitialAccessPath(response.getId()), rep); rep.setCount(4); - resource.create(rep); + response = resource.create(rep); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientInitialAccessPath(response.getId()), rep); List list = resource.list(); assertEquals(3, list.size()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java index c2303ef32c09..e26bcbce5a1b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RoleByIdResourceTest.java @@ -17,11 +17,14 @@ package org.keycloak.testsuite.admin; +import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.RoleByIdResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.util.AdminEventPaths; import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.RoleBuilder; @@ -63,6 +66,8 @@ public void before() { } resource = adminClient.realm(REALM_NAME).rolesById(); + + assertAdminEvents.clear(); // Tested in RealmRolesTest already } @Test @@ -82,6 +87,7 @@ public void updateRole() { role.setDescription("Role A New"); resource.updateRole(ids.get("role-a"), role); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.roleByIdResourcePath(ids.get("role-a")), role); role = resource.getRole(ids.get("role-a")); @@ -95,6 +101,8 @@ public void updateRole() { public void deleteRole() { assertNotNull(resource.getRole(ids.get("role-a"))); resource.deleteRole(ids.get("role-a")); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.roleByIdResourcePath(ids.get("role-a"))); + try { resource.getRole(ids.get("role-a")); fail("Expected 404"); @@ -112,6 +120,10 @@ public void composites() { l.add(RoleBuilder.create().id(ids.get("role-c")).build()); resource.addComposites(ids.get("role-a"), l); + // TODO adminEvents: Fix once composite roles events will be fixed... + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, Matchers.startsWith(AdminEventPaths.roleByIdResourceCompositesPath(ids.get("role-a")))); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, Matchers.startsWith(AdminEventPaths.roleByIdResourceCompositesPath(ids.get("role-a")))); + Set composites = resource.getRoleComposites(ids.get("role-a")); assertTrue(resource.getRole(ids.get("role-a")).isComposite()); @@ -124,6 +136,7 @@ public void composites() { Assert.assertNames(clientComposites, "role-c"); resource.deleteComposites(ids.get("role-a"), l); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.roleByIdResourceCompositesPath(ids.get("role-a"))); assertFalse(resource.getRole(ids.get("role-a")).isComposite()); assertEquals(0, resource.getRoleComposites(ids.get("role-a")).size()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java index c1267ff5a40b..0db5f7c5b43f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationMapperTest.java @@ -32,6 +32,7 @@ import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.UserFederationProviderResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper; import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory; import org.keycloak.federation.ldap.mappers.membership.role.RoleLDAPFederationMapperFactory; @@ -43,6 +44,7 @@ import org.keycloak.representations.idm.UserFederationSyncResultRepresentation; import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.federation.DummyUserFederationMapper; +import org.keycloak.testsuite.util.AdminEventPaths; import org.keycloak.testsuite.util.UserFederationProviderBuilder; /** @@ -63,6 +65,7 @@ public void initFederationProviders() { Response resp = realm.userFederation().create(ldapRep); this.ldapProviderId = ApiUtil.getCreatedId(resp); resp.close(); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userFederationCreateResourcePath(), ldapRep); UserFederationProviderRepresentation dummyRep = UserFederationProviderBuilder.create() .displayName("dummy-1") @@ -72,12 +75,16 @@ public void initFederationProviders() { resp = realm.userFederation().create(dummyRep); this.dummyProviderId = ApiUtil.getCreatedId(resp); resp.close(); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userFederationCreateResourcePath(), dummyRep); } @After public void cleanFederationProviders() { realm.userFederation().get(ldapProviderId).remove(); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userFederationResourcePath(ldapProviderId)); + realm.userFederation().get(dummyProviderId).remove(); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userFederationResourcePath(dummyProviderId)); } @@ -138,7 +145,7 @@ public void testUserAttributeMapperCRUD() { // Test create success when all mandatory attributes available attrMapper.getConfig().put(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail"); - String mapperId = createMapper(attrMapper); + String mapperId = createMapper(ldapProviderId, attrMapper); // Test get UserFederationMapperRepresentation mapperRep = ldapProviderResource().getMapperById(mapperId); @@ -162,12 +169,15 @@ public void testUserAttributeMapperCRUD() { mapperRep.getConfig().put(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "email-updated"); mapperRep.getConfig().put(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail-updated"); ldapProviderResource().updateMapper(mapperId, mapperRep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userFederationMapperResourcePath(ldapProviderId, mapperId), mapperRep); mapperRep = ldapProviderResource().getMapperById(mapperId); assertMapper(mapperRep, mapperId, "email-mapper", UserAttributeLDAPFederationMapperFactory.PROVIDER_ID, UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "email-updated", UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "mail-updated"); // Test removed successfully ldapProviderResource().removeMapper(mapperId); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userFederationMapperResourcePath(ldapProviderId, mapperId)); + try { ldapProviderResource().getMapperById(mapperId); Assert.fail("Not expected find to success as mapper was removed"); @@ -176,11 +186,15 @@ public void testUserAttributeMapperCRUD() { } } - private String createMapper(UserFederationMapperRepresentation mapper) { - Response response = ldapProviderResource().addMapper(mapper); + private String createMapper(String userFederationProviderId, UserFederationMapperRepresentation mapper) { + Response response = realm.userFederation().get(userFederationProviderId).addMapper(mapper); Assert.assertEquals(201, response.getStatus()); response.close(); - return ApiUtil.getCreatedId(response); + String mapperId = ApiUtil.getCreatedId(response); + + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userFederationMapperResourcePath(userFederationProviderId , mapperId), mapper); + + return mapperId; } @@ -196,7 +210,7 @@ public void testRoleMapper() { // Fix config and create successfully roleMapper.getConfig().put(RoleMapperConfig.USE_REALM_ROLES_MAPPING, "true"); - String roleMapperId = createMapper(roleMapper); + String roleMapperId = createMapper(ldapProviderId, roleMapper); // Assert builtin mappers List mappers = ldapProviderResource().getMappers(); @@ -213,6 +227,8 @@ public void testRoleMapper() { // Remove role mapper and assert not found anymore ldapProviderResource().removeMapper(roleMapperId); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userFederationMapperResourcePath(ldapProviderId, roleMapperId)); + mappers = ldapProviderResource().getMappers(); Assert.assertNull(findMapperByName(mappers, "role-mapper")); } @@ -225,23 +241,25 @@ public void testSyncMapper() { dummyMapperRep.setName("some-dummy"); dummyMapperRep.setFederationMapperType(DummyUserFederationMapper.PROVIDER_NAME); dummyMapperRep.setFederationProviderDisplayName("dummy-1"); - String mapperId = createMapper(dummyMapperRep); + String mapperId = createMapper(dummyProviderId, dummyMapperRep); // Try to sync with unknown action - fail try { - realm.userFederation().get(dummyProviderId).syncMapperData(mapperId, "unknown"); + ldapProviderResource().syncMapperData(mapperId, "unknown"); Assert.fail("Not expected to pass"); } catch (NotFoundException nfe) { // Expected } // Try fed To Keycloak sync - UserFederationSyncResultRepresentation result = realm.userFederation().get(dummyProviderId).syncMapperData(mapperId, "fedToKeycloak"); + UserFederationSyncResultRepresentation result = ldapProviderResource().syncMapperData(mapperId, "fedToKeycloak"); Assert.assertEquals("dummyFedToKeycloakSuccess mapper=some-dummy", result.getStatus()); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userFederationMapperResourcePath(ldapProviderId, mapperId) + "/sync"); // Try keycloak to fed - result = realm.userFederation().get(dummyProviderId).syncMapperData(mapperId, "keycloakToFed"); + result = ldapProviderResource().syncMapperData(mapperId, "keycloakToFed"); Assert.assertEquals("dummyKeycloakToFedSuccess mapper=some-dummy", result.getStatus()); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userFederationMapperResourcePath(ldapProviderId, mapperId) + "/sync"); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java index 4d0f9484b02a..82e4e68aaaa0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.keycloak.admin.client.resource.UserFederationProvidersResource; import org.keycloak.common.constants.KerberosConstants; +import org.keycloak.events.admin.OperationType; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.LDAPConstants; import org.keycloak.provider.ProviderConfigProperty; @@ -36,6 +37,7 @@ import org.keycloak.representations.idm.UserFederationSyncResultRepresentation; import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest; +import org.keycloak.testsuite.util.AdminEventPaths; import org.keycloak.testsuite.util.UserFederationProviderBuilder; /** @@ -115,8 +117,8 @@ public void testCreateProvider() { assertFederationProvider(providerInstances.get(1), id1, id1, "dummy", 2, 1000, 500, 123); // Remove providers - userFederation().get(id1).remove(); - userFederation().get(id2).remove(); + removeUserFederationProvider(id1); + removeUserFederationProvider(id2); } @@ -147,6 +149,7 @@ public void testValidateAndCreateLdapProvider() { // Assert nothing created so far Assert.assertTrue(userFederation().getProviderInstances().isEmpty()); + assertAdminEvents.assertEmpty(); // Valid filter. Creation success @@ -170,8 +173,8 @@ public void testValidateAndCreateLdapProvider() { assertFederationProvider(providerInstances.get(1), id2, "ldap2", "ldap", 2, -1, -1, -1, LDAPConstants.BIND_DN, "cn=manager", LDAPConstants.BIND_CREDENTIAL, "password"); // Cleanup - userFederation().get(id1).remove(); - userFederation().get(id2).remove(); + removeUserFederationProvider(id1); + removeUserFederationProvider(id2); } @@ -204,6 +207,7 @@ public void testUpdateProvider() { // Change filter to be valid ldapRep.getConfig().put(LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something2)"); userFederation().get(id).update(ldapRep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userFederationResourcePath(id), ldapRep); // Assert updated successfully ldapRep = userFederation().get(id).toRepresentation(); @@ -213,13 +217,15 @@ public void testUpdateProvider() { // Assert update displayName ldapRep.setDisplayName("ldap2"); userFederation().get(id).update(ldapRep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userFederationResourcePath(id), ldapRep); + assertFederationProvider(userFederation().get(id).toRepresentation(), id, "ldap2", "ldap", 2, -1, -1, -1, LDAPConstants.BIND_DN, "cn=manager-updated", LDAPConstants.BIND_CREDENTIAL, "password", LDAPConstants.CUSTOM_USER_SEARCH_FILTER, "(dc=something2)"); // Cleanup - userFederation().get(id).remove(); + removeUserFederationProvider(id); } @@ -249,6 +255,7 @@ public void testKerberosAuthenticatorEnabledAutomatically() { // update LDAP provider with kerberos ldapRep = userFederation().get(id).toRepresentation(); userFederation().get(id).update(ldapRep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userFederationResourcePath(id), ldapRep); // Assert kerberos authenticator ALTERNATIVE kerberosExecution = findKerberosExecution(); @@ -257,7 +264,7 @@ public void testKerberosAuthenticatorEnabledAutomatically() { // Cleanup kerberosExecution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED.toString()); realm.flows().updateExecutions("browser", kerberosExecution); - userFederation().get(id).remove(); + removeUserFederationProvider(id); } @@ -290,19 +297,23 @@ public void testSyncFederationProvider() { // Sync and assert it happened UserFederationSyncResultRepresentation syncResult = userFederation().get(id1).syncUsers("triggerFullSync"); Assert.assertEquals("0 imported users, 0 updated users", syncResult.getStatus()); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userFederationResourcePath(id1) + "/sync"); + int fullSyncTime = userFederation().get(id1).toRepresentation().getLastSync(); Assert.assertTrue(fullSyncTime > 0); // Changed sync setTimeOffset(50); syncResult = userFederation().get(id1).syncUsers("triggerChangedUsersSync"); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userFederationResourcePath(id1) + "/sync"); + Assert.assertEquals("0 imported users, 0 updated users", syncResult.getStatus()); int changedSyncTime = userFederation().get(id1).toRepresentation().getLastSync(); Assert.assertTrue(fullSyncTime + 50 <= changedSyncTime); // Cleanup resetTimeOffset(); - userFederation().get(id1).remove(); + removeUserFederationProvider(id1); } @@ -310,7 +321,16 @@ private String createUserFederationProvider(UserFederationProviderRepresentation Response resp = userFederation().create(rep); Assert.assertEquals(201, resp.getStatus()); resp.close(); - return ApiUtil.getCreatedId(resp); + String federationProviderId = ApiUtil.getCreatedId(resp); + + // TODO adminEvents: should be rather whole path include ID (consistency with UPDATE and DELETE) + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userFederationCreateResourcePath(), rep); + return federationProviderId; + } + + private void removeUserFederationProvider(String id) { + userFederation().get(id).remove(); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userFederationResourcePath(id)); } private void assertFederationProvider(UserFederationProviderRepresentation rep, String id, String displayName, String providerName, diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java index c1431ed8aeaf..843bdec91570 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserTest.java @@ -17,6 +17,7 @@ package org.keycloak.testsuite.admin; +import org.hamcrest.Matchers; import org.jboss.arquillian.drone.api.annotation.Drone; import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.test.api.ArquillianResource; @@ -27,11 +28,11 @@ import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RoleMappingResource; import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.ErrorRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation; -import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RealmRepresentation; @@ -42,10 +43,12 @@ import org.keycloak.testsuite.page.LoginPasswordUpdatePage; import org.keycloak.testsuite.pages.InfoPage; import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.util.AdminEventPaths; import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.GreenMailRule; import org.keycloak.testsuite.util.MailUtils; import org.keycloak.testsuite.util.OAuthClient; +import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.RoleBuilder; import org.keycloak.testsuite.util.UserBuilder; import org.openqa.selenium.WebDriver; @@ -56,7 +59,6 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import java.io.IOException; -import java.security.PublicKey; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -100,12 +102,23 @@ public String createUser(String username, String email) { user.setRequiredActions(Collections.emptyList()); user.setEnabled(true); - Response response = realm.users().create(user); + return createUser(user); + } + + private String createUser(UserRepresentation userRep) { + Response response = realm.users().create(userRep); String createdId = ApiUtil.getCreatedId(response); response.close(); + + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userResourcePath(createdId), userRep); return createdId; } + private void updateUser(UserResource user, UserRepresentation userRep) { + user.update(userRep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userResourcePath(userRep.getId()), userRep); + } + @Test public void verifyCreateUser() { createUser(); @@ -119,6 +132,7 @@ public void createDuplicatedUser1() { user.setUsername("user1"); Response response = realm.users().create(user); assertEquals(409, response.getStatus()); + assertAdminEvents.assertEmpty(); // Just to show how to retrieve underlying error message ErrorRepresentation error = response.readEntity(ErrorRepresentation.class); @@ -196,6 +210,8 @@ public void createDuplicatedUser7() { assertEquals(409, response.getStatus()); response.close(); + assertAdminEvents.assertEmpty(); + } private void createUsers() { @@ -206,7 +222,7 @@ private void createUsers() { user.setFirstName("First" + i); user.setLastName("Last" + i); - realm.users().create(user).close(); + createUser(user); } } @@ -256,9 +272,11 @@ public void count() { @Test public void delete() { - Response response = realm.users().delete( createUser() ); + String userId = createUser(); + Response response = realm.users().delete( userId ); assertEquals(204, response.getStatus()); response.close(); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userResourcePath(userId)); } @Test @@ -266,6 +284,7 @@ public void deleteNonExistent() { Response response = realm.users().delete( "does-not-exist" ); assertEquals(404, response.getStatus()); response.close(); + assertAdminEvents.assertEmpty(); } @Test @@ -306,6 +325,7 @@ public void getFederatedIdentities() { link.setUserName("social-username"); Response response = user.addFederatedIdentity("social-provider-id", link); assertEquals(204, response.getStatus()); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userFederatedIdentityLink(id, "social-provider-id"), link); // Verify social link is here user = realm.users().get(id); @@ -318,6 +338,7 @@ public void getFederatedIdentities() { // Remove social link now user.removeFederatedIdentity("social-provider-id"); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.userFederatedIdentityLink(id, "social-provider-id")); assertEquals(0, user.getFederatedIdentity().size()); removeSampleIdentityProvider(); @@ -330,13 +351,16 @@ private void addSampleIdentityProvider() { IdentityProviderRepresentation rep = new IdentityProviderRepresentation(); rep.setAlias("social-provider-id"); rep.setProviderId("social-provider-type"); + realm.identityProviders().create(rep); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, Matchers.startsWith(AdminEventPaths.identityProviderCreatePath()), rep); } private void removeSampleIdentityProvider() { IdentityProviderResource resource = realm.identityProviders().get("social-provider-id"); Assert.assertNotNull(resource); resource.remove(); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.identityProviderPath("social-provider-id")); } @Test @@ -348,7 +372,7 @@ public void addRequiredAction() { UserRepresentation userRep = user.toRepresentation(); userRep.getRequiredActions().add("UPDATE_PASSWORD"); - user.update(userRep); + updateUser(user, userRep); assertEquals(1, user.toRepresentation().getRequiredActions().size()); assertEquals("UPDATE_PASSWORD", user.toRepresentation().getRequiredActions().get(0)); @@ -363,12 +387,12 @@ public void removeRequiredAction() { UserRepresentation userRep = user.toRepresentation(); userRep.getRequiredActions().add("UPDATE_PASSWORD"); - user.update(userRep); + updateUser(user, userRep); user = realm.users().get(id); userRep = user.toRepresentation(); userRep.getRequiredActions().clear(); - user.update(userRep); + updateUser(user, userRep); assertTrue(user.toRepresentation().getRequiredActions().isEmpty()); } @@ -380,9 +404,7 @@ public void attributes() { user1.singleAttribute("attr1", "value1user1"); user1.singleAttribute("attr2", "value2user1"); - Response response = realm.users().create(user1); - String user1Id = ApiUtil.getCreatedId(response); - response.close(); + String user1Id = createUser(user1); UserRepresentation user2 = new UserRepresentation(); user2.setUsername("user2"); @@ -392,9 +414,8 @@ public void attributes() { vals.add("value2user2_2"); user2.getAttributesAsListValues().put("attr2", vals); - response = realm.users().create(user2); - String user2Id = ApiUtil.getCreatedId(response); - response.close(); + String user2Id = createUser(user2); + user1 = realm.users().get(user1Id).toRepresentation(); assertEquals(2, user1.getAttributesAsListValues().size()); assertAttributeValue("value1user1", user1.getAttributesAsListValues().get("attr1")); @@ -410,7 +431,7 @@ public void attributes() { user1.singleAttribute("attr1", "value3user1"); user1.singleAttribute("attr3", "value4user1"); - realm.users().get(user1Id).update(user1); + updateUser(realm.users().get(user1Id), user1); user1 = realm.users().get(user1Id).toRepresentation(); assertEquals(3, user1.getAttributesAsListValues().size()); @@ -419,7 +440,7 @@ public void attributes() { assertAttributeValue("value4user1", user1.getAttributesAsListValues().get("attr3")); user1.getAttributes().remove("attr1"); - realm.users().get(user1Id).update(user1); + updateUser(realm.users().get(user1Id), user1); user1 = realm.users().get(user1Id).toRepresentation(); assertEquals(2, user1.getAttributesAsListValues().size()); @@ -427,7 +448,7 @@ public void attributes() { assertAttributeValue("value4user1", user1.getAttributesAsListValues().get("attr3")); user1.getAttributes().clear(); - realm.users().get(user1Id).update(user1); + updateUser(realm.users().get(user1Id), user1); user1 = realm.users().get(user1Id).toRepresentation(); assertNull(user1.getAttributes()); @@ -442,9 +463,9 @@ private void assertAttributeValue(String expectedValue, List attrValues) public void sendResetPasswordEmail() { UserRepresentation userRep = new UserRepresentation(); userRep.setUsername("user1"); - Response response = realm.users().create(userRep); - String id = ApiUtil.getCreatedId(response); - response.close(); + + String id = createUser(userRep); + UserResource user = realm.users().get(id); List actions = new LinkedList<>(); try { @@ -460,7 +481,8 @@ public void sendResetPasswordEmail() { userRep = user.toRepresentation(); userRep.setEmail("user1@localhost"); userRep.setEnabled(false); - user.update(userRep); + updateUser(user, userRep); + user.executeActionsEmail(actions); fail("Expected failure"); } catch (ClientErrorException e) { @@ -471,7 +493,8 @@ public void sendResetPasswordEmail() { } try { userRep.setEnabled(true); - user.update(userRep); + updateUser(user, userRep); + user.executeActionsEmail("invalidClientId", actions); fail("Expected failure"); } catch (ClientErrorException e) { @@ -488,13 +511,14 @@ public void sendResetPasswordEmailSuccess() throws IOException, MessagingExcepti userRep.setEnabled(true); userRep.setUsername("user1"); userRep.setEmail("user1@test.com"); - Response response = realm.users().create(userRep); - String id = ApiUtil.getCreatedId(response); - response.close(); + + String id = createUser(userRep); + UserResource user = realm.users().get(id); List actions = new LinkedList<>(); actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name()); user.executeActionsEmail("account", actions); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email"); Assert.assertEquals(1, greenMail.getReceivedMessages().length); @@ -520,9 +544,8 @@ public void sendResetPasswordEmailSuccess() throws IOException, MessagingExcepti public void sendVerifyEmail() throws IOException, MessagingException { UserRepresentation userRep = new UserRepresentation(); userRep.setUsername("user1"); - Response response = realm.users().create(userRep); - String id = ApiUtil.getCreatedId(response); - response.close(); + + String id = createUser(userRep); UserResource user = realm.users().get(id); @@ -539,7 +562,8 @@ public void sendVerifyEmail() throws IOException, MessagingException { userRep = user.toRepresentation(); userRep.setEmail("user1@localhost"); userRep.setEnabled(false); - user.update(userRep); + updateUser(user, userRep); + user.sendVerifyEmail(); fail("Expected failure"); } catch (ClientErrorException e) { @@ -550,7 +574,8 @@ public void sendVerifyEmail() throws IOException, MessagingException { } try { userRep.setEnabled(true); - user.update(userRep); + updateUser(user, userRep); + user.sendVerifyEmail("invalidClientId"); fail("Expected failure"); } catch (ClientErrorException e) { @@ -561,6 +586,8 @@ public void sendVerifyEmail() throws IOException, MessagingException { } user.sendVerifyEmail(); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/send-verify-email"); + Assert.assertEquals(1, greenMail.getReceivedMessages().length); String link = MailUtils.getPasswordResetEmailLink(greenMail.getReceivedMessages()[0]); @@ -578,7 +605,7 @@ public void updateUserWithNewUsername() { UserResource user = realm.users().get(id); UserRepresentation userRep = user.toRepresentation(); userRep.setUsername("user11"); - user.update(userRep); + updateUser(user, userRep); userRep = realm.users().get(id).toRepresentation(); assertEquals("user11", userRep.getUsername()); @@ -596,11 +623,13 @@ public void updateUserWithoutUsername() { rep.setFirstName("Firstname"); user.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userResourcePath(id), rep); rep = new UserRepresentation(); rep.setLastName("Lastname"); user.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.userResourcePath(id), rep); rep = realm.users().get(id).toRepresentation(); @@ -617,7 +646,7 @@ public void updateUserWithNewUsernameNotPossible() { UserResource user = realm.users().get(id); UserRepresentation userRep = user.toRepresentation(); userRep.setUsername("user11"); - user.update(userRep); + updateUser(user, userRep); userRep = realm.users().get(id).toRepresentation(); assertEquals("user1", userRep.getUsername()); @@ -632,7 +661,7 @@ public void updateUserWithNewUsernameAccessingViaOldUsername() { UserResource user = realm.users().get("user1"); UserRepresentation userRep = user.toRepresentation(); userRep.setUsername("user1"); - user.update(userRep); + updateUser(user, userRep); realm.users().get("user11").toRepresentation(); fail("Expected failure"); @@ -648,9 +677,8 @@ public void updateUserWithExistingUsername() { UserRepresentation userRep = new UserRepresentation(); userRep.setUsername("user2"); - Response response = realm.users().create(userRep); - String createdId = ApiUtil.getCreatedId(response); - response.close(); + + String createdId = createUser(userRep); try { UserResource user = realm.users().get(createdId); @@ -660,6 +688,10 @@ public void updateUserWithExistingUsername() { fail("Expected failure"); } catch (ClientErrorException e) { assertEquals(409, e.getResponse().getStatus()); + + // TODO adminEvents: Event queue should be empty, but it's not because of bug in UsersResource.updateUser, which sends event earlier than transaction commit. + // assertAdminEvents.assertEmpty(); + assertAdminEvents.poll(); } } @@ -673,6 +705,7 @@ public void resetUserPassword() { cred.setTemporary(false); realm.users().get(userId).resetPassword(cred); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResetPasswordPath(userId)); String accountUrl = RealmsResource.accountUrl(UriBuilder.fromUri(getAuthServerRoot())).build(REALM_NAME).toString(); @@ -699,6 +732,7 @@ public void resetUserInvalidPassword() { } catch (ClientErrorException e) { assertEquals(400, e.getResponse().getStatus()); e.getResponse().close(); + assertAdminEvents.assertEmpty(); } } @@ -725,25 +759,33 @@ public void testDefaultRequiredActionAdded() { @Test public void roleMappings() { RealmResource realm = adminClient.realms().realm("test"); + + // Enable events + RealmRepresentation realmRep = RealmBuilder.edit(realm.toRepresentation()).testEventListener().build(); + realm.update(realmRep); + realm.roles().create(RoleBuilder.create().name("realm-role").build()); realm.roles().create(RoleBuilder.create().name("realm-composite").build()); realm.roles().create(RoleBuilder.create().name("realm-child").build()); realm.roles().get("realm-composite").addComposites(Collections.singletonList(realm.roles().get("realm-child").toRepresentation())); Response response = realm.clients().create(ClientBuilder.create().clientId("myclient").build()); - String clientId = ApiUtil.getCreatedId(response); + String clientUuid = ApiUtil.getCreatedId(response); response.close(); - realm.clients().get(clientId).roles().create(RoleBuilder.create().name("client-role").build()); - realm.clients().get(clientId).roles().create(RoleBuilder.create().name("client-role2").build()); - realm.clients().get(clientId).roles().create(RoleBuilder.create().name("client-composite").build()); - realm.clients().get(clientId).roles().create(RoleBuilder.create().name("client-child").build()); - realm.clients().get(clientId).roles().get("client-composite").addComposites(Collections.singletonList(realm.clients().get(clientId).roles().get("client-child").toRepresentation())); + realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role").build()); + realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-role2").build()); + realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-composite").build()); + realm.clients().get(clientUuid).roles().create(RoleBuilder.create().name("client-child").build()); + realm.clients().get(clientUuid).roles().get("client-composite").addComposites(Collections.singletonList(realm.clients().get(clientUuid).roles().get("client-child").toRepresentation())); response = realm.users().create(UserBuilder.create().username("myuser").build()); String userId = ApiUtil.getCreatedId(response); response.close(); + // Admin events for creating role, client or user tested already in other places + assertAdminEvents.clear(); + RoleMappingResource roles = realm.users().get(userId).roles(); assertNames(roles.realmLevel().listAll(), "user", "offline_access"); @@ -752,10 +794,14 @@ public void roleMappings() { l.add(realm.roles().get("realm-role").toRepresentation()); l.add(realm.roles().get("realm-composite").toRepresentation()); roles.realmLevel().add(l); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.userRealmRoleMappingsPath(userId))); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.userRealmRoleMappingsPath(userId))); // Add client roles - roles.clientLevel(clientId).add(Collections.singletonList(realm.clients().get(clientId).roles().get("client-role").toRepresentation())); - roles.clientLevel(clientId).add(Collections.singletonList(realm.clients().get(clientId).roles().get("client-composite").toRepresentation())); + roles.clientLevel(clientUuid).add(Collections.singletonList(realm.clients().get(clientUuid).roles().get("client-role").toRepresentation())); + roles.clientLevel(clientUuid).add(Collections.singletonList(realm.clients().get(clientUuid).roles().get("client-composite").toRepresentation())); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid))); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid))); // List realm roles assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", "user", "offline_access"); @@ -763,9 +809,9 @@ public void roleMappings() { assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access"); // List client roles - assertNames(roles.clientLevel(clientId).listAll(), "client-role", "client-composite"); - assertNames(roles.clientLevel(clientId).listAvailable(), "client-role2"); - assertNames(roles.clientLevel(clientId).listEffective(), "client-role", "client-composite", "client-child"); + assertNames(roles.clientLevel(clientUuid).listAll(), "client-role", "client-composite"); + assertNames(roles.clientLevel(clientUuid).listAvailable(), "client-role2"); + assertNames(roles.clientLevel(clientUuid).listEffective(), "client-role", "client-composite", "client-child"); // Get mapping representation MappingsRepresentation all = roles.getAll(); @@ -775,18 +821,27 @@ public void roleMappings() { assertNames(all.getClientMappings().get("account").getMappings(), "manage-account", "view-profile"); // Remove realm role - roles.realmLevel().remove(Collections.singletonList(realm.roles().get("realm-role").toRepresentation())); + RoleRepresentation realmRoleRep = realm.roles().get("realm-role").toRepresentation(); + roles.realmLevel().remove(Collections.singletonList(realmRoleRep)); + assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.userRealmRoleMappingsPath(userId) + "/" + realmRoleRep.getId()); + assertNames(roles.realmLevel().listAll(), "realm-composite", "user", "offline_access"); // Remove client role - roles.clientLevel(clientId).remove(Collections.singletonList(realm.clients().get(clientId).roles().get("client-role").toRepresentation())); - assertNames(roles.clientLevel(clientId).listAll(), "client-composite"); + RoleRepresentation clientRoleRep = realm.clients().get(clientUuid).roles().get("client-role").toRepresentation(); + roles.clientLevel(clientUuid).remove(Collections.singletonList(clientRoleRep)); + + // TODO: Inconsistency between event for delete realm role mapping and client role mapping (the latter doesn't have roleRep.getId() in the path) + assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid)); + + assertNames(roles.clientLevel(clientUuid).listAll(), "client-composite"); } private void switchEditUsernameAllowedOn() { RealmRepresentation rep = realm.toRepresentation(); rep.setEditUsernameAllowed(true); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java index 9913c1764c48..2fb5ccd411f6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java @@ -19,12 +19,21 @@ import java.util.List; import javax.ws.rs.core.Response; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.AbstractAuthTest; import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.events.EventsListenerProviderFactory; +import org.keycloak.testsuite.util.AdminEventPaths; +import org.keycloak.testsuite.util.AssertAdminEvents; +import org.keycloak.testsuite.util.RealmBuilder; /** * @@ -32,10 +41,32 @@ */ public abstract class AbstractClientTest extends AbstractAuthTest { + @Rule + public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this); + + @Before + public void setupAdminEvents() { + RealmRepresentation realm = testRealmResource().toRepresentation(); + if (realm.getEventsListeners() == null || !realm.getEventsListeners().contains(EventsListenerProviderFactory.PROVIDER_ID)) { + realm = RealmBuilder.edit(testRealmResource().toRepresentation()).testEventListener().build(); + testRealmResource().update(realm); + } + } + + @After + public void tearDownAdminEvents() { + RealmRepresentation realm = RealmBuilder.edit(testRealmResource().toRepresentation()).removeTestEventListener().build(); + testRealmResource().update(realm); + } + protected RealmRepresentation realmRep() { return testRealmResource().toRepresentation(); } + protected String getRealmId() { + return "master"; + } + // returns UserRepresentation retrieved from server, with all fields, including id protected UserRepresentation getFullUserRep(String userName) { List results = testRealmResource().users().search(userName, null, null, null, null, null); @@ -64,7 +95,17 @@ protected String createSamlClient(String name) { protected String createClient(ClientRepresentation clientRep) { Response resp = testRealmResource().clients().create(clientRep); resp.close(); - return ApiUtil.getCreatedId(resp); + String id = ApiUtil.getCreatedId(resp); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientResourcePath(id), clientRep); + + return id; + } + + protected void removeClient(String clientDbId) { + testRealmResource().clients().get(clientDbId).remove(); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientResourcePath(clientDbId)); } protected ClientRepresentation findClientRepresentation(String name) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java index 108467c8c0d6..e245e428efc0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java @@ -17,13 +17,20 @@ package org.keycloak.testsuite.admin.client; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.core.type.TypeReference; import org.keycloak.admin.client.resource.ProtocolMappersResource; +import org.keycloak.events.admin.OperationType; +import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.testsuite.Assert; +import org.keycloak.util.JsonSerialization; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -93,16 +100,31 @@ protected List mappersToAdd(List oldMappers = resource.getMappersPerProtocol(resourceName); - List builtins = builtinMappers.get(resourceName); + protected void testAddAllBuiltinMappers(ProtocolMappersResource resource, String protocolName, String adminEventPath) { + List oldMappers = resource.getMappersPerProtocol(protocolName); + List builtins = builtinMappers.get(protocolName); List mappersToAdd = mappersToAdd(oldMappers, builtins); // This is used by admin console to add builtin mappers resource.createMapper(mappersToAdd); - List newMappers = resource.getMappersPerProtocol(resourceName); + AdminEventRepresentation adminEvent = assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, adminEventPath + "/add-models"); + try { + List eventMappers = JsonSerialization.readValue(new ByteArrayInputStream(adminEvent.getRepresentation().getBytes()), new TypeReference>() { + }); + Assert.assertEquals(eventMappers.size(), mappersToAdd.size()); + for (int i=0 ; i< mappersToAdd.size() ; i++) { + ProtocolMapperRepresentation repExpected = mappersToAdd.get(i); + ProtocolMapperRepresentation repActual = eventMappers.get(i); + assertEqualMappers(repExpected, repActual); + } + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + + + List newMappers = resource.getMappersPerProtocol(protocolName); assertEquals(oldMappers.size() + mappersToAdd.size(), newMappers.size()); for (ProtocolMapperRepresentation rep : mappersToAdd) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java index e167a03f8e17..439c0df9d779 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java @@ -24,8 +24,11 @@ import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ProtocolMappersResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.util.AdminEventPaths; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,17 +40,19 @@ public class ClientProtocolMapperTest extends AbstractProtocolMapperTest { private ClientResource oidcClientRsc; + private String oidcClientId; private ProtocolMappersResource oidcMappersRsc; private ClientResource samlClientRsc; + private String samlClientId; private ProtocolMappersResource samlMappersRsc; @Before public void init() { - createOidcClient("oidcMapperClient"); + oidcClientId = createOidcClient("oidcMapperClient"); oidcClientRsc = findClientResource("oidcMapperClient"); oidcMappersRsc = oidcClientRsc.getProtocolMappers(); - createSamlClient("samlMapperClient"); + samlClientId = createSamlClient("samlMapperClient"); samlClientRsc = findClientResource("samlMapperClient"); samlMappersRsc = samlClientRsc.getProtocolMappers(); @@ -56,8 +61,8 @@ public void init() { @After public void tearDown() { - oidcClientRsc.remove(); - samlClientRsc.remove(); + removeClient(oidcClientId); + removeClient(samlClientId); } @Test @@ -68,12 +73,12 @@ public void testGetMappersList() { @Test public void testCreateOidcMappersFromList() { - testAddAllBuiltinMappers(oidcMappersRsc, "openid-connect"); + testAddAllBuiltinMappers(oidcMappersRsc, "openid-connect", AdminEventPaths.clientProtocolMappersPath(oidcClientId)); } @Test public void testCreateSamlMappersFromList() { - testAddAllBuiltinMappers(samlMappersRsc, "saml"); + testAddAllBuiltinMappers(samlMappersRsc, "saml", AdminEventPaths.clientProtocolMappersPath(samlClientId)); } @Test @@ -91,10 +96,12 @@ public void testCreateSamlProtocolMapper() { int totalSamlMappers = samlMappersRsc.getMappersPerProtocol("saml").size(); Response resp = samlMappersRsc.createMapper(rep); resp.close(); + String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientProtocolMapperPath(samlClientId, createdId), rep); + assertEquals(totalMappers + 1, samlMappersRsc.getMappers().size()); assertEquals(totalSamlMappers + 1, samlMappersRsc.getMappersPerProtocol("saml").size()); - String createdId = ApiUtil.getCreatedId(resp); ProtocolMapperRepresentation created = samlMappersRsc.getMapperById(createdId); assertEqualMappers(rep, created); } @@ -113,12 +120,15 @@ public void testCreateOidcProtocolMapper() { int totalOidcMappers = oidcMappersRsc.getMappersPerProtocol("openid-connect").size(); Response resp = oidcMappersRsc.createMapper(rep); resp.close(); + String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientProtocolMapperPath(oidcClientId, createdId), rep); + assertEquals(totalMappers + 1, oidcMappersRsc.getMappers().size()); assertEquals(totalOidcMappers + 1, oidcMappersRsc.getMappersPerProtocol("openid-connect").size()); - String createdId = ApiUtil.getCreatedId(resp); ProtocolMapperRepresentation created = oidcMappersRsc.getMapperById(createdId);//findByName(samlMappersRsc, "saml-role-name-mapper"); assertEqualMappers(rep, created); + } @Test @@ -127,13 +137,14 @@ public void testUpdateSamlMapper() { Response resp = samlMappersRsc.createMapper(rep); resp.close(); - String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientProtocolMapperPath(samlClientId, createdId), rep); rep.getConfig().put("role", "account.manage-account"); rep.setId(createdId); rep.setConsentRequired(false); samlMappersRsc.update(createdId, rep); + assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientProtocolMapperPath(samlClientId, createdId), rep); ProtocolMapperRepresentation updated = samlMappersRsc.getMapperById(createdId); assertEqualMappers(rep, updated); @@ -145,44 +156,57 @@ public void testUpdateOidcMapper() { Response resp = oidcMappersRsc.createMapper(rep); resp.close(); - String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientProtocolMapperPath(oidcClientId, createdId), rep); rep.getConfig().put("role", "myotherrole"); rep.setId(createdId); rep.setConsentRequired(false); oidcMappersRsc.update(createdId, rep); + assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientProtocolMapperPath(oidcClientId, createdId), rep); ProtocolMapperRepresentation updated = oidcMappersRsc.getMapperById(createdId); assertEqualMappers(rep, updated); } - @Test (expected = NotFoundException.class) + @Test public void testDeleteSamlMapper() { ProtocolMapperRepresentation rep = makeSamlMapper("saml-role-name-mapper3"); Response resp = samlMappersRsc.createMapper(rep); resp.close(); - String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientProtocolMapperPath(samlClientId, createdId), rep); samlMappersRsc.delete(createdId); - - samlMappersRsc.getMapperById(createdId); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientProtocolMapperPath(samlClientId, createdId)); + + try { + samlMappersRsc.getMapperById(createdId); + Assert.fail("Not expected to find mapper"); + } catch (NotFoundException nfe) { + // Expected + } } - @Test (expected = NotFoundException.class) + @Test public void testDeleteOidcMapper() { ProtocolMapperRepresentation rep = makeOidcMapper("oidc-hardcoded-role-mapper3"); Response resp = oidcMappersRsc.createMapper(rep); resp.close(); - String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientProtocolMapperPath(oidcClientId, createdId), rep); oidcMappersRsc.delete(createdId); - - oidcMappersRsc.getMapperById(createdId); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientProtocolMapperPath(oidcClientId, createdId)); + + try { + oidcMappersRsc.getMapperById(createdId); + Assert.fail("Not expected to find mapper"); + } catch (NotFoundException nfe) { + // Expected + } } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientRolesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientRolesTest.java index 60aec166277a..19c982289670 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientRolesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientRolesTest.java @@ -17,13 +17,16 @@ package org.keycloak.testsuite.admin.client; +import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.RolesResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.util.AdminEventPaths; import org.keycloak.testsuite.util.RoleBuilder; import java.util.LinkedList; @@ -41,11 +44,12 @@ public class ClientRolesTest extends AbstractClientTest { private ClientResource clientRsc; + private String clientDbId; private RolesResource rolesRsc; @Before public void init() { - createOidcClient("roleClient"); + clientDbId = createOidcClient("roleClient"); clientRsc = findClientResource("roleClient"); rolesRsc = clientRsc.roles(); } @@ -72,30 +76,42 @@ private boolean hasRole(RolesResource rolesRsc, String name) { @Test public void testAddRole() { rolesRsc.create(makeRole("role1")); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.clientRolesResourcePath(clientDbId))); assertTrue(hasRole(rolesRsc, "role1")); } @Test public void testRemoveRole() { rolesRsc.create(makeRole("role2")); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.clientRolesResourcePath(clientDbId))); + rolesRsc.deleteRole("role2"); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientRoleResourcePath(clientDbId, "role2")); + assertFalse(hasRole(rolesRsc, "role2")); } @Test public void testComposites() { rolesRsc.create(makeRole("role-a")); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.clientRolesResourcePath(clientDbId))); assertFalse(rolesRsc.get("role-a").toRepresentation().isComposite()); assertEquals(0, rolesRsc.get("role-a").getRoleComposites().size()); rolesRsc.create(makeRole("role-b")); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.clientRolesResourcePath(clientDbId))); + testRealmResource().roles().create(makeRole("role-c")); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.rolesResourcePath())); List l = new LinkedList<>(); l.add(rolesRsc.get("role-b").toRepresentation()); l.add(testRealmResource().roles().get("role-c").toRepresentation()); rolesRsc.get("role-a").addComposites(l); + // TODO adminEvents: Fix once composite roles events will be fixed... + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.clientRoleResourceCompositesPath(clientDbId, "role-a"))); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.clientRoleResourceCompositesPath(clientDbId, "role-a"))); Set composites = rolesRsc.get("role-a").getRoleComposites(); @@ -109,6 +125,7 @@ public void testComposites() { Assert.assertNames(clientComposites, "role-b"); rolesRsc.get("role-a").deleteComposites(l); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientRoleResourceCompositesPath(clientDbId, "role-a")); assertFalse(rolesRsc.get("role-a").toRepresentation().isComposite()); assertEquals(0, rolesRsc.get("role-a").getRoleComposites().size()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateProtocolMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateProtocolMapperTest.java index 9106f715cedb..39fd22063150 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateProtocolMapperTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateProtocolMapperTest.java @@ -29,11 +29,13 @@ import org.keycloak.admin.client.resource.ClientTemplateResource; import org.keycloak.admin.client.resource.ClientTemplatesResource; import org.keycloak.admin.client.resource.ProtocolMappersResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.util.AdminEventPaths; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -44,26 +46,26 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class ClientTemplateProtocolMapperTest extends AbstractProtocolMapperTest { - private ClientTemplateResource oidcClientTemplateRsc; + private String oidcClientTemplateId; private ProtocolMappersResource oidcMappersRsc; - private ClientTemplateResource samlClientTemplateRsc; + private String samlClientTemplateId; private ProtocolMappersResource samlMappersRsc; @Before public void init() { - oidcClientTemplateRsc = createTemplate("oidcMapperClient-template", OIDCLoginProtocol.LOGIN_PROTOCOL); - oidcMappersRsc = oidcClientTemplateRsc.getProtocolMappers(); + oidcClientTemplateId = createTemplate("oidcMapperClient-template", OIDCLoginProtocol.LOGIN_PROTOCOL); + oidcMappersRsc = clientTemplates().get(oidcClientTemplateId).getProtocolMappers(); - samlClientTemplateRsc = createTemplate("samlMapperClient-template", SamlProtocol.LOGIN_PROTOCOL); - samlMappersRsc = samlClientTemplateRsc.getProtocolMappers(); + samlClientTemplateId = createTemplate("samlMapperClient-template", SamlProtocol.LOGIN_PROTOCOL); + samlMappersRsc = clientTemplates().get(samlClientTemplateId).getProtocolMappers(); super.initBuiltinMappers(); } @After public void tearDown() { - oidcClientTemplateRsc.remove(); - samlClientTemplateRsc.remove(); + removeTemplate(oidcClientTemplateId); + removeTemplate(samlClientTemplateId); } @Test @@ -74,12 +76,12 @@ public void test01GetMappersList() { @Test public void test02CreateOidcMappersFromList() { - testAddAllBuiltinMappers(oidcMappersRsc, "openid-connect"); + testAddAllBuiltinMappers(oidcMappersRsc, "openid-connect", AdminEventPaths.clientTemplateProtocolMappersPath(oidcClientTemplateId)); } @Test public void test03CreateSamlMappersFromList() { - testAddAllBuiltinMappers(samlMappersRsc, "saml"); + testAddAllBuiltinMappers(samlMappersRsc, "saml", AdminEventPaths.clientTemplateProtocolMappersPath(samlClientTemplateId)); } @Test @@ -97,10 +99,13 @@ public void test04CreateSamlProtocolMapper() { int totalSamlMappers = samlMappersRsc.getMappersPerProtocol("saml").size(); Response resp = samlMappersRsc.createMapper(rep); resp.close(); + String createdId = ApiUtil.getCreatedId(resp); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateProtocolMapperPath(samlClientTemplateId, createdId), rep); + assertEquals(totalMappers + 1, samlMappersRsc.getMappers().size()); assertEquals(totalSamlMappers + 1, samlMappersRsc.getMappersPerProtocol("saml").size()); - String createdId = ApiUtil.getCreatedId(resp); ProtocolMapperRepresentation created = samlMappersRsc.getMapperById(createdId); assertEqualMappers(rep, created); } @@ -119,10 +124,13 @@ public void test05CreateOidcProtocolMapper() { int totalOidcMappers = oidcMappersRsc.getMappersPerProtocol("openid-connect").size(); Response resp = oidcMappersRsc.createMapper(rep); resp.close(); + String createdId = ApiUtil.getCreatedId(resp); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateProtocolMapperPath(oidcClientTemplateId, createdId), rep); + assertEquals(totalMappers + 1, oidcMappersRsc.getMappers().size()); assertEquals(totalOidcMappers + 1, oidcMappersRsc.getMappersPerProtocol("openid-connect").size()); - String createdId = ApiUtil.getCreatedId(resp); ProtocolMapperRepresentation created = oidcMappersRsc.getMapperById(createdId);//findByName(samlMappersRsc, "saml-role-name-mapper"); assertEqualMappers(rep, created); } @@ -133,13 +141,14 @@ public void test06UpdateSamlMapper() { Response resp = samlMappersRsc.createMapper(rep); resp.close(); - String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateProtocolMapperPath(samlClientTemplateId, createdId), rep); rep.getConfig().put("role", "account.manage-account"); rep.setId(createdId); rep.setConsentRequired(false); samlMappersRsc.update(createdId, rep); + assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientTemplateProtocolMapperPath(samlClientTemplateId, createdId), rep); ProtocolMapperRepresentation updated = samlMappersRsc.getMapperById(createdId); assertEqualMappers(rep, updated); @@ -151,44 +160,57 @@ public void test07UpdateOidcMapper() { Response resp = oidcMappersRsc.createMapper(rep); resp.close(); - String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateProtocolMapperPath(oidcClientTemplateId, createdId), rep); rep.getConfig().put("role", "myotherrole"); rep.setId(createdId); rep.setConsentRequired(false); oidcMappersRsc.update(createdId, rep); + assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientTemplateProtocolMapperPath(oidcClientTemplateId, createdId), rep); ProtocolMapperRepresentation updated = oidcMappersRsc.getMapperById(createdId); assertEqualMappers(rep, updated); } - @Test (expected = NotFoundException.class) + @Test public void testDeleteSamlMapper() { ProtocolMapperRepresentation rep = makeSamlMapper("saml-role-name-mapper3"); Response resp = samlMappersRsc.createMapper(rep); resp.close(); - String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateProtocolMapperPath(samlClientTemplateId, createdId), rep); samlMappersRsc.delete(createdId); - - samlMappersRsc.getMapperById(createdId); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateProtocolMapperPath(samlClientTemplateId, createdId)); + + try { + samlMappersRsc.getMapperById(createdId); + Assert.fail("Not expected to find mapper"); + } catch (NotFoundException nfe) { + // Expected + } } - @Test (expected = NotFoundException.class) + @Test public void testDeleteOidcMapper() { ProtocolMapperRepresentation rep = makeOidcMapper("oidc-hardcoded-role-mapper3"); Response resp = oidcMappersRsc.createMapper(rep); resp.close(); - String createdId = ApiUtil.getCreatedId(resp); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateProtocolMapperPath(oidcClientTemplateId, createdId), rep); oidcMappersRsc.delete(createdId); - - oidcMappersRsc.getMapperById(createdId); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateProtocolMapperPath(oidcClientTemplateId, createdId)); + + try { + oidcMappersRsc.getMapperById(createdId); + Assert.fail("Not expected to find mapper"); + } catch (NotFoundException nfe) { + // Expected + } } @@ -196,7 +218,7 @@ private ClientTemplatesResource clientTemplates() { return testRealmResource().clientTemplates(); } - private ClientTemplateResource createTemplate(String templateName, String protocol) { + private String createTemplate(String templateName, String protocol) { ClientTemplateRepresentation rep = new ClientTemplateRepresentation(); rep.setName(templateName); rep.setFullScopeAllowed(false); @@ -204,7 +226,15 @@ private ClientTemplateResource createTemplate(String templateName, String protoc Response resp = clientTemplates().create(rep); Assert.assertEquals(201, resp.getStatus()); resp.close(); - String clientTemplateId = ApiUtil.getCreatedId(resp); - return clientTemplates().get(clientTemplateId); + String templateId = ApiUtil.getCreatedId(resp); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateResourcePath(templateId), rep); + + return templateId; + } + + private void removeTemplate(String templateId) { + clientTemplates().get(templateId).remove(); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateResourcePath(templateId)); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateTest.java index ffb6e8897f3b..86ab24964fb1 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateTest.java @@ -27,10 +27,12 @@ import javax.ws.rs.NotFoundException; import javax.ws.rs.core.Response; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.keycloak.admin.client.resource.ClientTemplatesResource; import org.keycloak.admin.client.resource.RoleMappingResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.models.AccountRoles; import org.keycloak.models.Constants; import org.keycloak.protocol.oidc.OIDCLoginProtocol; @@ -41,6 +43,7 @@ import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.util.AdminEventPaths; import static org.junit.Assert.assertEquals; @@ -64,7 +67,7 @@ public void testAddDuplicatedTemplate() { Assert.assertEquals("Client Template template1 already exists", error.getErrorMessage()); // Cleanup - clientTemplates().get(templateId).remove(); + removeTemplate(templateId); } @@ -94,7 +97,7 @@ public void testRemoveTemplate() { Assert.assertEquals(2, clientTemplates.size()); // Remove template1 - clientTemplates().get(template1Id).remove(); + removeTemplate(template1Id); clientTemplates = clientTemplates().findAll(); Assert.assertEquals(1, clientTemplates.size()); @@ -102,7 +105,7 @@ public void testRemoveTemplate() { // Remove template2 - clientTemplates().get(template2Id).remove(); + removeTemplate(template2Id); clientTemplates = clientTemplates().findAll(); Assert.assertEquals(0, clientTemplates.size()); @@ -135,6 +138,8 @@ public void testUpdateTemplate() { clientTemplates().get(template1Id).update(templateRep); + assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientTemplateResourcePath(template1Id), templateRep); + // Assert updated attributes templateRep = clientTemplates().get(template1Id).toRepresentation(); Assert.assertEquals("template1-updated", templateRep.getName()); @@ -150,20 +155,14 @@ public void testUpdateTemplate() { @Test public void testScopes() { // Add realm role1 - RoleRepresentation roleRep1 = new RoleRepresentation(); - roleRep1.setName("role1"); - testRealmResource().roles().create(roleRep1); - roleRep1 = testRealmResource().roles().get("role1").toRepresentation(); + RoleRepresentation roleRep1 = createRealmRole("role1"); // Add realm role2 - RoleRepresentation roleRep2 = roleRep2 = new RoleRepresentation(); - roleRep2.setName("role2"); - testRealmResource().roles().create(roleRep2); - roleRep2 = testRealmResource().roles().get("role2").toRepresentation(); + RoleRepresentation roleRep2 = createRealmRole("role2"); // Add role2 as composite to role1 testRealmResource().roles().get("role1").addComposites(Collections.singletonList(roleRep2)); - + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.roleResourceCompositesPath("role1"))); // create client template ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation(); @@ -177,7 +176,10 @@ public void testScopes() { RoleMappingResource scopesResource = clientTemplates().get(templateId).getScopeMappings(); scopesResource.realmLevel().add(Collections.singletonList(roleRep1)); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateScopeMappingsRealmLevelPath(templateId) + "/" + roleRep1.getId()); + scopesResource.clientLevel(accountMgmtId).add(Collections.singletonList(viewAccountRoleRep)); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateScopeMappingsClientLevelPath(templateId, accountMgmtId) + "/" + viewAccountRoleRep.getId()); // test that scopes are available (also through composite role) List allRealm = scopesResource.realmLevel().listAll(); @@ -196,7 +198,10 @@ public void testScopes() { // remove scopes scopesResource.realmLevel().remove(Collections.singletonList(roleRep1)); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateScopeMappingsRealmLevelPath(templateId) + "/" + roleRep1.getId()); + scopesResource.clientLevel(accountMgmtId).remove(Collections.singletonList(viewAccountRoleRep)); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateScopeMappingsClientLevelPath(templateId, accountMgmtId) + "/" + viewAccountRoleRep.getId()); // assert scopes are removed allRealm = scopesResource.realmLevel().listAll(); @@ -209,7 +214,7 @@ public void testScopes() { assertRolesNotPresent(accountRoles, AccountRoles.VIEW_PROFILE); // remove template - clientTemplates().get(templateId).remove(); + removeTemplate(templateId); } private void assertRolesPresent(List roles, String... expectedRoleNames) { @@ -241,10 +246,7 @@ private void assertRolesNotPresent(List roles, String... not @Test public void testRemoveScopedRole() { // Add realm role - RoleRepresentation roleRep = new RoleRepresentation(); - roleRep.setName("foo-role"); - testRealmResource().roles().create(roleRep); - roleRep = testRealmResource().roles().get("foo-role").toRepresentation(); + RoleRepresentation roleRep = createRealmRole("foo-role"); // Add client template ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation(); @@ -254,6 +256,7 @@ public void testRemoveScopedRole() { // Add realm role to scopes of clientTemplate clientTemplates().get(templateId).getScopeMappings().realmLevel().add(Collections.singletonList(roleRep)); + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateScopeMappingsRealmLevelPath(templateId) + "/" + roleRep.getId()); List roleReps = clientTemplates().get(templateId).getScopeMappings().realmLevel().listAll(); Assert.assertEquals(1, roleReps.size()); @@ -261,13 +264,24 @@ public void testRemoveScopedRole() { // Remove realm role testRealmResource().roles().deleteRole("foo-role"); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.roleResourcePath("foo-role")); // Get scope mappings roleReps = clientTemplates().get(templateId).getScopeMappings().realmLevel().listAll(); Assert.assertEquals(0, roleReps.size()); // Cleanup - clientTemplates().get(templateId).remove(); + removeTemplate(templateId); + } + + private RoleRepresentation createRealmRole(String roleName) { + RoleRepresentation roleRep = new RoleRepresentation(); + roleRep.setName(roleName); + testRealmResource().roles().create(roleRep); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, Matchers.startsWith(AdminEventPaths.rolesResourcePath())); + + return testRealmResource().roles().get(roleName).toRepresentation(); } @@ -295,13 +309,14 @@ public void testRemoveTemplateInUse() { } catch (BadRequestException bre) { ErrorRepresentation error = bre.getResponse().readEntity(ErrorRepresentation.class); Assert.assertEquals("Cannot remove client template, it is currently in use", error.getErrorMessage()); + assertAdminEvents.assertEmpty(); } // Remove client - testRealmResource().clients().get(clientDbId).remove(); + removeClient(clientDbId); // Can remove clientTemplate now - clientTemplates().get(templateId).remove(); + removeTemplate(templateId); } @@ -313,7 +328,16 @@ private String createTemplate(ClientTemplateRepresentation templateRep) { Response resp = clientTemplates().create(templateRep); Assert.assertEquals(201, resp.getStatus()); resp.close(); - return ApiUtil.getCreatedId(resp); + String templateId = ApiUtil.getCreatedId(resp); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientTemplateResourcePath(templateId), templateRep); + + return templateId; + } + + private void removeTemplate(String templateId) { + clientTemplates().get(templateId).remove(); + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientTemplateResourcePath(templateId)); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTest.java index ecce3064d733..5216c3649e29 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTest.java @@ -18,9 +18,14 @@ package org.keycloak.testsuite.admin.client; import java.util.List; + +import org.junit.Rule; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.testsuite.util.AdminEventPaths; +import org.keycloak.testsuite.util.AssertAdminEvents; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -34,7 +39,6 @@ */ public class ClientTest extends AbstractClientTest { - public static void assertEqualClients(ClientRepresentation expected, ClientRepresentation actual) { assertEquals(expected.getClientId(), actual.getClientId()); assertEquals(expected.getName(), actual.getName()); @@ -49,16 +53,20 @@ public static void assertEqualClients(ClientRepresentation expected, ClientRepre @Test public void testCreateClient() { createOidcClient("foo"); - assertEquals("foo", findClientRepresentation("foo").getName()); + ClientRepresentation found = findClientRepresentation("foo"); + assertEquals("foo", found.getName()); } @Test public void testDeleteClient() { - createOidcClient("deleteMe"); + String clientDbId = createOidcClient("deleteMe"); + ClientResource clientRsc = findClientResource("deleteMe"); assertNotNull(clientRsc); clientRsc.remove(); assertNull(findClientResource("deleteMe")); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientResourcePath(clientDbId)); } @Test @@ -74,6 +82,12 @@ public void testUpdateClient() { assertNotNull(updatedClient); assertEquals("updateMe", updatedClient.getClientId()); assertEquals("iWasUpdated", updatedClient.getName()); + + // Assert admin event + ClientRepresentation expectedClientRep = new ClientRepresentation(); + expectedClientRep.setClientId("updateMe"); + expectedClientRep.setName("iWasUpdated"); + assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(clientRep.getId()), expectedClientRep); } @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/CredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/CredentialsTest.java index 5db15e01eccb..a9d038ed90be 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/CredentialsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/CredentialsTest.java @@ -24,9 +24,11 @@ import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.KeyStoreConfig; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.CertificateRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.testsuite.util.AdminEventPaths; import javax.ws.rs.core.MediaType; @@ -53,16 +55,23 @@ public class CredentialsTest extends AbstractClientTest { private ClientResource accountClient; + private String accountClientDbId; @Before public void init() { accountClient = findClientResourceById("account"); + accountClientDbId = accountClient.toRepresentation().getId(); } @Test public void testGetAndRegenerateSecret() { CredentialRepresentation oldCredential = accountClient.getSecret(); CredentialRepresentation newCredential = accountClient.generateNewSecret(); + + CredentialRepresentation secretRep = new CredentialRepresentation(); + secretRep.setType(CredentialRepresentation.SECRET); + assertAdminEvents.assertEvent(getRealmId(), OperationType.ACTION, AdminEventPaths.clientGenerateSecretPath(accountClientDbId), secretRep); + assertNotNull(oldCredential); assertNotNull(newCredential); assertNotEquals(newCredential.getValue(), oldCredential.getValue()); @@ -77,6 +86,12 @@ public void testGetAndRegenerateRegistrationAccessToken() { assertNull(oldToken); // registration access token not saved in ClientRep assertNotNull(newToken); // it's only available via regenerateRegistrationAccessToken() assertNull(accountClient.toRepresentation().getRegistrationAccessToken()); + + // Test event + ClientRepresentation testedRep = new ClientRepresentation(); + testedRep.setClientId(rep.getClientId()); + testedRep.setRegistrationAccessToken(newToken); + assertAdminEvents.assertEvent(getRealmId(), OperationType.ACTION, AdminEventPaths.clientRegenerateRegistrationAccessTokenPath(accountClientDbId), testedRep); } @Test @@ -86,6 +101,8 @@ public void testGetCertificateResource() { CertificateRepresentation certFromGet = certRsc.getKeyInfo(); assertEquals(cert.getCertificate(), certFromGet.getCertificate()); assertEquals(cert.getPrivateKey(), certFromGet.getPrivateKey()); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.ACTION, AdminEventPaths.clientCertificateGenerateSecretPath(accountClientDbId, "jwt.credential"), cert); } @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java index 71371988aa83..fb03891e1568 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java @@ -21,6 +21,7 @@ import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; @@ -37,21 +38,23 @@ public class InstallationTest extends AbstractClientTest { private static final String SAML_NAME = "samlInstallationClient"; private ClientResource oidcClient; + private String oidcClientId; private ClientResource samlClient; + private String samlClientId; @Before public void createClients() { - createOidcClient(OIDC_NAME); + oidcClientId = createOidcClient(OIDC_NAME); oidcClient = findClientResource(OIDC_NAME); - createSamlClient(SAML_NAME); + samlClientId = createSamlClient(SAML_NAME); samlClient = findClientResource(SAML_NAME); } @After public void tearDown() { - oidcClient.remove(); - samlClient.remove(); + removeClient(oidcClientId); + removeClient(samlClientId); } private String authServerUrl() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/SessionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/SessionTest.java index cf15f960a9cf..8685603452ed 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/SessionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/SessionTest.java @@ -22,9 +22,11 @@ import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; import org.keycloak.testsuite.auth.page.account.AccountManagement; +import org.keycloak.testsuite.util.AdminEventPaths; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -42,7 +44,12 @@ public class SessionTest extends AbstractClientTest { @Before public void init() { // make user test user exists in test realm - if (!testUserCreated) createTestUserWithAdminClient(); + if (!testUserCreated) { + createTestUserWithAdminClient(); + + assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.userResourcePath(testUser.getId())); + assertAdminEvents.assertEvent(getRealmId(), OperationType.ACTION, AdminEventPaths.userResetPasswordPath(testUser.getId())); + } testUserCreated = true; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AdminEventAuthDetailsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AdminEventAuthDetailsTest.java new file mode 100644 index 000000000000..9413836c850d --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/event/AdminEventAuthDetailsTest.java @@ -0,0 +1,146 @@ +/* + * 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.event; + +import java.util.Collections; +import java.util.List; + +import org.junit.Before; +import org.junit.ComparisonFailure; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.events.admin.OperationType; +import org.keycloak.models.AdminRoles; +import org.keycloak.models.Constants; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.AbstractAuthTest; +import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; +import org.keycloak.testsuite.util.AdminEventPaths; +import org.keycloak.testsuite.util.AssertAdminEvents; +import org.keycloak.testsuite.util.ClientBuilder; +import org.keycloak.testsuite.util.RealmBuilder; +import org.keycloak.testsuite.util.UserBuilder; + +import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN; +import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; + +/** + * Test authDetails in admin events + * + * @author Marek Posolda + */ +public class AdminEventAuthDetailsTest extends AbstractAuthTest { + + @Rule + public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this); + + private String masterAdminCliUuid; + private String masterAdminUserId; + private String masterAdminUser2Id; + + private String realmUuid; + private String client1Uuid; + private String adminCliUuid; + private String admin1Id; + private String admin2Id; + private String appUserId; + + @Override + public void addTestRealms(List testRealms) { + RealmBuilder realm = RealmBuilder.create().name("test").testEventListener(); + client1Uuid = KeycloakModelUtils.generateId(); + realm.client(ClientBuilder.create().id(client1Uuid).clientId("client1").publicClient().directAccessGrants()); + + admin1Id = KeycloakModelUtils.generateId(); + realm.user(UserBuilder.create().id(admin1Id).username("admin1").password("password").role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN)); + + admin2Id = KeycloakModelUtils.generateId(); + realm.user(UserBuilder.create().id(admin2Id).username("admin2").password("password").role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN)); + + appUserId = KeycloakModelUtils.generateId(); + realm.user(UserBuilder.create().id(appUserId).username("app-user").password("password")); + + testRealms.add(realm.build()); + } + + @Before + public void initConfig() { + RealmResource masterRealm = adminClient.realm(MASTER); + masterAdminCliUuid = ApiUtil.findClientByClientId(masterRealm, Constants.ADMIN_CLI_CLIENT_ID).toRepresentation().getId(); + masterAdminUserId = ApiUtil.findUserByUsername(masterRealm, "admin").getId(); + masterAdminUser2Id = ApiUtil.createUserAndResetPasswordWithAdminClient(masterRealm, UserBuilder.create().username("admin2").build(), "password"); + masterRealm.users().get(masterAdminUser2Id).roles().realmLevel().add(Collections.singletonList(masterRealm.roles().get("admin").toRepresentation())); + + RealmResource testRealm = adminClient.realm("test"); + realmUuid = testRealm.toRepresentation().getId(); + adminCliUuid = ApiUtil.findClientByClientId(testRealm, Constants.ADMIN_CLI_CLIENT_ID).toRepresentation().getId(); + } + + @Test + public void testAuth() { + testClient(MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID, MASTER, masterAdminCliUuid, masterAdminUserId); + testClient(MASTER, "admin2", "password", Constants.ADMIN_CLI_CLIENT_ID, MASTER, masterAdminCliUuid, masterAdminUser2Id); + + testClient("test", "admin1", "password", Constants.ADMIN_CLI_CLIENT_ID, realmUuid, adminCliUuid, admin1Id); + testClient("test", "admin2", "password", Constants.ADMIN_CLI_CLIENT_ID, realmUuid, adminCliUuid, admin2Id); + testClient("test", "admin1", "password", "client1", realmUuid, client1Uuid, admin1Id); + testClient("test", "admin2", "password", "client1", realmUuid, client1Uuid, admin2Id); + + // Should fail due to different client UUID + try { + testClient("test", "admin1", "password", "client1", realmUuid, adminCliUuid, admin1Id); + Assert.fail("Not expected to pass"); + } catch (ComparisonFailure expected) { + // expected + } + + // Should fail due to different user ID + try { + testClient("test", "admin1", "password", "client1", realmUuid, client1Uuid, admin2Id); + Assert.fail("Not expected to pass"); + } catch (ComparisonFailure expected) { + // expected + } + + } + + private void testClient(String realmName, String username, String password, String clientId, String expectedRealmId, String expectedClientUuid, String expectedUserId) { + Keycloak keycloak = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth", + realmName, username, password, clientId); + try { + UserRepresentation rep = UserBuilder.create().id(appUserId).username("app-user").email("foo@email.org").build(); + keycloak.realm("test").users().get(appUserId).update(rep); + + assertAdminEvents.expect() + .realmId(realmUuid) + .operationType(OperationType.UPDATE) + .resourcePath(AdminEventPaths.userResourcePath(appUserId)) + .representation(rep) + .authDetails(expectedRealmId, expectedClientUuid, expectedUserId) + .assertEvent(); + } finally { + keycloak.close(); + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java index 2cc6813d3062..b7c98c3bbc4d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/AbstractGroupTest.java @@ -31,6 +31,7 @@ import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; +import org.keycloak.testsuite.util.AssertAdminEvents; import org.keycloak.testsuite.util.OAuthClient.AccessTokenResponse; import java.security.PublicKey; @@ -46,6 +47,9 @@ public abstract class AbstractGroupTest extends AbstractKeycloakTest { @Rule public AssertEvents events = new AssertEvents(this); + @Rule + public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this); + AccessToken login(String login, String clientId, String clientSecret, String userId) throws Exception { AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("test", login, "password", null, clientId, clientSecret); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java index 962ef51841b7..a1049b1ffadd 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupTest.java @@ -17,10 +17,12 @@ package org.keycloak.testsuite.admin.group; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RoleMappingResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.representations.AccessToken; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; @@ -30,6 +32,7 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.util.AdminEventPaths; import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.RoleBuilder; import org.keycloak.testsuite.util.URLAssert; @@ -92,26 +95,47 @@ public void addTestRealms(List testRealms) { @Test public void testClientRemoveWithClientRoleGroupMapping() throws Exception { RealmResource realm = adminClient.realms().realm("test"); + ClientRepresentation client = new ClientRepresentation(); client.setClientId("foo"); client.setRootUrl("http://foo"); client.setProtocol("openid-connect"); Response response = realm.clients().create(client); response.close(); + String clientUuid = ApiUtil.getCreatedId(response); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.clientResourcePath(clientUuid), client); client = realm.clients().findByClientId("foo").get(0); + RoleRepresentation role = new RoleRepresentation(); role.setName("foo-role"); realm.clients().get(client.getId()).roles().create(role); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.clientRolesResourcePath(clientUuid)), role); role = realm.clients().get(client.getId()).roles().get("foo-role").toRepresentation(); + GroupRepresentation group = new GroupRepresentation(); group.setName("2716"); - realm.groups().add(group).close(); - group = realm.getGroupByPath("/2716"); + group = createGroup(realm, group); + List list = new LinkedList<>(); list.add(role); realm.groups().group(group.getId()).roles().clientLevel(client.getId()).add(list); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.groupRolesClientRolesPath(group.getId(), clientUuid)); + realm.clients().get(client.getId()).remove(); + assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.clientResourcePath(clientUuid)); + } + + private GroupRepresentation createGroup(RealmResource realm, GroupRepresentation group) { + Response response = realm.groups().add(group); + String groupId = ApiUtil.getCreatedId(response); + response.close(); + + // TODO adminEvents: ID of group is missing in create event + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.groupsPath(), group); + // Set ID to the original rep + group.setId(groupId); + return group; } @Test @@ -136,21 +160,24 @@ public void createAndTestGroups() throws Exception { } RoleRepresentation level3Role = realm.roles().get("level3Role").toRepresentation(); + // Role events tested elsewhere + assertAdminEvents.clear(); GroupRepresentation topGroup = new GroupRepresentation(); topGroup.setName("top"); - Response response = realm.groups().add(topGroup); - response.close(); - topGroup = realm.getGroupByPath("/top"); - Assert.assertNotNull(topGroup); + topGroup = createGroup(realm, topGroup); + List roles = new LinkedList<>(); roles.add(topRole); realm.groups().group(topGroup.getId()).roles().realmLevel().add(roles); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.groupRolesRealmRolesPath(topGroup.getId()))); GroupRepresentation level2Group = new GroupRepresentation(); level2Group.setName("level2"); - response = realm.groups().group(topGroup.getId()).subGroup(level2Group); + Response response = realm.groups().group(topGroup.getId()).subGroup(level2Group); response.close(); + // TODO adminEvents: Should be CREATE + assertAdminEvents.assertEvent("test", OperationType.UPDATE, AdminEventPaths.groupSubgroupsPath(topGroup.getId()), level2Group); URI location = response.getLocation(); final String level2Id = ApiUtil.getCreatedId(response); @@ -171,16 +198,21 @@ protected void assertResponseBody(String body) throws IOException { roles.clear(); roles.add(level2Role); realm.groups().group(level2Group.getId()).roles().realmLevel().add(roles); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.groupRolesRealmRolesPath(level2Group.getId()))); GroupRepresentation level3Group = new GroupRepresentation(); level3Group.setName("level3"); response = realm.groups().group(level2Group.getId()).subGroup(level3Group); response.close(); + // TODO adminEvents: Should be CREATE + assertAdminEvents.assertEvent("test", OperationType.UPDATE, AdminEventPaths.groupSubgroupsPath(level2Group.getId()), level3Group); + level3Group = realm.getGroupByPath("/top/level2/level3"); Assert.assertNotNull(level3Group); roles.clear(); roles.add(level3Role); realm.groups().group(level3Group.getId()).roles().realmLevel().add(roles); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.groupRolesRealmRolesPath(level3Group.getId()))); topGroup = realm.getGroupByPath("/top"); assertEquals(1, topGroup.getRealmRoles().size()); @@ -200,6 +232,8 @@ protected void assertResponseBody(String body) throws IOException { UserRepresentation user = realm.users().search("direct-login", -1, -1).get(0); realm.users().get(user.getId()).joinGroup(level3Group.getId()); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.userGroupPath(user.getId(), level3Group.getId()))); + List membership = realm.users().get(user.getId()).groups(); assertEquals(1, membership.size()); assertEquals("level3", membership.get(0).getName()); @@ -219,9 +253,11 @@ protected void assertResponseBody(String body) throws IOException { newUser.setUsername("groupUser"); newUser.setEmail("group@group.com"); response = realm.users().create(newUser); + String userId = ApiUtil.getCreatedId(response); response.close(); - newUser = realm.users().search("groupUser", -1, -1).get(0); - membership = realm.users().get(newUser.getId()).groups(); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userResourcePath(userId), newUser); + + membership = realm.users().get(userId).groups(); assertEquals(1, membership.size()); assertEquals("level3", membership.get(0).getName()); @@ -230,6 +266,7 @@ protected void assertResponseBody(String body) throws IOException { assertEquals(0, defaultGroups.size()); realm.groups().group(topGroup.getId()).remove(); + assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.groupPath(topGroup.getId())); try { realm.getGroupByPath("/top/level2/level3"); @@ -263,10 +300,9 @@ public void updateGroup() { attrs.put("attr1", Collections.singletonList("attrval1")); attrs.put("attr2", Collections.singletonList("attrval2")); group.setAttributes(attrs); - - Response response = realm.groups().add(group); - response.close(); + createGroup(realm, group); group = realm.getGroupByPath("/group"); + Assert.assertNotNull(group); assertEquals("group", group.getName()); assertEquals(2, group.getAttributes().size()); @@ -282,6 +318,7 @@ public void updateGroup() { group.getAttributes().put("attr3", Collections.singletonList("attrval2")); realm.groups().group(group.getId()).update(group); + assertAdminEvents.assertEvent("test", OperationType.UPDATE, AdminEventPaths.groupPath(group.getId()), group); group = realm.getGroupByPath("/group-new"); @@ -297,29 +334,32 @@ public void groupMembership() { GroupRepresentation group = new GroupRepresentation(); group.setName("group"); - Response response = realm.groups().add(group); - String groupId = ApiUtil.getCreatedId(response); - response.close(); + String groupId = createGroup(realm, group).getId(); - response = realm.users().create(UserBuilder.create().username("user-a").build()); + Response response = realm.users().create(UserBuilder.create().username("user-a").build()); String userAId = ApiUtil.getCreatedId(response); response.close(); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userResourcePath(userAId)); response = realm.users().create(UserBuilder.create().username("user-b").build()); String userBId = ApiUtil.getCreatedId(response); response.close(); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userResourcePath(userBId)); realm.users().get(userAId).joinGroup(groupId); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userGroupPath(userAId, groupId)); List members = realm.groups().group(groupId).members(0, 10); assertNames(members, "user-a"); realm.users().get(userBId).joinGroup(groupId); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userGroupPath(userBId, groupId)); members = realm.groups().group(groupId).members(0, 10); assertNames(members, "user-a", "user-b"); realm.users().get(userAId).leaveGroup(groupId); + assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.userGroupPath(userAId, groupId)); members = realm.groups().group(groupId).members(0, 10); assertNames(members, "user-b"); @@ -361,11 +401,13 @@ public void roleMappings() { realm.clients().get(clientId).roles().create(RoleBuilder.create().name("client-child").build()); realm.clients().get(clientId).roles().get("client-composite").addComposites(Collections.singletonList(realm.clients().get(clientId).roles().get("client-child").toRepresentation())); + + // Roles+clients tested elsewhere + assertAdminEvents.clear(); + GroupRepresentation group = new GroupRepresentation(); group.setName("group"); - response = realm.groups().add(group); - String groupId = ApiUtil.getCreatedId(response); - response.close(); + String groupId = createGroup(realm, group).getId(); RoleMappingResource roles = realm.groups().group(groupId).roles(); assertEquals(0, roles.realmLevel().listAll().size()); @@ -375,10 +417,14 @@ public void roleMappings() { l.add(realm.roles().get("realm-role").toRepresentation()); l.add(realm.roles().get("realm-composite").toRepresentation()); roles.realmLevel().add(l); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.groupRolesRealmRolesPath(group.getId()))); + assertAdminEvents.assertEvent("test", OperationType.CREATE, Matchers.startsWith(AdminEventPaths.groupRolesRealmRolesPath(group.getId()))); // Add client roles roles.clientLevel(clientId).add(Collections.singletonList(realm.clients().get(clientId).roles().get("client-role").toRepresentation())); roles.clientLevel(clientId).add(Collections.singletonList(realm.clients().get(clientId).roles().get("client-composite").toRepresentation())); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.groupRolesClientRolesPath(group.getId(), clientId)); + assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.groupRolesClientRolesPath(group.getId(), clientId)); // List realm roles assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite"); @@ -397,11 +443,16 @@ public void roleMappings() { assertNames(all.getClientMappings().get("myclient").getMappings(), "client-role", "client-composite"); // Remove realm role - roles.realmLevel().remove(Collections.singletonList(realm.roles().get("realm-role").toRepresentation())); + // TODO adminEvents: DEleting group realmRole mapping has ID in the end. For deleting clientRole not. + RoleRepresentation realmRoleRep = realm.roles().get("realm-role").toRepresentation(); + roles.realmLevel().remove(Collections.singletonList(realmRoleRep)); + assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.groupRolesRealmRolesPath(group.getId()) + "/" + realmRoleRep.getId()); assertNames(roles.realmLevel().listAll(), "realm-composite"); // Remove client role - roles.clientLevel(clientId).remove(Collections.singletonList(realm.clients().get(clientId).roles().get("client-role").toRepresentation())); + RoleRepresentation clientRoleRep = realm.clients().get(clientId).roles().get("client-role").toRepresentation(); + roles.clientLevel(clientId).remove(Collections.singletonList(clientRoleRep)); + assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.groupRolesClientRolesPath(group.getId(), clientId)); assertNames(roles.clientLevel(clientId).listAll(), "client-composite"); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java index d9acf656dac3..168a6e1e207d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialimport/PartialImportTest.java @@ -19,21 +19,30 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; + import javax.ws.rs.core.Response; + +import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.admin.client.resource.RoleResource; import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.events.admin.OperationType; import org.keycloak.partialimport.PartialImportResult; import org.keycloak.partialimport.PartialImportResults; +import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.PartialImportRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.AbstractAuthTest; @@ -45,7 +54,11 @@ import org.keycloak.representations.idm.PartialImportRepresentation.Policy; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RolesRepresentation; +import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.util.AdminEventPaths; +import org.keycloak.testsuite.util.AssertAdminEvents; +import org.keycloak.testsuite.util.RealmBuilder; /** * Tests for the partial import endpoint in admin client. Also tests the @@ -56,6 +69,9 @@ */ public class PartialImportTest extends AbstractAuthTest { + @Rule + public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this); + private static final int NUM_RESOURCE_TYPES = 6; private static final String CLIENT_ROLES_CLIENT = "clientRolesClient"; private static final String USER_PREFIX = "user"; @@ -67,12 +83,23 @@ public class PartialImportTest extends AbstractAuthTest { private static final int NUM_ENTITIES = IDP_ALIASES.length; private PartialImportRepresentation piRep; + private String realmId; @Before - public void init() { + public void initAdminEvents() { + RealmRepresentation realmRep = RealmBuilder.edit(testRealmResource().toRepresentation()).testEventListener().build(); + realmId = realmRep.getId(); + adminClient.realm(realmRep.getRealm()).update(realmRep); + piRep = new PartialImportRepresentation(); } + @After + public void tearDownAdminEvents() { + RealmRepresentation realmRep = RealmBuilder.edit(testRealmResource().toRepresentation()).removeTestEventListener().build(); + adminClient.realm(realmRep.getRealm()).update(realmRep); + } + @Before public void createClientForClientRoles() { ClientRepresentation client = new ClientRepresentation(); @@ -251,17 +278,34 @@ private void addClientRoles() { @Test public void testAddUsers() { + assertAdminEvents.clear(); + setFail(); addUsers(); PartialImportResults results = doImport(); assertEquals(NUM_ENTITIES, results.getAdded()); + // Need to do this way as admin events from partial import are unsorted + Set userIds = new HashSet<>(); + for (int i=0 ; i composites = resource.get("role-a").getRoleComposites(); @@ -131,6 +154,7 @@ public void composites() { Assert.assertNames(clientComposites, "role-c"); resource.get("role-a").deleteComposites(l); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.roleResourceCompositesPath("role-a")); assertFalse(resource.get("role-a").toRepresentation().isComposite()); assertEquals(0, resource.get("role-a").getRoleComposites().size()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java index 599c1ccb3adf..5aa88e5ab6f5 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite.admin.realm; import org.apache.commons.io.IOUtils; +import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; import org.keycloak.OAuth2Constants; @@ -25,6 +26,7 @@ import org.keycloak.admin.client.resource.ServerInfoResource; import org.keycloak.common.util.StreamUtil; import org.keycloak.common.util.Time; +import org.keycloak.events.admin.OperationType; import org.keycloak.models.Constants; import org.keycloak.representations.adapters.action.GlobalRequestResult; import org.keycloak.representations.adapters.action.PushNotBeforeAction; @@ -39,9 +41,9 @@ import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; import org.keycloak.testsuite.auth.page.AuthRealm; +import org.keycloak.testsuite.util.AdminEventPaths; 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 org.keycloak.util.JsonSerialization; @@ -191,6 +193,7 @@ public void updateRealm() { rep.setEditUsernameAllowed(true); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); rep = realm.toRepresentation(); @@ -207,6 +210,7 @@ public void updateRealm() { rep.setEditUsernameAllowed(false); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); rep = realm.toRepresentation(); assertEquals(Boolean.FALSE, rep.isRegistrationAllowed()); @@ -222,6 +226,7 @@ public void updateRealmWithNewRepresentation() { rep.setSupportedLocales(new HashSet<>(Arrays.asList("en", "de"))); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); rep = realm.toRepresentation(); @@ -233,6 +238,7 @@ public void updateRealmWithNewRepresentation() { rep.setEditUsernameAllowed(false); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); rep = realm.toRepresentation(); assertEquals(Boolean.FALSE, rep.isEditUsernameAllowed()); @@ -252,10 +258,11 @@ public void getRealmRepresentation() { } @Test - // KEYCLOAK-1110 TODO: Functionality more related to roles. So maybe rather move to RolesTest once we have it + // KEYCLOAK-1110 public void deleteDefaultRole() { RoleRepresentation role = new RoleRepresentation("test", "test", false); realm.roles().create(role); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, Matchers.startsWith(AdminEventPaths.rolesResourcePath())); assertNotNull(realm.roles().get("test").toRepresentation()); @@ -264,8 +271,10 @@ public void deleteDefaultRole() { rep.getDefaultRoles().add("test"); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); realm.roles().deleteRole("test"); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.roleResourcePath("test")); try { realm.roles().get("testsadfsadf").toRepresentation(); @@ -378,6 +387,7 @@ public void uploadRealmKeys() throws Exception { fail("Expected BadRequestException"); } catch (BadRequestException e) { // Expected + assertAdminEvents.assertEmpty(); } rep.setPrivateKey(PRIVATE_KEY); @@ -388,12 +398,14 @@ public void uploadRealmKeys() throws Exception { fail("Expected BadRequestException"); } catch (BadRequestException e) { // Expected + assertAdminEvents.assertEmpty(); } Assert.assertEquals(originalPublicKey, realm.toRepresentation().getPublicKey()); rep.setPublicKey(PUBLIC_KEY); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); assertEquals(PUBLIC_KEY, rep.getPublicKey()); @@ -407,6 +419,7 @@ public void uploadRealmKeys() throws Exception { fail("Expected BadRequestException"); } catch (BadRequestException e) { // Expected + assertAdminEvents.assertEmpty(); } Assert.assertEquals(PUBLIC_KEY, realm.toRepresentation().getPublicKey()); @@ -414,6 +427,7 @@ public void uploadRealmKeys() throws Exception { rep.setPublicKey(publicKey2048); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); Assert.assertEquals(publicKey2048, realm.toRepresentation().getPublicKey()); @@ -423,6 +437,7 @@ public void uploadRealmKeys() throws Exception { rep.setPublicKey(publicKey4096); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); Assert.assertEquals(publicKey4096, realm.toRepresentation().getPublicKey()); } @@ -433,6 +448,7 @@ public void uploadCertificate() throws IOException { rep.setCertificate(CERTIFICATE); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); assertEquals(CERTIFICATE, rep.getCertificate()); @@ -440,6 +456,7 @@ public void uploadCertificate() throws IOException { rep.setCertificate(certificate); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); assertEquals(certificate, rep.getCertificate()); } @@ -448,7 +465,10 @@ public void uploadCertificate() throws IOException { public void clearRealmCache() { RealmRepresentation realmRep = realm.toRepresentation(); assertTrue(testingClient.testing().isCached("realms", realmRep.getId())); - adminClient.realm("master").clearRealmCache(); + + realm.clearRealmCache(); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, "clear-realm-cache"); + assertFalse(testingClient.testing().isCached("realms", realmRep.getId())); } @@ -459,11 +479,15 @@ public void clearUserCache() { Response response = realm.users().create(user); String userId = ApiUtil.getCreatedId(response); response.close(); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userResourcePath(userId), user); realm.users().get(userId).toRepresentation(); assertTrue(testingClient.testing().isCached("users", userId)); - adminClient.realm("master").clearUserCache(); + + realm.clearUserCache(); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, "clear-user-cache"); + assertFalse(testingClient.testing().isCached("users", userId)); } @@ -476,8 +500,11 @@ public void pushNotBefore() { RealmRepresentation rep = realm.toRepresentation(); rep.setNotBefore(time); realm.update(rep); + assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep); GlobalRequestResult globalRequestResult = realm.pushRevocation(); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, "push-revocation"); + assertEquals(1, globalRequestResult.getSuccessRequests().size()); assertEquals("http://localhost:8180/auth/realms/master/app/admin", globalRequestResult.getSuccessRequests().get(0)); assertNull(globalRequestResult.getFailedRequests()); @@ -493,12 +520,16 @@ public void logoutAll() { Response response = realm.users().create(UserBuilder.create().username("user").build()); String userId = ApiUtil.getCreatedId(response); response.close(); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userResourcePath(userId)); realm.users().get(userId).resetPassword(CredentialBuilder.create().password("password").build()); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResetPasswordPath(userId)); oauth.doLogin("user", "password"); GlobalRequestResult globalRequestResult = realm.logoutAll(); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, "logout-all"); + assertEquals(1, globalRequestResult.getSuccessRequests().size()); assertEquals("http://localhost:8180/auth/realms/master/app/admin", globalRequestResult.getSuccessRequests().get(0)); assertNull(globalRequestResult.getFailedRequests()); @@ -518,10 +549,13 @@ public void deleteSession() { assertNotNull(event); realm.deleteSession(event.getSessionId()); + assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.deleteSessionPath(event.getSessionId())); try { realm.deleteSession(event.getSessionId()); fail("Expected 404"); } catch (NotFoundException e) { + // Expected + assertAdminEvents.assertEmpty(); } tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "secret"); @@ -550,8 +584,6 @@ public void clientSessionStats() { } private void setupTestAppAndUser() { - realm.update(RealmBuilder.edit(realm.toRepresentation()).testEventListener().build()); - testingClient.testApp().clearAdminActions(); String redirectUri = oauth.getRedirectUri().replace("/master/", "/" + REALM_NAME + "/"); @@ -561,16 +593,22 @@ private void setupTestAppAndUser() { client.setAdminUrl(suiteContext.getAuthServerInfo().getContextRoot() + "/auth/realms/master/app/admin"); client.setRedirectUris(Collections.singletonList(redirectUri)); client.setSecret("secret"); - realm.clients().create(client); + Response resp = realm.clients().create(client); + String clientDbId = ApiUtil.getCreatedId(resp); + resp.close(); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientResourcePath(clientDbId), client); oauth.realm(REALM_NAME); oauth.redirectUri(redirectUri); - Response response = realm.users().create(UserBuilder.create().username("testuser").build()); + UserRepresentation userRep = UserBuilder.create().username("testuser").build(); + Response response = realm.users().create(userRep); String userId = ApiUtil.getCreatedId(response); response.close(); + assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.userResourcePath(userId), userRep); realm.users().get(userId).resetPassword(CredentialBuilder.create().password("password").build()); + assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResetPasswordPath(userId)); testingClient.testApp().clearAdminActions(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java new file mode 100644 index 000000000000..7ff548a653e2 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java @@ -0,0 +1,356 @@ +/* + * 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 java.net.URI; + +import javax.ws.rs.core.UriBuilder; + +import org.keycloak.admin.client.resource.ClientAttributeCertificateResource; +import org.keycloak.admin.client.resource.ClientInitialAccessResource; +import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.admin.client.resource.ClientTemplateResource; +import org.keycloak.admin.client.resource.ClientTemplatesResource; +import org.keycloak.admin.client.resource.ClientsResource; +import org.keycloak.admin.client.resource.GroupResource; +import org.keycloak.admin.client.resource.GroupsResource; +import org.keycloak.admin.client.resource.IdentityProviderResource; +import org.keycloak.admin.client.resource.IdentityProvidersResource; +import org.keycloak.admin.client.resource.ProtocolMappersResource; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.admin.client.resource.RoleByIdResource; +import org.keycloak.admin.client.resource.RoleMappingResource; +import org.keycloak.admin.client.resource.RoleResource; +import org.keycloak.admin.client.resource.RolesResource; +import org.keycloak.admin.client.resource.UserFederationProviderResource; +import org.keycloak.admin.client.resource.UserFederationProvidersResource; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.admin.client.resource.UsersResource; + +/** + * @author Marek Posolda + */ +public class AdminEventPaths { + + // REALM + + public static String deleteSessionPath(String userSessionId) { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "deleteSession").build(userSessionId); + return uri.toString(); + } + + + // CLIENT RESOURCE + + public static String clientResourcePath(String clientDbId) { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "clients").path(ClientsResource.class, "get").build(clientDbId); + return uri.toString(); + } + + public static String clientRolesResourcePath(String clientDbId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "roles").build(); + return uri.toString(); + } + + public static String clientRoleResourcePath(String clientDbId, String roleName) { + URI uri = UriBuilder.fromUri(clientRolesResourcePath(clientDbId)).path(RolesResource.class, "get").build(roleName); + return uri.toString(); + } + + public static String clientRoleResourceCompositesPath(String clientDbId, String roleName) { + URI uri = UriBuilder.fromUri(clientRoleResourcePath(clientDbId, roleName)) + .path(RoleResource.class, "getRoleComposites").build(); + return uri.toString(); + } + + public static String clientProtocolMappersPath(String clientDbId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)) + .path(ClientResource.class, "getProtocolMappers") + .build(); + return uri.toString(); + } + + + public static String clientProtocolMapperPath(String clientDbId, String protocolMapperId) { + URI uri = UriBuilder.fromUri(clientProtocolMappersPath(clientDbId)) + .path(ProtocolMappersResource.class, "getMapperById") + .build(protocolMapperId); + return uri.toString(); + } + + public static String clientPushRevocationPath(String clientDbId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "pushRevocation").build(); + return uri.toString(); + } + + public static String clientNodesPath(String clientDbId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "registerNode").build(); + return uri.toString(); + } + + public static String clientNodePath(String clientDbId, String node) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "unregisterNode").build(node); + return uri.toString(); + } + + public static String clientTestNodesAvailablePath(String clientDbId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "testNodesAvailable").build(); + return uri.toString(); + } + + public static String clientGenerateSecretPath(String clientDbId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "generateNewSecret").build(); + return uri.toString(); + } + + public static String clientRegenerateRegistrationAccessTokenPath(String clientDbId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "regenerateRegistrationAccessToken").build(); + return uri.toString(); + } + + public static String clientCertificateGenerateSecretPath(String clientDbId, String certificateAttribute) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)) + .path(ClientResource.class, "getCertficateResource") + .path(ClientAttributeCertificateResource.class, "generate") + .build(certificateAttribute); + return uri.toString(); + } + + + public static String clientScopeMappingsRealmLevelPath(String clientDbId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "getScopeMappings") + .path(RoleMappingResource.class, "realmLevel") + .build(); + return uri.toString(); + } + + public static String clientScopeMappingsClientLevelPath(String clientDbId, String clientOwningRoleId) { + URI uri = UriBuilder.fromUri(clientResourcePath(clientDbId)).path(ClientResource.class, "getScopeMappings") + .path(RoleMappingResource.class, "clientLevel") + .build(clientOwningRoleId); + return uri.toString(); + } + + + + // CLIENT TEMPLATES + + public static String clientTemplateResourcePath(String clientTemplateId) { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "clientTemplates").path(ClientTemplatesResource.class, "get").build(clientTemplateId); + return uri.toString(); + } + + public static String clientTemplateScopeMappingsRealmLevelPath(String clientTemplateDbId) { + URI uri = UriBuilder.fromUri(clientTemplateResourcePath(clientTemplateDbId)).path(ClientTemplateResource.class, "getScopeMappings") + .path(RoleMappingResource.class, "realmLevel") + .build(); + return uri.toString(); + } + + public static String clientTemplateScopeMappingsClientLevelPath(String clientTemplateDbId, String clientOwningRoleId) { + URI uri = UriBuilder.fromUri(clientTemplateResourcePath(clientTemplateDbId)).path(ClientTemplateResource.class, "getScopeMappings") + .path(RoleMappingResource.class, "clientLevel") + .build(clientOwningRoleId); + return uri.toString(); + } + + public static String clientTemplateProtocolMappersPath(String clientTemplateDbId) { + URI uri = UriBuilder.fromUri(clientTemplateResourcePath(clientTemplateDbId)) + .path(ClientTemplateResource.class, "getProtocolMappers") + .build(); + return uri.toString(); + } + + + public static String clientTemplateProtocolMapperPath(String clientTemplateDbId, String protocolMapperId) { + URI uri = UriBuilder.fromUri(clientTemplateProtocolMappersPath(clientTemplateDbId)) + .path(ProtocolMappersResource.class, "getMapperById") + .build(protocolMapperId); + return uri.toString(); + } + + // ROLES + + public static String rolesResourcePath() { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "roles").build(); + return uri.toString(); + } + + public static String roleResourcePath(String roleName) { + URI uri = UriBuilder.fromUri(rolesResourcePath()).path(RolesResource.class, "get").build(roleName); + return uri.toString(); + } + + public static String roleResourceCompositesPath(String roleName) { + URI uri = UriBuilder.fromUri(roleResourcePath(roleName)).path(RoleResource.class, "getRoleComposites").build(); + return uri.toString(); + } + + public static String rolesByIdResourcePath() { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "rolesById").build(); + return uri.toString(); + } + + public static String roleByIdResourcePath(String roleId) { + URI uri = UriBuilder.fromUri(rolesByIdResourcePath()).path(RoleByIdResource.class, "getRole").build(roleId); + return uri.toString(); + } + + public static String roleByIdResourceCompositesPath(String roleId) { + URI uri = UriBuilder.fromUri(rolesByIdResourcePath()).path(RoleByIdResource.class, "getRoleComposites").build(roleId); + return uri.toString(); + } + + // USERS + + public static String userResourcePath(String userId) { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "users").path(UsersResource.class, "get").build(userId); + return uri.toString(); + } + + public static String userResetPasswordPath(String userId) { + URI uri = UriBuilder.fromUri(userResourcePath(userId)).path(UserResource.class, "resetPassword").build(userId); + return uri.toString(); + } + + public static String userRealmRoleMappingsPath(String userId) { + URI uri = UriBuilder.fromUri(userResourcePath(userId)) + .path(UserResource.class, "roles") + .path(RoleMappingResource.class, "realmLevel").build(); + return uri.toString(); + } + + public static String userClientRoleMappingsPath(String userId, String clientDbId) { + URI uri = UriBuilder.fromUri(userResourcePath(userId)) + .path(UserResource.class, "roles") + .path(RoleMappingResource.class, "clientLevel").build(clientDbId); + return uri.toString(); + } + + public static String userFederatedIdentityLink(String userId, String idpAlias) { + URI uri = UriBuilder.fromUri(userResourcePath(userId)) + .path(UserResource.class, "addFederatedIdentity") + .build(idpAlias); + return uri.toString(); + } + + public static String userGroupPath(String userId, String groupId) { + URI uri = UriBuilder.fromUri(userResourcePath(userId)) + .path(UserResource.class, "joinGroup") + .build(groupId); + return uri.toString(); + } + + // IDENTITY PROVIDERS + + public static String identityProvidersPath() { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "identityProviders").build(); + return uri.toString(); + } + + public static String identityProviderCreatePath() { + URI uri = UriBuilder.fromUri(identityProvidersPath()).path(IdentityProvidersResource.class, "create").build(); + return uri.toString(); + } + + public static String identityProviderPath(String idpAlias) { + URI uri = UriBuilder.fromUri(identityProvidersPath()).path(IdentityProvidersResource.class, "get").build(idpAlias); + return uri.toString(); + } + + public static String identityProviderMapperPath(String idpAlias, String idpMapperId) { + URI uri = UriBuilder.fromUri(identityProviderPath(idpAlias)).path(IdentityProviderResource.class, "getMapperById").build(idpMapperId); + return uri.toString(); + } + + // USER FEDERATION PROVIDERS AND MAPPERS + + public static String userFederationsResourcePath() { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "userFederation").build(); + return uri.toString(); + } + + public static String userFederationCreateResourcePath() { + URI uri = UriBuilder.fromUri(userFederationsResourcePath()).path(UserFederationProvidersResource.class, "create").build(); + return uri.toString(); + } + + public static String userFederationResourcePath(String userFederationId) { + URI uri = UriBuilder.fromUri(userFederationsResourcePath()).path(UserFederationProvidersResource.class, "get").build(userFederationId); + return uri.toString(); + } + + public static String userFederationMapperResourcePath(String userFederationId, String userFederationMapperId) { + URI uri = UriBuilder.fromUri(userFederationResourcePath(userFederationId)) + .path(UserFederationProviderResource.class, "getMapperById").build(userFederationMapperId); + return uri.toString(); + } + + // CLIENT INITIAL ACCESS + + public static String clientInitialAccessPath(String clientInitialAccessId) { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "clientInitialAccess") + .path(ClientInitialAccessResource.class, "delete") + .build(clientInitialAccessId); + return uri.toString(); + } + + // GROUPS + + public static String groupsPath() { + URI uri = UriBuilder.fromUri("").path(RealmResource.class, "groups") + .build(); + return uri.toString(); + } + + public static String groupPath(String groupId) { + URI uri = UriBuilder.fromUri(groupsPath()).path(GroupsResource.class, "group") + .build(groupId); + return uri.toString(); + } + + public static String groupRolesPath(String groupId) { + URI uri = UriBuilder.fromUri(groupPath(groupId)) + .path(GroupResource.class, "roles") + .build(); + return uri.toString(); + } + + public static String groupRolesRealmRolesPath(String groupId) { + URI uri = UriBuilder.fromUri(groupRolesPath(groupId)) + .path(RoleMappingResource.class, "realmLevel") + .build(); + return uri.toString(); + } + + public static String groupRolesClientRolesPath(String groupId, String clientDbId) { + URI uri = UriBuilder.fromUri(groupRolesPath(groupId)) + .path(RoleMappingResource.class, "clientLevel") + .build(clientDbId); + return uri.toString(); + } + + public static String groupSubgroupsPath(String groupId) { + URI uri = UriBuilder.fromUri(groupPath(groupId)) + .path(GroupResource.class, "subGroup") + .build(); + return uri.toString(); + } + + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AssertAdminEvents.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AssertAdminEvents.java new file mode 100644 index 000000000000..0383a45ea027 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AssertAdminEvents.java @@ -0,0 +1,245 @@ +/* + * 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 java.io.IOException; +import java.lang.reflect.Method; + +import javax.ws.rs.core.Response; + +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.junit.rules.TestRule; +import org.junit.runners.model.Statement; +import org.keycloak.common.util.ObjectUtil; +import org.keycloak.common.util.reflections.Reflections; +import org.keycloak.events.admin.OperationType; +import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; +import org.keycloak.representations.AccessToken; +import org.keycloak.representations.idm.AdminEventRepresentation; +import org.keycloak.representations.idm.AuthDetailsRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.Assert; +import org.keycloak.util.JsonSerialization; + +/** + * @author Marek Posolda + */ +public class AssertAdminEvents implements TestRule { + + private AbstractKeycloakTest context; + + public AssertAdminEvents(AbstractKeycloakTest ctx) { + context = ctx; + } + + @Override + public Statement apply(final Statement base, org.junit.runner.Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + // TODO: Ideally clear the queue just before testClass rather then before each method + context.getTestingClient().testing().clearAdminEventQueue(); + base.evaluate(); + // TODO Test should fail if there are leftover events + } + }; + } + + public AdminEventRepresentation poll() { + AdminEventRepresentation event = fetchNextEvent(); + Assert.assertNotNull("Admin event expected", event); + + return event; + } + + public void assertEmpty() { + AdminEventRepresentation event = fetchNextEvent(); + Assert.assertNull("Empty admin event queue expected, but there is " + event, event); + } + + // Clears both "classic" and admin events for now + public void clear() { + Response res = context.getTestingClient().testing().clearAdminEventQueue(); + try { + Assert.assertEquals("clear-admin-event-queue success", res.getStatus(), 200); + } finally { + res.close(); + } + } + + private AdminEventRepresentation fetchNextEvent() { + return context.getTestingClient().testing().pollAdminEvent(); + } + + public ExpectedAdminEvent expect() { + return new ExpectedAdminEvent(); + } + + + + public AdminEventRepresentation assertEvent(String realmId, OperationType operationType, String resourcePath) { + return assertEvent(realmId, operationType, resourcePath, null); + } + + public AdminEventRepresentation assertEvent(String realmId, OperationType operationType, Matcher resourcePath) { + return assertEvent(realmId, operationType, resourcePath, null); + } + + public AdminEventRepresentation assertEvent(String realmId, OperationType operationType, String resourcePath, Object representation) { + return assertEvent(realmId, operationType, Matchers.equalTo(resourcePath), representation); + } + + public AdminEventRepresentation assertEvent(String realmId, OperationType operationType, Matcher resourcePath, Object representation) { + return expect().realmId(realmId) + .operationType(operationType) + .resourcePath(resourcePath) + .representation(representation) + .assertEvent(); + } + + + + public class ExpectedAdminEvent { + + private AdminEventRepresentation expected = new AdminEventRepresentation(); + private Matcher resourcePath; + private Object expectedRep; + + public ExpectedAdminEvent realmId(String realmId) { + expected.setRealmId(realmId); + return this; + } + + public ExpectedAdminEvent realm(RealmRepresentation realm) { + return realmId(realm.getId()); + } + + public ExpectedAdminEvent operationType(OperationType operationType) { + expected.setOperationType(operationType.toString()); + updateOperationTypeIfError(); + return this; + } + + public ExpectedAdminEvent resourcePath(String resourcePath) { + return resourcePath(Matchers.equalTo(resourcePath)); + } + + public ExpectedAdminEvent resourcePath(Matcher resourcePath) { + this.resourcePath = resourcePath; + return this; + } + + public ExpectedAdminEvent error(String error) { + expected.setError(error); + updateOperationTypeIfError(); + return this; + } + + private void updateOperationTypeIfError() { + if (expected.getError() != null && expected.getOperationType() != null) { + expected.setOperationType(expected.getOperationType() + "_ERROR"); + } + } + + public ExpectedAdminEvent authDetails(String realmId, String clientId, String userId) { + AuthDetailsRepresentation authDetails = new AuthDetailsRepresentation(); + authDetails.setRealmId(realmId); + authDetails.setClientId(clientId); + authDetails.setUserId(userId); + expected.setAuthDetails(authDetails); + return this; + } + + public ExpectedAdminEvent representation(Object representation) { + this.expectedRep = representation; + return this; + } + + public AdminEventRepresentation assertEvent() { + return assertEvent(poll()); + } + + public AdminEventRepresentation assertEvent(AdminEventRepresentation actual) { + Assert.assertEquals(expected.getRealmId(), actual.getRealmId()); + Assert.assertEquals(expected.getOperationType(), actual.getOperationType()); + Assert.assertThat(actual.getResourcePath(), resourcePath); + + Assert.assertTrue(ObjectUtil.isEqualOrBothNull(expected.getError(), actual.getError())); + + // AuthDetails + AuthDetailsRepresentation expectedAuth = expected.getAuthDetails(); + if (expectedAuth == null) { + expectedAuth = defaultAuthDetails(); + } + + AuthDetailsRepresentation actualAuth = actual.getAuthDetails(); + Assert.assertEquals(expectedAuth.getRealmId(), actualAuth.getRealmId()); + Assert.assertEquals(expectedAuth.getUserId(), actualAuth.getUserId()); + if (expectedAuth.getClientId() != null) { + Assert.assertEquals(expectedAuth.getClientId(), actualAuth.getClientId()); + } + + // Representation - compare the non-null fields of "expected" representation with the actual representation + if (expectedRep != null) { + if (actual.getRepresentation() == null) { + Assert.fail("Expected representation " + expectedRep + " but no representation was available on actual event"); + } else { + try { + Object actualRep = JsonSerialization.readValue(actual.getRepresentation(), expectedRep.getClass()); + + for (Method method : Reflections.getAllDeclaredMethods(expectedRep.getClass())) { + if (method.getName().startsWith("get") || method.getName().startsWith("is")) { + Object expectedValue = Reflections.invokeMethod(method, expectedRep); + if (expectedValue != null) { + Object actualValue = Reflections.invokeMethod(method, actualRep); + Assert.assertEquals("Property " + method.getName() + " of representation not equal.", expectedValue, actualValue); + } + } + } + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + } + + return actual; + } + + } + + private AuthDetailsRepresentation defaultAuthDetails() { + String accessTokenString = context.getAdminClient().tokenManager().getAccessTokenString(); + try { + JWSInput input = new JWSInput(accessTokenString); + AccessToken token = input.readJsonContent(AccessToken.class); + + AuthDetailsRepresentation authDetails = new AuthDetailsRepresentation(); + String realmId = token.getIssuer().substring(token.getIssuer().lastIndexOf('/') + 1); + authDetails.setRealmId(realmId); + authDetails.setUserId(token.getSubject()); + return authDetails; + } catch (JWSInputException jwe) { + throw new RuntimeException(jwe); + } + } + + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RealmBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RealmBuilder.java index 4576c2fecbdc..445cc14fedc5 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RealmBuilder.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RealmBuilder.java @@ -20,6 +20,7 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.events.EventsListenerProviderFactory; import sun.security.krb5.Realm; import java.util.HashMap; @@ -80,7 +81,18 @@ public RealmBuilder testEventListener() { rep.setEventsListeners(new LinkedList()); } - rep.getEventsListeners().add("event-queue"); + if (!rep.getEventsListeners().contains(EventsListenerProviderFactory.PROVIDER_ID)) { + rep.getEventsListeners().add(EventsListenerProviderFactory.PROVIDER_ID); + } + + return this; + } + + public RealmBuilder removeTestEventListener() { + if (rep.getEventsListeners() != null && rep.getEventsListeners().contains(EventsListenerProviderFactory.PROVIDER_ID)) { + rep.getEventsListeners().remove(EventsListenerProviderFactory.PROVIDER_ID); + } + return this; }