diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPConfig.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPConfig.java index c9eac258940..2ed7df97fe1 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPConfig.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPConfig.java @@ -79,6 +79,14 @@ public class LDAPConfig { return usersDn; } + public String getRelativeCreateDn() { + String relativeCreateDn = config.getFirst(LDAPConstants.RELATIVE_CREATE_DN); + if(relativeCreateDn != null) { + return relativeCreateDn + ","; + } + return ""; + } + public String getBaseDn() { return config.getFirst(LDAPConstants.BASE_DN); } diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java index 87c8fbb6bcb..a6dacb20d2d 100755 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java @@ -137,6 +137,9 @@ public class LDAPStorageProviderFactory implements UserStorageProviderFactory> additionalAttributes) { LDAPObject ldapGroup = LDAPUtils.createLDAPGroup(ldapProvider, groupName, config.getGroupNameLdapAttribute(), config.getGroupObjectClasses(ldapProvider), - config.getGroupsDn(), additionalAttributes, config.getMembershipLdapAttribute()); + config.getRelativeCreateDn() + config.getGroupsDn(), additionalAttributes, config.getMembershipLdapAttribute()); logger.debugf("Creating group [%s] to LDAP with DN [%s]", groupName, ldapGroup.getDn().toString()); return ldapGroup; diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapperFactory.java index b4bab3ddb4b..68549bf1fb7 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapperFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupLDAPStorageMapperFactory.java @@ -98,6 +98,11 @@ public class GroupLDAPStorageMapperFactory extends AbstractLDAPStorageMapperFact .type(ProviderConfigProperty.STRING_TYPE) .required(true) .add() + .property().name(GroupMapperConfig.GROUPS_RELATIVE_CREATE_DN) + .label("Relative creation DN") + .helpText("LDAP DN where are groups of this tree will be created relative to the 'LDAP Groups DN' ") + .type(ProviderConfigProperty.STRING_TYPE) + .add() .property().name(GroupMapperConfig.GROUP_NAME_LDAP_ATTRIBUTE) .label("Group Name LDAP Attribute") .helpText("Name of LDAP attribute, which is used in group objects for name and RDN of group. Usually it will be 'cn' . In this case typical group/role object may have DN like 'cn=Group1,ou=groups,dc=example,dc=org' ") diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupMapperConfig.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupMapperConfig.java index f5067a53604..d5a313c5092 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupMapperConfig.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/group/GroupMapperConfig.java @@ -35,6 +35,7 @@ public class GroupMapperConfig extends CommonLDAPGroupMapperConfig { // LDAP DN where are groups of this tree saved. public static final String GROUPS_DN = "groups.dn"; + public static final String GROUPS_RELATIVE_CREATE_DN = "groups.relative.create.dn"; // Name of LDAP attribute, which is used in group objects for name and RDN of group. Usually it will be "cn" public static final String GROUP_NAME_LDAP_ATTRIBUTE = "group.name.ldap.attribute"; @@ -79,6 +80,14 @@ public class GroupMapperConfig extends CommonLDAPGroupMapperConfig { return groupsDn; } + public String getRelativeCreateDn() { + String relativeCreateDn = mapperModel.getConfig().getFirst(GROUPS_RELATIVE_CREATE_DN); + if(relativeCreateDn != null) { + return relativeCreateDn + ","; + } + return ""; + } + @Override public String getLDAPGroupsDn() { return getGroupsDn(); diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java index 8f867cdcd96..ffb9decd059 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java @@ -266,7 +266,7 @@ public class RoleLDAPStorageMapper extends AbstractLDAPStorageMapper implements public LDAPObject createLDAPRole(String roleName) { LDAPObject ldapRole = LDAPUtils.createLDAPGroup(ldapProvider, roleName, config.getRoleNameLdapAttribute(), config.getRoleObjectClasses(ldapProvider), - config.getRolesDn(), Collections.>emptyMap(), config.getMembershipLdapAttribute()); + config.getRelativeCreateDn() + config.getRolesDn(), Collections.>emptyMap(), config.getMembershipLdapAttribute()); logger.debugf("Creating role [%s] to LDAP with DN [%s]", roleName, ldapRole.getDn().toString()); return ldapRole; diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapperFactory.java index e6c4acba068..5c706ccf268 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapperFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapperFactory.java @@ -96,6 +96,11 @@ public class RoleLDAPStorageMapperFactory extends AbstractLDAPStorageMapperFacto .type(ProviderConfigProperty.STRING_TYPE) .required(true) .add() + .property().name(RoleMapperConfig.ROLES_RELATIVE_CREATE_DN) + .label("Relative creation DN") + .helpText("LDAP DN where are roles of this tree will be created relative to the 'LDAP Roles DN' ") + .type(ProviderConfigProperty.STRING_TYPE) + .add() .property().name(RoleMapperConfig.ROLE_NAME_LDAP_ATTRIBUTE) .label("Role Name LDAP Attribute") .helpText("Name of LDAP attribute, which is used in role objects for name and RDN of role. Usually it will be 'cn' . In this case typical group/role object may have DN like 'cn=role1,ou=finance,dc=example,dc=org' ") diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleMapperConfig.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleMapperConfig.java index 0dbb87c4d8a..b3b747f29da 100755 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleMapperConfig.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleMapperConfig.java @@ -32,6 +32,7 @@ public class RoleMapperConfig extends CommonLDAPGroupMapperConfig { // LDAP DN where are roles of this tree saved. public static final String ROLES_DN = "roles.dn"; + public static final String ROLES_RELATIVE_CREATE_DN = "roles.relative.create.dn"; // Name of LDAP attribute, which is used in role objects for name and RDN of role. Usually it will be "cn" public static final String ROLE_NAME_LDAP_ATTRIBUTE = "role.name.ldap.attribute"; @@ -66,6 +67,14 @@ public class RoleMapperConfig extends CommonLDAPGroupMapperConfig { return rolesDn; } + public String getRelativeCreateDn() { + String relativeCreateDn = mapperModel.getConfig().getFirst(ROLES_RELATIVE_CREATE_DN); + if(relativeCreateDn != null) { + return relativeCreateDn + ","; + } + return ""; + } + @Override public String getLDAPGroupsDn() { return getRolesDn(); diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index 42d641bf360..6b8fa99b59c 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -674,6 +674,8 @@ certSubject=CERT_SUBJECT userCredentialsHelpText=The top level handlers allow you to shift the priority of the credential for the user, the topmost credential having the highest priority. The handlers within one expandable panel allow you to change the visual order of the credentials, the topmost credential will show at the most left. ldapAdvancedSettingsDescription=This section contains all the other options for more fine-grained configuration of the LDAP storage provider. usersDN=Users DN +relativeUserCreateDn=Relative user creation DN +relativeUserCreateDnHelp=Relative DN from the 'Users DN' to where users will be created. For example, this allows users to be created in other DN than the parent 'Users DN' when using a subtree search scope. secretSize=Secret size included.custom.audience.label=Included Custom Audience max-clients.label=Max Clients Per Realm diff --git a/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsSearching.tsx b/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsSearching.tsx index ccf2b26cba0..8fef2120dcc 100644 --- a/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsSearching.tsx +++ b/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsSearching.tsx @@ -54,6 +54,11 @@ export const LdapSettingsSearching = ({ required: t("validateUsersDn"), }} /> +