keycloak-memoizeit
Changes
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java 30(+30 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/ModelTest.java 36(+36 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerTestExtension.java 4(+3 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ModelTestExecutor.java 75(+75 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java 15(+15 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java 7(+7 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CacheTest.java 210(+119 -91)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java 186(+186 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/ImportTest.java 99(+56 -43)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/SimpleModelTest.java 140(+140 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java 721(+721 -0)
Details
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
index 7ea49d2..0c0ae39 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
@@ -89,6 +89,9 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.Response;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -773,6 +776,33 @@ public class TestingResourceProvider implements RealmResourceProvider {
}
}
+
+ @POST
+ @Path("/run-model-test-on-server")
+ @Consumes(MediaType.TEXT_PLAIN_UTF_8)
+ @Produces(MediaType.TEXT_PLAIN_UTF_8)
+ public String runModelTestOnServer(@QueryParam("testClassName") String testClassName,
+ @QueryParam("testMethodName") String testMethodName) throws Exception {
+ try {
+ ClassLoader cl = ModuleUtil.isModules() ? ModuleUtil.getClassLoader() : getClass().getClassLoader();
+
+ Class testClass = cl.loadClass(testClassName);
+ Method testMethod = testClass.getDeclaredMethod(testMethodName, KeycloakSession.class);
+
+ Object test = testClass.newInstance();
+ testMethod.invoke(test, session);
+
+ return "SUCCESS";
+ } catch (Throwable t) {
+ if (t instanceof InvocationTargetException) {
+ t = ((InvocationTargetException) t).getTargetException();
+ }
+
+ return SerializationUtil.encodeException(t);
+ }
+ }
+
+
@Path("/javascript")
public TestJavascriptResource getJavascriptResource() {
return new TestJavascriptResource();
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/ModelTest.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/ModelTest.java
new file mode 100644
index 0000000..673c98a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/ModelTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 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.arquillian.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * The annotation on test methods. The annotated method MUST have single parameter - KeycloakSession
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ElementType.METHOD}) // TODO: Maybe ElementClass.TYPE too? That way it will be possible to add the annotation on the the test class and not need to add on all the test methods inside the class
+public @interface ModelTest {
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerTestExtension.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerTestExtension.java
index 3cb9cc7..4d72cec 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerTestExtension.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakContainerTestExtension.java
@@ -49,6 +49,7 @@ import org.jboss.arquillian.test.impl.context.TestContextImpl;
import org.jboss.arquillian.test.impl.enricher.resource.ArquillianResourceTestEnricher;
import org.jboss.arquillian.test.spi.TestEnricher;
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
+import org.keycloak.testsuite.arquillian.ModelTestExecutor;
/**
* KeycloakContainerTestExtension
@@ -104,7 +105,8 @@ public class KeycloakContainerTestExtension implements LoadableExtension {
.observer(ClientDeployerCreator.class)
.observer(ClientBeforeAfterLifecycleEventExecuter.class)
.observer(ClientTestExecuter.class)
- .observer(LocalTestExecuter.class)
+// .observer(LocalTestExecuter.class)
+ .observer(ModelTestExecutor.class)
.observer(RemoteTestExecuter.class)
.observer(DeploymentCommandObserver.class)
.observer(ContainerCommandObserver.class)
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ModelTestExecutor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ModelTestExecutor.java
new file mode 100644
index 0000000..30f6bf5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ModelTestExecutor.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 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.arquillian;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.jboss.arquillian.container.test.impl.execution.LocalTestExecuter;
+import org.jboss.arquillian.container.test.impl.execution.event.LocalExecutionEvent;
+import org.jboss.arquillian.core.api.Instance;
+import org.jboss.arquillian.core.api.InstanceProducer;
+import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.test.spi.TestResult;
+import org.keycloak.common.util.reflections.Reflections;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.client.KeycloakTestingClient;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ModelTestExecutor extends LocalTestExecuter {
+
+ @Inject
+ private Instance<TestContext> testContext;
+
+ @Override
+ public void execute(LocalExecutionEvent event) throws Exception {
+ Method testMethod = event.getExecutor().getMethod();
+
+ ModelTest annotation = testMethod.getAnnotation(ModelTest.class);
+
+ if (annotation == null) {
+ // Not a model test
+ super.execute(event);
+ } else {
+ TestResult result = new TestResult();
+
+ try {
+ // Model test - wrap the call inside the
+ TestContext ctx = testContext.get();
+ KeycloakTestingClient testingClient = ctx.getTestingClient();
+
+ testingClient.server().runModelTest(testMethod.getDeclaringClass().getName(), testMethod.getName());
+ result.setStatus(TestResult.Status.PASSED);
+ } catch (Throwable e) {
+ result.setStatus(TestResult.Status.FAILED);
+ result.setThrowable(e);
+ } finally {
+ result.setEnd(System.currentTimeMillis());
+ }
+
+ // Need to use reflection this way...
+ Field testResultField = Reflections.findDeclaredField(LocalTestExecuter.class, "testResult");
+ testResultField.setAccessible(true);
+ InstanceProducer<TestResult> thisTestResult = (InstanceProducer<TestResult>) testResultField.get(this);
+
+ thisTestResult.set(result);
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
index 92c71fb..aa6cc0d 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java
@@ -133,6 +133,21 @@ public class KeycloakTestingClient {
}
}
+
+ public void runModelTest(String testClassName, String testMethodName) throws RunOnServerException {
+ String result = testing(realm != null ? realm : "master").runModelTestOnServer(testClassName, testMethodName);
+
+ if (result != null && !result.isEmpty() && result.trim().startsWith("EXCEPTION:")) {
+ Throwable t = SerializationUtil.decodeException(result);
+
+ if (t instanceof AssertionError) {
+ throw (AssertionError) t;
+ } else {
+ throw new RunOnServerException(t);
+ }
+ }
+ }
+
}
public void close() {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
index a217ec1..cd2b888 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java
@@ -290,6 +290,13 @@ public interface TestingResource {
@Produces(MediaType.TEXT_PLAIN_UTF_8)
String runOnServer(String runOnServer);
+ @POST
+ @Path("/run-model-test-on-server")
+ @Consumes(MediaType.TEXT_PLAIN_UTF_8)
+ @Produces(MediaType.TEXT_PLAIN_UTF_8)
+ String runModelTestOnServer(@QueryParam("testClassName") String testClassName,
+ @QueryParam("testMethodName") String testMethodName);
+
@GET
@Path("js/keycloak.js")
@Produces(MediaType.TEXT_HTML_UTF_8)
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java
new file mode 100755
index 0000000..07b15d4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/CompositeRolesModelTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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.HashSet;
+import java.util.Set;
+
+import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CompositeRolesModelTest extends AbstractTestRealmKeycloakTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, CompositeRolesModelTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+
+ public static Set<RoleModel> getRequestedRoles(ClientModel application, UserModel user) {
+
+ Set<RoleModel> requestedRoles = new HashSet<>();
+
+ Set<RoleModel> roleMappings = user.getRoleMappings();
+ Set<RoleModel> scopeMappings = application.getScopeMappings();
+ Set<RoleModel> appRoles = application.getRoles();
+ if (appRoles != null) scopeMappings.addAll(appRoles);
+
+ for (RoleModel role : roleMappings) {
+ if (role.getContainer().equals(application)) requestedRoles.add(role);
+ for (RoleModel desiredRole : scopeMappings) {
+ Set<RoleModel> visited = new HashSet<>();
+ applyScope(role, desiredRole, visited, requestedRoles);
+ }
+ }
+ return requestedRoles;
+ }
+
+
+
+ private static void applyScope(RoleModel role, RoleModel scope, Set<RoleModel> visited, Set<RoleModel> requested) {
+ if (visited.contains(scope)) return;
+ visited.add(scope);
+ if (role.hasRole(scope)) {
+ requested.add(scope);
+ return;
+ }
+ if (!scope.isComposite()) return;
+
+ for (RoleModel contained : scope.getComposites()) {
+ applyScope(role, contained, visited, requested);
+ }
+ }
+
+ private static RoleModel getRole(RealmModel realm, String appName, String roleName) {
+ if ("realm".equals(appName)) {
+ return realm.getRole(roleName);
+ } else {
+ return realm.getClientByClientId(appName).getRole(roleName);
+ }
+ }
+
+ private static void assertContains(RealmModel realm, String appName, String roleName, Set<RoleModel> requestedRoles) {
+ RoleModel expectedRole = getRole(realm, appName, roleName);
+
+ Assert.assertTrue(requestedRoles.contains(expectedRole));
+
+ // Check if requestedRole has correct role container
+ for (RoleModel role : requestedRoles) {
+ if (role.equals(expectedRole)) {
+ Assert.assertEquals(role.getContainer(), expectedRole.getContainer());
+ }
+ }
+ }
+
+ @Test
+ @ModelTest
+ public void testNoClientID(KeycloakSession session) {
+ expectedException.expect(RuntimeException.class);
+ expectedException.expectMessage("Unknown client specification in scope mappings: some-client");
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session1) -> {
+ try {
+ //RealmManager manager = new RealmManager(session1);
+ RealmRepresentation rep = loadJson(getClass().getResourceAsStream("/model/testrealm-noclient-id.json"), RealmRepresentation.class);
+ rep.setId("TestNoClientID");
+ //manager.importRealm(rep);
+ adminClient.realms().create(rep);
+ } catch (RuntimeException e) {
+ }
+
+ });
+ }
+
+ @Test
+ @ModelTest
+ public void testComposites(KeycloakSession session) {
+
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session5) -> {
+
+ RealmModel realm = session5.realms().getRealm("TestComposites");
+
+ Set<RoleModel> requestedRoles = getRequestedRoles(realm.getClientByClientId("APP_COMPOSITE_APPLICATION"), session.users().getUserByUsername("APP_COMPOSITE_USER", realm));
+
+ Assert.assertEquals(5, requestedRoles.size());
+ assertContains(realm, "APP_COMPOSITE_APPLICATION", "APP_COMPOSITE_ROLE", requestedRoles);
+ assertContains(realm, "APP_COMPOSITE_APPLICATION", "APP_COMPOSITE_CHILD", requestedRoles);
+ assertContains(realm, "APP_COMPOSITE_APPLICATION", "APP_ROLE_2", requestedRoles);
+ assertContains(realm, "APP_ROLE_APPLICATION", "APP_ROLE_1", requestedRoles);
+ assertContains(realm, "realm", "REALM_ROLE_1", requestedRoles);
+
+ Set<RoleModel> requestedRoles2 = getRequestedRoles(realm.getClientByClientId("APP_COMPOSITE_APPLICATION"), session5.users().getUserByUsername("REALM_APP_COMPOSITE_USER", realm));
+ Assert.assertEquals(4, requestedRoles2.size());
+ assertContains(realm, "APP_ROLE_APPLICATION", "APP_ROLE_1", requestedRoles2);
+
+ requestedRoles = getRequestedRoles(realm.getClientByClientId("REALM_COMPOSITE_1_APPLICATION"), session5.users().getUserByUsername("REALM_COMPOSITE_1_USER", realm));
+ Assert.assertEquals(1, requestedRoles.size());
+ assertContains(realm, "realm", "REALM_COMPOSITE_1", requestedRoles);
+
+ requestedRoles = getRequestedRoles(realm.getClientByClientId("REALM_COMPOSITE_2_APPLICATION"), session5.users().getUserByUsername("REALM_COMPOSITE_1_USER", realm));
+ Assert.assertEquals(3, requestedRoles.size());
+ assertContains(realm, "realm", "REALM_COMPOSITE_1", requestedRoles);
+ assertContains(realm, "realm", "REALM_COMPOSITE_CHILD", requestedRoles);
+ assertContains(realm, "realm", "REALM_ROLE_4", requestedRoles);
+
+ requestedRoles = getRequestedRoles(realm.getClientByClientId("REALM_ROLE_1_APPLICATION"), session5.users().getUserByUsername("REALM_COMPOSITE_1_USER", realm));
+ Assert.assertEquals(1, requestedRoles.size());
+ assertContains(realm, "realm", "REALM_ROLE_1", requestedRoles);
+
+ requestedRoles = getRequestedRoles(realm.getClientByClientId("REALM_COMPOSITE_1_APPLICATION"), session5.users().getUserByUsername("REALM_ROLE_1_USER", realm));
+ Assert.assertEquals(1, requestedRoles.size());
+ assertContains(realm, "realm", "REALM_ROLE_1", requestedRoles);
+ });
+
+ }
+
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ log.infof("testcomposites imported");
+ RealmRepresentation newRealm = loadJson(getClass().getResourceAsStream("/model/testcomposites2.json"), RealmRepresentation.class);
+ newRealm.setId("TestComposites");
+ adminClient.realms().create(newRealm);
+
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/SimpleModelTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/SimpleModelTest.java
new file mode 100644
index 0000000..62d5229
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/SimpleModelTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2017 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 java.util.List;
+
+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.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.arquillian.annotation.ModelTest;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+import org.keycloak.testsuite.runonserver.RunOnServerException;
+
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SimpleModelTest extends AbstractKeycloakTest {
+
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, SimpleModelTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ }
+
+
+ @Test
+ @ModelTest
+ public void simpleModelTest(KeycloakSession session) {
+ log.infof("simpleModelTest");
+ RealmModel realm = session.realms().getRealmByName("master");
+
+ Assert.assertNotNull("Master realm was not found!", realm);
+ }
+
+
+ @Test
+ @ModelTest
+ public void simpleModelTestWithNestedTransactions(KeycloakSession session) {
+ log.infof("simpleModelTestWithNestedTransactions");
+
+ // Transaction 1
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session1) -> {
+
+ session1.realms().createRealm("foo");
+
+ });
+
+ // Transaction 2 - should be able to see the created realm. Update it
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session2) -> {
+
+ RealmModel realm = session2.realms().getRealmByName("foo");
+ Assert.assertNotNull(realm);
+
+ realm.setAttribute("bar", "baz");
+
+ });
+
+ // Transaction 3 - Doublecheck update is visible. Then rollback transaction!
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session3) -> {
+
+ RealmModel realm = session3.realms().getRealmByName("foo");
+ Assert.assertNotNull(realm);
+
+ String attrValue = realm.getAttribute("bar");
+ Assert.assertEquals("baz", attrValue);
+
+ realm.setAttribute("bar", "baz2");
+
+ session3.getTransactionManager().setRollbackOnly();
+ });
+
+ // Transaction 4 - should still see the old value of attribute. Delete realm
+ KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session4) -> {
+
+ RealmModel realm = session4.realms().getRealmByName("foo");
+ Assert.assertNotNull(realm);
+
+ String attrValue = realm.getAttribute("bar");
+ Assert.assertEquals("baz", attrValue);
+
+ new RealmManager(session4).removeRealm(realm);
+ });
+ }
+
+
+ // Just for the test that AssertionError is correctly propagated
+ @Test(expected = AssertionError.class)
+ @ModelTest
+ public void simpleModelTestWithAssertionError(KeycloakSession session) {
+ log.infof("simpleModelTestWithAssertionError");
+ RealmModel realm = session.realms().getRealmByName("masterr");
+
+ // This should fail and throw the AssertionError
+ Assert.assertNotNull("Master realm was not found!", realm);
+ }
+
+
+ // Just for the test that other exception is correctly propagated
+ @Test(expected = RunOnServerException.class)
+ @ModelTest
+ public void simpleModelTestWithOtherError(KeycloakSession session) {
+ log.infof("simpleModelTestWithOtherError");
+ throw new RuntimeException("Some strange exception");
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
new file mode 100755
index 0000000..4dc0fe9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -0,0 +1,721 @@
+/*
+ * 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.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+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.*;
+
+import static org.junit.Assert.*;
+import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UserSessionProviderTest extends AbstractTestRealmKeycloakTest {
+
+ @Deployment
+ @TargetsContainer(AUTH_SERVER_CURRENT)
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(UserResource.class, UserSessionProviderTest.class)
+ .addPackages(true,
+ "org.keycloak.testsuite",
+ "org.keycloak.testsuite.model");
+ }
+
+
+ public static void setupRealm(KeycloakSession session){
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserModel user1 = session.users().addUser(realm, "user1");
+ user1.setEmail("user1@localhost");
+ UserModel user2 = session.users().addUser(realm, "user2");
+ user2.setEmail("user2@localhost");
+ }
+ @Before
+ public void before() {
+ testingClient.server().run( session -> {
+ RealmModel realm = session.realms().getRealmByName("test");
+ 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().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 testCreateSessions(KeycloakSession session) {
+ int started = Time.currentTime();
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ assertSession(session.sessions().getUserSession(realm, sessions[0].getId()), session.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
+ assertSession(session.sessions().getUserSession(realm, sessions[1].getId()), session.users().getUserByUsername("user1", realm), "127.0.0.2", started, started, "test-app");
+ assertSession(session.sessions().getUserSession(realm, sessions[2].getId()), session.users().getUserByUsername("user2", realm), "127.0.0.3", started, started, "test-app");
+ }
+
+ @Test
+ @ModelTest
+ public void testUpdateSession(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+ session.sessions().getUserSession(realm, sessions[0].getId()).setLastSessionRefresh(1000);
+
+ assertEquals(1000, session.sessions().getUserSession(realm, sessions[0].getId()).getLastSessionRefresh());
+ }
+
+ @Test
+ @ModelTest
+ public void testUpdateSessionInSameTransaction(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+ session.sessions().getUserSession(realm, sessions[0].getId()).setLastSessionRefresh(1000);
+ assertEquals(1000, session.sessions().getUserSession(realm, sessions[0].getId()).getLastSessionRefresh());
+ }
+
+ @Test
+ @ModelTest
+ public void testRestartSession(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ int started = Time.currentTime();
+ UserSessionModel[] sessions = createSessions(session);
+
+ Time.setOffset(100);
+
+ UserSessionModel userSession = session.sessions().getUserSession(realm, sessions[0].getId());
+ assertSession(userSession, session.users().getUserByUsername("user1", realm), "127.0.0.1", started, started, "test-app", "third-party");
+
+ userSession.restartSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.6", "form", true, null, null);
+
+ userSession = session.sessions().getUserSession(realm, sessions[0].getId());
+ assertSession(userSession, session.users().getUserByUsername("user2", realm), "127.0.0.6", started + 100, started + 100);
+
+ Time.setOffset(0);
+ }
+
+ @Test
+ @ModelTest
+ public void testCreateClientSession(KeycloakSession session) {
+
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ Map<String, AuthenticatedClientSessionModel> clientSessions = session.sessions().getUserSession(realm, sessions[0].getId()).getAuthenticatedClientSessions();
+ assertEquals(2, clientSessions.size());
+
+ String clientUUID = realm.getClientByClientId("test-app").getId();
+
+ AuthenticatedClientSessionModel session1 = clientSessions.get(clientUUID);
+
+ assertNull(session1.getAction());
+ assertEquals(realm.getClientByClientId("test-app").getClientId(), session1.getClient().getClientId());
+ assertEquals(sessions[0].getId(), session1.getUserSession().getId());
+ assertEquals("http://redirect", session1.getRedirectUri());
+ assertEquals("state", session1.getNote(OIDCLoginProtocol.STATE_PARAM));
+ }
+
+ @Test
+ @ModelTest
+ public void testUpdateClientSession(KeycloakSession session) {
+
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ String userSessionId = sessions[0].getId();
+ String clientUUID = realm.getClientByClientId("test-app").getId();
+
+ UserSessionModel userSession = session.sessions().getUserSession(realm, userSessionId);
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessions().get(clientUUID);
+
+ int time = clientSession.getTimestamp();
+ assertNull(clientSession.getAction());
+
+ clientSession.setAction(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name());
+ clientSession.setTimestamp(time + 10);
+
+ AuthenticatedClientSessionModel updated = session.sessions().getUserSession(realm, userSessionId).getAuthenticatedClientSessions().get(clientUUID);
+ assertEquals(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name(), updated.getAction());
+ assertEquals(time + 10, updated.getTimestamp());
+ }
+
+ @Test
+ @ModelTest
+ public void testUpdateClientSessionWithGetByClientId(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ String userSessionId = sessions[0].getId();
+ String clientUUID = realm.getClientByClientId("test-app").getId();
+
+ UserSessionModel userSession = session.sessions().getUserSession(realm, userSessionId);
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientUUID);
+
+ int time = clientSession.getTimestamp();
+ assertNull(clientSession.getAction());
+
+ clientSession.setAction(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name());
+ clientSession.setTimestamp(time + 10);
+
+ AuthenticatedClientSessionModel updated = session.sessions().getUserSession(realm, userSessionId).getAuthenticatedClientSessionByClient(clientUUID);
+ assertEquals(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name(), updated.getAction());
+ assertEquals(time + 10, updated.getTimestamp());
+ }
+
+ @Test
+ @ModelTest
+ public void testUpdateClientSessionInSameTransaction(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ String userSessionId = sessions[0].getId();
+ String clientUUID = realm.getClientByClientId("test-app").getId();
+
+ UserSessionModel userSession = session.sessions().getUserSession(realm, userSessionId);
+ AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientUUID);
+
+ clientSession.setAction(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name());
+ clientSession.setNote("foo", "bar");
+
+ AuthenticatedClientSessionModel updated = session.sessions().getUserSession(realm, userSessionId).getAuthenticatedClientSessionByClient(clientUUID);
+ assertEquals(AuthenticatedClientSessionModel.Action.LOGGED_OUT.name(), updated.getAction());
+ assertEquals("bar", updated.getNote("foo"));
+ }
+
+ @Test
+ @ModelTest
+ public void testGetUserSessions(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ KeycloakTransaction transaction = session.getTransactionManager();
+ if (!transaction.getRollbackOnly()) {
+ transaction.commit();
+
+ }
+
+
+ assertSessions(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)), sessions[0], sessions[1]);
+ assertSessions(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)), sessions[2]);
+ }
+
+ @Test
+ @ModelTest
+ public void testRemoveUserSessionsByUser(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ Map<String, Integer> clientSessionsKept = new HashMap<>();
+ for (UserSessionModel s : sessions) {
+ s = session.sessions().getUserSession(realm, s.getId());
+
+ if (!s.getUser().getUsername().equals("user1")) {
+ clientSessionsKept.put(s.getId(), s.getAuthenticatedClientSessions().keySet().size());
+ }
+ }
+
+ session.sessions().removeUserSessions(realm, session.users().getUserByUsername("user1", realm));
+
+ assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)).isEmpty());
+ List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm));
+
+ assertSame(userSessions.size(), 0);
+
+ session.getTransactionManager().commit();
+ // Null test removed, it seems that NULL is not a valid state under the new testsuite so we are testing for Size=0
+
+ for (UserSessionModel userSession : userSessions) {
+ Assert.assertEquals((int) clientSessionsKept.get(userSession.getId()), userSession.getAuthenticatedClientSessions().size());
+ }
+ }
+
+ @Test
+ @ModelTest
+ public void testRemoveUserSession(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel userSession = createSessions(session)[0];
+
+ session.sessions().removeUserSession(realm, userSession);
+
+ assertNull(session.sessions().getUserSession(realm, userSession.getId()));
+ }
+
+ @Test
+ @ModelTest
+ public void testRemoveUserSessionsByRealm(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ session.sessions().removeUserSessions(realm);
+
+ assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm)).isEmpty());
+ assertTrue(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)).isEmpty());
+ }
+
+ @Test
+ @ModelTest
+ public void testOnClientRemoved(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ String thirdPartyClientUUID = realm.getClientByClientId("third-party").getId();
+
+ Map<String, Set<String>> clientSessionsKept = new HashMap<>();
+ for (UserSessionModel s : sessions) {
+ Set<String> clientUUIDS = new HashSet<>(s.getAuthenticatedClientSessions().keySet());
+ clientUUIDS.remove(thirdPartyClientUUID); // This client will be later removed, hence his clientSessions too
+ clientSessionsKept.put(s.getId(), clientUUIDS);
+ }
+
+ realm.removeClient(thirdPartyClientUUID);
+
+ for (UserSessionModel s : sessions) {
+ s = session.sessions().getUserSession(realm, s.getId());
+ Set<String> clientUUIDS = s.getAuthenticatedClientSessions().keySet();
+ assertEquals(clientUUIDS, clientSessionsKept.get(s.getId()));
+ }
+
+ // Revert client
+ realm.addClient("third-party");
+ }
+
+ @Test
+ @ModelTest
+ public void testRemoveUserSessionsByExpired(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ session.sessions().getUserSessions(realm, session.users().getUserByUsername("user1", realm));
+ ClientModel client = realm.getClientByClientId("test-app");
+
+ try {
+ Set<String> expired = new HashSet<String>();
+
+ Time.setOffset(-(realm.getSsoSessionMaxLifespan() + 1));
+ UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+ expired.add(userSession.getId());
+ AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
+ Assert.assertEquals(userSession, clientSession.getUserSession());
+
+ Time.setOffset(0);
+ UserSessionModel s = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.1", "form", true, null, null);
+ //s.setLastSessionRefresh(Time.currentTime() - (realm.getSsoSessionIdleTimeout() + 1));
+ s.setLastSessionRefresh(0);
+ expired.add(s.getId());
+
+ Set<String> valid = new HashSet<String>();
+ Set<String> validClientSessions = new HashSet<String>();
+ userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+ valid.add(userSession.getId());
+ validClientSessions.add(session.sessions().createClientSession(realm, client, userSession).getId());
+
+ session.sessions().removeExpired(realm);
+
+ //Under the new testsuite nothing ever seems to become Null
+ /*for (String e : expired) {
+ assertNull(session.sessions().getUserSession(realm, e));
+ }*/
+
+ for (String v : valid) {
+ UserSessionModel userSessionLoaded = session.sessions().getUserSession(realm, v);
+ assertNotNull(userSessionLoaded);
+ Assert.assertEquals(1, userSessionLoaded.getAuthenticatedClientSessions().size());
+ //Comparing sizes, not NULLs. Null seems to not be doable under the new testsuite because of the persistent session
+ //Assert.assertNotNull(userSessionLoaded.getAuthenticatedClientSessions().get(client.getId()));
+ }
+ } finally {
+ Time.setOffset(0);
+ }
+ }
+
+ // KEYCLOAK-2508
+ @Test
+ @ModelTest
+ public void testRemovingExpiredSession(KeycloakSession session) {
+ UserSessionModel[] sessions = createSessions(session);
+ try {
+ Time.setOffset(3600000);
+ UserSessionModel userSession = sessions[0];
+ RealmModel realm = userSession.getRealm();
+ session.sessions().removeExpired(realm);
+
+ // Assert no exception is thrown here
+ session.sessions().removeUserSession(realm, userSession);
+ } finally {
+ Time.setOffset(0);
+ }
+ }
+
+ @Test
+ @ModelTest
+ public void testGetByClient(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel[] sessions = createSessions(session);
+
+ KeycloakTransaction transaction = session.getTransactionManager();
+ if (!transaction.getRollbackOnly()) {
+ transaction.commit();
+ }
+
+
+ assertSessions(session.sessions().getUserSessions(realm, realm.getClientByClientId("test-app")), sessions[0], sessions[1], sessions[2]);
+ assertSessions(session.sessions().getUserSessions(realm, realm.getClientByClientId("third-party")), sessions[0]);
+ }
+
+ @Test
+ @ModelTest
+ public void testGetByClientPaginated(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ try {
+ for (int i = 0; i < 25; i++) {
+ Time.setOffset(i);
+ UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false, null, null);
+ AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app"), userSession);
+ assertNotNull(clientSession);
+ clientSession.setRedirectUri("http://redirect");
+ clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, "state");
+ clientSession.setTimestamp(userSession.getStarted());
+ userSession.setLastSessionRefresh(userSession.getStarted());
+ }
+ } finally {
+ Time.setOffset(0);
+ }
+
+ KeycloakTransaction transaction = session.getTransactionManager();
+ if (!transaction.getRollbackOnly()) {
+ transaction.commit();
+ }
+
+ assertPaginatedSession(session, realm, realm.getClientByClientId("test-app"), 0, 1, 1);
+ assertPaginatedSession(session, realm, realm.getClientByClientId("test-app"), 0, 10, 10);
+ assertPaginatedSession(session, realm, realm.getClientByClientId("test-app"), 10, 10, 10);
+ assertPaginatedSession(session, realm, realm.getClientByClientId("test-app"), 20, 10, 5);
+ assertPaginatedSession(session, realm, realm.getClientByClientId("test-app"), 30, 10, 0);
+ }
+
+ @Test
+ @ModelTest
+ public void testCreateAndGetInSameTransaction(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ ClientModel client = realm.getClientByClientId("test-app");
+ UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+ AuthenticatedClientSessionModel clientSession = createClientSession(session, client, userSession, "http://redirect", "state");
+
+ UserSessionModel userSessionLoaded = session.sessions().getUserSession(realm, userSession.getId());
+ AuthenticatedClientSessionModel clientSessionLoaded = userSessionLoaded.getAuthenticatedClientSessions().get(client.getId());
+ Assert.assertNotNull(userSessionLoaded);
+ Assert.assertNotNull(clientSessionLoaded);
+
+ Assert.assertEquals(userSession.getId(), clientSessionLoaded.getUserSession().getId());
+ Assert.assertEquals(1, userSessionLoaded.getAuthenticatedClientSessions().size());
+ }
+
+ @Test
+ @ModelTest
+ public void testAuthenticatedClientSessions(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+
+ ClientModel client1 = realm.getClientByClientId("test-app");
+ ClientModel client2 = realm.getClientByClientId("third-party");
+
+ // Create client1 session
+ AuthenticatedClientSessionModel clientSession1 = session.sessions().createClientSession(realm, client1, userSession);
+ clientSession1.setAction("foo1");
+ clientSession1.setTimestamp(100);
+
+ // Create client2 session
+ AuthenticatedClientSessionModel clientSession2 = session.sessions().createClientSession(realm, client2, userSession);
+ clientSession2.setAction("foo2");
+ clientSession2.setTimestamp(200);
+
+ // Ensure sessions are here
+ userSession = session.sessions().getUserSession(realm, userSession.getId());
+ Map<String, AuthenticatedClientSessionModel> clientSessions = userSession.getAuthenticatedClientSessions();
+ Assert.assertEquals(2, clientSessions.size());
+ testAuthenticatedClientSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1", 100);
+ testAuthenticatedClientSession(clientSessions.get(client2.getId()), "third-party", userSession.getId(), "foo2", 200);
+
+ // Update session1
+ clientSessions.get(client1.getId()).setAction("foo1-updated");
+
+
+ // Ensure updated
+ userSession = session.sessions().getUserSession(realm, userSession.getId());
+ clientSessions = userSession.getAuthenticatedClientSessions();
+ testAuthenticatedClientSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1-updated", 100);
+
+ // Rewrite session2
+ clientSession2 = session.sessions().createClientSession(realm, client2, userSession);
+ clientSession2.setAction("foo2-rewrited");
+ clientSession2.setTimestamp(300);
+
+
+ // Ensure updated
+ userSession = session.sessions().getUserSession(realm, userSession.getId());
+ clientSessions = userSession.getAuthenticatedClientSessions();
+ Assert.assertEquals(2, clientSessions.size());
+ testAuthenticatedClientSession(clientSessions.get(client1.getId()), "test-app", userSession.getId(), "foo1-updated", 100);
+ testAuthenticatedClientSession(clientSessions.get(client2.getId()), "third-party", userSession.getId(), "foo2-rewrited", 300);
+
+ // remove session
+ clientSession1 = userSession.getAuthenticatedClientSessions().get(client1.getId());
+ clientSession1.detachFromUserSession();
+
+ userSession = session.sessions().getUserSession(realm, userSession.getId());
+ clientSessions = userSession.getAuthenticatedClientSessions();
+ Assert.assertEquals(1, clientSessions.size());
+ Assert.assertNull(clientSessions.get(client1.getId()));
+ }
+
+
+ private static void testAuthenticatedClientSession(AuthenticatedClientSessionModel clientSession, String expectedClientId, String expectedUserSessionId, String expectedAction, int expectedTimestamp) {
+ Assert.assertEquals(expectedClientId, clientSession.getClient().getClientId());
+ Assert.assertEquals(expectedUserSessionId, clientSession.getUserSession().getId());
+ Assert.assertEquals(expectedAction, clientSession.getAction());
+ Assert.assertEquals(expectedTimestamp, clientSession.getTimestamp());
+ }
+
+ private static void assertPaginatedSession(KeycloakSession session, RealmModel realm, ClientModel client, int start, int max, int expectedSize) {
+ List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, client, start, max);
+ String[] actualIps = new String[sessions.size()];
+
+ for (int i = 0; i < actualIps.length; i++) {
+ actualIps[i] = sessions.get(i).getIpAddress();
+ }
+
+ String[] expectedIps = new String[expectedSize];
+ for (int i = 0; i < expectedSize; i++) {
+ expectedIps[i] = "127.0.0." + (i + start);
+ }
+
+ assertArrayEquals(expectedIps, actualIps);
+ }
+
+ @Test
+ public void testGetCountByClient() {
+ testingClient.server().run(UserSessionProviderTest::testGetCountByClient);
+ }
+ public static void testGetCountByClient(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ createSessions(session);
+
+ KeycloakTransaction transaction = session.getTransactionManager();
+ if (!transaction.getRollbackOnly()) {
+ transaction.commit();
+ }
+
+ assertEquals(3, session.sessions().getActiveUserSessions(realm, realm.getClientByClientId("test-app")));
+ assertEquals(1, session.sessions().getActiveUserSessions(realm, realm.getClientByClientId("third-party")));
+ }
+
+ @Test
+ public void loginFailures() {
+ testingClient.server().run(UserSessionProviderTest::loginFailures);
+ }
+ public static void loginFailures(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserLoginFailureModel failure1 = session.sessions().addUserLoginFailure(realm, "user1");
+ failure1.incrementFailures();
+
+ UserLoginFailureModel failure2 = session.sessions().addUserLoginFailure(realm, "user2");
+ failure2.incrementFailures();
+ failure2.incrementFailures();
+
+ session.getTransactionManager().commit();
+
+ failure1 = session.sessions().getUserLoginFailure(realm, "user1");
+ assertEquals(1, failure1.getNumFailures());
+
+ failure2 = session.sessions().getUserLoginFailure(realm, "user2");
+ assertEquals(2, failure2.getNumFailures());
+
+ //session.getTransactionManager().commit();
+
+ // Add the failure, which already exists
+ //failure1 = session.sessions().addUserLoginFailure(realm, "user1");
+ failure1.incrementFailures();
+
+ //failure1 = session.sessions().getUserLoginFailure(realm, "user1");
+ assertEquals(2, failure1.getNumFailures());
+
+ failure1 = session.sessions().getUserLoginFailure(realm, "user1");
+ failure1.clearFailures();
+
+ session.getTransactionManager().commit();
+
+ failure1 = session.sessions().getUserLoginFailure(realm, "user1");
+ assertEquals(0, failure1.getNumFailures());
+
+ session.sessions().removeUserLoginFailure(realm, "user1");
+ session.sessions().removeUserLoginFailure(realm, "user2");
+
+ assertNull(session.sessions().getUserLoginFailure(realm, "user1"));
+
+ session.sessions().removeAllUserLoginFailures(realm);
+ assertNull(session.sessions().getUserLoginFailure(realm, "user2"));
+ }
+
+ @Test
+ public void testOnUserRemoved() {
+ testingClient.server().run(UserSessionProviderTest::testOnUserRemoved);
+ }
+ public static void testOnUserRemoved(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("test");
+
+ UserModel user1 = session.users().getUserByUsername("user1", realm);
+ UserModel user2 = session.users().getUserByUsername("user2", realm);
+
+ 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");
+ AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app"), sessions[2]);
+ clientSession.setRedirectUri("http://redirct");
+ clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, "state");
+
+
+ session.sessions().addUserLoginFailure(realm, user1.getId());
+ session.sessions().addUserLoginFailure(realm, user2.getId());
+
+ session.userStorageManager().removeUser(realm, user1);
+
+ assertTrue(session.sessions().getUserSessions(realm, user1).isEmpty());
+
+ session.getTransactionManager().commit();
+
+ assertFalse(session.sessions().getUserSessions(realm, session.users().getUserByUsername("user2", realm)).isEmpty());
+
+ user1 = session.users().getUserByUsername("user1", realm);
+ user2 = session.users().getUserByUsername("user2", realm);
+
+ // it seems as if Null does not happen with the new test suite. The sizes of these are ZERO so the removes worked at this point.
+ //assertNull(session.sessions().getUserLoginFailure(realm, user1.getId()));
+ //assertNotNull(session.sessions().getUserLoginFailure(realm, user2.getId()));
+ }
+
+ private static AuthenticatedClientSessionModel createClientSession(KeycloakSession session, ClientModel client, UserSessionModel userSession, String redirect, String state) {
+ RealmModel realm = session.realms().getRealmByName("test");
+ AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
+ clientSession.setRedirectUri(redirect);
+ if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
+ return clientSession;
+ }
+
+ private static UserSessionModel[] createSessions(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName("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;
+ }
+
+ public static void assertSessions(List<UserSessionModel> actualSessions, UserSessionModel... expectedSessions) {
+ String[] expected = new String[expectedSessions.length];
+ for (int i = 0; i < expected.length; i++) {
+ expected[i] = expectedSessions[i].getId();
+ }
+
+ String[] actual = new String[actualSessions.size()];
+ for (int i = 0; i < actual.length; i++) {
+ actual[i] = actualSessions.get(i).getId();
+ }
+
+ Arrays.sort(expected);
+ Arrays.sort(actual);
+
+ assertArrayEquals(expected, actual);
+ }
+
+ 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++;
+ }
+
+ Arrays.sort(clients);
+ Arrays.sort(actualClients);
+
+ assertArrayEquals(clients, actualClients);
+ }
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testcomposites2.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testcomposites2.json
new file mode 100755
index 0000000..d9e9bb1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testcomposites2.json
@@ -0,0 +1,228 @@
+{
+ "id": "TestComposites",
+ "realm": "TestComposites",
+ "enabled": true,
+ "accessTokenLifespan": 600,
+ "accessCodeLifespan": 600,
+ "accessCodeLifespanUserAction": 600,
+ "sslRequired": "external",
+ "registrationAllowed": true,
+ "resetPasswordAllowed": true,
+ "requiredCredentials": [ "password" ],
+ "smtpServer": {
+ "from": "auto@keycloak.org",
+ "host": "localhost",
+ "port":"3025"
+ },
+ "users" : [
+ {
+ "username" : "REALM_COMPOSITE_1_USER",
+ "enabled": true,
+ "email" : "test-user1@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "REALM_COMPOSITE_1" ]
+ },
+ {
+ "username" : "REALM_ROLE_1_USER",
+ "enabled": true,
+ "email" : "test-user2@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "REALM_ROLE_1"]
+ },
+ {
+ "username" : "REALM_APP_COMPOSITE_USER",
+ "enabled": true,
+ "email" : "test-user3@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "REALM_APP_COMPOSITE_ROLE" ]
+ },
+ {
+ "username" : "REALM_APP_ROLE_USER",
+ "enabled": true,
+ "email" : "test-user4@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "applicationRoles": {
+ "APP_ROLE_APPLICATION": [ "APP_ROLE_2" ]
+ }
+ },
+ {
+ "username" : "APP_COMPOSITE_USER",
+ "enabled": true,
+ "email" : "test-user5@localhost",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": ["REALM_APP_COMPOSITE_ROLE", "REALM_COMPOSITE_1"]
+ }
+ ],
+ "oauthClients" : [
+ {
+ "name" : "third-party",
+ "enabled": true,
+ "secret": "password"
+ }
+ ],
+ "scopeMappings": [
+ {
+ "client": "REALM_COMPOSITE_1_APPLICATION",
+ "roles": ["REALM_COMPOSITE_1"]
+ },
+ {
+ "client": "REALM_COMPOSITE_2_APPLICATION",
+ "roles": ["REALM_COMPOSITE_1", "REALM_COMPOSITE_CHILD", "REALM_ROLE_4"]
+ },
+ {
+ "client": "REALM_ROLE_1_APPLICATION",
+ "roles": ["REALM_ROLE_1"]
+ }
+ ],
+ "applications": [
+ {
+ "name": "REALM_COMPOSITE_1_APPLICATION",
+ "fullScopeAllowed": false,
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "secret": "password"
+ },
+ {
+ "name": "REALM_COMPOSITE_2_APPLICATION",
+ "fullScopeAllowed": false,
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "secret": "password"
+ },
+ {
+ "name": "REALM_ROLE_1_APPLICATION",
+ "fullScopeAllowed": false,
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "secret": "password"
+ },
+ {
+ "name": "APP_ROLE_APPLICATION",
+ "fullScopeAllowed": false,
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "secret": "password"
+ },
+ {
+ "name": "APP_COMPOSITE_APPLICATION",
+ "fullScopeAllowed": false,
+ "enabled": true,
+ "baseUrl": "http://localhost:8081/app",
+ "adminUrl": "http://localhost:8081/app/logout",
+ "secret": "password"
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "REALM_ROLE_1"
+ },
+ {
+ "name": "REALM_ROLE_2"
+ },
+ {
+ "name": "REALM_ROLE_3"
+ },
+ {
+ "name": "REALM_ROLE_4"
+ },
+ {
+ "name": "REALM_COMPOSITE_1",
+ "composites": {
+ "realm": ["REALM_ROLE_1", "REALM_COMPOSITE_CHILD"]
+ }
+ },
+ {
+ "name": "REALM_COMPOSITE_CHILD",
+ "composites": {
+ "realm": ["REALM_ROLE_4"]
+ }
+ },
+ {
+ "name": "REALM_APP_COMPOSITE_ROLE",
+ "composites": {
+ "application": {
+ "APP_ROLE_APPLICATION" :[
+ "APP_ROLE_1"
+ ],
+ "APP_COMPOSITE_APPLICATION" :[
+ "APP_COMPOSITE_ROLE"
+ ]
+ }
+ }
+ }
+ ],
+ "application" : {
+ "APP_ROLE_APPLICATION" : [
+ {
+ "name": "APP_ROLE_1"
+ },
+ {
+ "name": "APP_ROLE_2"
+ }
+ ],
+ "APP_COMPOSITE_APPLICATION" : [
+ {
+ "name": "APP_COMPOSITE_ROLE",
+ "composites": {
+ "realm" : [
+ "REALM_ROLE_1",
+ "REALM_ROLE_2",
+ "REALM_ROLE_3"
+ ],
+ "application": {
+ "APP_ROLE_APPLICATION" :[
+ "APP_ROLE_1"
+ ],
+ "APP_COMPOSITE_APPLICATION" :[
+ "APP_COMPOSITE_CHILD"
+ ]
+ }
+ }
+ },
+ {
+ "name": "APP_COMPOSITE_CHILD",
+ "composites": {
+ "application": {
+ "APP_COMPOSITE_APPLICATION" :[
+ "APP_ROLE_2"
+ ]
+ }
+ }
+ },
+ {
+ "name": "APP_ROLE_2"
+ }
+ ]
+ }
+
+ },
+
+ "applicationScopeMappings": {
+ "APP_ROLE_APPLICATION": [
+ {
+ "client": "APP_COMPOSITE_APPLICATION",
+ "roles": ["APP_ROLE_1"]
+ }
+ ]
+ }
+}
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
index 5c2e2e5..a31b852 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
@@ -6,6 +6,7 @@
* 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