keycloak-uncached
Changes
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java 272(+272 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/DBLockTest.java 107(+64 -43)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ImportTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java 192(+192 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java 411(+411 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java 420(+420 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserModelTest.java 615(+615 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java 611(+611 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java 636(+636 -0)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java 158(+0 -158)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java 234(+0 -234)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java 122(+0 -122)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java 286(+0 -286)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java 296(+0 -296)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserModelTest.java 442(+0 -442)
Details
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java
new file mode 100755
index 0000000..ebe9285
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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.model;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.logging.Logger;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.models.*;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ConcurrentTransactionsTest extends AbstractTestRealmKeycloakTest {
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, UserSessionProviderOfflineTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+ private static final Logger logger = Logger.getLogger(ConcurrentTransactionsTest.class);
+
+ @Test
+ @ModelTest
+ public void persistClient(KeycloakSession session) {
+
+ final ClientModel[] client = {null};
+ AtomicReference<String> clientDBIdAtomic = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionSetup) -> {
+
+ RealmModel realm = sessionSetup.realms().getRealm("test");
+ sessionSetup.users().addUser(realm, "user1").setEmail("user1@localhost");
+ sessionSetup.users().addUser(realm, "user2").setEmail("user2@localhost");
+
+ realm = sessionSetup.realms().createRealm("original");
+
+ client[0] = sessionSetup.realms().addClient(realm, "client");
+ client[0].setSecret("old");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session1) -> {
+ String clientDBId = client[0].getId();
+ clientDBIdAtomic.set(clientDBId);
+
+ final KeycloakSessionFactory sessionFactory = session1.getKeycloakSessionFactory();
+
+ final CountDownLatch transactionsCounter = new CountDownLatch(2);
+ final CountDownLatch readLatch = new CountDownLatch(1);
+ final CountDownLatch updateLatch = new CountDownLatch(1);
+
+ Thread thread1 = new Thread(() -> {
+ KeycloakModelUtils.runJobInTransaction(sessionFactory, session11 -> {
+ try {
+ KeycloakSession currentSession = session11;
+ // Wait until transaction in both threads started
+ transactionsCounter.countDown();
+ logger.info("transaction1 started");
+ transactionsCounter.await();
+
+ // Read client
+ RealmModel realm1 = currentSession.realms().getRealmByName("original");
+ ClientModel client1 = currentSession.realms().getClientByClientId("client", realm1);
+ logger.info("transaction1: Read client finished");
+ readLatch.countDown();
+
+ // Wait until thread2 updates client and commits
+ updateLatch.await();
+ logger.info("transaction1: Going to read client again");
+
+ client1 = currentSession.realms().getClientByClientId("client", realm1);
+ logger.info("transaction1: secret: " + client1.getSecret());
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ });
+
+ Thread thread2 = new Thread(() -> {
+ KeycloakModelUtils.runJobInTransaction(sessionFactory, session22 -> {
+ try {
+ KeycloakSession currentSession = session22;
+ // Wait until transaction in both threads started
+ transactionsCounter.countDown();
+ logger.info("transaction2 started");
+ transactionsCounter.await();
+
+ readLatch.await();
+ logger.info("transaction2: Going to update client secret");
+
+ RealmModel realm12 = currentSession.realms().getRealmByName("original");
+ ClientModel client12 = currentSession.realms().getClientByClientId("client", realm12);
+ client12.setSecret("new");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ logger.info("transaction2: commited");
+ updateLatch.countDown();
+ });
+
+ thread1.start();
+ thread2.start();
+
+ try {
+ thread1.join();
+ thread2.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ logger.info("after thread join");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session2) -> {
+ RealmModel realm = session2.realms().getRealmByName("original");
+ String clientDBId = clientDBIdAtomic.get();
+
+ ClientModel clientFromCache = session2.realms().getClientById(clientDBId, realm);
+ ClientModel clientFromDB = session2.getProvider(RealmProvider.class).getClientById(clientDBId, realm);
+
+ logger.info("SECRET FROM DB : " + clientFromDB.getSecret());
+ logger.info("SECRET FROM CACHE : " + clientFromCache.getSecret());
+
+ Assert.assertEquals("new", clientFromDB.getSecret());
+ Assert.assertEquals("new", clientFromCache.getSecret());
+
+ session2.sessions().removeUserSessions(realm);
+
+ tearDownRealm(session2, "user1", "user2");
+ });
+ }
+
+
+ // KEYCLOAK-3296 , KEYCLOAK-3494
+ @Test
+ @ModelTest
+ public void removeUserAttribute(KeycloakSession session) throws Exception {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionSet) -> {
+
+ RealmModel realm = sessionSet.realms().createRealm("original");
+
+ UserModel john = sessionSet.users().addUser(realm, "john");
+ john.setSingleAttribute("foo", "val1");
+
+ UserModel john2 = sessionSet.users().addUser(realm, "john2");
+ john2.setAttribute("foo", Arrays.asList("val1", "val2"));
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session2) -> {
+
+ final KeycloakSessionFactory sessionFactory = session2.getKeycloakSessionFactory();
+
+ AtomicReference<Exception> reference = new AtomicReference<>();
+
+ final CountDownLatch readAttrLatch = new CountDownLatch(2);
+
+ Runnable runnable = () -> {
+ try {
+ KeycloakModelUtils.runJobInTransaction(sessionFactory, session1 -> {
+ try {
+ // Read user attribute
+ RealmModel realm = session1.realms().getRealmByName("original");
+ UserModel john = session1.users().getUserByUsername("john", realm);
+ String attrVal = john.getFirstAttribute("foo");
+
+ UserModel john2 = session1.users().getUserByUsername("john2", realm);
+ String attrVal2 = john2.getFirstAttribute("foo");
+
+ // Wait until it's read in both threads
+ readAttrLatch.countDown();
+ readAttrLatch.await();
+
+ // KEYCLOAK-3296 : Remove user attribute in both threads
+ john.removeAttribute("foo");
+
+ // KEYCLOAK-3494 : Set single attribute in both threads
+ john2.setSingleAttribute("foo", "bar");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (Exception e) {
+ reference.set(e);
+ throw new RuntimeException(e);
+ } finally {
+ readAttrLatch.countDown();
+ }
+ };
+
+ Thread thread1 = new Thread(runnable);
+ Thread thread2 = new Thread(runnable);
+
+ thread1.start();
+ thread2.start();
+
+ try {
+ thread1.join();
+ thread2.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ logger.info("removeUserAttribute: after thread join");
+ if (reference.get() != null) {
+ Assert.fail("Exception happened in some of threads. Details: " + reference.get().getMessage());
+ }
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionTearDown) -> {
+ tearDownRealm(sessionTearDown, "john", "john2");
+ });
+ }
+
+ private void tearDownRealm(KeycloakSession session, String user1, String user2) {
+ KeycloakSession currentSession = session;
+
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel realmUser1 = currentSession.users().getUserByUsername(user1, realm);
+ UserModel realmUser2 = currentSession.users().getUserByUsername(user2, realm);
+
+ UserManager um = new UserManager(currentSession);
+ if (realmUser1 != null) {
+ um.removeUser(realm, realmUser1);
+ }
+ if (realmUser2 != null) {
+ um.removeUser(realm, realmUser2);
+ }
+
+ assert (currentSession.realms().removeRealm(realm.getId()));
+ }
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index 1e84d27..85da96c 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -57,7 +57,7 @@ public class ImportTest extends AbstractTestRealmKeycloakTest {
@Deployment
@TargetsContainer(AUTH_SERVER_CURRENT)
public static WebArchive deploy() {
- return RunOnServerDeployment.create(UserResource.class, AbstractLDAPTest.class)
+ return RunOnServerDeployment.create(UserResource.class, ImportTest.class)
.addPackages(true,
"org.keycloak.testsuite",
"org.keycloak.testsuite.model");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java
new file mode 100755
index 0000000..bb0b710
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/MultipleRealmsTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.model;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.models.*;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MultipleRealmsTest extends AbstractTestRealmKeycloakTest {
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, MultipleRealmsTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+ public static void createObjects(KeycloakSession session, RealmModel realm) {
+ ClientModel app1 = realm.addClient("app1");
+ realm.addClient("app2");
+
+ session.users().addUser(realm, "user1");
+ session.users().addUser(realm, "user2");
+
+ realm.addRole("role1");
+ realm.addRole("role2");
+
+ app1.addRole("app1Role1");
+ app1.addScopeMapping(realm.getRole("role1"));
+
+ realm.addClient("cl1");
+ }
+
+ @Test
+ @ModelTest
+ public void testUsers(KeycloakSession session) {
+
+ AtomicReference<UserModel> r1user1Atomic = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionTestUser1) -> {
+ KeycloakSession currentSession = sessionTestUser1;
+
+ RealmModel realm1 = currentSession.realms().createRealm("id1", "realm1");
+ RealmModel realm2 = currentSession.realms().createRealm("id2", "realm2");
+
+ createObjects(currentSession, realm1);
+ createObjects(currentSession, realm2);
+
+ UserModel r1user1 = currentSession.users().getUserByUsername("user1", realm1);
+ UserModel r2user1 = currentSession.users().getUserByUsername("user1", realm2);
+
+ r1user1Atomic.set(r1user1);
+
+ Assert.assertEquals(r1user1.getUsername(), r2user1.getUsername());
+ Assert.assertNotEquals(r1user1.getId(), r2user1.getId());
+
+ // Test password
+ currentSession.userCredentialManager().updateCredential(realm1, r1user1, UserCredentialModel.password("pass1"));
+ currentSession.userCredentialManager().updateCredential(realm2, r2user1, UserCredentialModel.password("pass2"));
+
+ Assert.assertTrue(currentSession.userCredentialManager().isValid(realm1, r1user1, UserCredentialModel.password("pass1")));
+ Assert.assertFalse(currentSession.userCredentialManager().isValid(realm1, r1user1, UserCredentialModel.password("pass2")));
+ Assert.assertFalse(currentSession.userCredentialManager().isValid(realm2, r2user1, UserCredentialModel.password("pass1")));
+ Assert.assertTrue(currentSession.userCredentialManager().isValid(realm2, r2user1, UserCredentialModel.password("pass2")));
+
+ // Test searching
+ Assert.assertEquals(2, currentSession.users().searchForUser("user", realm1).size());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionTestUser2) -> {
+ KeycloakSession currentSession = sessionTestUser2;
+
+ RealmModel realm1 = currentSession.realms().getRealm("id1");
+ RealmModel realm2 = currentSession.realms().getRealm("id2");
+
+ UserModel r1user1 = r1user1Atomic.get();
+
+ currentSession.users().removeUser(realm1, r1user1);
+ UserModel user2 = currentSession.users().getUserByUsername("user2", realm1);
+ currentSession.users().removeUser(realm1, user2);
+ Assert.assertEquals(0, currentSession.users().searchForUser("user", realm1).size());
+ Assert.assertEquals(2, currentSession.users().searchForUser("user", realm2).size());
+
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm1);
+ UserModel user1a = currentSession.users().getUserByUsername("user1", realm2);
+
+ UserManager um = new UserManager(session);
+ if (user1 != null) {
+ um.removeUser(realm1, user1);
+ }
+ if (user1a != null) {
+ um.removeUser(realm2, user1a);
+ }
+
+ currentSession.realms().removeRealm("id1");
+ currentSession.realms().removeRealm("id2");
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testGetById(KeycloakSession session) {
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionById) -> {
+ KeycloakSession currentSession = sessionById;
+
+ RealmModel realm1 = currentSession.realms().createRealm("id1", "realm1");
+ RealmModel realm2 = currentSession.realms().createRealm("id2", "realm2");
+
+ createObjects(currentSession, realm1);
+ createObjects(currentSession, realm2);
+
+ Assert.assertEquals(realm1, currentSession.realms().getRealm("id1"));
+ Assert.assertEquals(realm1, currentSession.realms().getRealmByName("realm1"));
+ Assert.assertEquals(realm2, currentSession.realms().getRealm("id2"));
+ Assert.assertEquals(realm2, currentSession.realms().getRealmByName("realm2"));
+
+ ClientModel r1app1 = realm1.getClientByClientId("app1");
+
+ Assert.assertNotNull(realm1.getClientByClientId("app2"));
+ Assert.assertNotNull(realm2.getClientByClientId("app1"));
+ Assert.assertNotNull(realm2.getClientByClientId("app2"));
+
+ Assert.assertEquals(r1app1, realm1.getClientById(r1app1.getId()));
+ Assert.assertNull(realm2.getClientById(r1app1.getId()));
+
+ ClientModel r2cl1 = realm2.getClientByClientId("cl1");
+ Assert.assertEquals(r2cl1.getId(), realm2.getClientById(r2cl1.getId()).getId());
+ Assert.assertNull(realm1.getClientByClientId(r2cl1.getId()));
+
+ RoleModel r1App1Role = r1app1.getRole("app1Role1");
+ Assert.assertEquals(r1App1Role, realm1.getRoleById(r1App1Role.getId()));
+ Assert.assertNull(realm2.getRoleById(r1App1Role.getId()));
+
+ RoleModel r2Role1 = realm2.getRole("role2");
+ Assert.assertNull(realm1.getRoleById(r2Role1.getId()));
+ Assert.assertEquals(r2Role1, realm2.getRoleById(r2Role1.getId()));
+
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm1);
+ UserModel user1a = currentSession.users().getUserByUsername("user1", realm2);
+
+ UserManager um = new UserManager(currentSession);
+ if (user1 != null) {
+ um.removeUser(realm1, user1);
+ }
+ if (user1a != null) {
+ um.removeUser(realm2, user1a);
+ }
+
+ currentSession.realms().removeRealm("id1");
+ currentSession.realms().removeRealm("id2");
+ });
+ }
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java
new file mode 100644
index 0000000..75255e1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java
@@ -0,0 +1,411 @@
+/*
+ * 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.model;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.*;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.storage.client.ClientStorageProviderModel;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.federation.HardcodedClientStorageProviderFactory;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UserConsentModelTest extends AbstractTestRealmKeycloakTest {
+
+ private static ComponentModel clientStorageComponent;
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, UserConsentModelTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+ @Before
+ public void before() {
+ testingClient.server().run(session -> {
+ setupEnv(session);
+ });
+ }
+
+ @After
+ public void after() {
+ testingClient.server().run(session -> {
+
+ RealmManager realmManager = new RealmManager(session);
+ RealmModel realm = realmManager.getRealmByName("original");
+
+ if (realm != null) {
+
+ session.sessions().removeUserSessions(realm);
+ UserModel user = session.users().getUserByUsername("user", realm);
+ UserModel user1 = session.users().getUserByUsername("user1", realm);
+ UserModel user2 = session.users().getUserByUsername("user2", realm);
+ UserModel user3 = session.users().getUserByUsername("user3", realm);
+
+ UserManager um = new UserManager(session);
+ if (user != null) {
+ um.removeUser(realm, user);
+ }
+ if (user1 != null) {
+ um.removeUser(realm, user1);
+ }
+ if (user2 != null) {
+ um.removeUser(realm, user2);
+ }
+ if (user3 != null) {
+ um.removeUser(realm, user3);
+ }
+ realmManager.removeRealm(realm);
+ }
+ });
+ }
+
+ public static void setupEnv(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionEnv) -> {
+ KeycloakSession currentSession = sessionEnv;
+
+ RealmManager realmManager = new RealmManager(currentSession);
+ RealmModel realm = realmManager.createRealm("original");
+
+ ClientModel fooClient = realm.addClient("foo-client");
+ ClientModel barClient = realm.addClient("bar-client");
+
+ ClientScopeModel fooScope = realm.addClientScope("foo");
+ fooScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+
+ ClientScopeModel barScope = realm.addClientScope("bar");
+ fooScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+
+ UserModel john = currentSession.users().addUser(realm, "john");
+ UserModel mary = currentSession.users().addUser(realm, "mary");
+
+ UserConsentModel johnFooGrant = new UserConsentModel(fooClient);
+ johnFooGrant.addGrantedClientScope(fooScope);
+ realmManager.getSession().users().addConsent(realm, john.getId(), johnFooGrant);
+
+ UserConsentModel johnBarGrant = new UserConsentModel(barClient);
+ johnBarGrant.addGrantedClientScope(barScope);
+
+ // Update should fail as grant doesn't yet exists
+ try {
+ realmManager.getSession().users().updateConsent(realm, john.getId(), johnBarGrant);
+ Assert.fail("Not expected to end here");
+ } catch (ModelException expected) {
+ }
+
+ realmManager.getSession().users().addConsent(realm, john.getId(), johnBarGrant);
+
+ UserConsentModel maryFooGrant = new UserConsentModel(fooClient);
+ maryFooGrant.addGrantedClientScope(fooScope);
+ realmManager.getSession().users().addConsent(realm, mary.getId(), maryFooGrant);
+
+ ClientStorageProviderModel clientStorage = new ClientStorageProviderModel();
+ clientStorage.setProviderId(HardcodedClientStorageProviderFactory.PROVIDER_ID);
+ clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.CLIENT_ID, "hardcoded-client");
+ clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.REDIRECT_URI, "http://localhost:8081/*");
+ clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.CONSENT, "true");
+ clientStorage.setParentId(realm.getId());
+ clientStorageComponent = realm.addComponentModel(clientStorage);
+
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+
+ Assert.assertNotNull(hardcodedClient);
+
+ UserConsentModel maryHardcodedGrant = new UserConsentModel(hardcodedClient);
+ realmManager.getSession().users().addConsent(realm, mary.getId(), maryHardcodedGrant);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void basicConsentTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCT) -> {
+ KeycloakSession currentSession = sessionCT;
+
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ ClientModel barClient = realm.getClientByClientId("bar-client");
+
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+
+ UserConsentModel johnFooConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+ Assert.assertEquals(johnFooConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", johnFooConsent));
+ Assert.assertNotNull("Created Date should be set", johnFooConsent.getCreatedDate());
+ Assert.assertNotNull("Last Updated Date should be set", johnFooConsent.getLastUpdatedDate());
+
+ UserConsentModel johnBarConsent = currentSession.users().getConsentByClient(realm, john.getId(), barClient.getId());
+ Assert.assertEquals(johnBarConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "bar", johnBarConsent));
+ Assert.assertNotNull("Created Date should be set", johnBarConsent.getCreatedDate());
+ Assert.assertNotNull("Last Updated Date should be set", johnBarConsent.getLastUpdatedDate());
+
+ UserConsentModel maryConsent = currentSession.users().getConsentByClient(realm, mary.getId(), fooClient.getId());
+ Assert.assertEquals(maryConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", maryConsent));
+ Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate());
+ Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate());
+
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+ UserConsentModel maryHardcodedConsent = currentSession.users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId());
+ Assert.assertEquals(maryHardcodedConsent.getGrantedClientScopes().size(), 0);
+ Assert.assertNotNull("Created Date should be set", maryHardcodedConsent.getCreatedDate());
+ Assert.assertNotNull("Last Updated Date should be set", maryHardcodedConsent.getLastUpdatedDate());
+
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, mary.getId(), barClient.getId()));
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, john.getId(), hardcodedClient.getId()));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void getAllConsentTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionACT) -> {
+ KeycloakSession currentSession = sessionACT;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+
+ List<UserConsentModel> johnConsents = currentSession.users().getConsents(realm, john.getId());
+ Assert.assertEquals(2, johnConsents.size());
+
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+
+ List<UserConsentModel> maryConsents = currentSession.users().getConsents(realm, mary.getId());
+ Assert.assertEquals(2, maryConsents.size());
+ UserConsentModel maryConsent = maryConsents.get(0);
+ UserConsentModel maryHardcodedConsent = maryConsents.get(1);
+ if (maryConsents.get(0).getClient().getId().equals(hardcodedClient.getId())) {
+ maryConsent = maryConsents.get(1);
+ maryHardcodedConsent = maryConsents.get(0);
+
+ }
+ Assert.assertEquals(maryConsent.getClient().getId(), fooClient.getId());
+ Assert.assertEquals(maryConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", maryConsent));
+
+ Assert.assertEquals(maryHardcodedConsent.getClient().getId(), hardcodedClient.getId());
+ Assert.assertEquals(maryHardcodedConsent.getGrantedClientScopes().size(), 0);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void updateWithClientScopeRemovalTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession removalTestSession1) -> {
+ KeycloakSession currentSession = removalTestSession1;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+
+ UserConsentModel johnConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+ Assert.assertEquals(1, johnConsent.getGrantedClientScopes().size());
+
+ // Remove foo protocol mapper from johnConsent
+ ClientScopeModel fooScope = KeycloakModelUtils.getClientScopeByName(realm, "foo");
+ johnConsent.getGrantedClientScopes().remove(fooScope);
+
+ currentSession.users().updateConsent(realm, john.getId(), johnConsent);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession removalTestSession2) -> {
+ KeycloakSession currentSession = removalTestSession2;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ UserConsentModel johnConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+
+ Assert.assertEquals(johnConsent.getGrantedClientScopes().size(), 0);
+ Assert.assertTrue("Created date should be less than last updated date", johnConsent.getCreatedDate() < johnConsent.getLastUpdatedDate());
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void revokeTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRT1) -> {
+ KeycloakSession currentSession = sessionRT1;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+
+ currentSession.users().revokeConsentForClient(realm, john.getId(), fooClient.getId());
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+ currentSession.users().revokeConsentForClient(realm, mary.getId(), hardcodedClient.getId());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRT2) -> {
+ KeycloakSession currentSession = sessionRT2;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId()));
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId()));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void deleteUserTest(KeycloakSession session) {
+ // Validate user deleted without any referential constraint errors
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionUT) -> {
+ KeycloakSession currentSession = sessionUT;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ currentSession.users().removeUser(realm, john);
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+ currentSession.users().removeUser(realm, mary);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void deleteClientScopeTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionST1) -> {
+ KeycloakSession currentSession = sessionST1;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientScopeModel fooScope = KeycloakModelUtils.getClientScopeByName(realm, "foo");
+ realm.removeClientScope(fooScope.getId());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionST2) -> {
+ KeycloakSession currentSession = sessionST2;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ UserConsentModel johnConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+
+ Assert.assertEquals(johnConsent.getGrantedClientScopes().size(), 0);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void deleteClientTest(KeycloakSession session) {
+
+ AtomicReference<String> barClientID = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionDCT1) -> {
+ KeycloakSession currentSession = sessionDCT1;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel barClient = realm.getClientByClientId("bar-client");
+ barClientID.set(barClient.getId());
+
+ realm.removeClient(barClient.getId());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionDCT2) -> {
+ KeycloakSession currentSession = sessionDCT2;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ Assert.assertNull(realm.getClientByClientId("bar-client"));
+
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ ClientModel barClient = realm.getClientByClientId("bar-client");
+
+ UserConsentModel johnFooConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+ Assert.assertEquals(johnFooConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", johnFooConsent));
+
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, john.getId(), barClientID.get()));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void deleteClientStorageTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCST1) -> {
+ KeycloakSession currentSession = sessionCST1;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ realm.removeComponent(clientStorageComponent);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCST2) -> {
+ KeycloakSession currentSession = sessionCST2;
+ RealmModel realm = currentSession.realms().getRealm("original");
+
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+ Assert.assertNull(hardcodedClient);
+
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+
+ List<UserConsentModel> maryConsents = currentSession.users().getConsents(realm, mary.getId());
+ Assert.assertEquals(1, maryConsents.size());
+ });
+ }
+
+ private boolean isClientScopeGranted(RealmModel realm, String scopeName, UserConsentModel consentModel) {
+ ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(realm, scopeName);
+ return consentModel.isClientScopeGranted(clientScope);
+ }
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java
new file mode 100644
index 0000000..23a83d0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java
@@ -0,0 +1,420 @@
+/*
+ * 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.model;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.models.*;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.storage.UserStorageProviderModel;
+import org.keycloak.storage.client.ClientStorageProviderModel;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.federation.HardcodedClientStorageProviderFactory;
+import org.keycloak.testsuite.federation.UserMapStorageFactory;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UserConsentWithUserStorageModelTest extends AbstractTestRealmKeycloakTest {
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, UserConsentWithUserStorageModelTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+ private static ComponentModel clientStorageComponent;
+
+ @Before
+ public void before() {
+ testingClient.server().run(currentSession -> {
+ setupEnv(currentSession);
+ });
+ }
+
+ @After
+ public void after() {
+ testingClient.server().run(session -> {
+
+ RealmManager realmManager = new RealmManager(session);
+ RealmModel realm = realmManager.getRealmByName("original");
+
+ if (realm != null) {
+
+ session.sessions().removeUserSessions(realm);
+ UserModel user = session.users().getUserByUsername("user", realm);
+ UserModel user1 = session.users().getUserByUsername("user1", realm);
+ UserModel user2 = session.users().getUserByUsername("user2", realm);
+ UserModel user3 = session.users().getUserByUsername("user3", realm);
+
+ UserManager um = new UserManager(session);
+ if (user != null) {
+ um.removeUser(realm, user);
+ }
+ if (user1 != null) {
+ um.removeUser(realm, user1);
+ }
+ if (user2 != null) {
+ um.removeUser(realm, user2);
+ }
+ if (user3 != null) {
+ um.removeUser(realm, user3);
+ }
+ realmManager.removeRealm(realm);
+ }
+ });
+ }
+
+ public static void setupEnv(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionSetUpEnv) -> {
+ KeycloakSession currentSession = sessionSetUpEnv;
+
+ RealmManager realmManager = new RealmManager(currentSession);
+ RealmModel realm = realmManager.createRealm("original");
+
+ UserStorageProviderModel model = new UserStorageProviderModel();
+ model.setName("memory");
+ model.setPriority(0);
+ model.setProviderId(UserMapStorageFactory.PROVIDER_ID);
+ model.setParentId(realm.getId());
+ realm.addComponentModel(model);
+
+ ClientModel fooClient = realm.addClient("foo-client");
+ ClientModel barClient = realm.addClient("bar-client");
+
+ ClientScopeModel fooScope = realm.addClientScope("foo");
+ fooScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+
+ ClientScopeModel barScope = realm.addClientScope("bar");
+ fooScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+
+ UserModel john = currentSession.users().addUser(realm, "john");
+ UserModel mary = currentSession.users().addUser(realm, "mary");
+
+ UserConsentModel johnFooGrant = new UserConsentModel(fooClient);
+ johnFooGrant.addGrantedClientScope(fooScope);
+ realmManager.getSession().users().addConsent(realm, john.getId(), johnFooGrant);
+
+ UserConsentModel johnBarGrant = new UserConsentModel(barClient);
+ johnBarGrant.addGrantedClientScope(barScope);
+
+ // Update should fail as grant doesn't yet exists
+ try {
+ currentSession.users().updateConsent(realm, john.getId(), johnBarGrant);
+ Assert.fail("Not expected to end here");
+ } catch (ModelException expected) {
+ }
+
+ realmManager.getSession().users().addConsent(realm, john.getId(), johnBarGrant);
+
+ UserConsentModel maryFooGrant = new UserConsentModel(fooClient);
+ maryFooGrant.addGrantedClientScope(fooScope);
+ realmManager.getSession().users().addConsent(realm, mary.getId(), maryFooGrant);
+
+ ClientStorageProviderModel clientStorage = new ClientStorageProviderModel();
+ clientStorage.setProviderId(HardcodedClientStorageProviderFactory.PROVIDER_ID);
+ clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.CLIENT_ID, "hardcoded-client");
+ clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.REDIRECT_URI, "http://localhost:8081/*");
+ clientStorage.getConfig().putSingle(HardcodedClientStorageProviderFactory.CONSENT, "true");
+ clientStorage.setParentId(realm.getId());
+ clientStorageComponent = realm.addComponentModel(clientStorage);
+
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+
+ Assert.assertNotNull(hardcodedClient);
+
+ UserConsentModel maryHardcodedGrant = new UserConsentModel(hardcodedClient);
+ realmManager.getSession().users().addConsent(realm, mary.getId(), maryHardcodedGrant);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void basicConsentTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession currentSessionCT) -> {
+ KeycloakSession currentSession = currentSessionCT;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ ClientModel barClient = realm.getClientByClientId("bar-client");
+
+ UserModel john = currentSessionCT.users().getUserByUsername("john", realm);
+ UserModel mary = currentSessionCT.users().getUserByUsername("mary", realm);
+
+ UserConsentModel johnFooConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+ Assert.assertEquals(johnFooConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", johnFooConsent));
+ Assert.assertNotNull("Created Date should be set", johnFooConsent.getCreatedDate());
+ Assert.assertNotNull("Last Updated Date should be set", johnFooConsent.getLastUpdatedDate());
+
+ UserConsentModel johnBarConsent = currentSession.users().getConsentByClient(realm, john.getId(), barClient.getId());
+ Assert.assertEquals(johnBarConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "bar", johnBarConsent));
+ Assert.assertNotNull("Created Date should be set", johnBarConsent.getCreatedDate());
+ Assert.assertNotNull("Last Updated Date should be set", johnBarConsent.getLastUpdatedDate());
+
+ UserConsentModel maryConsent = currentSession.users().getConsentByClient(realm, mary.getId(), fooClient.getId());
+ Assert.assertEquals(maryConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", maryConsent));
+ Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate());
+ Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate());
+
+ ClientModel hardcodedClient = currentSessionCT.realms().getClientByClientId("hardcoded-client", realm);
+ UserConsentModel maryHardcodedConsent = currentSession.users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId());
+ Assert.assertEquals(maryHardcodedConsent.getGrantedClientScopes().size(), 0);
+ Assert.assertNotNull("Created Date should be set", maryHardcodedConsent.getCreatedDate());
+ Assert.assertNotNull("Last Updated Date should be set", maryHardcodedConsent.getLastUpdatedDate());
+
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, mary.getId(), barClient.getId()));
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, john.getId(), hardcodedClient.getId()));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void getAllConsentTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession currentSessionACT) -> {
+ KeycloakSession currentSession = currentSessionACT;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+
+ UserModel john = currentSessionACT.users().getUserByUsername("john", realm);
+ UserModel mary = currentSessionACT.users().getUserByUsername("mary", realm);
+
+ List<UserConsentModel> johnConsents = currentSession.users().getConsents(realm, john.getId());
+ Assert.assertEquals(2, johnConsents.size());
+
+ ClientModel hardcodedClient = currentSessionACT.realms().getClientByClientId("hardcoded-client", realm);
+
+ List<UserConsentModel> maryConsents = currentSession.users().getConsents(realm, mary.getId());
+ Assert.assertEquals(2, maryConsents.size());
+ UserConsentModel maryConsent = maryConsents.get(0);
+ UserConsentModel maryHardcodedConsent = maryConsents.get(1);
+ if (maryConsents.get(0).getClient().getId().equals(hardcodedClient.getId())) {
+ maryConsent = maryConsents.get(1);
+ maryHardcodedConsent = maryConsents.get(0);
+
+ }
+ Assert.assertEquals(maryConsent.getClient().getId(), fooClient.getId());
+ Assert.assertEquals(maryConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", maryConsent));
+
+ Assert.assertEquals(maryHardcodedConsent.getClient().getId(), hardcodedClient.getId());
+ Assert.assertEquals(maryHardcodedConsent.getGrantedClientScopes().size(), 0);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void updateWithClientScopeRemovalTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionScopeRemoval1) -> {
+ KeycloakSession currentSession = sessionScopeRemoval1;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+
+ UserConsentModel johnConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+ Assert.assertEquals(1, johnConsent.getGrantedClientScopes().size());
+
+ // Remove foo protocol mapper from johnConsent
+ ClientScopeModel fooScope = KeycloakModelUtils.getClientScopeByName(realm, "foo");
+ johnConsent.getGrantedClientScopes().remove(fooScope);
+
+ currentSession.users().updateConsent(realm, john.getId(), johnConsent);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionScopeRemoval2) -> {
+ KeycloakSession currentSession = sessionScopeRemoval2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ UserConsentModel johnConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+
+ Assert.assertEquals(johnConsent.getGrantedClientScopes().size(), 0);
+ Assert.assertTrue("Created date should be less than last updated date", johnConsent.getCreatedDate() < johnConsent.getLastUpdatedDate());
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void revokeTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRevoke1) -> {
+ KeycloakSession currentSession = sessionRevoke1;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+
+ currentSession.users().revokeConsentForClient(realm, john.getId(), fooClient.getId());
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+ currentSession.users().revokeConsentForClient(realm, mary.getId(), hardcodedClient.getId());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRevoke2) -> {
+ KeycloakSession currentSession = sessionRevoke2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId()));
+
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+ Assert.assertNull(currentSession.users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId()));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void deleteUserTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionDelete) -> {
+ // Validate user deleted without any referential constraint errors
+ KeycloakSession currentSession = sessionDelete;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ currentSession.users().removeUser(realm, john);
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+ currentSession.users().removeUser(realm, mary);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void deleteClientScopeTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesDelClScope1) -> {
+ KeycloakSession currentSession = sesDelClScope1;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ ClientScopeModel fooScope = KeycloakModelUtils.getClientScopeByName(realm, "foo");
+ realm.removeClientScope(fooScope.getId());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesDelClScope2) -> {
+ KeycloakSession currentSession = sesDelClScope2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ UserModel john = currentSession.users().getUserByUsername("john", realm);
+ UserConsentModel johnConsent = currentSession.users().getConsentByClient(realm, john.getId(), fooClient.getId());
+
+ Assert.assertEquals(johnConsent.getGrantedClientScopes().size(), 0);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void deleteClientTest(KeycloakSession session) {
+ AtomicReference<String> barClientID = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesDelClient1) -> {
+ KeycloakSession currentSession = sesDelClient1;
+ RealmManager realmManager = new RealmManager(currentSession);
+ RealmModel realm = realmManager.getRealmByName("original");
+
+ ClientModel barClient = realm.getClientByClientId("bar-client");
+ barClientID.set(barClient.getId());
+
+ realm.removeClient(barClient.getId());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesDelClient2) -> {
+ KeycloakSession currentSession = sesDelClient2;
+ RealmManager realmManager = new RealmManager(currentSession);
+ RealmModel realm = realmManager.getRealm("original");
+
+ ClientModel fooClient = realm.getClientByClientId("foo-client");
+ Assert.assertNull(realm.getClientByClientId("bar-client"));
+
+ UserModel john = realmManager.getSession().users().getUserByUsername("john", realm);
+
+ UserConsentModel johnFooConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
+ Assert.assertEquals(johnFooConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", johnFooConsent));
+
+ Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClientID.get()));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void deleteClientStorageTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesDelClientStore1) -> {
+ KeycloakSession currentSession = sesDelClientStore1;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ realm.removeComponent(clientStorageComponent);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesDelClientStore2) -> {
+ KeycloakSession currentSession = sesDelClientStore2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ ClientModel hardcodedClient = currentSession.realms().getClientByClientId("hardcoded-client", realm);
+ Assert.assertNull(hardcodedClient);
+
+ UserModel mary = currentSession.users().getUserByUsername("mary", realm);
+
+ List<UserConsentModel> maryConsents = currentSession.users().getConsents(realm, mary.getId());
+ Assert.assertEquals(1, maryConsents.size());
+ });
+ }
+
+ private boolean isClientScopeGranted(RealmModel realm, String scopeName, UserConsentModel consentModel) {
+ ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(realm, scopeName);
+ return consentModel.isClientScopeGranted(clientScope);
+ }
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
new file mode 100755
index 0000000..d8db8e9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
@@ -0,0 +1,615 @@
+/*
+ * 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.model;
+
+import com.google.common.collect.ImmutableMap;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.models.*;
+import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.ClientManager;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.assertNotNull;
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserModelTest extends AbstractTestRealmKeycloakTest {
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, UserModelTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model",
+ "com.google.common");
+ }
+
+ @After
+ public void after() {
+ testingClient.server().run(session -> {
+
+ RealmModel realm = session.realms().getRealmByName("original");
+
+ if (realm != null) {
+ session.sessions().removeUserSessions(realm);
+ UserModel user = session.users().getUserByUsername("user", realm);
+ UserModel user1 = session.users().getUserByUsername("user1", realm);
+ UserModel user2 = session.users().getUserByUsername("user2", realm);
+ UserModel user3 = session.users().getUserByUsername("user3", realm);
+
+ UserManager um = new UserManager(session);
+ if (user != null) {
+ um.removeUser(realm, user);
+ }
+ if (user1 != null) {
+ um.removeUser(realm, user1);
+ }
+ if (user2 != null) {
+ um.removeUser(realm, user2);
+ }
+ if (user3 != null) {
+ um.removeUser(realm, user3);
+ }
+ session.realms().removeRealm(realm.getId());
+ }
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void persistUser(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesPersistUser) -> {
+ KeycloakSession currentSession = sesPersistUser;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ UserModel user = currentSession.users().addUser(realm, "user");
+ user.setFirstName("first-name");
+ user.setLastName("last-name");
+ user.setEmail("email");
+ assertNotNull(user.getCreatedTimestamp());
+ // test that timestamp is current with 10s tollerance
+ Assert.assertTrue((System.currentTimeMillis() - user.getCreatedTimestamp()) < 10000);
+
+ user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
+ user.addRequiredAction(RequiredAction.UPDATE_PASSWORD);
+
+ RealmModel searchRealm = currentSession.realms().getRealm(realm.getId());
+ UserModel persisted = currentSession.users().getUserByUsername("user", searchRealm);
+
+ assertEquals(user, persisted);
+
+ searchRealm = currentSession.realms().getRealm(realm.getId());
+ UserModel persisted2 = currentSession.users().getUserById(user.getId(), searchRealm);
+ assertEquals(user, persisted2);
+
+ Map<String, String> attributes = new HashMap<>();
+ attributes.put(UserModel.LAST_NAME, "last-name");
+ List<UserModel> search = currentSession.users().searchForUser(attributes, realm);
+ Assert.assertEquals(search.size(), 1);
+ Assert.assertEquals(search.get(0).getUsername(), "user");
+
+ attributes.clear();
+ attributes.put(UserModel.EMAIL, "email");
+ search = currentSession.users().searchForUser(attributes, realm);
+ Assert.assertEquals(search.size(), 1);
+ Assert.assertEquals(search.get(0).getUsername(), "user");
+
+ attributes.clear();
+ attributes.put(UserModel.LAST_NAME, "last-name");
+ attributes.put(UserModel.EMAIL, "email");
+ search = currentSession.users().searchForUser(attributes, realm);
+ Assert.assertEquals(search.size(), 1);
+ Assert.assertEquals(search.get(0).getUsername(), "user");
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void webOriginSetTest(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesWebOrigin) -> {
+ KeycloakSession currentSession = sesWebOrigin;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ ClientModel client = realm.addClient("user");
+
+ Assert.assertTrue(client.getWebOrigins().isEmpty());
+
+ client.addWebOrigin("origin-1");
+ Assert.assertEquals(1, client.getWebOrigins().size());
+
+ client.addWebOrigin("origin-2");
+ Assert.assertEquals(2, client.getWebOrigins().size());
+
+ client.removeWebOrigin("origin-2");
+ Assert.assertEquals(1, client.getWebOrigins().size());
+
+ client.removeWebOrigin("origin-1");
+ Assert.assertTrue(client.getWebOrigins().isEmpty());
+
+ client = realm.addClient("oauthclient2");
+
+ Assert.assertTrue(client.getWebOrigins().isEmpty());
+
+ client.addWebOrigin("origin-1");
+ Assert.assertEquals(1, client.getWebOrigins().size());
+
+ client.addWebOrigin("origin-2");
+ Assert.assertEquals(2, client.getWebOrigins().size());
+
+ client.removeWebOrigin("origin-2");
+ Assert.assertEquals(1, client.getWebOrigins().size());
+
+ client.removeWebOrigin("origin-1");
+ Assert.assertTrue(client.getWebOrigins().isEmpty());
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testUserRequiredActions(KeycloakSession session) throws Exception {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUserReqActions) -> {
+ KeycloakSession currentSession = sesUserReqActions;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ UserModel user = currentSession.users().addUser(realm, "user");
+
+ Assert.assertTrue(user.getRequiredActions().isEmpty());
+
+ user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
+ String id = realm.getId();
+
+ realm = currentSession.realms().getRealm(id);
+ user = currentSession.users().getUserByUsername("user", realm);
+
+ Assert.assertEquals(1, user.getRequiredActions().size());
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP.name()));
+
+ user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
+ user = currentSession.users().getUserByUsername("user", realm);
+
+ Assert.assertEquals(1, user.getRequiredActions().size());
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP.name()));
+
+ user.addRequiredAction(RequiredAction.VERIFY_EMAIL.name());
+ user = currentSession.users().getUserByUsername("user", realm);
+
+ Assert.assertEquals(2, user.getRequiredActions().size());
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP.name()));
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
+
+ user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP.name());
+ user = currentSession.users().getUserByUsername("user", realm);
+
+ Assert.assertEquals(1, user.getRequiredActions().size());
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
+
+ user.removeRequiredAction(RequiredAction.VERIFY_EMAIL.name());
+ user = currentSession.users().getUserByUsername("user", realm);
+
+ Assert.assertTrue(user.getRequiredActions().isEmpty());
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testUserMultipleAttributes(KeycloakSession session) throws Exception {
+ AtomicReference<List<String>> attrValsAtomic = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesMultipleAtr1) -> {
+ KeycloakSession currentSession = sesMultipleAtr1;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ UserModel user = currentSession.users().addUser(realm, "user");
+ currentSession.users().addUser(realm, "user-noattrs");
+
+ user.setSingleAttribute("key1", "value1");
+
+ List<String> attrVals = new ArrayList<>(Arrays.asList("val21", "val22"));
+ attrValsAtomic.set(attrVals);
+
+ user.setAttribute("key2", attrVals);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesMultipleAtr2) -> {
+ KeycloakSession currentSession = sesMultipleAtr2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ // Test read attributes
+ UserModel user = currentSession.users().getUserByUsername("user", realm);
+
+ List<String> attrVals = attrValsAtomic.get();
+
+ attrVals = user.getAttribute("key1");
+ Assert.assertEquals(1, attrVals.size());
+ Assert.assertEquals("value1", attrVals.get(0));
+ Assert.assertEquals("value1", user.getFirstAttribute("key1"));
+
+ attrVals = user.getAttribute("key2");
+ Assert.assertEquals(2, attrVals.size());
+ Assert.assertTrue(attrVals.contains("val21"));
+ Assert.assertTrue(attrVals.contains("val22"));
+
+ attrVals = user.getAttribute("key3");
+ Assert.assertTrue(attrVals.isEmpty());
+ Assert.assertNull(user.getFirstAttribute("key3"));
+
+ Map<String, List<String>> allAttrVals = user.getAttributes();
+ Assert.assertEquals(2, allAttrVals.size());
+ Assert.assertEquals(allAttrVals.get("key1"), user.getAttribute("key1"));
+ Assert.assertEquals(allAttrVals.get("key2"), user.getAttribute("key2"));
+
+ // Test remove and rewrite attribute
+ user.removeAttribute("key1");
+ user.setSingleAttribute("key2", "val23");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesMultipleAtr3) -> {
+ KeycloakSession currentSession = sesMultipleAtr3;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel user = currentSession.users().getUserByUsername("user", realm);
+ Assert.assertNull(user.getFirstAttribute("key1"));
+
+ List<String> attrVals = attrValsAtomic.get();
+ attrVals = user.getAttribute("key2");
+
+ Assert.assertEquals(1, attrVals.size());
+ Assert.assertEquals("val23", attrVals.get(0));
+
+ });
+ }
+
+ // KEYCLOAK-3494
+ @Test
+ @ModelTest
+ public void testUpdateUserAttribute(KeycloakSession session) throws Exception {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateAtr1) -> {
+ KeycloakSession currentSession = sesUpdateAtr1;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ UserModel user = currentSession.users().addUser(realm, "user");
+
+ user.setSingleAttribute("key1", "value1");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateAtr2) -> {
+ KeycloakSession currentSession = sesUpdateAtr2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel user = currentSession.users().getUserByUsername("user", realm);
+
+ // Update attribute
+ List<String> attrVals = new ArrayList<>(Arrays.asList("val2"));
+ user.setAttribute("key1", attrVals);
+ Map<String, List<String>> allAttrVals = user.getAttributes();
+
+ // Ensure same transaction is able to see updated value
+ Assert.assertEquals(1, allAttrVals.size());
+ Assert.assertEquals(allAttrVals.get("key1"), Arrays.asList("val2"));
+
+ });
+ }
+
+ // KEYCLOAK-3608
+ @Test
+ @ModelTest
+ public void testUpdateUserSingleAttribute(KeycloakSession session) {
+
+ AtomicReference<Map<String, List<String>>> expectedAtomic = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateUserSingleAtr) -> {
+ KeycloakSession currentSession = sesUpdateUserSingleAtr;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+
+ Map<String, List<String>> expected = ImmutableMap.of(
+ "key1", Arrays.asList("value3"),
+ "key2", Arrays.asList("value2"));
+ expectedAtomic.set(expected);
+
+ UserModel user = currentSession.users().addUser(realm, "user");
+
+ user.setSingleAttribute("key1", "value1");
+ user.setSingleAttribute("key2", "value2");
+
+ // Overwrite the first attribute
+ user.setSingleAttribute("key1", "value3");
+
+ Assert.assertEquals(expected, user.getAttributes());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateUserSingleAtr2) -> {
+ KeycloakSession currentSession = sesUpdateUserSingleAtr2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ Map<String, List<String>> expected = expectedAtomic.get();
+ Assert.assertEquals(expected, currentSession.users().getUserByUsername("user", realm).getAttributes());
+
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testSearchByString(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesSearchString1) -> {
+ KeycloakSession currentSession = sesSearchString1;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ currentSession.users().addUser(realm, "user1");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesSearchString1) -> {
+ KeycloakSession currentSession = sesSearchString1;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+
+ List<UserModel> users = currentSession.users().searchForUser("user", realm, 0, 7);
+ Assert.assertTrue(users.contains(user1));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testSearchByUserAttribute(KeycloakSession session) throws Exception {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesSearchAtr1) -> {
+ KeycloakSession currentSession = sesSearchAtr1;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ UserModel user1 = currentSession.users().addUser(realm, "user1");
+ UserModel user2 = currentSession.users().addUser(realm, "user2");
+ UserModel user3 = currentSession.users().addUser(realm, "user3");
+ RealmModel otherRealm = currentSession.realms().createRealm("other");
+ UserModel otherRealmUser = currentSession.users().addUser(otherRealm, "user1");
+
+ user1.setSingleAttribute("key1", "value1");
+ user1.setSingleAttribute("key2", "value21");
+
+ user2.setSingleAttribute("key1", "value1");
+ user2.setSingleAttribute("key2", "value22");
+
+ user3.setSingleAttribute("key2", "value21");
+
+ otherRealmUser.setSingleAttribute("key2", "value21");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesSearchAtr2) -> {
+ KeycloakSession currentSession = sesSearchAtr2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+ UserModel user2 = currentSession.users().getUserByUsername("user2", realm);
+ UserModel user3 = currentSession.users().getUserByUsername("user3", realm);
+
+ List<UserModel> users = currentSession.users().searchForUserByUserAttribute("key1", "value1", realm);
+ Assert.assertEquals(2, users.size());
+ Assert.assertTrue(users.contains(user1));
+ Assert.assertTrue(users.contains(user2));
+
+ users = currentSession.users().searchForUserByUserAttribute("key2", "value21", realm);
+ Assert.assertEquals(2, users.size());
+ Assert.assertTrue(users.contains(user1));
+ Assert.assertTrue(users.contains(user3));
+
+ users = currentSession.users().searchForUserByUserAttribute("key2", "value22", realm);
+ Assert.assertEquals(1, users.size());
+ Assert.assertTrue(users.contains(user2));
+
+ users = currentSession.users().searchForUserByUserAttribute("key3", "value3", realm);
+ Assert.assertEquals(0, users.size());
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testServiceAccountLink(KeycloakSession session) throws Exception {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesServiceLink1) -> {
+ KeycloakSession currentSession = sesServiceLink1;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ ClientModel client = realm.addClient("foo");
+
+ UserModel user1 = currentSession.users().addUser(realm, "user1");
+ user1.setFirstName("John");
+ user1.setLastName("Doe");
+
+ UserModel user2 = currentSession.users().addUser(realm, "user2");
+ user2.setFirstName("John");
+ user2.setLastName("Doe");
+
+ // Search
+ Assert.assertNull(currentSession.users().getServiceAccount(client));
+ List<UserModel> users = currentSession.users().searchForUser("John Doe", realm);
+ Assert.assertEquals(2, users.size());
+ Assert.assertTrue(users.contains(user1));
+ Assert.assertTrue(users.contains(user2));
+
+ // Link service account
+ user1.setServiceAccountClientLink(client.getId());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesServiceLink2) -> {
+ KeycloakSession currentSession = sesServiceLink2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+ UserModel user2 = currentSession.users().getUserByUsername("user2", realm);
+
+ // Search and assert service account user not found
+ ClientModel client = realm.getClientByClientId("foo");
+ UserModel searched = currentSession.users().getServiceAccount(client);
+ Assert.assertEquals(searched, user1);
+ List<UserModel> users = currentSession.users().searchForUser("John Doe", realm);
+ Assert.assertEquals(1, users.size());
+ Assert.assertFalse(users.contains(user1));
+ Assert.assertTrue(users.contains(user2));
+
+ users = currentSession.users().getUsers(realm, false);
+ Assert.assertEquals(1, users.size());
+ Assert.assertFalse(users.contains(user1));
+ Assert.assertTrue(users.contains(user2));
+
+ users = currentSession.users().getUsers(realm, true);
+ Assert.assertEquals(2, users.size());
+ Assert.assertTrue(users.contains(user1));
+ Assert.assertTrue(users.contains(user2));
+
+ Assert.assertEquals(2, currentSession.users().getUsersCount(realm, true));
+ Assert.assertEquals(1, currentSession.users().getUsersCount(realm, false));
+
+ // Remove client
+ RealmManager realmMgr = new RealmManager(currentSession);
+ ClientManager clientMgr = new ClientManager(realmMgr);
+
+ clientMgr.removeClient(realm, client);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesServiceLink3) -> {
+ KeycloakSession currentSession = sesServiceLink3;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+ // Assert service account removed as well
+ Assert.assertNull(currentSession.users().getUserByUsername("user1", realm));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testGrantToAll(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesGrantToAll1) -> {
+ KeycloakSession currentSession = sesGrantToAll1;
+
+ if (currentSession.realms().getRealm("realm1") != null)
+ currentSession.realms().removeRealm("realm1");
+
+ RealmModel realm1 = currentSession.realms().createRealm("realm1");
+
+ realm1.addRole("role1");
+ currentSession.users().addUser(realm1, "user1");
+ currentSession.users().addUser(realm1, "user2");
+
+ if (currentSession.realms().getRealm("realm2") != null)
+ currentSession.realms().removeRealm("realm2");
+
+ RealmModel realm2 = currentSession.realms().createRealm("realm2");
+ currentSession.users().addUser(realm2, "user1");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesGrantToAll2) -> {
+ KeycloakSession currentSession = sesGrantToAll2;
+ RealmModel realm1 = currentSession.realms().getRealmByName("realm1");
+
+ RoleModel role1 = realm1.getRole("role1");
+ currentSession.users().grantToAllUsers(realm1, role1);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesGrantToAll2) -> {
+ KeycloakSession currentSession = sesGrantToAll2;
+ RealmModel realm1 = currentSession.realms().getRealmByName("realm1");
+
+ RoleModel role1 = realm1.getRole("role1");
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm1);
+ UserModel user2 = currentSession.users().getUserByUsername("user2", realm1);
+ Assert.assertTrue(user1.hasRole(role1));
+ Assert.assertTrue(user2.hasRole(role1));
+
+ RealmModel realm2 = currentSession.realms().getRealmByName("realm2");
+ UserModel realm2User1 = currentSession.users().getUserByUsername("user1", realm2);
+ Assert.assertFalse(realm2User1.hasRole(role1));
+
+ currentSession.realms().removeRealm(realm1.getId());
+ currentSession.realms().removeRealm(realm2.getId());
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testUserNotBefore(KeycloakSession session) throws Exception {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUserNotBefore1) -> {
+ KeycloakSession currentSession = sesUserNotBefore1;
+ RealmModel realm = currentSession.realms().createRealm("original");
+
+ UserModel user1 = currentSession.users().addUser(realm, "user1");
+ currentSession.users().setNotBeforeForUser(realm, user1, 10);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUserNotBefore2) -> {
+ KeycloakSession currentSession = sesUserNotBefore2;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+ int notBefore = currentSession.users().getNotBeforeOfUser(realm, user1);
+ Assert.assertEquals(10, notBefore);
+
+ // Try to update
+ currentSession.users().setNotBeforeForUser(realm, user1, 20);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUserNotBefore3) -> {
+ KeycloakSession currentSession = sesUserNotBefore3;
+ RealmModel realm = currentSession.realms().getRealmByName("original");
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+ int notBefore = currentSession.users().getNotBeforeOfUser(realm, user1);
+ Assert.assertEquals(20, notBefore);
+ });
+ }
+
+ public static void assertEquals(UserModel expected, UserModel actual) {
+ Assert.assertEquals(expected.getUsername(), actual.getUsername());
+ Assert.assertEquals(expected.getCreatedTimestamp(), actual.getCreatedTimestamp());
+ Assert.assertEquals(expected.getFirstName(), actual.getFirstName());
+ Assert.assertEquals(expected.getLastName(), actual.getLastName());
+
+ String[] expectedRequiredActions = expected.getRequiredActions().toArray(new String[expected.getRequiredActions().size()]);
+ Arrays.sort(expectedRequiredActions);
+ String[] actualRequiredActions = actual.getRequiredActions().toArray(new String[actual.getRequiredActions().size()]);
+ Arrays.sort(actualRequiredActions);
+
+ Assert.assertArrayEquals(expectedRequiredActions, actualRequiredActions);
+ }
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
new file mode 100644
index 0000000..6f80328
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
@@ -0,0 +1,611 @@
+/*
+ * 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.model;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.*;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.common.util.Time;
+import org.keycloak.models.*;
+import org.keycloak.models.session.UserSessionPersisterProvider;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.ClientManager;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UserSessionPersisterProviderTest extends AbstractTestRealmKeycloakTest {
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, UserSessionPersisterProvider.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+ @Before
+ public void before() {
+ testingClient.server().run(session -> {
+ initStuff(session);
+ });
+ }
+
+ public static void initStuff(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealm("test");
+ session.users().addUser(realm, "user1").setEmail("user1@localhost");
+ session.users().addUser(realm, "user2").setEmail("user2@localhost");
+ }
+
+ @After
+ public void after() {
+ testingClient.server().run(session -> {
+ RealmModel realm = session.realms().getRealm("test");
+ session.sessions().removeUserSessions(realm);
+ UserModel user1 = session.users().getUserByUsername("user1", realm);
+ UserModel user2 = session.users().getUserByUsername("user2", realm);
+
+ UserManager um = new UserManager(session);
+ if (user1 != null) {
+ um.removeUser(realm, user1);
+ }
+ if (user2 != null) {
+ um.removeUser(realm, user2);
+ }
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testPersistenceWithLoad(KeycloakSession session) {
+ int started = Time.currentTime();
+ UserSessionModel[][] origSessions = new UserSessionModel[1][1];
+ final UserSessionModel[] userSession = new UserSessionModel[1];
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionWL) -> {
+ // Create some sessions in infinispan
+ origSessions[0] = createSessions(sessionWL);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionWL22) -> {
+ // Persist 3 created userSessions and clientSessions as offline
+ RealmModel realm = sessionWL22.realms().getRealm("test");
+ ClientModel testApp = realm.getClientByClientId("test-app");
+ List<UserSessionModel> userSessions = sessionWL22.sessions().getUserSessions(realm, testApp);
+ for (UserSessionModel userSessionLooper : userSessions) {
+ persistUserSession(sessionWL22, userSessionLooper, true);
+ }
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionWL2) -> {
+ // Persist 1 online session
+ RealmModel realm = sessionWL2.realms().getRealm("test");
+ userSession[0] = sessionWL2.sessions().getUserSession(realm, origSessions[0][0].getId());
+ persistUserSession(sessionWL2, userSession[0], false);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionWL3) -> {// Assert online session
+ RealmModel realm = sessionWL3.realms().getRealm("test");
+ List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(sessionWL3, false, 1, 1, 1);
+ UserSessionProviderTest.assertSession(loadedSessions.get(0), sessionWL3.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionWL4) -> {
+ // Assert offline sessions
+ RealmModel realm = sessionWL4.realms().getRealm("test");
+ List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(sessionWL4, true, 2, 2, 3);
+ UserSessionProviderTest.assertSessions(loadedSessions, origSessions[0]);
+
+ assertSessionLoaded(loadedSessions, origSessions[0][0].getId(), sessionWL4.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
+ assertSessionLoaded(loadedSessions, origSessions[0][1].getId(), sessionWL4.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
+ assertSessionLoaded(loadedSessions, origSessions[0][2].getId(), sessionWL4.users().getUserByUsername("user2", realm), "127.0.0.3", started, started, "test-app");
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testUpdateAndRemove(KeycloakSession session) {
+ int started = Time.currentTime();
+
+ AtomicReference<UserSessionModel[]> origSessionsAt = new AtomicReference<>();
+ AtomicReference<List<UserSessionModel>> loadedSessionsAt = new AtomicReference<>();
+
+ AtomicReference<UserSessionModel> userSessionAt = new AtomicReference<>();
+ AtomicReference<UserSessionModel> persistedSessionAt = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateRemove1) -> {
+ KeycloakSession currentSession = sesUpdateRemove1;
+
+ // Create some sessions in infinispan
+ UserSessionModel[] origSessions = createSessions(currentSession);
+ origSessionsAt.set(origSessions);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateRemove2) -> {
+ KeycloakSession currentSession = sesUpdateRemove2;
+ RealmModel realm = currentSession.realms().getRealm("test");
+ UserSessionModel[] origSessions = origSessionsAt.get();
+
+ // Persist 1 offline session
+ UserSessionModel userSession = currentSession.sessions().getUserSession(realm, origSessions[1].getId());
+ userSessionAt.set(userSession);
+
+ persistUserSession(currentSession, userSession, true);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateRemove3) -> {
+ KeycloakSession currentSession = sesUpdateRemove3;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ UserSessionPersisterProvider persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+
+ // Load offline session
+ List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(currentSession, true, 10, 1, 1);
+ loadedSessionsAt.set(loadedSessions);
+
+ UserSessionModel persistedSession = loadedSessions.get(0);
+ persistedSessionAt.set(persistedSession);
+
+ UserSessionProviderTest.assertSession(persistedSession, currentSession.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
+
+ // create new clientSession
+ AuthenticatedClientSessionModel clientSession = createClientSession(currentSession, realm.getClientByClientId("third-party"), currentSession.sessions().getUserSession(realm, persistedSession.getId()),
+ "http://redirect", "state");
+ persister.createClientSession(clientSession, true);
+
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateRemove4) -> {
+ KeycloakSession currentSession = sesUpdateRemove4;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ UserSessionPersisterProvider persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+ UserSessionModel userSession = userSessionAt.get();
+
+ // Remove clientSession
+ persister.removeClientSession(userSession.getId(), realm.getClientByClientId("third-party").getId(), true);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateRemove5) -> {
+ KeycloakSession currentSession = sesUpdateRemove5;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ UserSessionPersisterProvider persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+ List<UserSessionModel> loadedSessions = loadedSessionsAt.get();
+ UserSessionModel persistedSession = persistedSessionAt.get();
+
+ // Assert clientSession removed
+ loadedSessions = loadPersistedSessionsPaginated(currentSession, true, 10, 1, 1);
+ persistedSession = loadedSessions.get(0);
+ UserSessionProviderTest.assertSession(persistedSession, currentSession.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
+
+ // Remove userSession
+ persister.removeUserSession(persistedSession.getId(), true);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sesUpdateRemove6) -> {
+ KeycloakSession currentSession = sesUpdateRemove6;
+ // Assert nothing found
+ loadPersistedSessionsPaginated(currentSession, true, 10, 0, 0);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testOnRealmRemoved(KeycloakSession session) {
+ AtomicReference<String> userSessionID = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR1) -> {
+ KeycloakSession currentSession = sessionRR1;
+ RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
+
+ fooRealm.addClient("foo-app");
+ currentSession.users().addUser(fooRealm, "user3");
+
+ UserSessionModel userSession = currentSession.sessions().createUserSession(fooRealm, currentSession.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+ userSessionID.set(userSession.getId());
+
+ createClientSession(currentSession, fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR2) -> {
+ KeycloakSession currentSession = sessionRR2;
+
+ // Persist offline session
+ RealmModel fooRealm = currentSession.realms().getRealm("foo");
+ UserSessionModel userSession = currentSession.sessions().getUserSession(fooRealm, userSessionID.get());
+ persistUserSession(currentSession, userSession, true);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR3) -> {
+ KeycloakSession currentSession = sessionRR3;
+
+ // Assert session was persisted
+ loadPersistedSessionsPaginated(currentSession, true, 10, 1, 1);
+
+ // Remove realm
+ RealmManager realmMgr = new RealmManager(currentSession);
+ realmMgr.removeRealm(realmMgr.getRealm("foo"));
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR4) -> {
+ KeycloakSession currentSession = sessionRR4;
+
+ // Assert nothing loaded
+ loadPersistedSessionsPaginated(currentSession, true, 10, 0, 0);
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testOnClientRemoved(KeycloakSession session) {
+ int started = Time.currentTime();
+ AtomicReference<String> userSessionID = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR1) -> {
+ KeycloakSession currentSession = sessionCR1;
+ RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
+
+ fooRealm.addClient("foo-app");
+ fooRealm.addClient("bar-app");
+ currentSession.users().addUser(fooRealm, "user3");
+
+ UserSessionModel userSession = currentSession.sessions().createUserSession(fooRealm, currentSession.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+ userSessionID.set(userSession.getId());
+
+ createClientSession(currentSession, fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
+ createClientSession(currentSession, fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR2) -> {
+ KeycloakSession currentSession = sessionCR2;
+ RealmModel fooRealm = currentSession.realms().getRealm("foo");
+
+ // Persist offline session
+ UserSessionModel userSession = currentSession.sessions().getUserSession(fooRealm, userSessionID.get());
+ persistUserSession(currentSession, userSession, true);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR3) -> {
+ KeycloakSession currentSession = sessionCR3;
+
+ RealmManager realmMgr = new RealmManager(currentSession);
+ ClientManager clientMgr = new ClientManager(realmMgr);
+ RealmModel fooRealm = realmMgr.getRealm("foo");
+
+ // Assert session was persisted with both clientSessions
+ UserSessionModel persistedSession = loadPersistedSessionsPaginated(currentSession, true, 10, 1, 1).get(0);
+ UserSessionProviderTest.assertSession(persistedSession, currentSession.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "foo-app", "bar-app");
+
+ // Remove foo-app client
+ ClientModel client = fooRealm.getClientByClientId("foo-app");
+ clientMgr.removeClient(fooRealm, client);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR4) -> {
+ KeycloakSession currentSession = sessionCR4;
+ RealmManager realmMgr = new RealmManager(currentSession);
+ ClientManager clientMgr = new ClientManager(realmMgr);
+ RealmModel fooRealm = realmMgr.getRealm("foo");
+
+ // Assert just one bar-app clientSession persisted now
+ UserSessionModel persistedSession = loadPersistedSessionsPaginated(currentSession, true, 10, 1, 1).get(0);
+ UserSessionProviderTest.assertSession(persistedSession, currentSession.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "bar-app");
+
+ // Remove bar-app client
+ ClientModel client = fooRealm.getClientByClientId("bar-app");
+ clientMgr.removeClient(fooRealm, client);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR5) -> {
+ KeycloakSession currentSession = sessionCR5;
+
+ // Assert loading still works - last userSession is still there, but no clientSession on it
+ loadPersistedSessionsPaginated(currentSession, true, 10, 1, 1);
+
+ // Cleanup
+ RealmManager realmMgr = new RealmManager(currentSession);
+ realmMgr.removeRealm(realmMgr.getRealm("foo"));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testOnUserRemoved(KeycloakSession session) {
+ int started = Time.currentTime();
+ AtomicReference<UserSessionModel[]> origSessionsAt = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionOR1) -> {
+ KeycloakSession currentSession = sessionOR1;
+
+ // Create some sessions in infinispan
+ UserSessionModel[] origSessions = createSessions(currentSession);
+ origSessionsAt.set(origSessions);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionOR2) -> {
+ KeycloakSession currentSession = sessionOR2;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ UserSessionModel[] origSessions = origSessionsAt.get();
+
+ // Persist 2 offline sessions of 2 users
+ UserSessionModel userSession1 = currentSession.sessions().getUserSession(realm, origSessions[1].getId());
+ UserSessionModel userSession2 = currentSession.sessions().getUserSession(realm, origSessions[2].getId());
+ persistUserSession(currentSession, userSession1, true);
+ persistUserSession(currentSession, userSession2, true);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionOR3) -> {
+ KeycloakSession currentSession = sessionOR3;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ // Load offline sessions
+ loadPersistedSessionsPaginated(currentSession, true, 10, 1, 2);
+
+ // Properly delete user and assert his offlineSession removed
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+ new UserManager(currentSession).removeUser(realm, user1);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionOR4) -> {
+ KeycloakSession currentSession = sessionOR4;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ UserSessionPersisterProvider persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+ Assert.assertEquals(1, persister.getUserSessionsCount(true));
+
+ List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(currentSession, true, 10, 1, 1);
+ UserSessionModel persistedSession = loadedSessions.get(0);
+ UserSessionProviderTest.assertSession(persistedSession, currentSession.users().getUserByUsername("user2", realm), "127.0.0.3", started, started, "test-app");
+
+ // KEYCLOAK-2431 Assert that userSessionPersister is resistent even to situation, when users are deleted "directly".
+ // No exception will happen. However session will be still there
+ UserModel user2 = currentSession.users().getUserByUsername("user2", realm);
+ currentSession.users().removeUser(realm, user2);
+
+ loadedSessions = loadPersistedSessionsPaginated(currentSession, true, 10, 1, 1);
+
+ // Cleanup
+ UserSessionModel userSession = loadedSessions.get(0);
+ currentSession.sessions().removeUserSession(realm, userSession);
+ persister.removeUserSession(userSession.getId(), userSession.isOffline());
+ });
+ }
+
+ // KEYCLOAK-1999
+ @Test
+ @ModelTest
+ public void testNoSessions(KeycloakSession session) {
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionNS) -> {
+ UserSessionPersisterProvider persister = sessionNS.getProvider(UserSessionPersisterProvider.class);
+ List<UserSessionModel> sessions = persister.loadUserSessions(0, 1, true, 0, "abc");
+ Assert.assertEquals(0, sessions.size());
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testMoreSessions(KeycloakSession session) {
+ AtomicReference<List<UserSessionModel>> userSessionsAt = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionMS1) -> {
+ KeycloakSession currentSession = sessionMS1;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ // Create 10 userSessions - each having 1 clientSession
+ List<UserSessionModel> userSessions = new ArrayList<>();
+ UserModel user = currentSession.users().getUserByUsername("user1", realm);
+
+ for (int i = 0; i < 20; i++) {
+ // Having different offsets for each session (to ensure that lastSessionRefresh is also different)
+ Time.setOffset(i);
+
+ UserSessionModel userSession = currentSession.sessions().createUserSession(realm, user, "user1", "127.0.0.1", "form", true, null, null);
+ createClientSession(currentSession, realm.getClientByClientId("test-app"), userSession, "http://redirect", "state");
+ userSessions.add(userSession);
+ }
+
+ userSessionsAt.set(userSessions);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionMS2) -> {
+ KeycloakSession currentSession = sessionMS2;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ List<UserSessionModel> userSessions = userSessionsAt.get();
+
+ for (UserSessionModel userSession : userSessions) {
+ UserSessionModel userSession2 = currentSession.sessions().getUserSession(realm, userSession.getId());
+ persistUserSession(currentSession, userSession2, true);
+ }
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionMS3) -> {
+ KeycloakSession currentSession = sessionMS3;
+ RealmModel realm = currentSession.realms().getRealm("test");
+
+ List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(currentSession, true, 2, 10, 20);
+ UserModel user = currentSession.users().getUserByUsername("user1", realm);
+ ClientModel testApp = realm.getClientByClientId("test-app");
+
+ for (UserSessionModel loadedSession : loadedSessions) {
+ assertEquals(user.getId(), loadedSession.getUser().getId());
+ assertEquals("127.0.0.1", loadedSession.getIpAddress());
+ assertEquals(user.getUsername(), loadedSession.getLoginUsername());
+
+ assertEquals(1, loadedSession.getAuthenticatedClientSessions().size());
+ assertTrue(loadedSession.getAuthenticatedClientSessions().containsKey(testApp.getId()));
+ }
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testExpiredSessions(KeycloakSession session) {
+ UserSessionModel[][] origSessions = {new UserSessionModel[1]};
+ int started = Time.currentTime();
+ final UserSessionModel[] userSession1 = {null};
+ final UserSessionModel[] userSession2 = {null};
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionES) -> {
+ // Create some sessions in infinispan
+ UserSessionPersisterProvider persister = sessionES.getProvider(UserSessionPersisterProvider.class);
+ origSessions[0] = createSessions(sessionES);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionES2) -> {
+ // Persist 2 offline sessions of 2 users
+ RealmModel realm = sessionES2.realms().getRealm("test");
+ UserSessionPersisterProvider persister = sessionES2.getProvider(UserSessionPersisterProvider.class);
+ userSession1[0] = sessionES2.sessions().getUserSession(realm, origSessions[0][1].getId());
+ userSession2[0] = sessionES2.sessions().getUserSession(realm, origSessions[0][2].getId());
+ persistUserSession(sessionES2, userSession1[0], true);
+ persistUserSession(sessionES2, userSession2[0], true);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionES3) -> {
+ // Update one of the sessions with lastSessionRefresh of 20 days ahead
+ int lastSessionRefresh = Time.currentTime() + 1728000;
+ RealmModel realm = sessionES3.realms().getRealm("test");
+ UserSessionPersisterProvider persister = sessionES3.getProvider(UserSessionPersisterProvider.class);
+
+ persister.updateLastSessionRefreshes(realm, lastSessionRefresh, Collections.singleton(userSession1[0].getId()), true);
+
+ // Increase time offset - 40 days
+ Time.setOffset(3456000);
+ try {
+ // Run expiration thread
+ persister.removeExpired(realm);
+
+ // Test the updated session is still in persister. Not updated session is not there anymore
+ List<UserSessionModel> loadedSessions = loadPersistedSessionsPaginated(sessionES3, true, 10, 1, 1);
+ UserSessionModel persistedSession = loadedSessions.get(0);
+ UserSessionProviderTest.assertSession(persistedSession, sessionES3.users().getUserByUsername("user1", realm), "127.0.0.2", started, lastSessionRefresh, "test-app");
+
+ } finally {
+ // Cleanup
+ Time.setOffset(0);
+ }
+ });
+ }
+
+ private AuthenticatedClientSessionModel createClientSession(KeycloakSession session, ClientModel client, UserSessionModel userSession, String redirect, String state) {
+ RealmModel realm = session.realms().getRealm("test");
+ AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
+ clientSession.setRedirectUri(redirect);
+ if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
+ return clientSession;
+ }
+
+ private UserSessionModel[] createSessions(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealm("test");
+ UserSessionModel[] sessions = new UserSessionModel[3];
+ sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+
+ createClientSession(session, realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state");
+ createClientSession(session, realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state");
+
+ sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+ createClientSession(session, realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state");
+
+ sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
+ createClientSession(session, realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state");
+
+ return sessions;
+ }
+
+ private void persistUserSession(KeycloakSession session, UserSessionModel userSession, boolean offline) {
+ UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
+ persister.createUserSession(userSession, offline);
+ for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
+ persister.createClientSession(clientSession, offline);
+ }
+ }
+
+ public static void assertSessionLoaded(List<UserSessionModel> sessions, String id, UserModel user, String ipAddress, int started, int lastRefresh, String... clients) {
+ for (UserSessionModel session : sessions) {
+ if (session.getId().equals(id)) {
+ UserSessionProviderTest.assertSession(session, user, ipAddress, started, lastRefresh, clients);
+ return;
+ }
+ }
+ Assert.fail("Session with ID " + id + " not found in the list");
+ }
+
+ private List<UserSessionModel> loadPersistedSessionsPaginated(KeycloakSession session, boolean offline, int sessionsPerPage, int expectedPageCount, int expectedSessionsCount) {
+ UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
+
+ int count = persister.getUserSessionsCount(offline);
+
+ int pageCount = 0;
+ boolean next = true;
+ List<UserSessionModel> result = new ArrayList<>();
+ int lastCreatedOn = 0;
+ String lastSessionId = "abc";
+
+ while (next) {
+ List<UserSessionModel> sess = persister.loadUserSessions(0, sessionsPerPage, offline, lastCreatedOn, lastSessionId);
+
+ if (sess.size() < sessionsPerPage) {
+ next = false;
+
+ // We had at least some session
+ if (sess.size() > 0) {
+ pageCount++;
+ }
+ } else {
+ pageCount++;
+
+ UserSessionModel lastSession = sess.get(sess.size() - 1);
+ lastCreatedOn = lastSession.getStarted();
+ lastSessionId = lastSession.getId();
+ }
+
+ result.addAll(sess);
+ }
+
+ Assert.assertEquals(expectedPageCount, pageCount);
+ Assert.assertEquals(expectedSessionsCount, result.size());
+ return result;
+ }
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
new file mode 100644
index 0000000..b29028d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
@@ -0,0 +1,636 @@
+/*
+ * 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.model;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.common.util.Time;
+import org.keycloak.models.*;
+import org.keycloak.models.session.UserSessionPersisterProvider;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.ClientManager;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.managers.UserSessionManager;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UserSessionProviderOfflineTest extends AbstractTestRealmKeycloakTest {
+ private static KeycloakSession currentSession;
+ private static RealmModel realm;
+ private static UserSessionManager sessionManager;
+ private static UserSessionPersisterProvider persister;
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, UserSessionProviderOfflineTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+ @Before
+ public void before() {
+ testingClient.server().run(session -> {
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionBefore) -> {
+ reloadState(sessionBefore, true);
+ persister = sessionBefore.getProvider(UserSessionPersisterProvider.class);
+ });
+ });
+ }
+
+ @After
+ public void after() {
+ testingClient.server().run(session -> {
+ RealmModel realm = session.realms().getRealmByName("test");
+ session.sessions().removeUserSessions(realm);
+ UserModel user1 = session.users().getUserByUsername("user1", realm);
+ UserModel user2 = session.users().getUserByUsername("user2", realm);
+
+ UserManager um = new UserManager(session);
+ if (user1 != null) {
+ um.removeUser(realm, user1);
+ }
+ if (user2 != null) {
+ um.removeUser(realm, user2);
+ }
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testOfflineSessionsCrud(KeycloakSession session) {
+ Map<String, Set<String>> offlineSessions = new HashMap<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud) -> {
+ // Create some online sessions in infinispan
+ reloadState(sessionCrud);
+ createSessions(sessionCrud);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud2) -> {
+ currentSession = sessionCrud2;
+ realm = currentSession.realms().getRealm("test");
+ sessionManager = new UserSessionManager(currentSession);
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+
+ // Key is userSession ID, values are client UUIDS
+ // Persist 3 created userSessions and clientSessions as offline
+ ClientModel testApp = realm.getClientByClientId("test-app");
+ List<UserSessionModel> userSessions = currentSession.sessions().getUserSessions(realm, testApp);
+ for (UserSessionModel userSession : userSessions) {
+ offlineSessions.put(userSession.getId(), createOfflineSessionIncludeClientSessions(currentSession, userSession));
+ }
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud3) -> {
+ currentSession = sessionCrud3;
+ realm = currentSession.realms().getRealm("test");
+ sessionManager = new UserSessionManager(currentSession);
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+
+ // Assert all previously saved offline sessions found
+ for (Map.Entry<String, Set<String>> entry : offlineSessions.entrySet()) {
+ UserSessionModel offlineSession = sessionManager.findOfflineUserSession(realm, entry.getKey());
+ Assert.assertNotNull(offlineSession);
+ Assert.assertEquals(offlineSession.getAuthenticatedClientSessions().keySet(), entry.getValue());
+ }
+
+ // Find clients with offline token
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+
+ Set<ClientModel> clients = sessionManager.findClientsWithOfflineToken(realm, user1);
+ Assert.assertEquals(clients.size(), 2);
+ for (ClientModel client : clients) {
+ Assert.assertTrue(client.getClientId().equals("test-app") || client.getClientId().equals("third-party"));
+ }
+
+ UserModel user2 = currentSession.users().getUserByUsername("user2", realm);
+
+ clients = sessionManager.findClientsWithOfflineToken(realm, user2);
+ Assert.assertEquals(clients.size(), 1);
+ Assert.assertEquals("test-app", clients.iterator().next().getClientId());
+
+ // Test count
+ ClientModel testApp = realm.getClientByClientId("test-app");
+ ClientModel thirdparty = realm.getClientByClientId("third-party");
+ Assert.assertEquals(3, currentSession.sessions().getOfflineSessionsCount(realm, testApp));
+ Assert.assertEquals(1, currentSession.sessions().getOfflineSessionsCount(realm, thirdparty));
+ // Revoke "test-app" for user1
+ sessionManager.revokeOfflineToken(user1, testApp);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud4) -> {
+ currentSession = sessionCrud4;
+ realm = currentSession.realms().getRealm("test");
+ sessionManager = new UserSessionManager(currentSession);
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+
+ // Assert userSession revoked
+ ClientModel testApp = realm.getClientByClientId("test-app");
+ ClientModel thirdparty = realm.getClientByClientId("third-party");
+
+ // Still 2 sessions. The count of sessions by client may not be accurate after revoke due the
+ // performance optimizations (the "127.0.0.1" currentSession still has another client "thirdparty" in it)
+ Assert.assertEquals(2, currentSession.sessions().getOfflineSessionsCount(realm, testApp));
+ Assert.assertEquals(1, currentSession.sessions().getOfflineSessionsCount(realm, thirdparty));
+
+ List<UserSessionModel> thirdpartySessions = currentSession.sessions().getOfflineUserSessions(realm, thirdparty, 0, 10);
+ Assert.assertEquals(1, thirdpartySessions.size());
+ Assert.assertEquals("127.0.0.1", thirdpartySessions.get(0).getIpAddress());
+ Assert.assertEquals("user1", thirdpartySessions.get(0).getUser().getUsername());
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+ UserModel user2 = currentSession.users().getUserByUsername("user2", realm);
+
+ Set<ClientModel> clients = sessionManager.findClientsWithOfflineToken(realm, user1);
+ Assert.assertEquals(1, clients.size());
+ Assert.assertEquals("third-party", clients.iterator().next().getClientId());
+ clients = sessionManager.findClientsWithOfflineToken(realm, user2);
+ Assert.assertEquals(1, clients.size());
+ Assert.assertEquals("test-app", clients.iterator().next().getClientId());
+
+ // Revoke the second currentSession for user1 too.
+ sessionManager.revokeOfflineToken(user1, thirdparty);
+
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud5) -> {
+ currentSession = sessionCrud5;
+ realm = currentSession.realms().getRealm("test");
+ sessionManager = new UserSessionManager(currentSession);
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+
+ ClientModel testApp = realm.getClientByClientId("test-app");
+ ClientModel thirdparty = realm.getClientByClientId("third-party");
+
+ // Accurate count now. All sessions of user1 cleared
+ Assert.assertEquals(1, currentSession.sessions().getOfflineSessionsCount(realm, testApp));
+ Assert.assertEquals(0, currentSession.sessions().getOfflineSessionsCount(realm, thirdparty));
+
+ List<UserSessionModel> testAppSessions = currentSession.sessions().getOfflineUserSessions(realm, testApp, 0, 10);
+
+ Assert.assertEquals(1, testAppSessions.size());
+ Assert.assertEquals("127.0.0.3", testAppSessions.get(0).getIpAddress());
+ Assert.assertEquals("user2", testAppSessions.get(0).getUser().getUsername());
+
+ UserModel user1 = currentSession.users().getUserByUsername("user1", realm);
+
+ Set<ClientModel> clients = sessionManager.findClientsWithOfflineToken(realm, user1);
+ Assert.assertEquals(0, clients.size());
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testOnRealmRemoved(KeycloakSession session) {
+ AtomicReference<String> userSessionID = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR1) -> {
+ currentSession = sessionRR1;
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+ RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
+ fooRealm.addClient("foo-app");
+ currentSession.users().addUser(fooRealm, "user3");
+
+ UserSessionModel userSession = currentSession.sessions().createUserSession(fooRealm, currentSession.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+ userSessionID.set(userSession.getId());
+
+ createClientSession(currentSession, fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR2) -> {
+ currentSession = sessionRR2;
+ sessionManager = new UserSessionManager(currentSession);
+
+ // Persist offline session
+ RealmModel fooRealm = currentSession.realms().getRealm("foo");
+ UserSessionModel userSession = currentSession.sessions().getUserSession(fooRealm, userSessionID.get());
+ createOfflineSessionIncludeClientSessions(currentSession, userSession);
+
+ UserSessionModel offlineUserSession = sessionManager.findOfflineUserSession(fooRealm, userSession.getId());
+ Assert.assertEquals(offlineUserSession.getAuthenticatedClientSessions().size(), 1);
+ AuthenticatedClientSessionModel offlineClientSession = offlineUserSession.getAuthenticatedClientSessions().values().iterator().next();
+ Assert.assertEquals("foo-app", offlineClientSession.getClient().getClientId());
+ Assert.assertEquals("user3", offlineClientSession.getUserSession().getUser().getUsername());
+
+ // Remove realm
+ RealmManager realmMgr = new RealmManager(currentSession);
+ realmMgr.removeRealm(realmMgr.getRealm("foo"));
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR3) -> {
+ currentSession = sessionRR3;
+ RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
+
+ fooRealm.addClient("foo-app");
+ currentSession.users().addUser(fooRealm, "user3");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR4) -> {
+ currentSession = sessionRR4;
+ RealmModel fooRealm = currentSession.realms().getRealm("foo");
+
+ Assert.assertEquals(0, currentSession.sessions().getOfflineSessionsCount(fooRealm, fooRealm.getClientByClientId("foo-app")));
+
+ // Cleanup
+ RealmManager realmMgr = new RealmManager(currentSession);
+ realmMgr.removeRealm(realmMgr.getRealm("foo"));
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testOnClientRemoved(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR) -> {
+ try {
+ int started = Time.currentTime();
+ AtomicReference<String> userSessionID = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR1) -> {
+ currentSession = sessionCR1;
+ sessionManager = new UserSessionManager(currentSession);
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+ RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
+
+ fooRealm.addClient("foo-app");
+ fooRealm.addClient("bar-app");
+ currentSession.users().addUser(fooRealm, "user3");
+
+ UserSessionModel userSession = currentSession.sessions().createUserSession(fooRealm, currentSession.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+ userSessionID.set(userSession.getId());
+
+ createClientSession(currentSession, fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
+ createClientSession(currentSession, fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR2) -> {
+ currentSession = sessionCR2;
+ // Create offline currentSession
+ RealmModel fooRealm = currentSession.realms().getRealm("foo");
+ UserSessionModel userSession = currentSession.sessions().getUserSession(fooRealm, userSessionID.get());
+ createOfflineSessionIncludeClientSessions(currentSession, userSession);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR3) -> {
+ currentSession = sessionCR3;
+ RealmManager realmMgr = new RealmManager(currentSession);
+ ClientManager clientMgr = new ClientManager(realmMgr);
+ RealmModel fooRealm = realmMgr.getRealm("foo");
+
+ // Assert currentSession was persisted with both clientSessions
+ UserSessionModel offlineSession = currentSession.sessions().getOfflineUserSession(fooRealm, userSessionID.get());
+ assertSession(offlineSession, currentSession.users().getUserByUsername("user3", fooRealm), "127.0.0.1", started, started, "foo-app", "bar-app");
+
+ // Remove foo-app client
+ ClientModel client = fooRealm.getClientByClientId("foo-app");
+ clientMgr.removeClient(fooRealm, client);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR4) -> {
+ currentSession = sessionCR4;
+ RealmManager realmMgr = new RealmManager(currentSession);
+ ClientManager clientMgr = new ClientManager(realmMgr);
+ RealmModel fooRealm = realmMgr.getRealm("foo");
+
+ // Assert just one bar-app clientSession persisted now
+ UserSessionModel offlineSession = currentSession.sessions().getOfflineUserSession(fooRealm, userSessionID.get());
+ Assert.assertEquals(1, offlineSession.getAuthenticatedClientSessions().size());
+ Assert.assertEquals("bar-app", offlineSession.getAuthenticatedClientSessions().values().iterator().next().getClient().getClientId());
+
+ // Remove bar-app client
+ ClientModel client = fooRealm.getClientByClientId("bar-app");
+ clientMgr.removeClient(fooRealm, client);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR5) -> {
+ currentSession = sessionCR5;
+ // Assert nothing loaded - userSession was removed as well because it was last userSession
+ RealmManager realmMgr = new RealmManager(currentSession);
+ RealmModel fooRealm = realmMgr.getRealm("foo");
+ UserSessionModel offlineSession = currentSession.sessions().getOfflineUserSession(fooRealm, userSessionID.get());
+ Assert.assertEquals(0, offlineSession.getAuthenticatedClientSessions().size());
+ });
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionTearDown) -> {
+ currentSession = sessionTearDown;
+ RealmManager realmMgr = new RealmManager(currentSession);
+ RealmModel fooRealm = realmMgr.getRealm("foo");
+ UserModel user3 = currentSession.users().getUserByUsername("user3", fooRealm);
+
+ // Remove user3
+ new UserManager(currentSession).removeUser(fooRealm, user3);
+
+ // Cleanup
+ realmMgr = new RealmManager(currentSession);
+ realmMgr.removeRealm(realmMgr.getRealm("foo"));
+ });
+ }
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testOnUserRemoved(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionUR) -> {
+ try {
+ int started = Time.currentTime();
+ AtomicReference<String> userSessionID = new AtomicReference<>();
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionUR1) -> {
+ currentSession = sessionUR1;
+ RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
+ fooRealm.addClient("foo-app");
+ currentSession.users().addUser(fooRealm, "user3");
+
+ UserSessionModel userSession = currentSession.sessions().createUserSession(fooRealm, currentSession.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+ userSessionID.set(userSession.getId());
+
+ createClientSession(currentSession, fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionUR2) -> {
+ currentSession = sessionUR2;
+
+ // Create offline session
+ RealmModel fooRealm = currentSession.realms().getRealm("foo");
+ UserSessionModel userSession = currentSession.sessions().getUserSession(fooRealm, userSessionID.get());
+ createOfflineSessionIncludeClientSessions(currentSession, userSession);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionUR3) -> {
+ currentSession = sessionUR3;
+
+ RealmManager realmMgr = new RealmManager(currentSession);
+ RealmModel fooRealm = realmMgr.getRealm("foo");
+ UserModel user3 = currentSession.users().getUserByUsername("user3", fooRealm);
+
+ // Assert session was persisted with both clientSessions
+ UserSessionModel offlineSession = currentSession.sessions().getOfflineUserSession(fooRealm, userSessionID.get());
+ assertSession(offlineSession, user3, "127.0.0.1", started, started, "foo-app");
+ });
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionTearDown) -> {
+ currentSession = sessionTearDown;
+
+ RealmManager realmMgr = new RealmManager(currentSession);
+ RealmModel fooRealm = realmMgr.getRealm("foo");
+ UserModel user3 = currentSession.users().getUserByUsername("user3", fooRealm);
+
+ // Remove user3
+ new UserManager(currentSession).removeUser(fooRealm, user3);
+
+ // Cleanup
+ realmMgr = new RealmManager(currentSession);
+ realmMgr.removeRealm(realmMgr.getRealm("foo"));
+ });
+ }
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testExpired(KeycloakSession session) {
+ AtomicReference<UserSessionModel[]> origSessionsAt = new AtomicReference<>();
+
+ // Key is userSessionId, value is set of client UUIDS
+ Map<String, Set<String>> offlineSessions = new HashMap<>();
+ ClientModel[] testApp = new ClientModel[1];
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionExpired1) -> {
+ // Create some online sessions in infinispan
+ currentSession = sessionExpired1;
+ reloadState(currentSession);
+ UserSessionModel[] origSessions = createSessions(currentSession);
+ origSessionsAt.set(origSessions);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionExpired2) -> {
+ currentSession = sessionExpired2;
+ realm = currentSession.realms().getRealm("test");
+ sessionManager = new UserSessionManager(currentSession);
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+
+ // Persist 3 created userSessions and clientSessions as offline
+ testApp[0] = realm.getClientByClientId("test-app");
+ List<UserSessionModel> userSessions = currentSession.sessions().getUserSessions(realm, testApp[0]);
+ for (UserSessionModel userSession : userSessions) {
+ offlineSessions.put(userSession.getId(), createOfflineSessionIncludeClientSessions(currentSession, userSession));
+ }
+
+ // Assert all previously saved offline sessions found
+ for (Map.Entry<String, Set<String>> entry : offlineSessions.entrySet()) {
+ UserSessionModel foundSession = sessionManager.findOfflineUserSession(realm, entry.getKey());
+ Assert.assertEquals(foundSession.getAuthenticatedClientSessions().keySet(), entry.getValue());
+ }
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionExpired3) -> {
+ currentSession = sessionExpired3;
+ realm = currentSession.realms().getRealm("test");
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+ UserSessionModel[] origSessions = origSessionsAt.get();
+
+ UserSessionModel session0 = currentSession.sessions().getOfflineUserSession(realm, origSessions[0].getId());
+ Assert.assertNotNull(session0);
+
+ // sessions are in persister too
+ Assert.assertEquals(3, persister.getUserSessionsCount(true));
+
+ Time.setOffset(300);
+
+ // Set lastSessionRefresh to currentSession[0] to 0
+ session0.setLastSessionRefresh(Time.currentTime());
+ });
+
+ try {
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionExpired4) -> {
+ currentSession = sessionExpired4;
+ realm = currentSession.realms().getRealm("test");
+ UserSessionModel[] origSessions = origSessionsAt.get();
+ // Increase timeOffset - 20 days
+ Time.setOffset(1728000);
+
+ UserSessionModel session0 = currentSession.sessions().getOfflineUserSession(realm, origSessions[0].getId());
+ session0.setLastSessionRefresh(Time.currentTime());
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionExpired5) -> {
+ currentSession = sessionExpired5;
+ realm = currentSession.realms().getRealm("test");
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+
+ // Increase timeOffset - 40 days
+ Time.setOffset(3456000);
+
+ // Expire and ensure that all sessions despite session0 were removed
+ currentSession.sessions().removeExpired(realm);
+ persister.removeExpired(realm);
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionExpired6) -> {
+ currentSession = sessionExpired6;
+ realm = currentSession.realms().getRealm("test");
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+ UserSessionModel[] origSessions = origSessionsAt.get();
+
+ // assert session0 is the only session found
+ Assert.assertNotNull(currentSession.sessions().getOfflineUserSession(realm, origSessions[0].getId()));
+ Assert.assertNull(currentSession.sessions().getOfflineUserSession(realm, origSessions[1].getId()));
+ Assert.assertNull(currentSession.sessions().getOfflineUserSession(realm, origSessions[2].getId()));
+
+ Assert.assertEquals(1, persister.getUserSessionsCount(true));
+
+ // Expire everything and assert nothing found
+ Time.setOffset(6000000);
+
+ currentSession.sessions().removeExpired(realm);
+ persister.removeExpired(realm);
+
+ });
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionExpired7) -> {
+ currentSession = sessionExpired7;
+ realm = currentSession.realms().getRealm("test");
+ sessionManager = new UserSessionManager(currentSession);
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+
+ for (String userSessionId : offlineSessions.keySet()) {
+ Assert.assertNull(sessionManager.findOfflineUserSession(realm, userSessionId));
+ }
+ Assert.assertEquals(0, persister.getUserSessionsCount(true));
+ });
+
+ } finally {
+ Time.setOffset(0);
+ }
+ }
+
+
+ private static Set<String> createOfflineSessionIncludeClientSessions(KeycloakSession session, UserSessionModel
+ userSession) {
+ Set<String> offlineSessions = new HashSet<>();
+ UserSessionManager localManager = new UserSessionManager(session);
+ for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
+ localManager.createOrUpdateOfflineSession(clientSession, userSession);
+ offlineSessions.add(clientSession.getClient().getId());
+ }
+
+ return offlineSessions;
+ }
+
+ public static void assertSession(UserSessionModel session, UserModel user, String ipAddress, int started,
+ int lastRefresh, String... clients) {
+ assertEquals(user.getId(), session.getUser().getId());
+ assertEquals(ipAddress, session.getIpAddress());
+ assertEquals(user.getUsername(), session.getLoginUsername());
+ assertEquals("form", session.getAuthMethod());
+ assertTrue(session.isRememberMe());
+ assertTrue((session.getStarted() >= started - 1) && (session.getStarted() <= started + 1));
+ assertTrue((session.getLastSessionRefresh() >= lastRefresh - 1) && (session.getLastSessionRefresh() <= lastRefresh + 1));
+
+ String[] actualClients = new String[session.getAuthenticatedClientSessions().size()];
+ int i = 0;
+ for (Map.Entry<String, AuthenticatedClientSessionModel> entry : session.getAuthenticatedClientSessions().entrySet()) {
+ String clientUUID = entry.getKey();
+ AuthenticatedClientSessionModel clientSession = entry.getValue();
+ Assert.assertEquals(clientUUID, clientSession.getClient().getId());
+ actualClients[i] = clientSession.getClient().getClientId();
+ i++;
+ }
+ }
+
+ private static AuthenticatedClientSessionModel createClientSession(KeycloakSession sessionParam, ClientModel
+ client, UserSessionModel userSession, String redirect, String state) {
+ AuthenticatedClientSessionModel clientSession = sessionParam.sessions().createClientSession(client.getRealm(), client, userSession);
+ clientSession.setRedirectUri(redirect);
+ if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
+ return clientSession;
+ }
+
+ private static UserSessionModel[] createSessions(KeycloakSession session) {
+ UserSessionModel[] sessions = new UserSessionModel[3];
+ sessions[0] = session.sessions().createUserSession(realm, currentSession.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+
+ Set<String> roles = new HashSet<String>();
+ roles.add("one");
+ roles.add("two");
+
+ Set<String> protocolMappers = new HashSet<String>();
+ protocolMappers.add("mapper-one");
+ protocolMappers.add("mapper-two");
+
+ createClientSession(session, realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state");
+ createClientSession(session, realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state");
+
+ sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+ createClientSession(session, realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state");
+
+ sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
+ createClientSession(session, realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state");
+
+ return sessions;
+ }
+
+ public static void reloadState(KeycloakSession session) {
+ reloadState(session, false);
+ }
+
+ public static void reloadState(KeycloakSession session, Boolean initialConfig) {
+ currentSession = session;
+ realm = currentSession.realms().getRealm("test");
+ if (initialConfig) {
+ currentSession.users().addUser(realm, "user1").setEmail("user1@localhost");
+ currentSession.users().addUser(realm, "user2").setEmail("user2@localhost");
+ }
+ sessionManager = new UserSessionManager(currentSession);
+ persister = currentSession.getProvider(UserSessionPersisterProvider.class);
+ }
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+
+ }
+}