keycloak-aplcache
Changes
federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java 3(+0 -3)
federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java 11(+10 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java 6(+3 -3)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java 4(+2 -2)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java 8(+4 -4)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java 9(+6 -3)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java 13(+6 -7)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java 4(+2 -2)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java 4(+2 -2)
server-spi/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java 37(+37 -0)
services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java 15(+11 -4)
services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java 24(+24 -0)
testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java 7(+7 -0)
Details
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java
index 0ba9a39..4be4ba4 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQueryConditionsBuilder.java
@@ -61,9 +61,6 @@ public class LDAPQueryConditionsBuilder {
public Condition addCustomLDAPFilter(String filter) {
filter = filter.trim();
- if (!filter.startsWith("(") || !filter.endsWith(")")) {
- throw new ModelException("Custom filter doesn't start with ( or doesn't end with ). ");
- }
return new CustomLDAPFilter(filter);
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
index d6dff7e..0250b11 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
@@ -34,6 +34,7 @@ import org.keycloak.federation.ldap.mappers.LDAPFederationMapper;
import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper;
import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory;
import org.keycloak.federation.ldap.mappers.msad.MSADUserAccountControlMapperFactory;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@@ -46,6 +47,7 @@ import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserFederationSyncResult;
+import org.keycloak.models.UserFederationValidatingProviderFactory;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -59,7 +61,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
-public class LDAPFederationProviderFactory extends UserFederationEventAwareProviderFactory {
+public class LDAPFederationProviderFactory extends UserFederationEventAwareProviderFactory implements UserFederationValidatingProviderFactory {
private static final Logger logger = Logger.getLogger(LDAPFederationProviderFactory.class);
public static final String PROVIDER_NAME = LDAPConstants.LDAP_PROVIDER;
@@ -77,6 +79,13 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
}
@Override
+ public void validateConfig(RealmModel realm, UserFederationProviderModel providerModel) throws FederationConfigValidationException {
+ LDAPConfig cfg = new LDAPConfig(providerModel.getConfig());
+ String customFilter = cfg.getCustomUserSearchFilter();
+ LDAPUtils.validateCustomLdapFilter(customFilter);
+ }
+
+ @Override
public void init(Config.Scope config) {
this.ldapStoreRegistry = new LDAPIdentityStoreRegistry();
}
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 4d64e2c..b075d19 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
@@ -31,6 +31,7 @@ import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilde
import org.keycloak.federation.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.federation.ldap.mappers.LDAPFederationMapper;
import org.keycloak.federation.ldap.mappers.membership.MembershipType;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
@@ -226,4 +227,19 @@ public class LDAPUtils {
public static String getMemberValueOfChildObject(LDAPObject ldapUser, MembershipType membershipType) {
return membershipType == MembershipType.DN ? ldapUser.getDn().toString() : ldapUser.getAttributeAsString(ldapUser.getRdnAttributeName());
}
+
+
+ public static void validateCustomLdapFilter(String customFilter) throws FederationConfigValidationException {
+ if (customFilter != null) {
+
+ customFilter = customFilter.trim();
+ if (customFilter.isEmpty()) {
+ return;
+ }
+
+ if (!customFilter.startsWith("(") || !customFilter.endsWith(")")) {
+ throw new FederationConfigValidationException("ldapErrorInvalidCustomFilter");
+ }
+ }
+ }
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
index 933d614..d681125 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
@@ -20,7 +20,7 @@ package org.keycloak.federation.ldap.mappers;
import org.keycloak.Config;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
-import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.mappers.UserFederationMapperFactory;
import org.keycloak.models.KeycloakSession;
@@ -85,10 +85,10 @@ public abstract class AbstractLDAPFederationMapperFactory implements UserFederat
return configProperty;
}
- protected void checkMandatoryConfigAttribute(String name, String displayName, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+ protected void checkMandatoryConfigAttribute(String name, String displayName, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
String attrConfigValue = mapperModel.getConfig().get(name);
if (attrConfigValue == null || attrConfigValue.trim().isEmpty()) {
- throw new MapperConfigValidationException("Missing configuration for '" + displayName + "'");
+ throw new FederationConfigValidationException("Missing configuration for '" + displayName + "'");
}
}
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 32b0acd..bc4b34a 100755
--- 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
@@ -24,7 +24,7 @@ import java.util.Map;
import org.keycloak.federation.ldap.LDAPConfig;
import org.keycloak.federation.ldap.LDAPFederationProvider;
-import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
@@ -90,7 +90,7 @@ public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationM
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
checkMandatoryConfigAttribute(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute", mapperModel);
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java
index ae07c0f..73a4cef 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java
@@ -23,7 +23,7 @@ import java.util.List;
import java.util.Map;
import org.keycloak.federation.ldap.LDAPFederationProvider;
-import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperModel;
@@ -77,14 +77,14 @@ public class HardcodedLDAPRoleMapperFactory extends AbstractLDAPFederationMapper
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
String roleName = mapperModel.getConfig().get(HardcodedLDAPRoleMapper.ROLE);
if (roleName == null) {
- throw new MapperConfigValidationException("Role can't be null");
+ throw new FederationConfigValidationException("Role can't be null");
}
RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName);
if (role == null) {
- throw new MapperConfigValidationException("There is no role corresponding to configured value");
+ throw new FederationConfigValidationException("There is no role corresponding to configured value");
}
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
index 0304788..e43a896 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
@@ -26,6 +26,7 @@ import java.util.Map;
import org.keycloak.federation.ldap.LDAPConfig;
import org.keycloak.federation.ldap.LDAPFederationProvider;
+import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper;
import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapperFactory;
import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapperConfig;
@@ -33,7 +34,7 @@ import org.keycloak.federation.ldap.mappers.membership.LDAPGroupMapperMode;
import org.keycloak.federation.ldap.mappers.membership.MembershipType;
import org.keycloak.federation.ldap.mappers.membership.UserRolesRetrieveStrategy;
import org.keycloak.federation.ldap.mappers.membership.role.RoleMapperConfig;
-import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
@@ -185,7 +186,7 @@ public class GroupLDAPFederationMapperFactory extends AbstractLDAPFederationMapp
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
checkMandatoryConfigAttribute(GroupMapperConfig.GROUPS_DN, "LDAP Groups DN", mapperModel);
checkMandatoryConfigAttribute(GroupMapperConfig.MODE, "Mode", mapperModel);
@@ -193,8 +194,10 @@ public class GroupLDAPFederationMapperFactory extends AbstractLDAPFederationMapp
MembershipType membershipType = mt==null ? MembershipType.DN : Enum.valueOf(MembershipType.class, mt);
boolean preserveGroupInheritance = Boolean.parseBoolean(mapperModel.getConfig().get(GroupMapperConfig.PRESERVE_GROUP_INHERITANCE));
if (preserveGroupInheritance && membershipType != MembershipType.DN) {
- throw new MapperConfigValidationException("Not possible to preserve group inheritance and use UID membership type together");
+ throw new FederationConfigValidationException("ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType");
}
+
+ LDAPUtils.validateCustomLdapFilter(mapperModel.getConfig().get(GroupMapperConfig.GROUPS_LDAP_FILTER));
}
@Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java
index ee5140b..fa5b13d 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapperFactory.java
@@ -26,12 +26,14 @@ import java.util.Map;
import org.keycloak.federation.ldap.LDAPConfig;
import org.keycloak.federation.ldap.LDAPFederationProvider;
+import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper;
import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapperFactory;
import org.keycloak.federation.ldap.mappers.membership.LDAPGroupMapperMode;
import org.keycloak.federation.ldap.mappers.membership.MembershipType;
import org.keycloak.federation.ldap.mappers.membership.UserRolesRetrieveStrategy;
-import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.federation.ldap.mappers.membership.group.GroupMapperConfig;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
@@ -178,7 +180,7 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
checkMandatoryConfigAttribute(RoleMapperConfig.ROLES_DN, "LDAP Roles DN", mapperModel);
checkMandatoryConfigAttribute(RoleMapperConfig.MODE, "Mode", mapperModel);
@@ -187,14 +189,11 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
if (!useRealmMappings) {
String clientId = mapperModel.getConfig().get(RoleMapperConfig.CLIENT_ID);
if (clientId == null || clientId.trim().isEmpty()) {
- throw new MapperConfigValidationException("Client ID needs to be provided in config when Realm Roles Mapping is not used");
+ throw new FederationConfigValidationException("ldapErrorMissingClientId");
}
}
- String customLdapFilter = mapperModel.getConfig().get(RoleMapperConfig.ROLES_LDAP_FILTER);
- if ((customLdapFilter != null && customLdapFilter.trim().length() > 0) && (!customLdapFilter.startsWith("(") || !customLdapFilter.endsWith(")"))) {
- throw new MapperConfigValidationException("Custom Roles LDAP filter must starts with '(' and ends with ')'");
- }
+ LDAPUtils.validateCustomLdapFilter(mapperModel.getConfig().get(RoleMapperConfig.ROLES_LDAP_FILTER));
}
@Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java
index 2ccc96e..13c605b 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/msad/MSADUserAccountControlMapperFactory.java
@@ -25,7 +25,7 @@ import java.util.Map;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper;
import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapperFactory;
-import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
@@ -75,7 +75,7 @@ public class MSADUserAccountControlMapperFactory extends AbstractLDAPFederationM
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
}
@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 6a85623..f90eec3 100755
--- 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
@@ -24,7 +24,7 @@ import java.util.Map;
import org.keycloak.federation.ldap.LDAPConfig;
import org.keycloak.federation.ldap.LDAPFederationProvider;
-import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
@@ -101,7 +101,7 @@ public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFedera
}
@Override
- public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+ public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException {
checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute", mapperModel);
checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute", mapperModel);
}
diff --git a/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java b/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
index 80eec6a..dcf0b77 100644
--- a/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
+++ b/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
@@ -52,10 +52,11 @@ public interface UserFederationMapperFactory extends ProviderFactory<UserFederat
/**
* Called when instance of mapperModel is created for this factory through admin endpoint
*
+ * @param realm
* @param mapperModel
- * @throws MapperConfigValidationException if configuration provided in mapperModel is not valid
+ * @throws FederationConfigValidationException if configuration provided in mapperModel is not valid
*/
- void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException;
+ void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws FederationConfigValidationException;
/**
* Used to detect what are default values for ProviderConfigProperties specified during mapper creation
diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java b/server-spi/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java
new file mode 100644
index 0000000..9624690
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java
@@ -0,0 +1,37 @@
+/*
+ * 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.models;
+
+import org.keycloak.mappers.FederationConfigValidationException;
+
+/**
+ * TODO: Merge with UserFederationProviderFactory and add "default" method validateConfig with empty body once we move to source level 1.8
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface UserFederationValidatingProviderFactory extends UserFederationProviderFactory {
+
+ /**
+ * Called when instance of mapperModel is created for this factory through admin endpoint
+ *
+ * @param realm
+ * @param providerModel
+ * @throws FederationConfigValidationException if configuration provided in mapperModel is not valid
+ */
+ void validateConfig(RealmModel realm, UserFederationProviderModel providerModel) throws FederationConfigValidationException;
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index f3606c9..f75bf11 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -411,8 +411,12 @@ public final class KeycloakModelUtils {
return mapperModel;
}
+ public static UserFederationProviderFactory getFederationProviderFactory(KeycloakSession session, UserFederationProviderModel model) {
+ return (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, model.getProviderName());
+ }
+
public static UserFederationProvider getFederationProviderInstance(KeycloakSession session, UserFederationProviderModel model) {
- UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, model.getProviderName());
+ UserFederationProviderFactory factory = getFederationProviderFactory(session, model);
return factory.getInstance(session, model);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
index 0eb1748..920fa57 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
@@ -16,12 +16,14 @@
*/
package org.keycloak.services.resources.admin;
+import java.text.MessageFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Properties;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -40,7 +42,7 @@ import javax.ws.rs.core.UriInfo;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.events.admin.OperationType;
-import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.mappers.UserFederationMapperFactory;
import org.keycloak.models.KeycloakSession;
@@ -63,7 +65,6 @@ import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.UsersSyncManager;
-import org.keycloak.timer.TimerProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -105,6 +106,9 @@ public class UserFederationProviderResource {
}
UserFederationProviderModel model = new UserFederationProviderModel(rep.getId(), rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
+
+ UserFederationProvidersResource.validateFederationProviderConfig(session, auth, realm, model);
+
realm.updateUserFederationProvider(model);
new UsersSyncManager().notifyToRefreshPeriodicSync(session, realm, model, false);
boolean kerberosCredsAdded = UserFederationProvidersResource.checkKerberosCredential(session, realm, model);
@@ -370,8 +374,11 @@ public class UserFederationProviderResource {
try {
UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, model.getFederationMapperType());
mapperFactory.validateConfig(realm, model);
- } catch (MapperConfigValidationException ex) {
- throw new ErrorResponseException("Validation error", ex.getMessage(), Response.Status.BAD_REQUEST);
+ } catch (FederationConfigValidationException ex) {
+ logger.error(ex.getMessage());
+ Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+ throw new ErrorResponseException(ex.getMessage(), MessageFormat.format(messages.getProperty(ex.getMessage(), ex.getMessage()), ex.getParameters()),
+ Response.Status.BAD_REQUEST);
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java
index 995cda4..9c9ac51 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java
@@ -21,11 +21,13 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.events.admin.OperationType;
+import org.keycloak.mappers.FederationConfigValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.UserFederationValidatingProviderFactory;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.provider.ConfiguredProvider;
@@ -35,6 +37,8 @@ import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.UsersSyncManager;
import org.keycloak.timer.TimerProvider;
@@ -51,9 +55,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.Properties;
/**
* Base resource for managing users
@@ -101,6 +107,20 @@ public class UserFederationProvidersResource {
return false;
}
+ public static void validateFederationProviderConfig(KeycloakSession session, RealmAuth auth, RealmModel realm, UserFederationProviderModel model) {
+ UserFederationProviderFactory providerFactory = KeycloakModelUtils.getFederationProviderFactory(session, model);
+ if (providerFactory instanceof UserFederationValidatingProviderFactory) {
+ try {
+ ((UserFederationValidatingProviderFactory) providerFactory).validateConfig(realm, model);
+ } catch (FederationConfigValidationException fcve) {
+ logger.error(fcve.getMessage());
+ Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+ throw new ErrorResponseException(fcve.getMessage(), MessageFormat.format(messages.getProperty(fcve.getMessage(), fcve.getMessage()), fcve.getParameters()),
+ Response.Status.BAD_REQUEST);
+ }
+ }
+ }
+
/**
* Get available provider factories
*
@@ -176,6 +196,10 @@ public class UserFederationProvidersResource {
if (displayName != null && displayName.trim().equals("")) {
displayName = null;
}
+
+ UserFederationProviderModel tempModel = new UserFederationProviderModel(null, rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName, rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
+ validateFederationProviderConfig(session, auth, realm, tempModel);
+
UserFederationProviderModel model = realm.addUserFederationProvider(rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
new UsersSyncManager().notifyToRefreshPeriodicSync(session, realm, model, false);
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java
index 5a34641..3503880 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java
@@ -49,6 +49,9 @@ public class LdapUserProviderForm extends Form {
@FindBy(id = "ldapBindCredential")
private WebElement ldapBindCredentialInput;
+ @FindBy(id = "customUserSearchFilter")
+ private WebElement customUserSearchFilterInput;
+
@FindBy(id = "searchScope")
private Select searchScopeSelect;
@@ -155,6 +158,10 @@ public class LdapUserProviderForm extends Form {
setInputValue(ldapBindCredentialInput, ldapBindCredential);
}
+ public void setCustomUserSearchFilter(String customUserSearchFilter) {
+ setInputValue(customUserSearchFilterInput, customUserSearchFilter);
+ }
+
public void setKerberosRealmInput(String kerberosRealm) {
setInputValue(kerberosRealmInput, kerberosRealm);
}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/federation/LdapUserFederationTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/federation/LdapUserFederationTest.java
index a127e41..a4eb399 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/federation/LdapUserFederationTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/federation/LdapUserFederationTest.java
@@ -105,8 +105,25 @@ public class LdapUserFederationTest extends AbstractConsoleTest {
createLdapUserProvider.form().save();
assertAlertDanger();
createLdapUserProvider.form().setLdapBindCredentialInput("secret");
+
+ createLdapUserProvider.form().setCustomUserSearchFilter("foo");
+ createLdapUserProvider.form().save();
+ assertAlertDanger();
+ createLdapUserProvider.form().setCustomUserSearchFilter("");
createLdapUserProvider.form().save();
assertAlertSuccess();
+
+ // Try updating invalid Custom LDAP Filter
+ createLdapUserProvider.form().setCustomUserSearchFilter("(foo=bar");
+ createLdapUserProvider.form().save();
+ assertAlertDanger();
+ createLdapUserProvider.form().setCustomUserSearchFilter("foo=bar)");
+ createLdapUserProvider.form().save();
+ assertAlertDanger();
+ createLdapUserProvider.form().setCustomUserSearchFilter("(foo=bar)");
+ createLdapUserProvider.form().save();
+ assertAlertSuccess();
+
}
@Test
diff --git a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties
index ab297dd..dcb50c3 100644
--- a/themes/src/main/resources/theme/base/admin/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/messages_en.properties
@@ -5,4 +5,8 @@ invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
-invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
\ No newline at end of file
+invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.
+
+ldapErrorInvalidCustomFilter=Custom configured LDAP filter does not start with "(" or does not end with ")".
+ldapErrorMissingClientId=Client ID needs to be provided in config when Realm Roles Mapping is not used.
+ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType=Not possible to preserve group inheritance and use UID membership type together.
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index eb896fa..4ca5bf8 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -705,6 +705,10 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
$location.url("/realms/" + realm.realm + "/user-federation/providers/" + $scope.instance.providerName + "/" + id);
Notifications.success("The provider has been created.");
+ }, function (errorResponse) {
+ if (errorResponse.data && errorResponse.data['error_description']) {
+ Notifications.error(errorResponse.data['error_description']);
+ }
});
} else {
UserFederationInstances.update({realm: realm.realm,
@@ -713,6 +717,10 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
$scope.instance, function () {
$route.reload();
Notifications.success("The provider has been updated.");
+ }, function (errorResponse) {
+ if (errorResponse.data && errorResponse.data['error_description']) {
+ Notifications.error(errorResponse.data['error_description']);
+ }
});
}
};
@@ -909,6 +917,10 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
$location.url("/realms/" + realm.realm + "/user-federation/providers/" + $scope.instance.providerName + "/" + id);
Notifications.success("The provider has been created.");
+ }, function (errorResponse) {
+ if (errorResponse.data && errorResponse.data['error_description']) {
+ Notifications.error(errorResponse.data['error_description']);
+ }
});
} else {
UserFederationInstances.update({realm: realm.realm,
@@ -917,8 +929,11 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
$scope.instance, function () {
$route.reload();
Notifications.success("The provider has been updated.");
+ }, function (errorResponse) {
+ if (errorResponse.data && errorResponse.data['error_description']) {
+ Notifications.error(errorResponse.data['error_description']);
+ }
});
-
}
};
@@ -1041,7 +1056,7 @@ module.controller('UserFederationMapperCtrl', function($scope, realm, provider,
Notifications.success("Your changes have been saved.");
}, function(error) {
if (error.status == 400 && error.data.error_description) {
- Notifications.error('Error in configuration of mapper: ' + error.data.error_description);
+ Notifications.error(error.data.error_description);
} else {
Notifications.error('Unexpected error when creating mapper');
}
@@ -1113,7 +1128,7 @@ module.controller('UserFederationMapperCreateCtrl', function($scope, realm, prov
Notifications.success("Mapper has been created.");
}, function(error) {
if (error.status == 400 && error.data.error_description) {
- Notifications.error('Error in configuration of mapper: ' + error.data.error_description);
+ Notifications.error(error.data.error_description);
} else {
Notifications.error('Unexpected error when creating mapper');
}