keycloak-uncached
Changes
testsuite/integration/pom.xml 10(+10 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPSyncTest.java 40(+1 -39)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncDummyUserFederationProviderFactory.java 100(+100 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java 165(+165 -0)
Details
diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationSyncResult.java b/server-spi/src/main/java/org/keycloak/models/UserFederationSyncResult.java
index 5ee0b56..b8911f2 100644
--- a/server-spi/src/main/java/org/keycloak/models/UserFederationSyncResult.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserFederationSyncResult.java
@@ -22,11 +22,21 @@ package org.keycloak.models;
*/
public class UserFederationSyncResult {
+ private boolean ignored;
+
private int added;
private int updated;
private int removed;
private int failed;
+ public boolean isIgnored() {
+ return ignored;
+ }
+
+ public void setIgnored(boolean ignored) {
+ this.ignored = ignored;
+ }
+
public int getAdded() {
return added;
}
@@ -83,11 +93,15 @@ public class UserFederationSyncResult {
}
public String getStatus() {
- String status = String.format("%d imported users, %d updated users, %d removed users", added, updated, removed);
- if (failed != 0) {
- status += String.format(", %d users failed sync! See server log for more details", failed);
+ if (ignored) {
+ return "Synchronization ignored as it's already in progress";
+ } else {
+ String status = String.format("%d imported users, %d updated users, %d removed users", added, updated, removed);
+ if (failed != 0) {
+ status += String.format(", %d users failed sync! See server log for more details", failed);
+ }
+ return status;
}
- return status;
}
@Override
testsuite/integration/pom.xml 10(+10 -0)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 8dafd44..3672b3d 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -237,6 +237,16 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>${postgresql.version}</version>
+ </dependency>
+
</dependencies>
<build>
<plugins>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncDummyUserFederationProviderFactory.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncDummyUserFederationProviderFactory.java
new file mode 100644
index 0000000..517d45e
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncDummyUserFederationProviderFactory.java
@@ -0,0 +1,100 @@
+/*
+ * 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.federation.sync;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.KeycloakSessionTask;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.UserFederationSyncResult;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.testsuite.DummyUserFederationProviderFactory;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SyncDummyUserFederationProviderFactory extends DummyUserFederationProviderFactory {
+
+ private static final Logger logger = Logger.getLogger(SyncDummyUserFederationProviderFactory.class);
+
+ public static final String SYNC_PROVIDER_ID = "sync-dummy";
+ public static final String WAIT_TIME = "wait-time"; // waitTime before transaction is commited
+
+ @Override
+ public String getId() {
+ return SYNC_PROVIDER_ID;
+ }
+
+ @Override
+ public Set<String> getConfigurationOptions() {
+ Set<String> list = super.getConfigurationOptions();
+ list.add(WAIT_TIME);
+ return list;
+ }
+
+ @Override
+ public UserFederationSyncResult syncChangedUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model, Date lastSync) {
+
+ KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
+
+ @Override
+ public void run(KeycloakSession session) {
+ int waitTime = Integer.parseInt(model.getConfig().get(WAIT_TIME));
+
+ logger.infof("Starting sync of changed users. Wait time is: %s", waitTime);
+
+ RealmModel realm = session.realms().getRealm(realmId);
+
+ // KEYCLOAK-2412 : Just remove and add some users for testing purposes
+ for (int i = 0; i<10; i++) {
+ String username = "dummyuser-" + i;
+ UserModel user = session.userStorage().getUserByUsername(username, realm);
+
+ if (user != null) {
+ session.userStorage().removeUser(realm, user);
+ }
+
+ user = session.userStorage().addUser(realm, username);
+ }
+
+ logger.infof("Finished sync of changed users. Waiting now for %d seconds", waitTime);
+
+
+ try {
+ Thread.sleep(waitTime * 1000);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Interrupted!", ie);
+ }
+
+ logger.infof("Finished waiting");
+ }
+
+ });
+
+ return new UserFederationSyncResult();
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java
new file mode 100644
index 0000000..8259fe8
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.federation.sync;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.keycloak.common.util.Time;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.UserFederationSyncResult;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.managers.UsersSyncManager;
+import org.keycloak.testsuite.DummyUserFederationProviderFactory;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.timer.TimerProvider;
+
+/**
+ * Test with Dummy providers (For LDAP see {@link org.keycloak.testsuite.federation.ldap.base.LDAPSyncTest}
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SyncFederationTest {
+
+ private static UserFederationProviderModel dummyModel = null;
+
+ @ClassRule
+ public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ // Other tests may left Time offset uncleared, which could cause issues
+ Time.setOffset(0);
+ }
+ });
+
+ @Test
+ public void test01PeriodicSync() {
+
+ // Enable timer for SyncDummyUserFederationProvider
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ dummyModel = appRealm.addUserFederationProvider(DummyUserFederationProviderFactory.PROVIDER_NAME, new HashMap<String, String>(), 1, "test-sync-dummy", -1, 1, 0);
+ }
+
+ });
+
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+ DummyUserFederationProviderFactory dummyFedFactory = (DummyUserFederationProviderFactory)sessionFactory.getProviderFactory(UserFederationProvider.class, DummyUserFederationProviderFactory.PROVIDER_NAME);
+ int full = dummyFedFactory.getFullSyncCounter();
+ int changed = dummyFedFactory.getChangedSyncCounter();
+
+ // Assert that after some period was DummyUserFederationProvider triggered
+ UsersSyncManager usersSyncManager = new UsersSyncManager();
+ usersSyncManager.bootstrapPeriodic(sessionFactory, session.getProvider(TimerProvider.class));
+ sleep(1800);
+
+ // Cancel timer
+ usersSyncManager.removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), dummyModel);
+
+ // Assert that DummyUserFederationProviderFactory.syncChangedUsers was invoked
+ int newChanged = dummyFedFactory.getChangedSyncCounter();
+ Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
+ Assert.assertTrue(newChanged > changed);
+
+ // Assert that dummy provider won't be invoked anymore
+ sleep(1800);
+ Assert.assertEquals(full, dummyFedFactory.getFullSyncCounter());
+ Assert.assertEquals(newChanged, dummyFedFactory.getChangedSyncCounter());
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ // remove dummyProvider
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.removeUserFederationProvider(dummyModel);
+ }
+
+ });
+ }
+
+ @Test
+ public void test02ConcurrentSync() {
+ // Enable timer for SyncDummyUserFederationProvider
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ Map<String, String> config = new HashMap<>();
+ config.put(SyncDummyUserFederationProviderFactory.WAIT_TIME, "2000");
+ dummyModel = appRealm.addUserFederationProvider(SyncDummyUserFederationProviderFactory.SYNC_PROVIDER_ID, config, 1, "test-sync-dummy", -1, 1, 0);
+ }
+
+ });
+
+ KeycloakSession session = keycloakRule.startSession();
+ try {
+ KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+
+ // bootstrap periodic sync
+ UsersSyncManager usersSyncManager = new UsersSyncManager();
+ usersSyncManager.bootstrapPeriodic(sessionFactory, session.getProvider(TimerProvider.class));
+
+ // Wait and then trigger sync manually. Assert it will be ignored
+ sleep(1800);
+ RealmModel realm = session.realms().getRealm("test");
+ UserFederationSyncResult syncResult = usersSyncManager.syncChangedUsers(sessionFactory, realm.getId(), dummyModel);
+ Assert.assertTrue(syncResult.isIgnored());
+
+ // Cancel timer
+ usersSyncManager.removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), dummyModel);
+ } finally {
+ keycloakRule.stopSession(session, true);
+ }
+
+ // remove provider
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.removeUserFederationProvider(dummyModel);
+ }
+
+ });
+ }
+
+ private void sleep(int time) {
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException ie) {
+ throw new RuntimeException(ie);
+ }
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/util/cli/InfinispanCLI.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/util/cli/InfinispanCLI.java
index b29b610..0efac4a 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/util/cli/InfinispanCLI.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/util/cli/InfinispanCLI.java
@@ -69,7 +69,8 @@ public class InfinispanCLI {
UserCommands.Create.class,
UserCommands.Remove.class,
UserCommands.Count.class,
- UserCommands.GetUser.class
+ UserCommands.GetUser.class,
+ SyncDummyFederationProviderCommand.class
};
private final KeycloakSessionFactory sessionFactory;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/util/cli/SyncDummyFederationProviderCommand.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/util/cli/SyncDummyFederationProviderCommand.java
new file mode 100644
index 0000000..7a31d6d
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/util/cli/SyncDummyFederationProviderCommand.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.util.cli;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.services.managers.UsersSyncManager;
+import org.keycloak.testsuite.federation.sync.SyncDummyUserFederationProviderFactory;
+import org.keycloak.timer.TimerProvider;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SyncDummyFederationProviderCommand extends AbstractCommand {
+
+ @Override
+ protected void doRunCommand(KeycloakSession session) {
+ int waitTime = getIntArg(0);
+ int changedSyncPeriod = getIntArg(1);
+
+ RealmModel realm = session.realms().getRealmByName("master");
+ UserFederationProviderModel fedProviderModel = KeycloakModelUtils.findUserFederationProviderByDisplayName("cluster-dummy", realm);
+ if (fedProviderModel == null) {
+ Map<String, String> cfg = new HashMap<>();
+ updateConfig(cfg, waitTime);
+ fedProviderModel = realm.addUserFederationProvider(SyncDummyUserFederationProviderFactory.SYNC_PROVIDER_ID, cfg, 1, "cluster-dummy", -1, changedSyncPeriod, -1);
+ } else {
+ Map<String, String> cfg = fedProviderModel.getConfig();
+ updateConfig(cfg, waitTime);
+ realm.updateUserFederationProvider(fedProviderModel);
+ }
+
+ new UsersSyncManager().refreshPeriodicSyncForProvider(sessionFactory, session.getProvider(TimerProvider.class), fedProviderModel, "master");
+
+ log.infof("User federation provider created and sync was started", waitTime);
+ }
+
+ private void updateConfig(Map<String, String> cfg, int waitTime) {
+ cfg.put(SyncDummyUserFederationProviderFactory.WAIT_TIME, String.valueOf(waitTime));
+ }
+
+
+ @Override
+ public String getName() {
+ return "startSyncDummy";
+ }
+
+ @Override
+ public String printUsage() {
+ return super.printUsage() + " <wait-time-before-sync-commit-in-seconds> <changed-sync-period-in-seconds>";
+ }
+}
diff --git a/testsuite/integration/src/test/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory b/testsuite/integration/src/test/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory
index c730b1e..1b4de73 100755
--- a/testsuite/integration/src/test/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory
+++ b/testsuite/integration/src/test/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory
@@ -15,4 +15,5 @@
# limitations under the License.
#
-org.keycloak.testsuite.DummyUserFederationProviderFactory
\ No newline at end of file
+org.keycloak.testsuite.DummyUserFederationProviderFactory
+org.keycloak.testsuite.federation.sync.SyncDummyUserFederationProviderFactory
\ No newline at end of file