diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
index 27869eb1a5..73b98121b6 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
@@ -2,6 +2,7 @@
+
@@ -21,6 +22,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -67,6 +95,11 @@
+
+
+
+
+
diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index 5bcb77ee99..a5af4652f9 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -9,6 +9,7 @@
org.keycloak.models.jpa.entities.RealmAttributeEntity
org.keycloak.models.jpa.entities.RequiredCredentialEntity
org.keycloak.models.jpa.entities.UserFederationProviderEntity
+ org.keycloak.models.jpa.entities.UserFederationMapperEntity
org.keycloak.models.jpa.entities.RoleEntity
org.keycloak.models.jpa.entities.FederatedIdentityEntity
org.keycloak.models.jpa.entities.MigrationModelEntity
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index 73d6b84f97..ed6844c3ed 100755
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -39,6 +39,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
"org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity",
"org.keycloak.models.entities.UserFederationProviderEntity",
+ "org.keycloak.models.entities.UserFederationMapperEntity",
"org.keycloak.models.entities.ProtocolMapperEntity",
"org.keycloak.models.entities.IdentityProviderMapperEntity",
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index 0240a8dbe4..6ff027ca7d 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -53,6 +53,7 @@ public class RealmRepresentation {
protected Map browserSecurityHeaders;
protected Map smtpServer;
protected List userFederationProviders;
+ protected List userFederationMappers;
protected String loginTheme;
protected String accountTheme;
protected String adminTheme;
@@ -536,6 +537,19 @@ public class RealmRepresentation {
this.userFederationProviders = userFederationProviders;
}
+ public List getUserFederationMappers() {
+ return userFederationMappers;
+ }
+
+ public void setUserFederationMappers(List userFederationMappers) {
+ this.userFederationMappers = userFederationMappers;
+ }
+
+ public void addUserFederationMapper(UserFederationMapperRepresentation userFederationMapper) {
+ if (userFederationMappers == null) userFederationMappers = new LinkedList<>();
+ userFederationMappers.add(userFederationMapper);
+ }
+
public List getIdentityProviders() {
return identityProviders;
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperRepresentation.java
new file mode 100644
index 0000000000..2e87e4e8c5
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperRepresentation.java
@@ -0,0 +1,56 @@
+package org.keycloak.representations.idm;
+
+import java.util.Map;
+
+/**
+ * @author Marek Posolda
+ */
+public class UserFederationMapperRepresentation {
+
+ protected String id;
+ protected String name;
+ protected String federationProviderDisplayName;
+ protected String federationMapperType;
+ protected Map config;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getFederationProviderDisplayName() {
+ return federationProviderDisplayName;
+ }
+
+ public void setFederationProviderDisplayName(String federationProviderDisplayName) {
+ this.federationProviderDisplayName = federationProviderDisplayName;
+ }
+
+ public String getFederationMapperType() {
+ return federationMapperType;
+ }
+
+ public void setFederationMapperType(String federationMapperType) {
+ this.federationMapperType = federationMapperType;
+ }
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+}
+
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperTypeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperTypeRepresentation.java
new file mode 100644
index 0000000000..f03b3765c0
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperTypeRepresentation.java
@@ -0,0 +1,55 @@
+package org.keycloak.representations.idm;
+
+import java.util.List;
+
+/**
+ * @author Marek Posolda
+ */
+public class UserFederationMapperTypeRepresentation {
+ protected String id;
+ protected String name;
+ protected String category;
+ protected String helpText;
+
+ protected List properties;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getHelpText() {
+ return helpText;
+ }
+
+ public void setHelpText(String helpText) {
+ this.helpText = helpText;
+ }
+
+ public List getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List properties) {
+ this.properties = properties;
+ }
+}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index ad55bc3534..fcdc11c17d 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -111,7 +111,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
proxied = new UnsyncedLDAPUserModelDelegate(local, this);
}
- List federationMappers = realm.getUserFederationMappers();
+ Set federationMappers = realm.getUserFederationMappers();
for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel);
proxied = ldapMapper.proxy(mapperModel, this, ldapObject, proxied, realm);
@@ -268,7 +268,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
UserModel imported = session.userStorage().addUser(realm, ldapUsername);
imported.setEnabled(true);
- List federationMappers = realm.getUserFederationMappers();
+ Set federationMappers = realm.getUserFederationMappers();
for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel);
ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, imported, realm, true);
@@ -404,7 +404,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
if ((fedModel.getId().equals(currentUser.getFederationLink())) && (ldapUser.getUuid().equals(currentUser.getAttribute(LDAPConstants.LDAP_ID)))) {
// Update keycloak user
- List federationMappers = realm.getUserFederationMappers();
+ Set federationMappers = realm.getUserFederationMappers();
for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel);
ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, currentUser, realm, false);
@@ -477,9 +477,9 @@ public class LDAPFederationProvider implements UserFederationProvider {
}
public LDAPFederationMapper getMapper(UserFederationMapperModel mapperModel) {
- LDAPFederationMapper ldapMapper = (LDAPFederationMapper) getSession().getProvider(UserFederationMapper.class, mapperModel.getFederationMapperId());
+ LDAPFederationMapper ldapMapper = (LDAPFederationMapper) getSession().getProvider(UserFederationMapper.class, mapperModel.getFederationMapperType());
if (ldapMapper == null) {
- throw new ModelException("Can't find mapper type with ID: " + mapperModel.getFederationMapperId());
+ throw new ModelException("Can't find mapper type with ID: " + mapperModel.getFederationMapperType());
}
return ldapMapper;
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
index 6c5ac93237..1e9a9e3e08 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPUtils.java
@@ -1,6 +1,7 @@
package org.keycloak.federation.ldap;
import java.util.List;
+import java.util.Set;
import org.keycloak.federation.ldap.idm.model.LDAPDn;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
@@ -34,7 +35,7 @@ public class LDAPUtils {
ldapObject.setRdnAttributeName(ldapConfig.getRdnLdapAttribute());
ldapObject.setObjectClasses(ldapConfig.getObjectClasses());
- List federationMappers = realm.getUserFederationMappers();
+ Set federationMappers = realm.getUserFederationMappers();
for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = ldapProvider.getMapper(mapperModel);
ldapMapper.onRegisterUserToLDAP(mapperModel, ldapProvider, ldapObject, user, realm);
@@ -130,7 +131,7 @@ public class LDAPUtils {
ldapQuery.addSearchDns(config.getUserDns());
ldapQuery.addObjectClasses(config.getObjectClasses());
- List mapperModels = realm.getUserFederationMappers();
+ Set mapperModels = realm.getUserFederationMappers();
ldapQuery.addMappers(mapperModels);
return ldapQuery;
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java
index 63b5443aee..0a2a767e3d 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java
@@ -475,12 +475,12 @@ public class LDAPOperationManager {
String url = this.config.getConnectionUrl();
- if (url == null) {
- throw new RuntimeException("url");
+ if (url != null) {
+ env.put(Context.PROVIDER_URL, url);
+ } else {
+ logger.warn("LDAP URL is null. LDAPOperationManager won't work correctly");
}
- env.put(Context.PROVIDER_URL, url);
-
String connectionPooling = this.config.getConnectionPooling();
if (connectionPooling != null) {
env.put("com.sun.jndi.ldap.connect.pool", connectionPooling);
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
index 682034551f..6ef69791dc 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
@@ -11,6 +11,8 @@ import org.keycloak.provider.ProviderConfigProperty;
*/
public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
+ public static final String ID = "full-name-ldap-mapper";
+
@Override
public String getHelpText() {
return "Some help text - full name mapper - TODO";
@@ -23,7 +25,7 @@ public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationM
@Override
public String getId() {
- return "full-name-ldap-mapper";
+ return ID;
}
@Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
index 321a0d5fa8..cbae85069f 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
@@ -11,6 +11,8 @@ import org.keycloak.provider.ProviderConfigProperty;
*/
public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
+ public static final String ID = "role-ldap-mapper";
+
@Override
public String getHelpText() {
return "Some help text - role mapper - TODO";
@@ -23,7 +25,7 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
@Override
public String getId() {
- return "role-ldap-mapper";
+ return ID ;
}
@Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
index 16d4879449..564b012491 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
@@ -11,6 +11,8 @@ import org.keycloak.provider.ProviderConfigProperty;
*/
public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
+ public static final String ID = "user-attribute-ldap-mapper";
+
@Override
public String getHelpText() {
return "Some help text TODO";
@@ -23,7 +25,7 @@ public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFedera
@Override
public String getId() {
- return "user-attribute-ldap-mapper";
+ return ID;
}
@Override
diff --git a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperSpi.java b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperSpi.java
index b67a23a850..f668058d3e 100644
--- a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperSpi.java
+++ b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperSpi.java
@@ -23,4 +23,9 @@ public class UserFederationMapperSpi implements Spi {
public Class extends ProviderFactory> getProviderFactoryClass() {
return UserFederationMapperFactory.class;
}
+
+ @Override
+ public boolean isPrivate() {
+ return false;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index 1247bd96ba..bfcaefb172 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -189,7 +189,13 @@ public interface RealmModel extends RoleContainerModel {
void removeUserFederationProvider(UserFederationProviderModel provider);
void setUserFederationProviders(List providers);
- List getUserFederationMappers();
+ Set getUserFederationMappers();
+ Set getUserFederationMappersByFederationProvider(String federationProviderId);
+ UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel mapper);
+ void removeUserFederationMapper(UserFederationMapperModel mapper);
+ void updateUserFederationMapper(UserFederationMapperModel mapper);
+ UserFederationMapperModel getUserFederationMapperById(String id);
+ UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name);
String getLoginTheme();
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationMapperModel.java b/model/api/src/main/java/org/keycloak/models/UserFederationMapperModel.java
index 50b47c3d9d..54a347bdfb 100644
--- a/model/api/src/main/java/org/keycloak/models/UserFederationMapperModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationMapperModel.java
@@ -9,7 +9,13 @@ public class UserFederationMapperModel {
protected String id;
protected String name;
- protected String federationMapperId;
+
+ // Refers to DB ID of federation provider
+ protected String federationProviderId;
+
+ // Refers to ID of UserFederationMapper implementation ( UserFederationMapperFactory.getId )
+ protected String federationMapperType;
+
protected Map config;
public String getId() {
@@ -28,12 +34,20 @@ public class UserFederationMapperModel {
this.name = name;
}
- public String getFederationMapperId() {
- return federationMapperId;
+ public String getFederationProviderId() {
+ return federationProviderId;
}
- public void setFederationMapperId(String federationMapperId) {
- this.federationMapperId = federationMapperId;
+ public void setFederationProviderId(String federationProviderId) {
+ this.federationProviderId = federationProviderId;
+ }
+
+ public String getFederationMapperType() {
+ return federationMapperType;
+ }
+
+ public void setFederationMapperType(String federationMapperType) {
+ this.federationMapperType = federationMapperType;
}
public Map getConfig() {
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index 718a1c5ed1..1b8ccf21df 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -53,6 +53,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private List requiredCredentials = new ArrayList();
private List userFederationProviders = new ArrayList();
+ private List userFederationMappers = new ArrayList();
private List identityProviders = new ArrayList();
private Map browserSecurityHeaders = new HashMap();
@@ -426,6 +427,14 @@ public class RealmEntity extends AbstractIdentifiableEntity {
this.userFederationProviders = userFederationProviders;
}
+ public List getUserFederationMappers() {
+ return userFederationMappers;
+ }
+
+ public void setUserFederationMappers(List userFederationMappers) {
+ this.userFederationMappers = userFederationMappers;
+ }
+
public List getIdentityProviders() {
return identityProviders;
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/UserFederationMapperEntity.java b/model/api/src/main/java/org/keycloak/models/entities/UserFederationMapperEntity.java
new file mode 100644
index 0000000000..a920047ecc
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/UserFederationMapperEntity.java
@@ -0,0 +1,55 @@
+package org.keycloak.models.entities;
+
+import java.util.Map;
+
+/**
+ * @author Marek Posolda
+ */
+public class UserFederationMapperEntity {
+
+ protected String id;
+ protected String name;
+ protected String federationProviderId;
+ protected String federationMapperType;
+ protected Map config;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getFederationProviderId() {
+ return federationProviderId;
+ }
+
+ public void setFederationProviderId(String federationProviderId) {
+ this.federationProviderId = federationProviderId;
+ }
+
+ public String getFederationMapperType() {
+ return federationMapperType;
+ }
+
+ public void setFederationMapperType(String federationMapperType) {
+ this.federationMapperType = federationMapperType;
+ }
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 64625c5c5e..382d9c0c8c 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -6,9 +6,12 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakSessionTask;
import org.keycloak.models.KeycloakTransaction;
+import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.util.CertificateUtils;
import org.keycloak.util.PemUtils;
@@ -23,6 +26,8 @@ import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -267,4 +272,49 @@ public final class KeycloakModelUtils {
}
return false;
}
+
+ /**
+ * Ensure that displayName of myProvider (if not null) is unique and there is no other provider with same displayName in the list.
+ *
+ * @param displayName to check for duplications
+ * @param myProvider provider, which is excluded from the list (if present)
+ * @param federationProviders
+ * @throws ModelDuplicateException if there is other provider with same displayName
+ */
+ public static void ensureUniqueDisplayName(String displayName, UserFederationProviderModel myProvider, List federationProviders) throws ModelDuplicateException {
+ if (displayName != null) {
+
+ for (UserFederationProviderModel federationProvider : federationProviders) {
+ if (myProvider != null && (myProvider.equals(federationProvider) || (myProvider.getId() != null && myProvider.getId().equals(federationProvider.getId())))) {
+ continue;
+ }
+
+ if (displayName.equals(federationProvider.getDisplayName())) {
+ throw new ModelDuplicateException("There is already existing federation provider with display name: " + displayName);
+ }
+ }
+ }
+ }
+
+ public static UserFederationProviderModel findUserFederationProviderByDisplayName(String displayName, RealmModel realm) {
+ if (displayName == null) {
+ return null;
+ }
+
+ for (UserFederationProviderModel fedProvider : realm.getUserFederationProviders()) {
+ if (displayName.equals(fedProvider.getDisplayName())) {
+ return fedProvider;
+ }
+ }
+ return null;
+ }
+
+ public static UserFederationProviderModel findUserFederationProviderById(String fedProviderId, RealmModel realm) {
+ for (UserFederationProviderModel fedProvider : realm.getUserFederationProviders()) {
+ if (fedProviderId.equals(fedProvider.getId())) {
+ return fedProvider;
+ }
+ }
+ return null;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 84248fdada..2d202667b2 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -5,12 +5,14 @@ import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.ModelException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@@ -25,6 +27,7 @@ import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserConsentRepresentation;
+import org.keycloak.representations.idm.UserFederationMapperRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
@@ -162,6 +165,10 @@ public class ModelToRepresentation {
rep.setUserFederationProviders(fedProviderReps);
}
+ for (UserFederationMapperModel mapper : realm.getUserFederationMappers()) {
+ rep.addUserFederationMapper(toRepresentation(realm, mapper));
+ }
+
for (IdentityProviderModel provider : realm.getIdentityProviders()) {
rep.addIdentityProvider(toRepresentation(provider));
}
@@ -291,6 +298,24 @@ public class ModelToRepresentation {
return rep;
}
+ public static UserFederationMapperRepresentation toRepresentation(RealmModel realm, UserFederationMapperModel model) {
+ UserFederationMapperRepresentation rep = new UserFederationMapperRepresentation();
+ rep.setId(model.getId());
+ rep.setName(model.getName());
+ rep.setFederationMapperType(model.getFederationMapperType());
+ Map config = new HashMap();
+ config.putAll(model.getConfig());
+ rep.setConfig(config);
+
+ UserFederationProviderModel fedProvider = KeycloakModelUtils.findUserFederationProviderById(model.getId(), realm);
+ if (fedProvider == null) {
+ throw new ModelException("Couldn't find federation provider with ID " + model.getId());
+ }
+ rep.setFederationProviderDisplayName(fedProvider.getDisplayName());
+
+ return rep;
+ }
+
public static IdentityProviderRepresentation toRepresentation(IdentityProviderModel identityProviderModel) {
IdentityProviderRepresentation providerRep = new IdentityProviderRepresentation();
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 9f84087138..f59f0b5629 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -11,6 +11,7 @@ import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelException;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -18,6 +19,7 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
@@ -34,6 +36,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
import org.keycloak.representations.idm.SocialLinkRepresentation;
import org.keycloak.representations.idm.UserConsentRepresentation;
+import org.keycloak.representations.idm.UserFederationMapperRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.util.UriUtils;
@@ -240,6 +243,11 @@ public class RepresentationToModel {
List providerModels = convertFederationProviders(rep.getUserFederationProviders());
newRealm.setUserFederationProviders(providerModels);
}
+ if (rep.getUserFederationMappers() != null) {
+ for (UserFederationMapperRepresentation representation : rep.getUserFederationMappers()) {
+ newRealm.addUserFederationMapper(toModel(newRealm, representation));
+ }
+ }
// create users and their role mappings and social mappings
@@ -475,6 +483,23 @@ public class RepresentationToModel {
return result;
}
+ public static UserFederationMapperModel toModel(RealmModel realm, UserFederationMapperRepresentation rep) {
+ UserFederationMapperModel model = new UserFederationMapperModel();
+ model.setId(rep.getId());
+ model.setName(rep.getName());
+ model.setFederationMapperType(rep.getFederationMapperType());
+ model.setConfig(rep.getConfig());
+
+ UserFederationProviderModel fedProvider = KeycloakModelUtils.findUserFederationProviderByDisplayName(rep.getFederationProviderDisplayName(), realm);
+ if (fedProvider == null) {
+ throw new ModelException("Couldn't find federation provider with display name [" + rep.getFederationProviderDisplayName() + "] referenced from mapper ["
+ + rep.getName());
+ }
+ model.setFederationProviderId(fedProvider.getId());
+
+ return model;
+ }
+
// Roles
public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
index 12ace4ce30..4092ffab4c 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
@@ -35,6 +35,7 @@ import org.keycloak.models.entities.IdentityProviderMapperEntity;
import org.keycloak.models.entities.RealmEntity;
import org.keycloak.models.entities.RequiredCredentialEntity;
import org.keycloak.models.entities.RoleEntity;
+import org.keycloak.models.entities.UserFederationMapperEntity;
import org.keycloak.models.entities.UserFederationProviderEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -812,6 +813,8 @@ public class RealmAdapter implements RealmModel {
@Override
public UserFederationProviderModel addUserFederationProvider(String providerName, Map config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
+ KeycloakModelUtils.ensureUniqueDisplayName(displayName, null, getUserFederationProviders());
+
UserFederationProviderEntity entity = new UserFederationProviderEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setPriority(priority);
@@ -837,6 +840,12 @@ public class RealmAdapter implements RealmModel {
if (entity.getId().equals(provider.getId())) {
session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
+
+ Set mappers = getUserFederationMapperEntitiesByFederationProvider(provider.getId());
+ for (UserFederationMapperEntity mapper : mappers) {
+ realm.getUserFederationMappers().remove(mapper);
+ }
+
it.remove();
}
}
@@ -844,6 +853,8 @@ public class RealmAdapter implements RealmModel {
@Override
public void updateUserFederationProvider(UserFederationProviderModel model) {
+ KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), model, getUserFederationProviders());
+
Iterator it = realm.getUserFederationProviders().iterator();
while (it.hasNext()) {
UserFederationProviderEntity entity = it.next();
@@ -889,6 +900,10 @@ public class RealmAdapter implements RealmModel {
@Override
public void setUserFederationProviders(List providers) {
+ for (UserFederationProviderModel currentProvider : providers) {
+ KeycloakModelUtils.ensureUniqueDisplayName(currentProvider.getDisplayName(), currentProvider, providers);
+ }
+
List entities = new LinkedList();
for (UserFederationProviderModel model : providers) {
UserFederationProviderEntity entity = new UserFederationProviderEntity();
@@ -1063,16 +1078,7 @@ public class RealmAdapter implements RealmModel {
public Set getIdentityProviderMappers() {
Set mappings = new HashSet<>();
for (IdentityProviderMapperEntity entity : this.realm.getIdentityProviderMappers()) {
- IdentityProviderMapperModel mapping = new IdentityProviderMapperModel();
- mapping.setId(entity.getId());
- mapping.setName(entity.getName());
- mapping.setIdentityProviderAlias(entity.getIdentityProviderAlias());
- mapping.setIdentityProviderMapper(entity.getIdentityProviderMapper());
- Map config = new HashMap();
- if (entity.getConfig() != null) {
- config.putAll(entity.getConfig());
- }
- mapping.setConfig(config);
+ IdentityProviderMapperModel mapping = entityToModel(entity);
mappings.add(mapping);
}
return mappings;
@@ -1084,16 +1090,7 @@ public class RealmAdapter implements RealmModel {
if (!entity.getIdentityProviderAlias().equals(brokerAlias)) {
continue;
}
- IdentityProviderMapperModel mapping = new IdentityProviderMapperModel();
- mapping.setId(entity.getId());
- mapping.setName(entity.getName());
- mapping.setIdentityProviderAlias(entity.getIdentityProviderAlias());
- mapping.setIdentityProviderMapper(entity.getIdentityProviderMapper());
- Map config = new HashMap();
- if (entity.getConfig() != null) {
- config.putAll(entity.getConfig());
- }
- mapping.setConfig(config);
+ IdentityProviderMapperModel mapping = entityToModel(entity);
mappings.add(mapping);
}
return mappings;
@@ -1102,7 +1099,7 @@ public class RealmAdapter implements RealmModel {
@Override
public IdentityProviderMapperModel addIdentityProviderMapper(IdentityProviderMapperModel model) {
if (getIdentityProviderMapperByName(model.getIdentityProviderAlias(), model.getIdentityProviderMapper()) != null) {
- throw new RuntimeException("protocol mapper name must be unique per protocol");
+ throw new RuntimeException("identity provider mapper name must be unique per identity provider");
}
String id = KeycloakModelUtils.generateId();
IdentityProviderMapperEntity entity = new IdentityProviderMapperEntity();
@@ -1186,8 +1183,116 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public List getUserFederationMappers() {
- throw new IllegalStateException("Not yet implemented");
+ public Set getUserFederationMappers() {
+ Set mappers = new HashSet();
+ for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
+ UserFederationMapperModel mapper = entityToModel(entity);
+ mappers.add(mapper);
+ }
+ return mappers;
}
+ @Override
+ public Set getUserFederationMappersByFederationProvider(String federationProviderId) {
+ Set mappers = new HashSet();
+ Set mapperEntities = getUserFederationMapperEntitiesByFederationProvider(federationProviderId);
+ for (UserFederationMapperEntity entity : mapperEntities) {
+ mappers.add(entityToModel(entity));
+ }
+ return mappers;
+ }
+
+ @Override
+ public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) {
+ if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) {
+ throw new ModelDuplicateException("User federation mapper must be unique per federation provider");
+ }
+ String id = KeycloakModelUtils.generateId();
+ UserFederationMapperEntity entity = new UserFederationMapperEntity();
+ entity.setId(id);
+ entity.setName(model.getName());
+ entity.setFederationProviderId(model.getFederationProviderId());
+ entity.setFederationMapperType(model.getFederationMapperType());
+ entity.setConfig(model.getConfig());
+
+ this.realm.getUserFederationMappers().add(entity);
+ return entityToModel(entity);
+ }
+
+ protected UserFederationMapperEntity getUserFederationMapperEntity(String id) {
+ for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
+ if (entity.getId().equals(id)) {
+ return entity;
+ }
+ }
+ return null;
+
+ }
+
+ protected UserFederationMapperEntity getUserFederationMapperEntityByName(String federationProviderId, String name) {
+ for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
+ if (entity.getFederationProviderId().equals(federationProviderId) && entity.getName().equals(name)) {
+ return entity;
+ }
+ }
+ return null;
+
+ }
+
+ protected Set getUserFederationMapperEntitiesByFederationProvider(String federationProviderId) {
+ Set mappers = new HashSet();
+ for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
+ if (federationProviderId.equals(entity.getFederationProviderId())) {
+ mappers.add(entity);
+ }
+ }
+ return mappers;
+ }
+
+ @Override
+ public void removeUserFederationMapper(UserFederationMapperModel mapper) {
+ UserFederationMapperEntity toDelete = getUserFederationMapperEntity(mapper.getId());
+ if (toDelete != null) {
+ this.realm.getUserFederationMappers().remove(toDelete);
+ }
+ }
+
+ @Override
+ public void updateUserFederationMapper(UserFederationMapperModel mapper) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntity(mapper.getId());
+ entity.setFederationProviderId(mapper.getFederationProviderId());
+ entity.setFederationMapperType(mapper.getFederationMapperType());
+ if (entity.getConfig() == null) {
+ entity.setConfig(mapper.getConfig());
+ } else {
+ entity.getConfig().clear();
+ entity.getConfig().putAll(mapper.getConfig());
+ }
+ }
+
+ @Override
+ public UserFederationMapperModel getUserFederationMapperById(String id) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntity(id);
+ if (entity == null) return null;
+ return entityToModel(entity);
+ }
+
+ @Override
+ public UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntityByName(federationProviderId, name);
+ if (entity == null) return null;
+ return entityToModel(entity);
+ }
+
+ protected UserFederationMapperModel entityToModel(UserFederationMapperEntity entity) {
+ UserFederationMapperModel mapper = new UserFederationMapperModel();
+ mapper.setId(entity.getId());
+ mapper.setName(entity.getName());
+ mapper.setFederationProviderId(entity.getFederationProviderId());
+ mapper.setFederationMapperType(entity.getFederationMapperType());
+ Map config = new HashMap();
+ if (entity.getConfig() != null) config.putAll(entity.getConfig());
+ mapper.setConfig(config);
+ return mapper;
+ }
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index d62d8d8001..778472470e 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -952,79 +952,67 @@ public class RealmAdapter implements RealmModel {
return null;
}
- public static String LDAP_MODE = "LDAP_ONLY";
-
@Override
- public List getUserFederationMappers() {
- // TODO: Some hardcoded stuff...
- List mappers = new ArrayList();
-
- mappers.add(createMapperModel("usn", "usernameMapper", "user-attribute-ldap-mapper",
- "user.model.attribute", UserModel.USERNAME,
- "ldap.attribute", LDAPConstants.UID));
-
- // Uncomment this for CN + SN config
- /*mappers.add(createMapperModel("fn", "firstNameMapper", "user-attribute-ldap-mapper",
- "user.model.attribute", UserModel.FIRST_NAME,
- "ldap.attribute", LDAPConstants.CN));*/
-
- // Uncomment this for CN + SN + givenname config
- mappers.add(createMapperModel("fn", "firstNameMapper", "user-attribute-ldap-mapper",
- "user.model.attribute", UserModel.FIRST_NAME,
- "ldap.attribute", LDAPConstants.GIVENNAME));
- mappers.add(createMapperModel("fulln", "fullNameMapper", "full-name-ldap-mapper",
- "ldap.full.name.attribute", LDAPConstants.CN));
-
- mappers.add(createMapperModel("ln", "lastNameMapper", "user-attribute-ldap-mapper",
- "user.model.attribute", UserModel.LAST_NAME,
- "ldap.attribute", LDAPConstants.SN));
-
- mappers.add(createMapperModel("emailMpr", "emailMapper", "user-attribute-ldap-mapper",
- "user.model.attribute", UserModel.EMAIL,
- "ldap.attribute", LDAPConstants.EMAIL));
- mappers.add(createMapperModel("postalCodeMpr", "postalCodeMapper", "user-attribute-ldap-mapper",
- "user.model.attribute", "postal_code",
- "ldap.attribute", LDAPConstants.POSTAL_CODE));
- mappers.add(createMapperModel("createdDateMpr", "createTimeStampMapper", "user-attribute-ldap-mapper",
- "user.model.attribute", LDAPConstants.CREATE_TIMESTAMP,
- "ldap.attribute", LDAPConstants.CREATE_TIMESTAMP,
- "read.only", "true"));
- mappers.add(createMapperModel("modifyDateMpr", "modifyTimeStampMapper", "user-attribute-ldap-mapper",
- "user.model.attribute", LDAPConstants.MODIFY_TIMESTAMP,
- "ldap.attribute", LDAPConstants.MODIFY_TIMESTAMP,
- "read.only", "true"));
-
- mappers.add(createMapperModel("realmRoleMpr", "realmRoleMapper", "role-ldap-mapper",
- "roles.dn", "ou=RealmRoles,dc=keycloak,dc=org",
- "use.realm.roles.mapping", "true",
- "mode", LDAP_MODE));
- mappers.add(createMapperModel("financeRoleMpr", "financeRoleMapper", "role-ldap-mapper",
- "roles.dn", "ou=FinanceRoles,dc=keycloak,dc=org",
- "use.realm.roles.mapping", "false",
- "client.id", "finance",
- "mode", LDAP_MODE));
-
+ public Set getUserFederationMappers() {
+ if (updated != null) return updated.getUserFederationMappers();
+ Set mappers = new HashSet();
+ for (List models : cached.getUserFederationMappers().values()) {
+ for (UserFederationMapperModel model : models) {
+ mappers.add(model);
+ }
+ }
return mappers;
}
- private static UserFederationMapperModel createMapperModel(String id, String name, String mapperId, String... config) {
- UserFederationMapperModel mapperModel = new UserFederationMapperModel();
- mapperModel.setId(id);
- mapperModel.setName(name);
- mapperModel.setFederationMapperId(mapperId);
+ @Override
+ public Set getUserFederationMappersByFederationProvider(String federationProviderId) {
+ if (updated != null) return updated.getUserFederationMappersByFederationProvider(federationProviderId);
+ Set mappers = new HashSet<>();
+ List list = cached.getUserFederationMappers().getList(federationProviderId);
+ for (UserFederationMapperModel entity : list) {
+ mappers.add(entity);
+ }
+ return mappers;
+ }
- Map configMap = new HashMap();
- String key = null;
- for (String configEntry : config) {
- if (key == null) {
- key = configEntry;
- } else {
- configMap.put(key, configEntry);
- key = null;
+ @Override
+ public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel mapper) {
+ getDelegateForUpdate();
+ return updated.addUserFederationMapper(mapper);
+ }
+
+ @Override
+ public void removeUserFederationMapper(UserFederationMapperModel mapper) {
+ getDelegateForUpdate();
+ updated.removeUserFederationMapper(mapper);
+ }
+
+ @Override
+ public void updateUserFederationMapper(UserFederationMapperModel mapper) {
+ getDelegateForUpdate();
+ updated.updateUserFederationMapper(mapper);
+ }
+
+ @Override
+ public UserFederationMapperModel getUserFederationMapperById(String id) {
+ if (updated != null) return updated.getUserFederationMapperById(id);
+ for (List models : cached.getUserFederationMappers().values()) {
+ for (UserFederationMapperModel model : models) {
+ if (model.getId().equals(id)) return model;
}
}
- mapperModel.setConfig(configMap);
- return mapperModel;
+ return null;
+ }
+
+ @Override
+ public UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name) {
+ if (updated != null) return updated.getUserFederationMapperByName(federationProviderId, name);
+ List models = cached.getUserFederationMappers().getList(federationProviderId);
+ if (models == null) return null;
+ for (UserFederationMapperModel model : models) {
+ if (model.getName().equals(name)) return model;
+ }
+ return null;
}
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index 22f57a95df..faf7ba27d5 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -9,6 +9,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.cache.RealmCache;
import org.keycloak.util.MultivaluedHashMap;
@@ -70,6 +71,7 @@ public class CachedRealm {
private List requiredCredentials = new ArrayList();
private List userFederationProviders = new ArrayList();
+ private MultivaluedHashMap userFederationMappers = new MultivaluedHashMap();
private List identityProviders = new ArrayList();
private Map browserSecurityHeaders = new HashMap();
@@ -136,6 +138,9 @@ public class CachedRealm {
requiredCredentials = model.getRequiredCredentials();
userFederationProviders = model.getUserFederationProviders();
+ for (UserFederationMapperModel mapper : model.getUserFederationMappers()) {
+ userFederationMappers.add(mapper.getFederationProviderId(), mapper);
+ }
this.identityProviders = new ArrayList<>();
@@ -373,6 +378,10 @@ public class CachedRealm {
return userFederationProviders;
}
+ public MultivaluedHashMap getUserFederationMappers() {
+ return userFederationMappers;
+ }
+
public String getCertificatePem() {
return certificatePem;
}
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 3dde3d36ba..990b087b4b 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
@@ -5,11 +5,14 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperModel;
+import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.IdentityProviderEntity;
@@ -18,6 +21,7 @@ import org.keycloak.models.jpa.entities.RealmAttributeEntity;
import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.RequiredCredentialEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.UserFederationMapperEntity;
import org.keycloak.models.jpa.entities.UserFederationProviderEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -760,6 +764,8 @@ public class RealmAdapter implements RealmModel {
@Override
public UserFederationProviderModel addUserFederationProvider(String providerName, Map config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
+ KeycloakModelUtils.ensureUniqueDisplayName(displayName, null, getUserFederationProviders());
+
String id = KeycloakModelUtils.generateId();
UserFederationProviderEntity entity = new UserFederationProviderEntity();
entity.setId(id);
@@ -786,7 +792,14 @@ public class RealmAdapter implements RealmModel {
while (it.hasNext()) {
UserFederationProviderEntity entity = it.next();
if (entity.getId().equals(provider.getId())) {
+
session.users().preRemove(this, provider);
+
+ Set mappers = getUserFederationMapperEntitiesByFederationProvider(provider.getId());
+ for (UserFederationMapperEntity mapper : mappers) {
+ realm.getUserFederationMappers().remove(mapper);
+ em.remove(mapper);
+ }
it.remove();
em.remove(entity);
return;
@@ -795,6 +808,8 @@ public class RealmAdapter implements RealmModel {
}
@Override
public void updateUserFederationProvider(UserFederationProviderModel model) {
+ KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), model, getUserFederationProviders());
+
Iterator it = realm.getUserFederationProviders().iterator();
while (it.hasNext()) {
UserFederationProviderEntity entity = it.next();
@@ -817,6 +832,9 @@ public class RealmAdapter implements RealmModel {
@Override
public void setUserFederationProviders(List providers) {
+ for (UserFederationProviderModel currentProvider : providers) {
+ KeycloakModelUtils.ensureUniqueDisplayName(currentProvider.getDisplayName(), currentProvider, providers);
+ }
Iterator it = realm.getUserFederationProviders().iterator();
while (it.hasNext()) {
@@ -881,6 +899,15 @@ public class RealmAdapter implements RealmModel {
}
}
+ protected UserFederationProviderEntity getUserFederationProviderEntityById(String federationProviderId) {
+ for (UserFederationProviderEntity entity : realm.getUserFederationProviders()) {
+ if (entity.getId().equals(federationProviderId)) {
+ return entity;
+ }
+ }
+ return null;
+ }
+
@Override
public RoleModel getRole(String name) {
TypedQuery query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
@@ -1225,16 +1252,7 @@ public class RealmAdapter implements RealmModel {
public Set getIdentityProviderMappers() {
Set mappings = new HashSet();
for (IdentityProviderMapperEntity entity : this.realm.getIdentityProviderMappers()) {
- IdentityProviderMapperModel mapping = new IdentityProviderMapperModel();
- mapping.setId(entity.getId());
- mapping.setName(entity.getName());
- mapping.setIdentityProviderAlias(entity.getIdentityProviderAlias());
- mapping.setIdentityProviderMapper(entity.getIdentityProviderMapper());
- Map config = new HashMap();
- if (entity.getConfig() != null) {
- config.putAll(entity.getConfig());
- }
- mapping.setConfig(config);
+ IdentityProviderMapperModel mapping = entityToModel(entity);
mappings.add(mapping);
}
return mappings;
@@ -1247,16 +1265,7 @@ public class RealmAdapter implements RealmModel {
if (!entity.getIdentityProviderAlias().equals(brokerAlias)) {
continue;
}
- IdentityProviderMapperModel mapping = new IdentityProviderMapperModel();
- mapping.setId(entity.getId());
- mapping.setName(entity.getName());
- mapping.setIdentityProviderAlias(entity.getIdentityProviderAlias());
- mapping.setIdentityProviderMapper(entity.getIdentityProviderMapper());
- Map config = new HashMap();
- if (entity.getConfig() != null) {
- config.putAll(entity.getConfig());
- }
- mapping.setConfig(config);
+ IdentityProviderMapperModel mapping = entityToModel(entity);
mappings.add(mapping);
}
return mappings;
@@ -1265,7 +1274,7 @@ public class RealmAdapter implements RealmModel {
@Override
public IdentityProviderMapperModel addIdentityProviderMapper(IdentityProviderMapperModel model) {
if (getIdentityProviderMapperByName(model.getIdentityProviderAlias(), model.getIdentityProviderMapper()) != null) {
- throw new RuntimeException("protocol mapper name must be unique per protocol");
+ throw new RuntimeException("identity provider mapper name must be unique per identity provider");
}
String id = KeycloakModelUtils.generateId();
IdentityProviderMapperEntity entity = new IdentityProviderMapperEntity();
@@ -1353,8 +1362,122 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public List getUserFederationMappers() {
- throw new IllegalStateException("Not yet implemented");
+ public Set getUserFederationMappers() {
+ Set mappers = new HashSet();
+ for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
+ UserFederationMapperModel mapper = entityToModel(entity);
+ mappers.add(mapper);
+ }
+ return mappers;
+ }
+
+ @Override
+ public Set getUserFederationMappersByFederationProvider(String federationProviderId) {
+ Set mappers = new HashSet();
+ Set mapperEntities = getUserFederationMapperEntitiesByFederationProvider(federationProviderId);
+ for (UserFederationMapperEntity entity : mapperEntities) {
+ UserFederationMapperModel mapper = entityToModel(entity);
+ mappers.add(mapper);
+ }
+ return mappers;
+ }
+
+ @Override
+ public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) {
+ if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) {
+ throw new ModelDuplicateException("User federation mapper must be unique per federation provider");
+ }
+ String id = KeycloakModelUtils.generateId();
+ UserFederationMapperEntity entity = new UserFederationMapperEntity();
+ entity.setId(id);
+ entity.setName(model.getName());
+ entity.setFederationProvider(getUserFederationProviderEntityById(model.getFederationProviderId()));
+ entity.setFederationMapperType(model.getFederationMapperType());
+ entity.setRealm(this.realm);
+ entity.setConfig(model.getConfig());
+
+ em.persist(entity);
+ this.realm.getUserFederationMappers().add(entity);
+ return entityToModel(entity);
+ }
+
+ @Override
+ public void removeUserFederationMapper(UserFederationMapperModel mapper) {
+ UserFederationMapperEntity toDelete = getUserFederationMapperEntity(mapper.getId());
+ if (toDelete != null) {
+ this.realm.getUserFederationMappers().remove(toDelete);
+ em.remove(toDelete);
+ }
+ }
+
+ protected UserFederationMapperEntity getUserFederationMapperEntity(String id) {
+ for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
+ if (entity.getId().equals(id)) {
+ return entity;
+ }
+ }
+ return null;
+
+ }
+
+ protected UserFederationMapperEntity getUserFederationMapperEntityByName(String federationProviderId, String name) {
+ for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
+ if (federationProviderId.equals(entity.getFederationProvider().getId()) && entity.getName().equals(name)) {
+ return entity;
+ }
+ }
+ return null;
+
+ }
+
+ protected Set getUserFederationMapperEntitiesByFederationProvider(String federationProviderId) {
+ Set mappers = new HashSet();
+ for (UserFederationMapperEntity entity : this.realm.getUserFederationMappers()) {
+ if (federationProviderId.equals(entity.getFederationProvider().getId())) {
+ mappers.add(entity);
+ }
+ }
+ return mappers;
+ }
+
+ @Override
+ public void updateUserFederationMapper(UserFederationMapperModel mapper) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntity(mapper.getId());
+ entity.setFederationProvider(getUserFederationProviderEntityById(mapper.getFederationProviderId()));
+ entity.setFederationMapperType(mapper.getFederationMapperType());
+ if (entity.getConfig() == null) {
+ entity.setConfig(mapper.getConfig());
+ } else {
+ entity.getConfig().clear();
+ entity.getConfig().putAll(mapper.getConfig());
+ }
+ em.flush();
+ }
+
+ @Override
+ public UserFederationMapperModel getUserFederationMapperById(String id) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntity(id);
+ if (entity == null) return null;
+ return entityToModel(entity);
+ }
+
+ @Override
+ public UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntityByName(federationProviderId, name);
+ if (entity == null) return null;
+ return entityToModel(entity);
+ }
+
+ protected UserFederationMapperModel entityToModel(UserFederationMapperEntity entity) {
+ UserFederationMapperModel mapper = new UserFederationMapperModel();
+ mapper.setId(entity.getId());
+ mapper.setName(entity.getName());
+ mapper.setFederationProviderId(entity.getFederationProvider().getId());
+ mapper.setFederationMapperType(entity.getFederationMapperType());
+ Map config = new HashMap();
+ if (entity.getConfig() != null) config.putAll(entity.getConfig());
+ mapper.setConfig(config);
+ return mapper;
}
}
\ No newline at end of file
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index cdf314c17c..6c28b9f9fa 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -103,6 +103,9 @@ public class RealmEntity {
@JoinTable(name="FED_PROVIDERS")
List userFederationProviders = new ArrayList();
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
+ Collection userFederationMappers = new ArrayList();
+
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
@JoinTable(name="REALM_CLIENT", joinColumns={ @JoinColumn(name="REALM_ID") }, inverseJoinColumns={ @JoinColumn(name="CLIENT_ID") })
Collection clients = new ArrayList<>();
@@ -475,6 +478,14 @@ public class RealmEntity {
this.userFederationProviders = userFederationProviders;
}
+ public Collection getUserFederationMappers() {
+ return userFederationMappers;
+ }
+
+ public void setUserFederationMappers(Collection userFederationMappers) {
+ this.userFederationMappers = userFederationMappers;
+ }
+
public Collection getAttributes() {
return attributes;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserFederationMapperEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserFederationMapperEntity.java
new file mode 100644
index 0000000000..4aa5e6e72f
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserFederationMapperEntity.java
@@ -0,0 +1,113 @@
+package org.keycloak.models.jpa.entities;
+
+import java.util.Map;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author Marek Posolda
+ */
+@Entity
+@Table(name="USER_FEDERATION_MAPPER")
+public class UserFederationMapperEntity {
+
+ @Id
+ @Column(name="ID", length = 36)
+ protected String id;
+
+ @Column(name="NAME")
+ protected String name;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "FEDERATION_PROVIDER_ID")
+ protected UserFederationProviderEntity federationProvider;
+
+ @Column(name = "FEDERATION_MAPPER_TYPE")
+ protected String federationMapperType;
+
+ @ElementCollection
+ @MapKeyColumn(name="NAME")
+ @Column(name="VALUE")
+ @CollectionTable(name="USER_FEDERATION_MAPPER_CONFIG", joinColumns={ @JoinColumn(name="USER_FEDERATION_MAPPER_ID") })
+ private Map config;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "REALM_ID")
+ private RealmEntity realm;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public UserFederationProviderEntity getFederationProvider() {
+ return federationProvider;
+ }
+
+ public void setFederationProvider(UserFederationProviderEntity federationProvider) {
+ this.federationProvider = federationProvider;
+ }
+
+ public String getFederationMapperType() {
+ return federationMapperType;
+ }
+
+ public void setFederationMapperType(String federationMapperType) {
+ this.federationMapperType = federationMapperType;
+ }
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ UserFederationMapperEntity that = (UserFederationMapperEntity) o;
+
+ if (!id.equals(that.id)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+}
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 9f68d8fd16..a327a8d050 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
@@ -9,6 +9,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
@@ -19,6 +20,7 @@ import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.entities.IdentityProviderEntity;
import org.keycloak.models.entities.IdentityProviderMapperEntity;
import org.keycloak.models.entities.RequiredCredentialEntity;
+import org.keycloak.models.entities.UserFederationMapperEntity;
import org.keycloak.models.entities.UserFederationProviderEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
@@ -833,6 +835,8 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
@Override
public UserFederationProviderModel addUserFederationProvider(String providerName, Map config, int priority, String displayName, int fullSyncPeriod, int changedSyncPeriod, int lastSync) {
+ KeycloakModelUtils.ensureUniqueDisplayName(displayName, null, getUserFederationProviders());
+
UserFederationProviderEntity entity = new UserFederationProviderEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setPriority(priority);
@@ -859,6 +863,12 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
if (entity.getId().equals(provider.getId())) {
session.users().preRemove(this, new UserFederationProviderModel(entity.getId(), entity.getProviderName(), entity.getConfig(), entity.getPriority(), entity.getDisplayName(),
entity.getFullSyncPeriod(), entity.getChangedSyncPeriod(), entity.getLastSync()));
+
+ Set mappers = getUserFederationMapperEntitiesByFederationProvider(provider.getId());
+ for (UserFederationMapperEntity mapper : mappers) {
+ getMongoEntity().getUserFederationMappers().remove(mapper);
+ }
+
it.remove();
}
}
@@ -867,6 +877,8 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
@Override
public void updateUserFederationProvider(UserFederationProviderModel model) {
+ KeycloakModelUtils.ensureUniqueDisplayName(model.getDisplayName(), model, getUserFederationProviders());
+
Iterator it = realm.getUserFederationProviders().iterator();
while (it.hasNext()) {
UserFederationProviderEntity entity = it.next();
@@ -913,6 +925,10 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
@Override
public void setUserFederationProviders(List providers) {
+ for (UserFederationProviderModel currentProvider : providers) {
+ KeycloakModelUtils.ensureUniqueDisplayName(currentProvider.getDisplayName(), currentProvider, providers);
+ }
+
List entities = new LinkedList();
for (UserFederationProviderModel model : providers) {
UserFederationProviderEntity entity = new UserFederationProviderEntity();
@@ -923,7 +939,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
entity.setPriority(model.getPriority());
String displayName = model.getDisplayName();
if (displayName == null) {
- entity.setDisplayName(entity.getId());
+ displayName = entity.getId();
}
entity.setDisplayName(displayName);
entity.setFullSyncPeriod(model.getFullSyncPeriod());
@@ -1089,16 +1105,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
public Set getIdentityProviderMappers() {
Set mappings = new HashSet();
for (IdentityProviderMapperEntity entity : getMongoEntity().getIdentityProviderMappers()) {
- IdentityProviderMapperModel mapping = new IdentityProviderMapperModel();
- mapping.setId(entity.getId());
- mapping.setName(entity.getName());
- mapping.setIdentityProviderAlias(entity.getIdentityProviderAlias());
- mapping.setIdentityProviderMapper(entity.getIdentityProviderMapper());
- Map config = new HashMap();
- if (entity.getConfig() != null) {
- config.putAll(entity.getConfig());
- }
- mapping.setConfig(config);
+ IdentityProviderMapperModel mapping = entityToModel(entity);
mappings.add(mapping);
}
return mappings;
@@ -1111,16 +1118,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
if (!entity.getIdentityProviderAlias().equals(brokerAlias)) {
continue;
}
- IdentityProviderMapperModel mapping = new IdentityProviderMapperModel();
- mapping.setId(entity.getId());
- mapping.setName(entity.getName());
- mapping.setIdentityProviderAlias(entity.getIdentityProviderAlias());
- mapping.setIdentityProviderMapper(entity.getIdentityProviderMapper());
- Map config = new HashMap();
- if (entity.getConfig() != null) {
- config.putAll(entity.getConfig());
- }
- mapping.setConfig(config);
+ IdentityProviderMapperModel mapping = entityToModel(entity);
mappings.add(mapping);
}
return mappings;
@@ -1129,7 +1127,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
@Override
public IdentityProviderMapperModel addIdentityProviderMapper(IdentityProviderMapperModel model) {
if (getIdentityProviderMapperByName(model.getIdentityProviderAlias(), model.getIdentityProviderMapper()) != null) {
- throw new RuntimeException("protocol mapper name must be unique per protocol");
+ throw new RuntimeException("identity provider mapper name must be unique per identity provider");
}
String id = KeycloakModelUtils.generateId();
IdentityProviderMapperEntity entity = new IdentityProviderMapperEntity();
@@ -1169,6 +1167,7 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
IdentityProviderMapperEntity toDelete = getIdentityProviderMapperEntity(mapping.getId());
if (toDelete != null) {
this.realm.getIdentityProviderMappers().remove(toDelete);
+ updateMongoEntity();
}
}
@@ -1215,7 +1214,119 @@ public class RealmAdapter extends AbstractMongoAdapter impleme
}
@Override
- public List getUserFederationMappers() {
- throw new IllegalStateException("Not yet implemented");
+ public Set getUserFederationMappers() {
+ Set mappers = new HashSet();
+ for (UserFederationMapperEntity entity : getMongoEntity().getUserFederationMappers()) {
+ UserFederationMapperModel mapper = entityToModel(entity);
+ mappers.add(mapper);
+ }
+ return mappers;
+ }
+
+ @Override
+ public Set getUserFederationMappersByFederationProvider(String federationProviderId) {
+ Set mappers = new HashSet();
+ Set mapperEntities = getUserFederationMapperEntitiesByFederationProvider(federationProviderId);
+ for (UserFederationMapperEntity entity : mapperEntities) {
+ mappers.add(entityToModel(entity));
+ }
+ return mappers;
+ }
+
+ @Override
+ public UserFederationMapperModel addUserFederationMapper(UserFederationMapperModel model) {
+ if (getUserFederationMapperByName(model.getFederationProviderId(), model.getName()) != null) {
+ throw new ModelDuplicateException("User federation mapper must be unique per federation provider");
+ }
+ String id = KeycloakModelUtils.generateId();
+ UserFederationMapperEntity entity = new UserFederationMapperEntity();
+ entity.setId(id);
+ entity.setName(model.getName());
+ entity.setFederationProviderId(model.getFederationProviderId());
+ entity.setFederationMapperType(model.getFederationMapperType());
+ entity.setConfig(model.getConfig());
+
+ getMongoEntity().getUserFederationMappers().add(entity);
+ updateMongoEntity();
+ return entityToModel(entity);
+ }
+
+ protected UserFederationMapperEntity getUserFederationMapperEntity(String id) {
+ for (UserFederationMapperEntity entity : getMongoEntity().getUserFederationMappers()) {
+ if (entity.getId().equals(id)) {
+ return entity;
+ }
+ }
+ return null;
+
+ }
+
+ protected UserFederationMapperEntity getUserFederationMapperEntityByName(String federationProviderId, String name) {
+ for (UserFederationMapperEntity entity : getMongoEntity().getUserFederationMappers()) {
+ if (entity.getFederationProviderId().equals(federationProviderId) && entity.getName().equals(name)) {
+ return entity;
+ }
+ }
+ return null;
+
+ }
+
+ protected Set getUserFederationMapperEntitiesByFederationProvider(String federationProviderId) {
+ Set mappers = new HashSet();
+ for (UserFederationMapperEntity entity : getMongoEntity().getUserFederationMappers()) {
+ if (federationProviderId.equals(entity.getFederationProviderId())) {
+ mappers.add(entity);
+ }
+ }
+ return mappers;
+ }
+
+ @Override
+ public void removeUserFederationMapper(UserFederationMapperModel mapper) {
+ UserFederationMapperEntity toDelete = getUserFederationMapperEntity(mapper.getId());
+ if (toDelete != null) {
+ this.realm.getUserFederationMappers().remove(toDelete);
+ updateMongoEntity();
+ }
+ }
+
+ @Override
+ public void updateUserFederationMapper(UserFederationMapperModel mapper) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntity(mapper.getId());
+ entity.setFederationProviderId(mapper.getFederationProviderId());
+ entity.setFederationMapperType(mapper.getFederationMapperType());
+ if (entity.getConfig() == null) {
+ entity.setConfig(mapper.getConfig());
+ } else {
+ entity.getConfig().clear();
+ entity.getConfig().putAll(mapper.getConfig());
+ }
+ updateMongoEntity();
+ }
+
+ @Override
+ public UserFederationMapperModel getUserFederationMapperById(String id) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntity(id);
+ if (entity == null) return null;
+ return entityToModel(entity);
+ }
+
+ @Override
+ public UserFederationMapperModel getUserFederationMapperByName(String federationProviderId, String name) {
+ UserFederationMapperEntity entity = getUserFederationMapperEntityByName(federationProviderId, name);
+ if (entity == null) return null;
+ return entityToModel(entity);
+ }
+
+ protected UserFederationMapperModel entityToModel(UserFederationMapperEntity entity) {
+ UserFederationMapperModel mapper = new UserFederationMapperModel();
+ mapper.setId(entity.getId());
+ mapper.setName(entity.getName());
+ mapper.setFederationProviderId(entity.getFederationProviderId());
+ mapper.setFederationMapperType(entity.getFederationMapperType());
+ Map config = new HashMap();
+ if (entity.getConfig() != null) config.putAll(entity.getConfig());
+ mapper.setConfig(config);
+ return mapper;
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java
index bdc3574ae8..eff3572bc1 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPRoleMappingsTest.java
@@ -144,7 +144,7 @@ public class LDAPRoleMappingsTest {
@Test
public void test01_ldapOnlyRoleMappings() {
// TODO: Remove me!!!
- RealmAdapter.LDAP_MODE = "LDAP_ONLY";
+ //RealmAdapter.LDAP_MODE = "LDAP_ONLY";
KeycloakSession session = keycloakRule.startSession();
try {
@@ -231,7 +231,7 @@ public class LDAPRoleMappingsTest {
@Test
public void test02_readOnlyRoleMappings() {
// TODO: Remove me!!!
- RealmAdapter.LDAP_MODE = "READ_ONLY";
+ //RealmAdapter.LDAP_MODE = "READ_ONLY";
KeycloakSession session = keycloakRule.startSession();
try {
@@ -293,7 +293,7 @@ public class LDAPRoleMappingsTest {
@Test
public void test03_importRoleMappings() {
// TODO: Remove me!!!
- RealmAdapter.LDAP_MODE = "IMPORT";
+ //RealmAdapter.LDAP_MODE = "IMPORT";
KeycloakSession session = keycloakRule.startSession();
try {
@@ -346,7 +346,7 @@ public class LDAPRoleMappingsTest {
}
private static UserFederationMapperModel findRoleMapperModel(RealmModel appRealm) {
- List fedMappers = appRealm.getUserFederationMappers();
+ Set fedMappers = appRealm.getUserFederationMappers();
for (UserFederationMapperModel mapper : fedMappers) {
if ("realmRoleMapper".equals(mapper.getName())) {
return mapper;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
index f7f48b3a74..8640d8e44a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
@@ -13,6 +13,7 @@ import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -23,8 +24,11 @@ import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import static org.junit.Assert.assertNotNull;
@@ -741,6 +745,59 @@ public class AdapterTest extends AbstractModelTest {
resetSession();
}
+ @Test
+ public void testUserFederationProviderDisplayNameCollisions() throws Exception {
+ RealmModel realm = realmManager.createRealm("JUGGLER1");
+ Map cfg = Collections.emptyMap();
+ realm.addUserFederationProvider("ldap", cfg, 1, "providerName1", -1, -1, 0);
+ realm.addUserFederationProvider("ldap", cfg, 1, "providerName2", -1, -1, 0);
+
+ commit();
+
+ // Try to add federation provider with same display name
+ try {
+ realmManager.getRealmByName("JUGGLER1").addUserFederationProvider("ldap", cfg, 1, "providerName1", -1, -1, 0);
+ commit();
+ Assert.fail("Expected exception");
+ } catch (ModelDuplicateException e) {
+ }
+ commit(true);
+
+ // Try to rename federation provider tu duplicate display name
+ try {
+ List fedProviders = realmManager.getRealmByName("JUGGLER1").getUserFederationProviders();
+ for (UserFederationProviderModel fedProvider : fedProviders) {
+ if ("providerName1".equals(fedProvider.getDisplayName())) {
+ fedProvider.setDisplayName("providerName2");
+ realm.updateUserFederationProvider(fedProvider);
+ break;
+ }
+ }
+ commit();
+ Assert.fail("Expected exception");
+ } catch (ModelDuplicateException e) {
+ }
+ commit(true);
+
+ // Try to rename federation provider tu duplicate display name
+ try {
+ List fedProviders = realmManager.getRealmByName("JUGGLER1").getUserFederationProviders();
+ for (UserFederationProviderModel fedProvider : fedProviders) {
+ if ("providerName1".equals(fedProvider.getDisplayName())) {
+ fedProvider.setDisplayName("providerName2");
+ break;
+ }
+ }
+
+ realm.setUserFederationProviders(fedProviders);
+ commit();
+ Assert.fail("Expected exception");
+ } catch (ModelDuplicateException e) {
+ }
+ commit(true);
+
+ }
+
private KeyPair generateKeypair() throws NoSuchAlgorithmException {
return KeyPairGenerator.getInstance("RSA").generateKeyPair();
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index 317c101496..492aa1d2c0 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -5,6 +5,8 @@ import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.constants.KerberosConstants;
+import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper;
+import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.FederatedIdentityModel;
@@ -15,6 +17,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
+import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
@@ -220,6 +223,15 @@ public class ImportTest extends AbstractModelTest {
Assert.assertEquals(1, ldap.getPriority());
Assert.assertEquals("ldap://foo", ldap.getConfig().get("important.config"));
+ // Test federation mappers
+ Set fedMappers = realm.getUserFederationMappers();
+ Assert.assertTrue(fedMappers.size() == 1);
+ UserFederationMapperModel fullNameMapper = fedMappers.iterator().next();
+ Assert.assertEquals("FullNameMapper", fullNameMapper.getName());
+ Assert.assertEquals(FullNameLDAPFederationMapperFactory.ID, fullNameMapper.getFederationMapperType());
+ Assert.assertEquals(ldap.getId(), fullNameMapper.getFederationProviderId());
+ Assert.assertEquals("cn", fullNameMapper.getConfig().get(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE));
+
// Assert that federation link wasn't created during import
UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, "dummy");
Assert.assertNull(factory.getInstance(session, null).getUserByUsername(realm, "wburke"));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserFederationModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserFederationModelTest.java
new file mode 100644
index 0000000000..51fc446caf
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserFederationModelTest.java
@@ -0,0 +1,118 @@
+package org.keycloak.testsuite.model;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationMapperModel;
+import org.keycloak.models.UserFederationProviderModel;
+
+/**
+ * @author Marek Posolda
+ */
+public class UserFederationModelTest extends AbstractModelTest {
+
+
+ @Test
+ public void federationMapperCrudTest() {
+ RealmModel realm = realmManager.createRealm("test-realm");
+ UserFederationProviderModel fedProvider = realm.addUserFederationProvider("dummy", new TreeMap(), 1, "my-cool-provider", -1, -1, 0);
+ UserFederationProviderModel fedProvider2 = realm.addUserFederationProvider("dummy", new TreeMap(), 1, "my-cool-provider2", -1, -1, 0);
+
+ UserFederationMapperModel mapperModel1 = createMapper("name1", fedProvider.getId(), "key1", "value1");
+ UserFederationMapperModel mapperModel2 = createMapper("name2", fedProvider.getId(), "key2", "value2");
+ UserFederationMapperModel mapperModel3 = createMapper("name1", fedProvider2.getId(), "key3", "value3");
+
+ mapperModel1 = realm.addUserFederationMapper(mapperModel1);
+ mapperModel2 = realm.addUserFederationMapper(mapperModel2);
+ mapperModel3 = realm.addUserFederationMapper(mapperModel3);
+
+ commit();
+
+ try {
+ UserFederationMapperModel conflictMapper = createMapper("name1", fedProvider.getId(), "key4", "value4");
+ realmManager.getRealmByName("test-realm").addUserFederationMapper(conflictMapper);
+ commit();
+ Assert.fail("Don't expect to end here");
+ } catch (ModelDuplicateException expected) {
+ }
+
+ realm = realmManager.getRealmByName("test-realm");
+ Set mappers = realm.getUserFederationMappers();
+ Assert.assertEquals(3, mappers.size());
+ Assert.assertTrue(mappers.contains(mapperModel1));
+ Assert.assertTrue(mappers.contains(mapperModel2));
+ Assert.assertTrue(mappers.contains(mapperModel3));
+
+ mappers = realm.getUserFederationMappersByFederationProvider(fedProvider.getId());
+ Assert.assertEquals(2, mappers.size());
+ Assert.assertTrue(mappers.contains(mapperModel1));
+ Assert.assertTrue(mappers.contains(mapperModel2));
+
+ mapperModel3.getConfig().put("otherKey", "otherValue");
+ realm.updateUserFederationMapper(mapperModel3);
+
+ commit();
+
+ realm = realmManager.getRealmByName("test-realm");
+ mapperModel3 = realm.getUserFederationMapperById(mapperModel3.getId());
+ Assert.assertEquals(2, mapperModel3.getConfig().size());
+ Assert.assertEquals("value3", mapperModel3.getConfig().get("key3"));
+ Assert.assertEquals("otherValue", mapperModel3.getConfig().get("otherKey"));
+ }
+
+
+ @Test
+ public void federationProviderRemovalTest() {
+ RealmModel realm = realmManager.createRealm("test-realm");
+ UserFederationProviderModel fedProvider = realm.addUserFederationProvider("dummy", new TreeMap(), 1, "my-cool-provider", -1, -1, 0);
+ UserFederationProviderModel fedProvider2 = realm.addUserFederationProvider("dummy", new TreeMap(), 1, "my-cool-provider2", -1, -1, 0);
+
+ UserFederationMapperModel mapperModel1 = createMapper("name1", fedProvider.getId(), "key1", "value1");
+ UserFederationMapperModel mapperModel2 = createMapper("name2", fedProvider.getId(), "key2", "value2");
+ UserFederationMapperModel mapperModel3 = createMapper("name1", fedProvider2.getId(), "key3", "value3");
+
+ mapperModel1 = realm.addUserFederationMapper(mapperModel1);
+ mapperModel2 = realm.addUserFederationMapper(mapperModel2);
+ mapperModel3 = realm.addUserFederationMapper(mapperModel3);
+
+ commit();
+
+ realmManager.getRealmByName("test-realm").removeUserFederationProvider(fedProvider);
+
+ commit();
+
+ realm = realmManager.getRealmByName("test-realm");
+ Set mappers = realm.getUserFederationMappers();
+ Assert.assertEquals(1, mappers.size());
+ Assert.assertEquals(mapperModel3, mappers.iterator().next());
+
+ realm = realmManager.getRealmByName("test-realm");
+ realmManager.removeRealm(realm);
+
+ commit();
+ }
+
+ private UserFederationMapperModel createMapper(String name, String fedProviderId, String... config) {
+ UserFederationMapperModel mapperModel = new UserFederationMapperModel();
+ mapperModel.setName(name);
+ mapperModel.setFederationMapperType("someType");
+ mapperModel.setFederationProviderId(fedProviderId);
+ Map configMap = new TreeMap();
+ String key = null;
+ for (String configEntry : config) {
+ if (key == null) {
+ key = configEntry;
+ } else {
+ configMap.put(key, configEntry);
+ key = null;
+ }
+ }
+ mapperModel.setConfig(configMap);
+ return mapperModel;
+ }
+}
diff --git a/testsuite/integration/src/test/resources/model/testrealm.json b/testsuite/integration/src/test/resources/model/testrealm.json
index 50aee59e6a..43458c93c5 100755
--- a/testsuite/integration/src/test/resources/model/testrealm.json
+++ b/testsuite/integration/src/test/resources/model/testrealm.json
@@ -33,6 +33,16 @@
}
}
],
+ "userFederationMappers": [
+ {
+ "name": "FullNameMapper",
+ "federationProviderDisplayName": "MyLDAPProvider",
+ "federationMapperType": "full-name-ldap-mapper",
+ "config": {
+ "ldap.full.name.attribute": "cn"
+ }
+ }
+ ],
"users": [
{
"username": "wburke",