From 41d22986d50e518300391ffe8490bb8b32c5f1f8 Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 22 Dec 2015 16:22:02 +0100 Subject: [PATCH] KEYCLOAK-1899 Added HardcodedLDAPRoleMapper --- .../broker/provider/HardcodedRoleMapper.java | 37 +----- .../oidc/mappers/ClaimToRoleMapper.java | 5 +- .../ExternalKeycloakRoleToRoleMapper.java | 5 +- .../saml/mappers/AttributeToRoleMapper.java | 5 +- .../FullNameLDAPFederationMapperFactory.java | 2 +- .../ldap/mappers/HardcodedLDAPRoleMapper.java | 109 ++++++++++++++++++ .../HardcodedLDAPRoleMapperFactory.java | 78 +++++++++++++ ...rAttributeLDAPFederationMapperFactory.java | 2 +- .../GroupLDAPFederationMapperFactory.java | 2 +- .../role/RoleLDAPFederationMapperFactory.java | 2 +- ...ycloak.mappers.UserFederationMapperFactory | 1 + .../mappers/UserFederationMapperFactory.java | 3 +- .../models/utils/KeycloakModelUtils.java | 30 +++++ .../protocol/ProtocolMapperUtils.java | 14 --- .../protocol/oidc/mappers/HardcodedRole.java | 3 +- .../protocol/oidc/mappers/RoleNameMapper.java | 5 +- .../admin/UserFederationProviderResource.java | 2 +- .../FederationProvidersIntegrationTest.java | 48 ++++++++ 18 files changed, 290 insertions(+), 63 deletions(-) create mode 100644 federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapper.java create mode 100644 federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedRoleMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedRoleMapper.java index 309e26a2bc..6f0b02a5e0 100755 --- a/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedRoleMapper.java +++ b/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedRoleMapper.java @@ -1,14 +1,11 @@ package org.keycloak.broker.provider; -import org.keycloak.broker.provider.AbstractIdentityProviderMapper; -import org.keycloak.broker.provider.BrokeredIdentityContext; -import org.keycloak.broker.provider.IdentityBrokerException; -import org.keycloak.models.ClientModel; import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.provider.ProviderConfigProperty; import java.util.ArrayList; @@ -20,7 +17,7 @@ import java.util.List; */ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper { public static final String ROLE = "role"; - protected static final List configProperties = new ArrayList(); + protected static final List configProperties = new ArrayList<>(); static { ProviderConfigProperty property; @@ -32,34 +29,6 @@ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper { configProperties.add(property); } - - - public static String[] parseRole(String role) { - int scopeIndex = role.lastIndexOf('.'); - if (scopeIndex > -1) { - String appName = role.substring(0, scopeIndex); - role = role.substring(scopeIndex + 1); - String[] rtn = {appName, role}; - return rtn; - } else { - String[] rtn = {null, role}; - return rtn; - - } - } - - public static RoleModel getRoleFromString(RealmModel realm, String roleName) { - String[] parsedRole = parseRole(roleName); - RoleModel role = null; - if (parsedRole[0] == null) { - role = realm.getRole(parsedRole[1]); - } else { - ClientModel client = realm.getClientByClientId(parsedRole[0]); - role = client.getRole(parsedRole[1]); - } - return role; - } - @Override public List getConfigProperties() { return configProperties; @@ -93,7 +62,7 @@ public class HardcodedRoleMapper extends AbstractIdentityProviderMapper { @Override public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { String roleName = mapperModel.getConfig().get(ROLE); - RoleModel role = getRoleFromString(realm, roleName); + RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName); user.grantRole(role); } diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ClaimToRoleMapper.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ClaimToRoleMapper.java index b3bd191d27..2d79a5b22b 100755 --- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ClaimToRoleMapper.java +++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ClaimToRoleMapper.java @@ -10,6 +10,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.provider.ProviderConfigProperty; import java.util.ArrayList; @@ -80,7 +81,7 @@ public class ClaimToRoleMapper extends AbstractClaimMapper { public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE); if (hasClaimValue(mapperModel, context)) { - RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName); + RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName); user.grantRole(role); } @@ -90,7 +91,7 @@ public class ClaimToRoleMapper extends AbstractClaimMapper { public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE); if (!hasClaimValue(mapperModel, context)) { - RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName); + RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName); user.deleteRoleMapping(role); } diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ExternalKeycloakRoleToRoleMapper.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ExternalKeycloakRoleToRoleMapper.java index e4e0de020b..f7ddb76f2c 100755 --- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ExternalKeycloakRoleToRoleMapper.java +++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ExternalKeycloakRoleToRoleMapper.java @@ -11,6 +11,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.JsonWebToken; @@ -85,7 +86,7 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper { JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN); //if (token == null) return; String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE); - String[] parseRole = HardcodedRoleMapper.parseRole(mapperModel.getConfig().get(EXTERNAL_ROLE)); + String[] parseRole = KeycloakModelUtils.parseRole(mapperModel.getConfig().get(EXTERNAL_ROLE)); String externalRoleName = parseRole[1]; String claimName = null; if (parseRole[0] == null) { @@ -95,7 +96,7 @@ public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper { } Object claim = getClaimValue(token, claimName); if (valueEquals(externalRoleName, claim)) { - RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName); + RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName); return role; } diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/AttributeToRoleMapper.java b/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/AttributeToRoleMapper.java index 4014a9a844..48d2ac89d7 100755 --- a/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/AttributeToRoleMapper.java +++ b/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/AttributeToRoleMapper.java @@ -15,6 +15,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.representations.JsonWebToken; @@ -95,7 +96,7 @@ public class AttributeToRoleMapper extends AbstractIdentityProviderMapper { public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE); if (isAttributePresent(mapperModel, context)) { - RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName); + RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName); user.grantRole(role); } @@ -125,7 +126,7 @@ public class AttributeToRoleMapper extends AbstractIdentityProviderMapper { public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE); if (!isAttributePresent(mapperModel, context)) { - RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName); + RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName); user.deleteRoleMapping(role); } 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 e08429ef23..7fbcf06318 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 @@ -75,7 +75,7 @@ public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationM } @Override - public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException { + public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException { checkMandatoryConfigAttribute(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute", mapperModel); } diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapper.java new file mode 100644 index 0000000000..866d2b1d77 --- /dev/null +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapper.java @@ -0,0 +1,109 @@ +package org.keycloak.federation.ldap.mappers; + +import java.util.Set; + +import org.jboss.logging.Logger; +import org.keycloak.federation.ldap.LDAPFederationProvider; +import org.keycloak.federation.ldap.idm.model.LDAPObject; +import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery; +import org.keycloak.models.ClientModel; +import org.keycloak.models.ModelException; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserFederationMapperModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.UserModelDelegate; + +/** + * @author Marek Posolda + */ +public class HardcodedLDAPRoleMapper extends AbstractLDAPFederationMapper { + + private static final Logger logger = Logger.getLogger(HardcodedLDAPRoleMapper.class); + + public static final String ROLE = "role"; + + public HardcodedLDAPRoleMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, RealmModel realm) { + super(mapperModel, ldapProvider, realm); + } + + @Override + public void beforeLDAPQuery(LDAPQuery query) { + } + + @Override + public UserModel proxy(LDAPObject ldapUser, UserModel delegate) { + return new UserModelDelegate(delegate) { + + @Override + public Set getRealmRoleMappings() { + Set roles = super.getRealmRoleMappings(); + + RoleModel role = getRole(); + if (role != null && role.getContainer().equals(realm)) { + roles.add(role); + } + + return roles; + } + + @Override + public Set getClientRoleMappings(ClientModel app) { + Set roles = super.getClientRoleMappings(app); + + RoleModel role = getRole(); + if (role != null && role.getContainer().equals(app)) { + roles.add(role); + } + + return roles; + } + + @Override + public boolean hasRole(RoleModel role) { + return super.hasRole(role) || role.equals(getRole()); + } + + @Override + public Set getRoleMappings() { + Set roles = super.getRoleMappings(); + + RoleModel role = getRole(); + if (role != null) { + roles.add(role); + } + + return roles; + } + + @Override + public void deleteRoleMapping(RoleModel role) { + if (role.equals(getRole())) { + throw new ModelException("Not possible to delete role. It's hardcoded by LDAP mapper"); + } else { + super.deleteRoleMapping(role); + } + } + }; + } + + @Override + public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser) { + + } + + @Override + public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, boolean isCreate) { + + } + + private RoleModel getRole() { + String roleName = mapperModel.getConfig().get(HardcodedLDAPRoleMapper.ROLE); + RoleModel role = KeycloakModelUtils.getRoleFromString(realm, roleName); + if (role == null) { + logger.warnf("Hardcoded role '%s' configured in mapper '%s' is not available anymore"); + } + return role; + } +} 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 new file mode 100644 index 0000000000..508a333417 --- /dev/null +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/HardcodedLDAPRoleMapperFactory.java @@ -0,0 +1,78 @@ +package org.keycloak.federation.ldap.mappers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.keycloak.federation.ldap.LDAPFederationProvider; +import org.keycloak.mappers.MapperConfigValidationException; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserFederationMapperModel; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.provider.ProviderConfigProperty; + +/** + * @author Marek Posolda + */ +public class HardcodedLDAPRoleMapperFactory extends AbstractLDAPFederationMapperFactory { + + public static final String PROVIDER_ID = "hardcoded-ldap-role-mapper"; + protected static final List configProperties = new ArrayList(); + + static { + ProviderConfigProperty roleAttr = createConfigProperty(HardcodedLDAPRoleMapper.ROLE, "Role", + "Role to grant to user. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole", + ProviderConfigProperty.ROLE_TYPE, null); + configProperties.add(roleAttr); + } + + @Override + public String getHelpText() { + return "When user is imported from LDAP, he will be automatically added into this configured role."; + } + + @Override + public String getDisplayCategory() { + return ROLE_MAPPER_CATEGORY; + } + + @Override + public String getDisplayType() { + return "Hardcoded Role"; + } + + @Override + public List getConfigProperties() { + return configProperties; + } + + @Override + public Map getDefaultConfig(UserFederationProviderModel providerModel) { + return new HashMap<>(); + } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException { + String roleName = mapperModel.getConfig().get(HardcodedLDAPRoleMapper.ROLE); + if (roleName == null) { + throw new MapperConfigValidationException("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"); + } + } + + @Override + protected AbstractLDAPFederationMapper createMapper(UserFederationMapperModel mapperModel, LDAPFederationProvider federationProvider, RealmModel realm) { + return new HardcodedLDAPRoleMapper(mapperModel, federationProvider, realm); + } +} 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 9b061a6a44..5d929007d7 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 @@ -87,7 +87,7 @@ public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFedera } @Override - public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException { + public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException { checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute", mapperModel); checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute", mapperModel); } 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 464cc0d92e..fa43ac9721 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 @@ -168,7 +168,7 @@ public class GroupLDAPFederationMapperFactory extends AbstractLDAPFederationMapp } @Override - public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException { + public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException { checkMandatoryConfigAttribute(GroupMapperConfig.GROUPS_DN, "LDAP Groups DN", mapperModel); checkMandatoryConfigAttribute(GroupMapperConfig.MODE, "Mode", mapperModel); 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 a4ba60f3a8..fe0b1e194c 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 @@ -161,7 +161,7 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe } @Override - public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException { + public void validateConfig(RealmModel realm, UserFederationMapperModel mapperModel) throws MapperConfigValidationException { checkMandatoryConfigAttribute(RoleMapperConfig.ROLES_DN, "LDAP Roles DN", mapperModel); checkMandatoryConfigAttribute(RoleMapperConfig.MODE, "Mode", mapperModel); diff --git a/federation/ldap/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory index 9e4a658dd7..ac130a551e 100644 --- a/federation/ldap/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory +++ b/federation/ldap/src/main/resources/META-INF/services/org.keycloak.mappers.UserFederationMapperFactory @@ -1,4 +1,5 @@ org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapperFactory org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory +org.keycloak.federation.ldap.mappers.HardcodedLDAPRoleMapperFactory org.keycloak.federation.ldap.mappers.membership.role.RoleLDAPFederationMapperFactory org.keycloak.federation.ldap.mappers.membership.group.GroupLDAPFederationMapperFactory \ No newline at end of file diff --git a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java index f009c3e2d6..eebc3d7d3e 100644 --- a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java +++ b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java @@ -2,6 +2,7 @@ package org.keycloak.mappers; import java.util.Map; +import org.keycloak.models.RealmModel; import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationProviderModel; import org.keycloak.provider.ConfiguredProvider; @@ -37,7 +38,7 @@ public interface UserFederationMapperFactory extends ProviderFactory -1) { + String appName = role.substring(0, scopeIndex); + role = role.substring(scopeIndex + 1); + String[] rtn = {appName, role}; + return rtn; + } else { + String[] rtn = {null, role}; + return rtn; + + } + } + } diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java index 2b8677363c..9022c269a6 100755 --- a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java +++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java @@ -47,20 +47,6 @@ public class ProtocolMapperUtils { return null; } - public static String[] parseRole(String role) { - int scopeIndex = role.lastIndexOf('.'); - if (scopeIndex > -1) { - String appName = role.substring(0, scopeIndex); - role = role.substring(scopeIndex + 1); - String[] rtn = {appName, role}; - return rtn; - } else { - String[] rtn = {null, role}; - return rtn; - - } - } - /** * Find the builtin locale mapper. * diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java index c49eebbf21..a839e31393 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java @@ -4,6 +4,7 @@ import org.keycloak.models.ClientSessionModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; @@ -67,7 +68,7 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) { String role = mappingModel.getConfig().get(ROLE_CONFIG); - String[] scopedRole = ProtocolMapperUtils.parseRole(role); + String[] scopedRole = KeycloakModelUtils.parseRole(role); String appName = scopedRole[0]; String roleName = scopedRole[1]; if (appName != null) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java index 3ce1e19d5f..5f0178cc6f 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java @@ -5,6 +5,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.provider.ProviderConfigProperty; @@ -78,8 +79,8 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc String role = mappingModel.getConfig().get(ROLE_CONFIG); String newName = mappingModel.getConfig().get(NEW_ROLE_NAME); - String[] scopedRole = ProtocolMapperUtils.parseRole(role); - String[] newScopedRole = ProtocolMapperUtils.parseRole(newName); + String[] scopedRole = KeycloakModelUtils.parseRole(role); + String[] newScopedRole = KeycloakModelUtils.parseRole(newName); String appName = scopedRole[0]; String roleName = scopedRole[1]; if (appName != null) { 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 104627685a..29402474df 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 @@ -353,7 +353,7 @@ public class UserFederationProviderResource { private void validateModel(UserFederationMapperModel model) { try { UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, model.getFederationMapperType()); - mapperFactory.validateConfig(model); + mapperFactory.validateConfig(realm, model); } catch (MapperConfigValidationException ex) { throw new ErrorResponseException("Validation error", ex.getMessage(), Response.Status.BAD_REQUEST); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java index 5702a451a5..1d41595f6f 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/FederationProvidersIntegrationTest.java @@ -15,11 +15,15 @@ import org.keycloak.federation.ldap.LDAPFederationProviderFactory; import org.keycloak.federation.ldap.idm.model.LDAPObject; import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper; import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory; +import org.keycloak.federation.ldap.mappers.HardcodedLDAPRoleMapper; +import org.keycloak.federation.ldap.mappers.HardcodedLDAPRoleMapperFactory; import org.keycloak.federation.ldap.mappers.UserAttributeLDAPFederationMapper; import org.keycloak.models.KeycloakSession; import org.keycloak.models.LDAPConstants; +import org.keycloak.models.ModelException; import org.keycloak.models.ModelReadOnlyException; import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialValueModel; import org.keycloak.models.UserFederationMapperModel; @@ -524,6 +528,50 @@ public class FederationProvidersIntegrationTest { } } + @Test + public void testHardcodedRoleMapper() { + KeycloakSession session = keycloakRule.startSession(); + UserFederationMapperModel firstNameMapper = null; + + try { + RealmModel appRealm = new RealmManager(session).getRealmByName("test"); + RoleModel hardcodedRole = appRealm.addRole("hardcoded-role"); + + // assert that user "johnkeycloak" doesn't have hardcoded role + UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm); + Assert.assertFalse(john.hasRole(hardcodedRole)); + + UserFederationMapperModel hardcodedMapperModel = KeycloakModelUtils.createUserFederationMapperModel("hardcoded role", ldapModel.getId(), HardcodedLDAPRoleMapperFactory.PROVIDER_ID, + HardcodedLDAPRoleMapper.ROLE, "hardcoded-role"); + appRealm.addUserFederationMapper(hardcodedMapperModel); + } finally { + keycloakRule.stopSession(session, true); + } + + session = keycloakRule.startSession(); + try { + RealmModel appRealm = new RealmManager(session).getRealmByName("test"); + RoleModel hardcodedRole = appRealm.getRole("hardcoded-role"); + + // Assert user is successfully imported in Keycloak DB now with correct firstName and lastName + UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm); + Assert.assertTrue(john.hasRole(hardcodedRole)); + + // Can't remove user from hardcoded role + try { + john.deleteRoleMapping(hardcodedRole); + Assert.fail("Didn't expected to remove role mapping"); + } catch (ModelException expected) { + } + + // Revert mappers + UserFederationMapperModel hardcodedMapperModel = appRealm.getUserFederationMapperByName(ldapModel.getId(), "hardcoded role"); + appRealm.removeUserFederationMapper(hardcodedMapperModel); + } finally { + keycloakRule.stopSession(session, true); + } + } + @Test public void testImportExistingUserFromLDAP() throws Exception { // Add LDAP user with same email like existing model user