ldap jpa migration

This commit is contained in:
Bill Burke 2016-11-10 16:52:18 -05:00
parent 94076a3b24
commit 8a5f817030
19 changed files with 1419 additions and 52 deletions

View file

@ -67,6 +67,10 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-ldap-federation</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-ldap-storage</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-kerberos-federation</artifactId>

View file

@ -21,7 +21,7 @@
</properties>
<resources>
<artifact name="${org.keycloak:keycloak-ldap-federation}"/>
<artifact name="${org.keycloak:keycloak-ldap-storage}"/>
</resources>
<dependencies>

View file

@ -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);

View file

@ -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);
}
}
}

View file

@ -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";
}
}

View file

@ -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;
}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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());

View file

@ -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)

View file

@ -132,7 +132,7 @@ public class UserStorageTest {
}
@Test
//@Test
public void testIDE() throws Exception {
Thread.sleep(100000000);
}

View file

@ -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());
}
}

View file

@ -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"
}
}