From 95724e36f32d3a656dc9ad96d434592a1c1052ad Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Mon, 25 Apr 2016 20:43:51 +0200 Subject: [PATCH] KEYCLOAK-2871 Extend coverage on RealmAdminResource --- .../admin/client/resource/RealmResource.java | 23 +++ .../rest/TestApplicationResourceProvider.java | 54 ++++++- ...estApplicationResourceProviderFactory.java | 12 +- .../rest/TestingResourceProvider.java | 12 ++ .../client/KeycloakTestingClient.java | 3 + .../resources/TestApplicationResource.java | 50 ++++++ .../client/resources/TestingResource.java | 7 + .../keycloak/testsuite/util/OAuthClient.java | 11 +- .../testsuite/admin/realm/RealmTest.java | 150 ++++++++++++++++++ 9 files changed, 317 insertions(+), 5 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java index 34ca64a436..b6db357cfc 100644 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java @@ -18,6 +18,7 @@ package org.keycloak.admin.client.resource; import org.jboss.resteasy.annotations.cache.NoCache; +import org.keycloak.representations.adapters.action.GlobalRequestResult; import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.EventRepresentation; @@ -170,4 +171,26 @@ public interface RealmResource { @QueryParam("bindDn") String bindDn, @QueryParam("bindCredential") String bindCredential, @QueryParam("useTruststoreSpi") String useTruststoreSpi); + @Path("clear-realm-cache") + @POST + void clearRealmCache(); + + @Path("clear-user-cache") + @POST + void clearUserCache(); + + @Path("push-revocation") + @POST + @Produces(MediaType.APPLICATION_JSON) + GlobalRequestResult pushRevocation(); + + @Path("logout-all") + @POST + @Produces(MediaType.APPLICATION_JSON) + GlobalRequestResult logoutAll(); + + @Path("sessions/{session}") + @DELETE + void deleteSession(@PathParam("session") String sessionId); + } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java index 52e5858db2..54149d930c 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java @@ -17,16 +17,26 @@ package org.keycloak.testsuite.rest; +import org.keycloak.jose.jws.JWSInput; +import org.keycloak.jose.jws.JWSInputException; import org.keycloak.models.KeycloakSession; +import org.keycloak.representations.adapters.action.LogoutAction; +import org.keycloak.representations.adapters.action.PushNotBeforeAction; import org.keycloak.services.resource.RealmResourceProvider; import org.keycloak.services.resources.RealmsResource; +import org.keycloak.testsuite.events.EventsListenerProvider; +import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; /** * @author Stian Thorgersen @@ -36,8 +46,50 @@ public class TestApplicationResourceProvider implements RealmResourceProvider { private KeycloakSession session; - public TestApplicationResourceProvider(KeycloakSession session) { + private final BlockingQueue adminLogoutActions; + private final BlockingQueue adminPushNotBeforeActions; + + public TestApplicationResourceProvider(KeycloakSession session, BlockingQueue adminLogoutActions, + BlockingQueue adminPushNotBeforeActions) { this.session = session; + this.adminLogoutActions = adminLogoutActions; + this.adminPushNotBeforeActions = adminPushNotBeforeActions; + } + + @POST + @Consumes(MediaType.TEXT_PLAIN) + @Path("/admin/k_logout") + public void adminLogout(String data) throws JWSInputException { + adminLogoutActions.add(new JWSInput(data).readJsonContent(LogoutAction.class)); + } + + @POST + @Consumes(MediaType.TEXT_PLAIN) + @Path("/admin/k_push_not_before") + public void adminPushNotBefore(String data) throws JWSInputException { + adminPushNotBeforeActions.add(new JWSInput(data).readJsonContent(PushNotBeforeAction.class)); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-admin-logout") + public LogoutAction getAdminLogoutAction() throws InterruptedException { + return adminLogoutActions.poll(10, TimeUnit.SECONDS); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-admin-not-before") + public PushNotBeforeAction getAdminPushNotBefore() throws InterruptedException { + return adminPushNotBeforeActions.poll(10, TimeUnit.SECONDS); + } + + @POST + @Path("/clear-admin-actions") + public Response clearAdminActions() { + adminLogoutActions.clear(); + adminPushNotBeforeActions.clear(); + return Response.noContent().build(); } @GET diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java index e795420b14..40d6e2d759 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java @@ -18,19 +18,29 @@ package org.keycloak.testsuite.rest; import org.keycloak.Config.Scope; +import org.keycloak.events.admin.AdminEvent; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.representations.adapters.action.AdminAction; +import org.keycloak.representations.adapters.action.LogoutAction; +import org.keycloak.representations.adapters.action.PushNotBeforeAction; import org.keycloak.services.resource.RealmResourceProvider; import org.keycloak.services.resource.RealmResourceProviderFactory; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; + /** * @author Stian Thorgersen */ public class TestApplicationResourceProviderFactory implements RealmResourceProviderFactory { + private BlockingQueue adminLogoutActions = new LinkedBlockingDeque<>(); + private BlockingQueue pushNotBeforeActions = new LinkedBlockingDeque<>(); + @Override public RealmResourceProvider create(KeycloakSession session) { - return new TestApplicationResourceProvider(session); + return new TestApplicationResourceProvider(session, adminLogoutActions, pushNotBeforeActions); } @Override 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 7b0bb29534..2730eb0e1a 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 @@ -17,7 +17,9 @@ package org.keycloak.testsuite.rest; +import org.infinispan.Cache; import org.keycloak.common.util.Time; +import org.keycloak.connections.infinispan.InfinispanConnectionProvider; import org.keycloak.events.Event; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -34,6 +36,7 @@ import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; @@ -130,6 +133,15 @@ public class TestingResourceProvider implements RealmResourceProvider { return Response.ok().build(); } + @GET + @Path("/cache/{cache}/{id}") + @Produces(MediaType.APPLICATION_JSON) + public boolean isCached(@PathParam("cache") String cacheName, @PathParam("id") String id) { + InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class); + Cache cache = provider.getCache(cacheName); + return cache.containsKey(id); + } + @Override public void close() { } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java index cbc639e380..46f7b23787 100755 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java @@ -20,6 +20,7 @@ package org.keycloak.testsuite.client; import org.jboss.resteasy.client.jaxrs.ResteasyClient; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; +import org.keycloak.testsuite.client.resources.TestApplicationResource; import org.keycloak.testsuite.client.resources.TestingResource; /** @@ -42,6 +43,8 @@ public class KeycloakTestingClient { return target.proxy(TestingResource.class); } + public TestApplicationResource testApp() { return target.proxy(TestApplicationResource.class); } + public void close() { client.close(); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java new file mode 100644 index 0000000000..8fb9b4c8b9 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.client.resources; + +import org.keycloak.representations.adapters.action.LogoutAction; +import org.keycloak.representations.adapters.action.PushNotBeforeAction; + +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +/** + * @author Stian Thorgersen + */ +@Path("/realms/master/app") +public interface TestApplicationResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-admin-logout") + LogoutAction getAdminLogoutAction(); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-admin-not-before") + PushNotBeforeAction getAdminPushNotBefore(); + + @POST + @Path("/clear-admin-actions") + Response clearAdminActions(); + +} 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 23337f0e91..310cffe298 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 @@ -24,6 +24,7 @@ import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; @@ -68,4 +69,10 @@ public interface TestingResource { @Path("/remove-expired") @Produces(MediaType.APPLICATION_JSON) Response removeExpired(@QueryParam("realm") final String realm); + + @GET + @Path("/cache/{cache}/{id}") + @Produces(MediaType.APPLICATION_JSON) + boolean isCached(@PathParam("cache") String cacheName, @PathParam("id") String id); + } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java index e294879d09..0ba382449b 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java @@ -101,9 +101,14 @@ public class OAuthClient { public AuthorizationCodeResponse doLogin(String username, String password) { openLoginForm(); String src = driver.getPageSource(); - driver.findElement(By.id("username")).sendKeys(username); - driver.findElement(By.id("password")).sendKeys(password); - driver.findElement(By.name("login")).click(); + try { + driver.findElement(By.id("username")).sendKeys(username); + driver.findElement(By.id("password")).sendKeys(password); + driver.findElement(By.name("login")).click(); + } catch (Throwable t) { + System.err.println(src); + throw t; + } return new AuthorizationCodeResponse(this); } 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 84bb1b5a80..599c1ccb3a 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,30 +18,46 @@ package org.keycloak.testsuite.admin.realm; import org.apache.commons.io.IOUtils; +import org.junit.Rule; import org.junit.Test; +import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.ServerInfoResource; import org.keycloak.common.util.StreamUtil; +import org.keycloak.common.util.Time; import org.keycloak.models.Constants; +import org.keycloak.representations.adapters.action.GlobalRequestResult; +import org.keycloak.representations.adapters.action.PushNotBeforeAction; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.admin.AbstractAdminTest; +import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; import org.keycloak.testsuite.auth.page.AuthRealm; +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; import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; import java.io.IOException; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.HashSet; import java.util.Arrays; +import java.util.Map; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -52,6 +68,9 @@ import static org.junit.Assert.fail; */ public class RealmTest extends AbstractAdminTest { + @Rule + public AssertEvents events = new AssertEvents(this); + public static final String PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y="; public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"; public static final String CERTIFICATE = "MIICsTCCAZkCBgFTLB5bhDANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFhZG1pbi1jbGllbnQtdGVzdDAeFw0xNjAyMjkwODIwMDBaFw0yNjAyMjgwODIxNDBaMBwxGjAYBgNVBAMMEWFkbWluLWNsaWVudC10ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAquzJtpAlpTFnJzILjTOHW+SOWav1eIsCtlAqiFTvBskbod6b4BtVaR3FVrQm8rFiwDOIEWT3IG3ZIz0LKYxnqvuffyLHGHjiroqrR63kY9Wa9B790lSEWVaGeNOMnKleqKu5QUNfL3wVebUh/C/QfxZ29R1EIbxNe2ThN8yuIca8Ltn43D5VlyatptojffxpCYiYqAmIwQDaq1um2cQ+4rPBLxC5jM9UBvYOMUP4u0caNSaPI1o9lHVKgTtWcdQzUeMmAGsnLV26XGhA/OwRduUxksumR1kh/KSqowasjgSrpVqtF/uo5TY57s7drD+zKG58cdHLreclB9AQNvNwZwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBh4iwg8GnadeQP52pV5vKJ4Z8A1R2aYCzoW7Lc3FI/pXWX9Af5dKILX5O2j/daamPS+WtDWxIuwvZC5drrkvJn/r8e4KstnXQzPQggIJbI9v3wfIX3VlFvwvZVGiuE5PSLSWb0L57PEojZVpIU5bLchq4yRSD2zK4dWX8Y6I/D40a74KDvPOlEL8405/T1iW7ytKT9awNJW04N91owoI+kdUL+DMnnGzIxDAoYAeZI/1vcwoaH24zyTLGItkzpKxqLOdB05cnxn5jCWY2Hyd1zqtRkadhgZaqu4lcDHAHEMDp6dEjLZW8ym8bnlto+MD2y//CsyPCzyCLlA726vrli"; @@ -425,4 +444,135 @@ public class RealmTest extends AbstractAdminTest { assertEquals(certificate, rep.getCertificate()); } + @Test + public void clearRealmCache() { + RealmRepresentation realmRep = realm.toRepresentation(); + assertTrue(testingClient.testing().isCached("realms", realmRep.getId())); + adminClient.realm("master").clearRealmCache(); + assertFalse(testingClient.testing().isCached("realms", realmRep.getId())); + } + + @Test + public void clearUserCache() { + UserRepresentation user = new UserRepresentation(); + user.setUsername("clearcacheuser"); + Response response = realm.users().create(user); + String userId = ApiUtil.getCreatedId(response); + response.close(); + + realm.users().get(userId).toRepresentation(); + + assertTrue(testingClient.testing().isCached("users", userId)); + adminClient.realm("master").clearUserCache(); + assertFalse(testingClient.testing().isCached("users", userId)); + } + + @Test + public void pushNotBefore() { + setupTestAppAndUser(); + + int time = Time.currentTime() - 60; + + RealmRepresentation rep = realm.toRepresentation(); + rep.setNotBefore(time); + realm.update(rep); + + GlobalRequestResult globalRequestResult = realm.pushRevocation(); + assertEquals(1, globalRequestResult.getSuccessRequests().size()); + assertEquals("http://localhost:8180/auth/realms/master/app/admin", globalRequestResult.getSuccessRequests().get(0)); + assertNull(globalRequestResult.getFailedRequests()); + + PushNotBeforeAction adminPushNotBefore = testingClient.testApp().getAdminPushNotBefore(); + assertEquals(time, adminPushNotBefore.getNotBefore()); + } + + @Test + public void logoutAll() { + setupTestAppAndUser(); + + Response response = realm.users().create(UserBuilder.create().username("user").build()); + String userId = ApiUtil.getCreatedId(response); + response.close(); + + realm.users().get(userId).resetPassword(CredentialBuilder.create().password("password").build()); + + oauth.doLogin("user", "password"); + + GlobalRequestResult globalRequestResult = realm.logoutAll(); + assertEquals(1, globalRequestResult.getSuccessRequests().size()); + assertEquals("http://localhost:8180/auth/realms/master/app/admin", globalRequestResult.getSuccessRequests().get(0)); + assertNull(globalRequestResult.getFailedRequests()); + + assertNotNull(testingClient.testApp().getAdminLogoutAction()); + } + + @Test + public void deleteSession() { + setupTestAppAndUser(); + + oauth.doLogin("testuser", "password"); + AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "secret"); + assertEquals(200, tokenResponse.getStatusCode()); + + EventRepresentation event = events.poll(); + assertNotNull(event); + + realm.deleteSession(event.getSessionId()); + try { + realm.deleteSession(event.getSessionId()); + fail("Expected 404"); + } catch (NotFoundException e) { + } + + tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "secret"); + assertEquals(400, tokenResponse.getStatusCode()); + assertEquals("Session not active", tokenResponse.getErrorDescription()); + } + + @Test + public void clientSessionStats() { + setupTestAppAndUser(); + + List> sessionStats = realm.getClientSessionStats(); + assertTrue(sessionStats.isEmpty()); + + System.out.println(sessionStats.size()); + + oauth.doLogin("testuser", "password"); + AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "secret"); + assertEquals(200, tokenResponse.getStatusCode()); + + sessionStats = realm.getClientSessionStats(); + + assertEquals(1, sessionStats.size()); + assertEquals("test-app", sessionStats.get(0).get("clientId")); + assertEquals("1", sessionStats.get(0).get("active")); + } + + private void setupTestAppAndUser() { + realm.update(RealmBuilder.edit(realm.toRepresentation()).testEventListener().build()); + + 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"); + realm.clients().create(client); + + oauth.realm(REALM_NAME); + oauth.redirectUri(redirectUri); + + Response response = realm.users().create(UserBuilder.create().username("testuser").build()); + String userId = ApiUtil.getCreatedId(response); + response.close(); + + realm.users().get(userId).resetPassword(CredentialBuilder.create().password("password").build()); + + testingClient.testApp().clearAdminActions(); + } + }