keycloak-aplcache
Changes
dependencies/server-all/pom.xml 4(+4 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml 2(+1 -1)
model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AbstractUserFedToComponent.java 182(+182 -0)
model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java 46(+46 -0)
model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java 4(+3 -1)
model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update2_4_0.java 172(+172 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java 18(+18 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPLegacyImportTest.java 184(+184 -0)
Details
dependencies/server-all/pom.xml 4(+4 -0)
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index 526a0cf..3dcb6e5 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -69,6 +69,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-ldap-storage</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-kerberos-federation</artifactId>
</dependency>
<!-- saml -->
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
index 43a1d64..58977da 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml
@@ -21,7 +21,7 @@
</properties>
<resources>
- <artifact name="${org.keycloak:keycloak-ldap-federation}"/>
+ <artifact name="${org.keycloak:keycloak-ldap-storage}"/>
</resources>
<dependencies>
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
index 8252f6b..069b34a 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
@@ -1341,6 +1341,13 @@ public class RealmAdapter implements CachedRealmModel {
return updated.addComponentModel(model);
}
+ @Override
+ public ComponentModel importComponentModel(ComponentModel model) {
+ getDelegateForUpdate();
+ evictUsers(model);
+ return updated.importComponentModel(model);
+ }
+
public void evictUsers(ComponentModel model) {
String parentId = model.getParentId();
evictUsers(parentId);
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AbstractUserFedToComponent.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AbstractUserFedToComponent.java
new file mode 100644
index 0000000..e86788b
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/AbstractUserFedToComponent.java
@@ -0,0 +1,182 @@
+/*
+ * 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.connections.jpa.updater.liquibase.custom;
+
+import liquibase.exception.CustomChangeException;
+import liquibase.statement.core.DeleteStatement;
+import liquibase.statement.core.InsertStatement;
+import liquibase.structure.core.Table;
+import org.jboss.logging.Logger;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.storage.UserStorageProvider;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class AbstractUserFedToComponent extends CustomKeycloakTask {
+ private final Logger logger = Logger.getLogger(getClass());
+ protected void convertFedProviderToComponent(String providerId, String newMapperType) throws CustomChangeException {
+ try {
+ PreparedStatement statement = jdbcConnection.prepareStatement("select ID, REALM_ID, PRIORITY, DISPLAY_NAME, FULL_SYNC_PERIOD, CHANGED_SYNC_PERIOD, LAST_SYNC from " + getTableName("USER_FEDERATION_PROVIDER") + " WHERE PROVIDER_NAME='" + providerId + "'");
+
+ try {
+ ResultSet resultSet = statement.executeQuery();
+ try {
+ while (resultSet.next()) {
+ int index = 1;
+ String id = resultSet.getString(index++);
+ String realmId = resultSet.getString(index++);
+ int priority = resultSet.getInt(index++);
+ String displayName = resultSet.getString(index++);
+ int fullSyncPeriod = resultSet.getInt(index++);
+ int changedSyncPeriod = resultSet.getInt(index++);
+ int lastSync = resultSet.getInt(index++);
+
+
+ InsertStatement insertComponent = new InsertStatement(null, null, database.correctObjectName("COMPONENT", Table.class))
+ .addColumnValue("ID", id)
+ .addColumnValue("REALM_ID", realmId)
+ .addColumnValue("PARENT_ID", realmId)
+ .addColumnValue("NAME", displayName)
+ .addColumnValue("PROVIDER_ID", LDAPConstants.LDAP_PROVIDER)
+ .addColumnValue("PROVIDER_TYPE", UserStorageProvider.class.getName());
+
+ statements.add(insertComponent);
+
+ statements.add(componentConfigStatement(id, "priority", Integer.toString(priority)));
+ statements.add(componentConfigStatement(id, "fullSyncPeriod", Integer.toString(fullSyncPeriod)));
+ statements.add(componentConfigStatement(id, "changedSyncPeriod", Integer.toString(changedSyncPeriod)));
+ statements.add(componentConfigStatement(id, "lastSync", Integer.toString(lastSync)));
+ PreparedStatement configStatement = jdbcConnection.prepareStatement("select name, VALUE from " + getTableName("USER_FEDERATION_CONFIG") + " WHERE USER_FEDERATION_PROVIDER_ID=?");
+ configStatement.setString(1, id);
+ try {
+ ResultSet configSet = configStatement.executeQuery();
+ try {
+ while (configSet.next()) {
+ String name = configSet.getString(1);
+ String value = configSet.getString(2);
+ //logger.info("adding component config: " + name + ": " + value);
+ statements.add(componentConfigStatement(id, name, value));
+ }
+ } finally {
+ configSet.close();
+ }
+ } finally {
+ configStatement.close();
+ }
+
+ if (newMapperType != null) {
+ convertFedMapperToComponent(realmId, id, newMapperType);
+ }
+
+ DeleteStatement configDelete = new DeleteStatement(null, null, database.correctObjectName("USER_FEDERATION_CONFIG", Table.class));
+ configDelete.setWhere("USER_FEDERATION_PROVIDER_ID='" + id + "'");
+ statements.add(configDelete);
+ DeleteStatement deleteStatement = new DeleteStatement(null, null, database.correctObjectName("USER_FEDERATION_PROVIDER", Table.class));
+ deleteStatement.setWhere("ID='" + id + "'");
+ statements.add(deleteStatement);
+
+ }
+ } finally {
+ resultSet.close();
+ }
+ } finally {
+ statement.close();
+ }
+
+ confirmationMessage.append("Updated " + statements.size() + " records in USER_FEDERATION_PROVIDER table for " + providerId + " conversion to component model");
+ } catch (Exception e) {
+ throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
+ }
+ }
+
+ protected InsertStatement componentConfigStatement(String componentId, String name, String value) {
+ return new InsertStatement(null, null, database.correctObjectName("COMPONENT_CONFIG", Table.class))
+ .addColumnValue("ID", KeycloakModelUtils.generateId())
+ .addColumnValue("COMPONENT_ID", componentId)
+ .addColumnValue("NAME", name)
+ .addColumnValue("VALUE", value);
+ }
+
+ protected void convertFedMapperToComponent(String realmId, String parentId, String newMapperType) throws CustomChangeException {
+ try {
+ PreparedStatement statement = jdbcConnection.prepareStatement("select ID, NAME, FEDERATION_MAPPER_TYPE from " + getTableName("USER_FEDERATION_MAPPER") + " WHERE FEDERATION_PROVIDER_ID='" + parentId + "'");
+
+ try {
+ ResultSet resultSet = statement.executeQuery();
+ try {
+ while (resultSet.next()) {
+ String id = resultSet.getString(1);
+ String mapperName = resultSet.getString(2);
+ String fedMapperType = resultSet.getString(3);
+
+ InsertStatement insertComponent = new InsertStatement(null, null, database.correctObjectName("COMPONENT", Table.class))
+ .addColumnValue("ID", id)
+ .addColumnValue("REALM_ID", realmId)
+ .addColumnValue("PARENT_ID", parentId)
+ .addColumnValue("NAME", mapperName)
+ .addColumnValue("PROVIDER_ID", fedMapperType)
+ .addColumnValue("PROVIDER_TYPE", newMapperType);
+
+ statements.add(insertComponent);
+
+
+
+ PreparedStatement configStatement = jdbcConnection.prepareStatement("select name, VALUE from " + getTableName("USER_FEDERATION_MAPPER_CONFIG") + " WHERE USER_FEDERATION_MAPPER_ID=?");
+ configStatement.setString(1, id);
+ try {
+ ResultSet configSet = configStatement.executeQuery();
+ try {
+ while (configSet.next()) {
+ String name = configSet.getString(1);
+ String value = configSet.getString(2);
+ statements.add(componentConfigStatement(id, name, value));
+ }
+ } finally {
+ configSet.close();
+ }
+ } finally {
+ configStatement.close();
+ }
+ DeleteStatement configDelete = new DeleteStatement(null, null, database.correctObjectName("USER_FEDERATION_MAPPER_CONFIG", Table.class));
+ configDelete.setWhere("USER_FEDERATION_MAPPER_ID='" + id + "'");
+ statements.add(configDelete);
+ DeleteStatement deleteStatement = new DeleteStatement(null, null, database.correctObjectName("USER_FEDERATION_MAPPER", Table.class));
+ deleteStatement.setWhere("ID='" + id + "'");
+ statements.add(deleteStatement);
+
+
+ }
+ } finally {
+ resultSet.close();
+ }
+ } finally {
+ statement.close();
+ }
+
+ confirmationMessage.append("Updated " + statements.size() + " records in USER_FEDERATION_MAPPER table for " + parentId + " conversion to component model");
+ } catch (Exception e) {
+ throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
+ }
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java
new file mode 100644
index 0000000..cf224b2
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java
@@ -0,0 +1,46 @@
+/*
+ * 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.connections.jpa.updater.liquibase.custom;
+
+import liquibase.exception.CustomChangeException;
+import liquibase.statement.core.InsertStatement;
+import liquibase.structure.core.Table;
+import org.keycloak.keys.KeyProvider;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.storage.UserStorageProvider;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PortLdapUserFedToComponentModel extends AbstractUserFedToComponent {
+
+ @Override
+ protected void generateStatementsImpl() throws CustomChangeException {
+ String providerId = LDAPConstants.LDAP_PROVIDER;
+ convertFedProviderToComponent(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
+ }
+
+ @Override
+ protected String getTaskId() {
+ return "Update 2.4.0.Final";
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 6de22a9..97aa4bd 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -2031,6 +2031,14 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
@Override
public ComponentModel addComponentModel(ComponentModel model) {
+ model = importComponentModel(model);
+ ComponentUtil.notifyCreated(session, this, model);
+
+ return model;
+ }
+
+ @Override
+ public ComponentModel importComponentModel(ComponentModel model) {
ComponentFactory componentFactory = ComponentUtil.getComponentFactory(session, model);
if (componentFactory == null) {
throw new IllegalArgumentException("Invalid component type");
@@ -2057,8 +2065,6 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
em.persist(c);
setConfig(model, c);
model.setId(c.getId());
- ComponentUtil.notifyCreated(session, this, model);
-
return model;
}
diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
index cc4c9ff..588caf7 100644
--- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml
@@ -37,4 +37,5 @@
<include file="META-INF/jpa-changelog-2.1.0.xml"/>
<include file="META-INF/jpa-changelog-2.2.0.xml"/>
<include file="META-INF/jpa-changelog-2.3.0.xml"/>
+ <include file="META-INF/jpa-changelog-2.4.0.xml"/>
</databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.4.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.4.0.xml
new file mode 100755
index 0000000..0aebd9a
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.4.0.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ ~ 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.
+ -->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+
+ <changeSet author="bburke@redhat.com" id="2.4.0">
+ <customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.PortLdapUserFedToComponentModel"/>
+ </changeSet>
+
+</databaseChangeLog>
\ No newline at end of file
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
index 8990fc4..194b2d5 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -37,4 +37,5 @@
<include file="META-INF/jpa-changelog-2.1.0.xml"/>
<include file="META-INF/jpa-changelog-2.2.0.xml"/>
<include file="META-INF/jpa-changelog-2.3.0.xml"/>
+ <include file="META-INF/jpa-changelog-2.4.0.xml"/>
</databaseChangeLog>
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
index 3e429e2..c7f5be8 100755
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
@@ -34,6 +34,7 @@ import org.keycloak.connections.mongo.updater.impl.updates.Update1_7_0;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_8_0;
import org.keycloak.connections.mongo.updater.impl.updates.Update1_9_2;
import org.keycloak.connections.mongo.updater.impl.updates.Update2_3_0;
+import org.keycloak.connections.mongo.updater.impl.updates.Update2_4_0;
import org.keycloak.models.KeycloakSession;
import java.util.Date;
@@ -59,7 +60,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
Update1_7_0.class,
Update1_8_0.class,
Update1_9_2.class,
- Update2_3_0.class
+ Update2_3_0.class,
+ Update2_4_0.class
};
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update2_4_0.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update2_4_0.java
new file mode 100644
index 0000000..f92eaaa
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update2_4_0.java
@@ -0,0 +1,172 @@
+/*
+ * 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.connections.mongo.updater.impl.updates;
+
+import com.mongodb.BasicDBList;
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import org.keycloak.keys.KeyProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.storage.UserStorageProvider;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class Update2_4_0 extends Update {
+
+ @Override
+ public String getId() {
+ return "2.4.0";
+ }
+
+ @Override
+ public void update(KeycloakSession session) {
+ portUserFedToComponent(LDAPConstants.LDAP_PROVIDER);
+ portUserFedMappersToComponent(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
+ }
+
+ public void portUserFedToComponent(String providerId) {
+ DBCollection realms = db.getCollection("realms");
+ DBCursor cursor = realms.find();
+ while (cursor.hasNext()) {
+ BasicDBObject realm = (BasicDBObject) cursor.next();
+
+ String realmId = realm.getString("_id");
+ Set<String> removedProviders = new HashSet<>();
+
+ BasicDBList componentEntities = (BasicDBList) realm.get("componentEntities");
+ BasicDBList federationProviders = (BasicDBList) realm.get("userFederationProviders");
+ for (Object obj : federationProviders) {
+ BasicDBObject fedProvider = (BasicDBObject)obj;
+ if (fedProvider.getString("providerName").equals(providerId)) {
+ String id = fedProvider.getString("id");
+ int priority = fedProvider.getInt("priority");
+ String displayName = fedProvider.getString("displayName");
+ int fullSyncPeriod = fedProvider.getInt("fullSyncPeriod");
+ int changedSyncPeriod = fedProvider.getInt("changedSyncPeriod");
+ int lastSync = fedProvider.getInt("lastSync");
+ BasicDBObject component = new BasicDBObject();
+ component.put("id", id);
+ component.put("name", displayName);
+ component.put("providerType", UserStorageProvider.class.getName());
+ component.put("providerId", providerId);
+ component.put("parentId", realmId);
+
+ BasicDBObject config = new BasicDBObject();
+ config.put("priority", Collections.singletonList(Integer.toString(priority)));
+ config.put("fullSyncPeriod", Collections.singletonList(Integer.toString(fullSyncPeriod)));
+ config.put("changedSyncPeriod", Collections.singletonList(Integer.toString(changedSyncPeriod)));
+ config.put("lastSync", Collections.singletonList(Integer.toString(lastSync)));
+
+ BasicDBObject fedConfig = (BasicDBObject)fedProvider.get("config");
+ if (fedConfig != null) {
+ for (Map.Entry<String, Object> attr : new HashSet<>(fedConfig.entrySet())) {
+ String attrName = attr.getKey();
+ String attrValue = attr.getValue().toString();
+ config.put(attrName, Collections.singletonList(attrValue));
+
+ }
+ }
+
+
+ component.put("config", config);
+
+ componentEntities.add(component);
+
+ }
+ }
+ Iterator<Object> it = federationProviders.iterator();
+ while (it.hasNext()) {
+ BasicDBObject fedProvider = (BasicDBObject)it.next();
+ String id = fedProvider.getString("id");
+ if (removedProviders.contains(id)) {
+ it.remove();
+ }
+
+ }
+ realms.update(new BasicDBObject().append("_id", realmId), realm);
+ }
+ }
+ public void portUserFedMappersToComponent(String providerId, String mapperType) {
+ DBCollection realms = db.getCollection("realms");
+ DBCursor cursor = realms.find();
+ while (cursor.hasNext()) {
+ BasicDBObject realm = (BasicDBObject) cursor.next();
+
+ String realmId = realm.getString("_id");
+ Set<String> removedProviders = new HashSet<>();
+
+ BasicDBList componentEntities = (BasicDBList) realm.get("componentEntities");
+ BasicDBList federationProviders = (BasicDBList) realm.get("userFederationProviders");
+ BasicDBList fedMappers = (BasicDBList) realm.get("userFederationMappers");
+ for (Object obj : federationProviders) {
+ BasicDBObject fedProvider = (BasicDBObject)obj;
+ if (fedProvider.getString("providerName").equals(providerId)) {
+ String id = fedProvider.getString("id");
+ for (Object obj2 : fedMappers) {
+ BasicDBObject fedMapper = (BasicDBObject)obj2;
+ if (fedMapper.getString("federationProviderId").equals(id)) {
+ String name = fedMapper.getString("name");
+ String mapperId = fedMapper.getString("id");
+ removedProviders.add(mapperId);
+ String mapperProviderId = fedMapper.getString("federationMapperType");
+ BasicDBObject component = new BasicDBObject();
+ component.put("id", mapperId);
+ component.put("name", name);
+ component.put("providerType", mapperType);
+ component.put("providerId", mapperProviderId);
+ component.put("parentId", providerId);
+
+ BasicDBObject fedConfig = (BasicDBObject)fedMapper.get("config");
+ BasicDBObject config = new BasicDBObject();
+ if (fedConfig != null) {
+ for (Map.Entry<String, Object> attr : new HashSet<>(fedConfig.entrySet())) {
+ String attrName = attr.getKey();
+ String attrValue = attr.getValue().toString();
+ config.put(attrName, Collections.singletonList(attrValue));
+
+ }
+ }
+ component.put("config", config);
+ componentEntities.add(component);
+ }
+ }
+ }
+ }
+ Iterator<Object> it = fedMappers.iterator();
+ while (it.hasNext()) {
+ BasicDBObject fedMapper = (BasicDBObject)it.next();
+ String id = fedMapper.getString("id");
+ if (removedProviders.contains(id)) {
+ it.remove();
+ }
+
+ }
+ realms.update(new BasicDBObject().append("_id", realmId), realm);
+ }
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 205f6b3..a79c478 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -1954,6 +1954,13 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
@Override
public ComponentModel addComponentModel(ComponentModel model) {
+ model = importComponentModel(model);
+ ComponentUtil.notifyCreated(session, this, model);
+ return model;
+ }
+
+ @Override
+ public ComponentModel importComponentModel(ComponentModel model) {
ComponentUtil.getComponentFactory(session, model).validateConfiguration(session, this, model);
ComponentEntity entity = new ComponentEntity();
@@ -1970,7 +1977,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
realm.getComponentEntities().add(entity);
updateRealm();
- ComponentUtil.notifyCreated(session, this, model);
return model;
}
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
index aa8b440..e9df047 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
@@ -274,7 +274,22 @@ public interface RealmModel extends RoleContainerModel {
public IdentityProviderMapperModel getIdentityProviderMapperByName(String brokerAlias, String name);
+ /**
+ * Adds component model. Will call onCreate() method of ComponentFactory
+ *
+ * @param model
+ * @return
+ */
ComponentModel addComponentModel(ComponentModel model);
+
+ /**
+ * Adds component model. Will NOT call onCreate() method of ComponentFactory
+ *
+ * @param model
+ * @return
+ */
+ ComponentModel importComponentModel(ComponentModel model);
+
void updateComponent(ComponentModel component);
void removeComponent(ComponentModel component);
void removeComponents(String parentId);
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index e35da61..bf4a6dc 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -352,19 +352,7 @@ public class ModelToRepresentation {
}
}
- List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
- if (fedProviderModels.size() > 0) {
- List<UserFederationProviderRepresentation> fedProviderReps = new ArrayList<UserFederationProviderRepresentation>();
- for (UserFederationProviderModel model : fedProviderModels) {
- UserFederationProviderRepresentation fedProvRep = toRepresentation(model);
- fedProviderReps.add(fedProvRep);
- }
- rep.setUserFederationProviders(fedProviderReps);
- }
-
- for (UserFederationMapperModel mapper : realm.getUserFederationMappers()) {
- rep.addUserFederationMapper(toRepresentation(realm, mapper));
- }
+ exportUserFederationProvidersAndMappers(realm, rep);
for (IdentityProviderModel provider : realm.getIdentityProviders()) {
rep.addIdentityProvider(toRepresentation(realm, provider));
@@ -396,6 +384,22 @@ public class ModelToRepresentation {
return rep;
}
+ public static void exportUserFederationProvidersAndMappers(RealmModel realm, RealmRepresentation rep) {
+ List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
+ if (fedProviderModels.size() > 0) {
+ List<UserFederationProviderRepresentation> fedProviderReps = new ArrayList<UserFederationProviderRepresentation>();
+ for (UserFederationProviderModel model : fedProviderModels) {
+ UserFederationProviderRepresentation fedProvRep = toRepresentation(model);
+ fedProviderReps.add(fedProvRep);
+ }
+ rep.setUserFederationProviders(fedProviderReps);
+ }
+
+ for (UserFederationMapperModel mapper : realm.getUserFederationMappers()) {
+ rep.addUserFederationMapper(toRepresentation(realm, mapper));
+ }
+ }
+
public static void exportGroups(RealmModel realm, RealmRepresentation rep) {
List<GroupRepresentation> groups = toGroupHierarchy(realm, true);
rep.setGroups(groups);
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index c2e654e..8be4a9b 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -54,6 +54,7 @@ import org.keycloak.models.GroupModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import org.keycloak.models.OTPPolicy;
import org.keycloak.models.PasswordPolicy;
@@ -101,6 +102,8 @@ import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentatio
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.util.JsonSerialization;
@@ -305,36 +308,8 @@ public class RepresentationToModel {
String parentId = newRealm.getId();
importComponents(newRealm, components, parentId);
}
+ importUserFederationProvidersAndMappers(rep, newRealm);
- List<UserFederationProviderModel> providerModels = null;
- if (rep.getUserFederationProviders() != null) {
- providerModels = convertFederationProviders(rep.getUserFederationProviders());
- newRealm.setUserFederationProviders(providerModels);
- }
- if (rep.getUserFederationMappers() != null) {
-
- // Remove builtin mappers for federation providers, which have some mappers already provided in JSON (likely due to previous export)
- if (rep.getUserFederationProviders() != null) {
- Set<String> providerNames = new TreeSet<String>();
- for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) {
- providerNames.add(representation.getFederationProviderDisplayName());
- }
- for (String providerName : providerNames) {
- for (UserFederationProviderModel providerModel : providerModels) {
- if (providerName.equals(providerModel.getDisplayName())) {
- Set<UserFederationMapperModel> toDelete = newRealm.getUserFederationMappersByFederationProvider(providerModel.getId());
- for (UserFederationMapperModel mapperModel : toDelete) {
- newRealm.removeUserFederationMapper(mapperModel);
- }
- }
- }
- }
- }
-
- for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) {
- newRealm.addUserFederationMapper(toModel(newRealm, representation));
- }
- }
if (rep.getGroups() != null) {
importGroups(newRealm, rep);
@@ -390,6 +365,64 @@ public class RepresentationToModel {
}
}
+ public static void importUserFederationProvidersAndMappers(RealmRepresentation rep, RealmModel newRealm) {
+ // providers to convert to component model
+ Set<String> convertSet = new HashSet<>();
+ convertSet.add(LDAPConstants.LDAP_PROVIDER);
+ Map<String, String> mapperConvertSet = new HashMap<>();
+ mapperConvertSet.put(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper");
+
+
+ List<UserFederationProviderModel> providerModels = null;
+ Map<String, ComponentModel> userStorageModels = new HashMap<>();
+
+ if (rep.getUserFederationProviders() != null) {
+ providerModels = new LinkedList<>();
+ for (UserFederationProviderRepresentation fedRep : rep.getUserFederationProviders()) {
+ if (convertSet.contains(fedRep.getProviderName())) {
+ ComponentModel component = convertFedProviderToComponent(newRealm.getId(), fedRep);
+ userStorageModels.put(fedRep.getDisplayName(), newRealm.importComponentModel(component));
+ } else {
+ providerModels.add(convertFederationProvider(fedRep));
+ }
+
+ }
+ newRealm.setUserFederationProviders(providerModels);
+ }
+ if (rep.getUserFederationMappers() != null) {
+
+ // Remove builtin mappers for federation providers, which have some mappers already provided in JSON (likely due to previous export)
+ if (rep.getUserFederationProviders() != null) {
+ Set<String> providerNames = new TreeSet<String>();
+ for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) {
+ providerNames.add(representation.getFederationProviderDisplayName());
+ }
+ for (String providerName : providerNames) {
+ for (UserFederationProviderModel providerModel : providerModels) {
+ if (providerName.equals(providerModel.getDisplayName())) {
+ Set<UserFederationMapperModel> toDelete = newRealm.getUserFederationMappersByFederationProvider(providerModel.getId());
+ for (UserFederationMapperModel mapperModel : toDelete) {
+ newRealm.removeUserFederationMapper(mapperModel);
+ }
+ }
+ }
+ }
+ }
+
+ for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) {
+ if (userStorageModels.containsKey(representation.getFederationProviderDisplayName())) {
+ ComponentModel parent = userStorageModels.get(representation.getFederationProviderDisplayName());
+ String newMapperType = mapperConvertSet.get(parent.getProviderId());
+ ComponentModel mapper = convertFedMapperToComponent(newRealm, parent, representation, newMapperType);
+ newRealm.importComponentModel(mapper);
+
+ } else {
+ newRealm.addUserFederationMapper(toModel(newRealm, representation));
+ }
+ }
+ }
+ }
+
protected static void importComponents(RealmModel newRealm, MultivaluedHashMap<String, ComponentExportRepresentation> components, String parentId) {
for (Map.Entry<String, List<ComponentExportRepresentation>> entry : components.entrySet()) {
String providerType = entry.getKey();
@@ -402,7 +435,7 @@ public class RepresentationToModel {
component.setProviderId(compRep.getProviderId());
component.setSubType(compRep.getSubType());
component.setParentId(parentId);
- component = newRealm.addComponentModel(component);
+ component = newRealm.importComponentModel(component);
if (compRep.getSubComponents() != null) {
importComponents(newRealm, compRep.getSubComponents(), component.getId());
}
@@ -865,14 +898,53 @@ public class RepresentationToModel {
List<UserFederationProviderModel> result = new ArrayList<UserFederationProviderModel>();
for (UserFederationProviderRepresentation representation : providers) {
- UserFederationProviderModel model = new UserFederationProviderModel(representation.getId(), representation.getProviderName(),
- representation.getConfig(), representation.getPriority(), representation.getDisplayName(),
- representation.getFullSyncPeriod(), representation.getChangedSyncPeriod(), representation.getLastSync());
+ UserFederationProviderModel model = convertFederationProvider(representation);
result.add(model);
}
return result;
}
+ private static UserFederationProviderModel convertFederationProvider(UserFederationProviderRepresentation representation) {
+ return new UserFederationProviderModel(representation.getId(), representation.getProviderName(),
+ representation.getConfig(), representation.getPriority(), representation.getDisplayName(),
+ representation.getFullSyncPeriod(), representation.getChangedSyncPeriod(), representation.getLastSync());
+ }
+
+ public static ComponentModel convertFedProviderToComponent(String realmId, UserFederationProviderRepresentation fedModel) {
+ UserStorageProviderModel model = new UserStorageProviderModel();
+ model.setId(fedModel.getId());
+ model.setName(fedModel.getDisplayName());
+ model.setParentId(realmId);
+ model.setProviderId(fedModel.getProviderName());
+ model.setProviderType(UserStorageProvider.class.getName());
+ model.setFullSyncPeriod(fedModel.getFullSyncPeriod());
+ model.setPriority(fedModel.getPriority());
+ model.setChangedSyncPeriod(fedModel.getChangedSyncPeriod());
+ model.setLastSync(fedModel.getLastSync());
+ if (fedModel.getConfig() != null) {
+ for (Map.Entry<String, String> entry : fedModel.getConfig().entrySet()) {
+ model.getConfig().putSingle(entry.getKey(), entry.getValue());
+ }
+ }
+ return model;
+ }
+
+ public static ComponentModel convertFedMapperToComponent(RealmModel realm, ComponentModel parent, UserFederationMapperRepresentation rep, String newMapperType) {
+ ComponentModel mapper = new ComponentModel();
+ mapper.setId(rep.getId());
+ mapper.setName(rep.getName());
+ mapper.setProviderId(rep.getFederationMapperType());
+ mapper.setProviderType(newMapperType);
+ mapper.setParentId(parent.getId());
+ if (rep.getConfig() != null) {
+ for (Map.Entry<String, String> entry : rep.getConfig().entrySet()) {
+ mapper.getConfig().putSingle(entry.getKey(), entry.getValue());
+ }
+ }
+ return mapper;
+ }
+
+
public static UserFederationMapperModel toModel(RealmModel realm, UserFederationMapperRepresentation rep) {
UserFederationMapperModel model = new UserFederationMapperModel();
model.setId(rep.getId());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
index 2809b97..11ed2f6 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java
@@ -48,7 +48,9 @@ import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.federation.ldap.FederationTestUtils;
@@ -61,8 +63,10 @@ import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.LDAPRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
import org.openqa.selenium.WebDriver;
+import java.io.FileOutputStream;
import java.util.List;
import java.util.Map;
@@ -104,6 +108,20 @@ public class FederationProvidersIntegrationTest {
}
});
+ /*
+ @Test
+ public void exportJson() throws Exception {
+ KeycloakSession session = keycloakRule.startSession();
+ FileOutputStream os = new FileOutputStream("/Users/williamburke/jboss/keycloak/p1b-repo/keycloak/fed-provider.json");
+ RealmManager manager = new RealmManager(session);
+ RealmModel appRealm = manager.getRealm("test");
+ RealmRepresentation rep = ModelToRepresentation.toRepresentation(appRealm, true);
+ JsonSerialization.writeValueToStream(os, rep);
+ os.close();
+
+ }
+ */
+
@ClassRule
public static TestRule chain = RuleChain
.outerRule(ldapRule)
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPLegacyImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPLegacyImportTest.java
new file mode 100755
index 0000000..fc34c86
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPLegacyImportTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.storage.ldap;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runners.MethodSorters;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.credential.CredentialModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.ModelReadOnlyException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.storage.UserStorageProvider;
+import org.keycloak.storage.UserStorageProviderModel;
+import org.keycloak.storage.ldap.LDAPConfig;
+import org.keycloak.storage.ldap.LDAPStorageProvider;
+import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
+import org.keycloak.storage.ldap.idm.model.LDAPObject;
+import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper;
+import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory;
+import org.keycloak.storage.ldap.mappers.HardcodedLDAPRoleStorageMapper;
+import org.keycloak.storage.ldap.mappers.HardcodedLDAPRoleStorageMapperFactory;
+import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
+import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.pages.AccountPasswordPage;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.RegisterPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.LDAPRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
+import org.openqa.selenium.WebDriver;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests that legacy UserFederationProvider json export is converted to ComponentModel
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class LDAPLegacyImportTest {
+
+ private static LDAPRule ldapRule = new LDAPRule();
+
+ private static ComponentModel ldapModel = null;
+
+
+ private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ LDAPTestUtils.addLocalUser(manager.getSession(), appRealm, "marykeycloak", "mary@test.com", "password-app");
+
+ RealmRepresentation imported = null;
+ try {
+ imported = JsonSerialization.readValue(getClass().getResourceAsStream("/ldap/fed-provider-export.json"), RealmRepresentation.class);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ RepresentationToModel.importUserFederationProvidersAndMappers(imported, appRealm);
+ ldapModel = appRealm.getComponents(appRealm.getId(), UserStorageProvider.class.getName()).get(0);
+ // Delete all LDAP users and add some new for testing
+ LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
+ LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm);
+
+ LDAPObject john = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
+ LDAPTestUtils.updateLDAPPassword(ldapFedProvider, john, "Password1");
+
+ LDAPObject existing = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", null, "5678");
+
+ appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
+ }
+ });
+
+ @ClassRule
+ public static TestRule chain = RuleChain
+ .outerRule(ldapRule)
+ .around(keycloakRule);
+
+ @Rule
+ public WebRule webRule = new WebRule(this);
+
+ @WebResource
+ protected OAuthClient oauth;
+
+ @WebResource
+ protected WebDriver driver;
+
+ @WebResource
+ protected AppPage appPage;
+
+ @WebResource
+ protected RegisterPage registerPage;
+
+ @WebResource
+ protected LoginPage loginPage;
+
+ @WebResource
+ protected AccountUpdateProfilePage profilePage;
+
+ @WebResource
+ protected AccountPasswordPage changePasswordPage;
+
+ //@Test
+ public void runit() throws Exception {
+ Thread.sleep(10000000);
+
+ }
+
+ private void loginSuccessAndLogout(String username, String password) {
+ loginPage.open();
+ loginPage.login(username, password);
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+ oauth.openLogout();
+ }
+
+
+ @Test
+ public void loginClassic() {
+ loginPage.open();
+ loginPage.login("marykeycloak", "password-app");
+
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+ }
+
+ @Test
+ public void loginLdap() {
+ loginPage.open();
+ loginPage.login("johnkeycloak", "Password1");
+
+ Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+ Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
+
+ profilePage.open();
+ Assert.assertEquals("John", profilePage.getFirstName());
+ Assert.assertEquals("Doe", profilePage.getLastName());
+ Assert.assertEquals("john@email.org", profilePage.getEmail());
+ }
+
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
index 6e79d69..a2b2636 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageTest.java
@@ -132,7 +132,7 @@ public class UserStorageTest {
}
- @Test
+ //@Test
public void testIDE() throws Exception {
Thread.sleep(100000000);
}
diff --git a/testsuite/integration/src/test/resources/ldap/fed-provider-export.json b/testsuite/integration/src/test/resources/ldap/fed-provider-export.json
new file mode 100644
index 0000000..4bbe374
--- /dev/null
+++ b/testsuite/integration/src/test/resources/ldap/fed-provider-export.json
@@ -0,0 +1,622 @@
+{
+ "id": "test",
+ "realm": "test",
+ "notBefore": 0,
+ "revokeRefreshToken": false,
+ "accessTokenLifespan": 300,
+ "accessTokenLifespanForImplicitFlow": 900,
+ "ssoSessionIdleTimeout": 1800,
+ "ssoSessionMaxLifespan": 36000,
+ "offlineSessionIdleTimeout": 2592000,
+ "accessCodeLifespan": 60,
+ "accessCodeLifespanUserAction": 300,
+ "accessCodeLifespanLogin": 1800,
+ "enabled": true,
+ "sslRequired": "external",
+ "registrationAllowed": true,
+ "registrationEmailAsUsername": false,
+ "rememberMe": false,
+ "verifyEmail": false,
+ "resetPasswordAllowed": true,
+ "editUsernameAllowed": true,
+ "bruteForceProtected": false,
+ "maxFailureWaitSeconds": 900,
+ "minimumQuickLoginWaitSeconds": 60,
+ "waitIncrementSeconds": 60,
+ "quickLoginCheckMilliSeconds": 1000,
+ "maxDeltaTimeSeconds": 43200,
+ "failureFactor": 30,
+ "groups": [
+ {
+ "id": "2aa57ddd-e48f-4a62-bb8e-53ebe2ff1057",
+ "name": "topGroup",
+ "path": "/topGroup",
+ "attributes": {
+ "topAttribute": [
+ "true"
+ ]
+ },
+ "realmRoles": [
+ "user"
+ ],
+ "clientRoles": {},
+ "subGroups": [
+ {
+ "id": "8e91afd4-b8e4-4de4-ba37-1edc7298d518",
+ "name": "level2group",
+ "path": "/topGroup/level2group",
+ "attributes": {
+ "level2Attribute": [
+ "true"
+ ]
+ },
+ "realmRoles": [
+ "admin"
+ ],
+ "clientRoles": {
+ "test-app": [
+ "customer-user"
+ ]
+ },
+ "subGroups": []
+ }
+ ]
+ }
+ ],
+ "defaultRoles": [
+ "user",
+ "offline_access",
+ "uma_authorization"
+ ],
+ "requiredCredentials": [
+ "password"
+ ],
+ "passwordPolicy": "hashIterations(20000)",
+ "otpPolicyType": "totp",
+ "otpPolicyAlgorithm": "HmacSHA1",
+ "otpPolicyInitialCounter": 0,
+ "otpPolicyDigits": 6,
+ "otpPolicyLookAheadWindow": 1,
+ "otpPolicyPeriod": 30,
+ "browserSecurityHeaders": {
+ "xContentTypeOptions": "nosniff",
+ "xFrameOptions": "SAMEORIGIN",
+ "contentSecurityPolicy": "frame-src 'self'"
+ },
+ "smtpServer": {
+ "host": "localhost",
+ "from": "auto@keycloak.org",
+ "port": "3025"
+ },
+ "userFederationProviders": [
+ {
+ "id": "1fc3afd2-4c18-48dd-9055-b4bbae9229b7",
+ "displayName": "test-ldap",
+ "providerName": "ldap",
+ "config": {
+ "serverPrincipal": "HTTP/localhost@KEYCLOAK.ORG",
+ "debug": "true",
+ "pagination": "true",
+ "keyTab": "/Users/williamburke/jboss/keycloak/p1b-repo/keycloak/testsuite/integration/target/test-classes/kerberos/http.keytab",
+ "connectionPooling": "true",
+ "usersDn": "ou=People,dc=keycloak,dc=org",
+ "useKerberosForPasswordAuthentication": "false",
+ "kerberosRealm": "KEYCLOAK.ORG",
+ "bindCredential": "secret",
+ "bindDn": "uid=admin,ou=system",
+ "allowPasswordAuthentication": "true",
+ "vendor": "other",
+ "editMode": "WRITABLE",
+ "allowKerberosAuthentication": "false",
+ "connectionUrl": "ldap://localhost:10389",
+ "syncRegistrations": "true",
+ "baseDn": "dc=keycloak,dc=org",
+ "batchSizeForSync": "3",
+ "updateProfileFirstLogin": "true"
+ },
+ "priority": 0,
+ "fullSyncPeriod": -1,
+ "changedSyncPeriod": -1,
+ "lastSync": 0
+ }
+ ],
+ "userFederationMappers": [
+ {
+ "id": "b2fc2d9c-2ea8-417f-96db-2565be62a646",
+ "name": "last name",
+ "federationProviderDisplayName": "test-ldap",
+ "federationMapperType": "user-attribute-ldap-mapper",
+ "config": {
+ "always.read.value.from.ldap": "true",
+ "read.only": "false",
+ "ldap.attribute": "sn",
+ "is.mandatory.in.ldap": "true",
+ "user.model.attribute": "lastName"
+ }
+ },
+ {
+ "id": "6dc25318-dc20-4927-ba19-9293ab31aa28",
+ "name": "zipCodeMapper",
+ "federationProviderDisplayName": "test-ldap",
+ "federationMapperType": "user-attribute-ldap-mapper",
+ "config": {
+ "always.read.value.from.ldap": "false",
+ "read.only": "false",
+ "ldap.attribute": "postalCode",
+ "is.mandatory.in.ldap": "false",
+ "user.model.attribute": "postal_code"
+ }
+ },
+ {
+ "id": "7afa12a2-f36e-4f87-b715-e941773c8534",
+ "name": "username",
+ "federationProviderDisplayName": "test-ldap",
+ "federationMapperType": "user-attribute-ldap-mapper",
+ "config": {
+ "always.read.value.from.ldap": "false",
+ "read.only": "false",
+ "ldap.attribute": "uid",
+ "is.mandatory.in.ldap": "true",
+ "user.model.attribute": "username"
+ }
+ },
+ {
+ "id": "abfe054c-6d2a-4870-a239-1a312c3e5a94",
+ "name": "creation date",
+ "federationProviderDisplayName": "test-ldap",
+ "federationMapperType": "user-attribute-ldap-mapper",
+ "config": {
+ "always.read.value.from.ldap": "true",
+ "read.only": "true",
+ "ldap.attribute": "createTimestamp",
+ "is.mandatory.in.ldap": "false",
+ "user.model.attribute": "createTimestamp"
+ }
+ },
+ {
+ "id": "6aef95e5-736e-4b1e-98d0-332f61f94ff9",
+ "name": "first name",
+ "federationProviderDisplayName": "test-ldap",
+ "federationMapperType": "user-attribute-ldap-mapper",
+ "config": {
+ "always.read.value.from.ldap": "true",
+ "read.only": "false",
+ "ldap.attribute": "cn",
+ "is.mandatory.in.ldap": "true",
+ "user.model.attribute": "firstName"
+ }
+ },
+ {
+ "id": "0601e4a2-fd63-4f6a-ae3b-13cc6f4f4f1c",
+ "name": "email",
+ "federationProviderDisplayName": "test-ldap",
+ "federationMapperType": "user-attribute-ldap-mapper",
+ "config": {
+ "always.read.value.from.ldap": "false",
+ "read.only": "false",
+ "ldap.attribute": "mail",
+ "is.mandatory.in.ldap": "false",
+ "user.model.attribute": "email"
+ }
+ },
+ {
+ "id": "fa308910-3be9-4bd8-8256-66cf04d8fcd2",
+ "name": "modify date",
+ "federationProviderDisplayName": "test-ldap",
+ "federationMapperType": "user-attribute-ldap-mapper",
+ "config": {
+ "always.read.value.from.ldap": "true",
+ "read.only": "true",
+ "ldap.attribute": "modifyTimestamp",
+ "is.mandatory.in.ldap": "false",
+ "user.model.attribute": "modifyTimestamp"
+ }
+ }
+ ],
+ "eventsEnabled": false,
+ "eventsListeners": [
+ "jboss-logging"
+ ],
+ "enabledEventTypes": [],
+ "adminEventsEnabled": false,
+ "adminEventsDetailsEnabled": false,
+ "internationalizationEnabled": true,
+ "supportedLocales": [
+ "de",
+ "en"
+ ],
+ "defaultLocale": "en",
+ "authenticationFlows": [
+ {
+ "id": "b12463a9-5d33-4f27-b010-4005db77e602",
+ "alias": "Handle Existing Account",
+ "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider",
+ "providerId": "basic-flow",
+ "topLevel": false,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "idp-confirm-link",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "idp-email-verification",
+ "requirement": "ALTERNATIVE",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "requirement": "ALTERNATIVE",
+ "priority": 30,
+ "flowAlias": "Verify Existing Account by Re-authentication",
+ "userSetupAllowed": false,
+ "autheticatorFlow": true
+ }
+ ]
+ },
+ {
+ "id": "c1684fc8-a99d-4e19-a795-478e4d793fb5",
+ "alias": "Verify Existing Account by Re-authentication",
+ "description": "Reauthentication of existing account",
+ "providerId": "basic-flow",
+ "topLevel": false,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "idp-username-password-form",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "auth-otp-form",
+ "requirement": "OPTIONAL",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "09af30d8-8c2a-45a4-a2be-b7617e9d0185",
+ "alias": "browser",
+ "description": "browser based authentication",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "auth-cookie",
+ "requirement": "ALTERNATIVE",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "auth-spnego",
+ "requirement": "DISABLED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "identity-provider-redirector",
+ "requirement": "ALTERNATIVE",
+ "priority": 25,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "requirement": "ALTERNATIVE",
+ "priority": 30,
+ "flowAlias": "forms",
+ "userSetupAllowed": false,
+ "autheticatorFlow": true
+ }
+ ]
+ },
+ {
+ "id": "6cdf31d0-9c91-4ea6-8e37-da6e8fa7544c",
+ "alias": "clients",
+ "description": "Base authentication for clients",
+ "providerId": "client-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "client-secret",
+ "requirement": "ALTERNATIVE",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "client-jwt",
+ "requirement": "ALTERNATIVE",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "c9a38de8-4c0c-496a-9936-b9753f73bfcc",
+ "alias": "direct grant",
+ "description": "OpenID Connect Resource Owner Grant",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "direct-grant-validate-username",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "direct-grant-validate-password",
+ "requirement": "REQUIRED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "direct-grant-validate-otp",
+ "requirement": "OPTIONAL",
+ "priority": 30,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "3755e297-7907-4c14-8c5f-d77e2bfe4b5d",
+ "alias": "first broker login",
+ "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticatorConfig": "review profile config",
+ "authenticator": "idp-review-profile",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticatorConfig": "create unique user config",
+ "authenticator": "idp-create-user-if-unique",
+ "requirement": "ALTERNATIVE",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "requirement": "ALTERNATIVE",
+ "priority": 30,
+ "flowAlias": "Handle Existing Account",
+ "userSetupAllowed": false,
+ "autheticatorFlow": true
+ }
+ ]
+ },
+ {
+ "id": "f35b2f00-3e84-4f2e-b48e-3e4159d88a06",
+ "alias": "forms",
+ "description": "Username, password, otp and other auth forms.",
+ "providerId": "basic-flow",
+ "topLevel": false,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "auth-username-password-form",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "auth-otp-form",
+ "requirement": "OPTIONAL",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "441b4480-1ace-483a-bffb-f0cb6659fe32",
+ "alias": "registration",
+ "description": "registration flow",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "registration-page-form",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "flowAlias": "registration form",
+ "userSetupAllowed": false,
+ "autheticatorFlow": true
+ }
+ ]
+ },
+ {
+ "id": "c7de2a37-29a1-471a-9b51-699a69032b00",
+ "alias": "registration form",
+ "description": "registration form",
+ "providerId": "form-flow",
+ "topLevel": false,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "registration-user-creation",
+ "requirement": "REQUIRED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "registration-profile-action",
+ "requirement": "REQUIRED",
+ "priority": 40,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "registration-password-action",
+ "requirement": "REQUIRED",
+ "priority": 50,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "registration-recaptcha-action",
+ "requirement": "DISABLED",
+ "priority": 60,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "d362be0a-df20-4ce7-9288-f8448e0c4647",
+ "alias": "reset credentials",
+ "description": "Reset credentials for a user if they forgot their password or something",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "reset-credentials-choose-user",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "reset-credential-email",
+ "requirement": "REQUIRED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "reset-password",
+ "requirement": "REQUIRED",
+ "priority": 30,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "reset-otp",
+ "requirement": "OPTIONAL",
+ "priority": 40,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "c2d7a1ae-57c9-4f3b-a4ce-55c3f0d9869f",
+ "alias": "saml ecp",
+ "description": "SAML ECP Profile Authentication Flow",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "http-basic-authenticator",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ }
+ ],
+ "authenticatorConfig": [
+ {
+ "id": "a2490828-becb-435f-9c3c-318b3939bf64",
+ "alias": "create unique user config",
+ "config": {
+ "require.password.update.after.registration": "false"
+ }
+ },
+ {
+ "id": "78421671-f733-4901-82bc-58bf50c43206",
+ "alias": "review profile config",
+ "config": {
+ "update.profile.on.first.login": "missing"
+ }
+ }
+ ],
+ "requiredActions": [
+ {
+ "alias": "CONFIGURE_TOTP",
+ "name": "Configure OTP",
+ "providerId": "CONFIGURE_TOTP",
+ "enabled": true,
+ "defaultAction": false,
+ "config": {}
+ },
+ {
+ "alias": "UPDATE_PASSWORD",
+ "name": "Update Password",
+ "providerId": "UPDATE_PASSWORD",
+ "enabled": true,
+ "defaultAction": false,
+ "config": {}
+ },
+ {
+ "alias": "UPDATE_PROFILE",
+ "name": "Update Profile",
+ "providerId": "UPDATE_PROFILE",
+ "enabled": true,
+ "defaultAction": false,
+ "config": {}
+ },
+ {
+ "alias": "VERIFY_EMAIL",
+ "name": "Verify Email",
+ "providerId": "VERIFY_EMAIL",
+ "enabled": true,
+ "defaultAction": false,
+ "config": {}
+ },
+ {
+ "alias": "terms_and_conditions",
+ "name": "Terms and Conditions",
+ "providerId": "terms_and_conditions",
+ "enabled": false,
+ "defaultAction": false,
+ "config": {}
+ }
+ ],
+ "browserFlow": "browser",
+ "registrationFlow": "registration",
+ "directGrantFlow": "direct grant",
+ "resetCredentialsFlow": "reset credentials",
+ "clientAuthenticationFlow": "clients",
+ "attributes": {
+ "_browser_header.xFrameOptions": "SAMEORIGIN",
+ "failureFactor": "30",
+ "quickLoginCheckMilliSeconds": "1000",
+ "maxDeltaTimeSeconds": "43200",
+ "_browser_header.xContentTypeOptions": "nosniff",
+ "bruteForceProtected": "false",
+ "maxFailureWaitSeconds": "900",
+ "_browser_header.contentSecurityPolicy": "frame-src 'self'",
+ "minimumQuickLoginWaitSeconds": "60",
+ "waitIncrementSeconds": "60"
+ }
+}
\ No newline at end of file