diff --git a/docs/configuration.md b/docs/configuration.md index 0037199d..a8c34efb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -69,6 +69,11 @@ For each realm we have the possibility to configure a default reader and a defau | fr.insee.sugoi.organizations.maxoutputsize | The default maximum number of organizations outputs allowed | 1000 | 100 | | fr.insee.sugoi.applications.maxoutputsize | The default maximum number of applications outputs allowed | 1000 | 100 | | fr.insee.sugoi.ldap.default.connection.timeout | Default response timeout for all types of operations with a ldap provider. | 30000 | 30000 | +| fr.insee.sugoi.ldap.default.user_dn_pattern | The default pattern for the DN of a user with name {id} given the user source is {source}. | uid={id},{source} | uid={id},{source} | +| fr.insee.sugoi.ldap.default.organization_dn_pattern | The default pattern for the DN of an organization with name {id} given the organization source is {source}. | uid={id},{source} | uid={id},{source} | +| fr.insee.sugoi.ldap.default.address_dn_pattern | The default pattern for the DN of an address with name {id} given the address source is {source}. | l={id},{source} | l={id},{source} | +| fr.insee.sugoi.ldap.default.application_dn_pattern | The default pattern for the DN of an application with name {id} given the application source is {source}. | ou={id},{source} | ou={id},{source} | +| fr.insee.sugoi.ldap.default.group_dn_pattern | The default pattern for the DN of a group with name {id} given the group source is {source}. | cn={id},{source} | cn={id},{source} | #### Jms Specific configuration diff --git a/docs/realm-configuration.md b/docs/realm-configuration.md index 225e25dd..6464207a 100644 --- a/docs/realm-configuration.md +++ b/docs/realm-configuration.md @@ -150,16 +150,21 @@ Userstorage properties on password generation and validation. See [general confi With an LDAP Store Provider the properties can be : -|  Key | Example | Optional | Default | Description | -|---------------------------|:--------------------------------------------------------------------:|---------:|----------------------------------------------------------------------------------------------------:|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| group_filter_pattern | "(cn={group}\_{appliname})" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.group_filter_pattern | Describe how should be name a group. {appliname} is replaced by the name of the application the group belongs to and {group} is replaced by a group name. If not set, cannot manage groups. | -| group_source_pattern | "ou={appliname}\_Objets,ou={appliname},ou=Applications,o=insee,c=fr" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.group_source_pattern | Describe where a group belonging to the application of name {appliname} should be fetch. | -| user_object_class | "top,person" | yes | value of property fr.insee.sugoi.ldap.default.user-object-classes or top,person | Object classes to put on a new user in ldap storage | -| organization_object_class | "top,organization" | yes | value of property fr.insee.sugoi.ldap.default.organization-object-classes or top,organization | Object classes to put on a new organization in ldap storage | -| group_object_class | "top,groupOfUniqueNames" | yes | value of property fr.insee.sugoi.ldap.default.group-object-classes or top,groupOfUniqueNames | Object classes to put on a new group in ldap storage | -| application_object_class | "top,organizationalUnit" | yes | value of property fr.insee.sugoi.ldap.default.application-object-classes or top,organizationalUnit | Object classes to put on a new application in ldap storage | -| address_object_class | "top,locality" | yes | value of property fr.insee.sugoi.ldap.default.adress-object-classes or top,locality | Object classes to put on a new address in ldap storage **not supported yet** | -| ldap_connection_timeout | "60000" | yes | value of property fr.insee.sugoi.ldap.default.connection.timeout | Response timeout for all types of operations with a ldap provider | +|  Key | Example | Optional | Default | Description | +|---------------------------|:--------------------------------------------------------------------:|---------:|-------------------------------------------------------------------------------------------------------:|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| group_filter_pattern | "(cn={group}\_{appliname})" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.group_filter_pattern | Describe how should be name a group. {appliname} is replaced by the name of the application the group belongs to and {group} is replaced by a group name. If not set, cannot manage groups. | +| group_source_pattern | "ou={appliname}\_Objets,ou={appliname},ou=Applications,o=insee,c=fr" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.group_source_pattern | Describe where a group belonging to the application of name {appliname} should be fetch. | +| user_object_class | "top,person" | yes | value of property fr.insee.sugoi.ldap.default.user-object-classes or top,person | Object classes to put on a new user in ldap storage | +| organization_object_class | "top,organization" | yes | value of property fr.insee.sugoi.ldap.default.organization-object-classes or top,organization | Object classes to put on a new organization in ldap storage | +| group_object_class | "top,groupOfUniqueNames" | yes | value of property fr.insee.sugoi.ldap.default.group-object-classes or top,groupOfUniqueNames | Object classes to put on a new group in ldap storage | +| application_object_class | "top,organizationalUnit" | yes | value of property fr.insee.sugoi.ldap.default.application-object-classes or top,organizationalUnit | Object classes to put on a new application in ldap storage | +| address_object_class | "top,locality" | yes | value of property fr.insee.sugoi.ldap.default.adress-object-classes or top,locality | Object classes to put on a new address in ldap storage **not supported yet** | +| ldap_connection_timeout | "60000" | yes | value of property fr.insee.sugoi.ldap.default.connection.timeout | Response timeout for all types of operations with a ldap provider | +| user_dn_pattern | "uid={id},{source}" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.user_dn_pattern | The DN of a user with name {id} given the user source is {source} | +| organization_dn_pattern | "uid={id},{source}" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.organization_dn_pattern | The DN of an organization with name {id} given the organization source is {source} | +| address_dn_pattern | "l={id},{source}" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.address_dn_pattern | The DN of an address with name {id} given the address source is {source} | +| application_dn_pattern | "ou={id},{source}" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.application_dn_pattern | The DN of an application with name {id} given the application source is {source} | +| group_dn_pattern | "cn={id},{source}" | yes | the default can be set via the instance property : fr.insee.sugoi.ldap.default.group_dn_pattern | The DN of a group with name {id} given the group source is {source} | ## Realm and Userstorage mappings with a LDAP Store Provider diff --git a/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapStore.java b/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapStore.java index ea328321..741c7cb9 100644 --- a/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapStore.java +++ b/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapStore.java @@ -90,36 +90,28 @@ protected boolean matchGroupWildcardPattern(String appName, String groupName) { protected String getApplicationDN(String applicationName) { if (StringUtils.isNotBlank(config.get(GlobalKeysConfig.APP_SOURCE))) { - return String.format( - "%s=%s,%s", - // TODO should be a param - "ou", - // - applicationName, - config.get(GlobalKeysConfig.APP_SOURCE)); + return config + .get(LdapConfigKeys.APPLICATION_DN_PATTERN) + .replace("{id}", applicationName) + .replace("{source}", config.get(GlobalKeysConfig.APP_SOURCE)); } else { throw new UnsupportedOperationException("Applications feature not configured for this realm"); } } protected String getGroupDN(String applicationName, String groupName) { - return String.format( - "%s=%s,%s", - // TODO should be a param - "cn", - // - groupName, - getGroupSource(applicationName)); + return config + .get(LdapConfigKeys.GROUP_DN_PATTERN) + .replace("{id}", groupName) + .replace("{source}", getGroupSource(applicationName)); } protected String getOrganizationDN(String organizationId) { if (StringUtils.isNotBlank(config.get(GlobalKeysConfig.ORGANIZATION_SOURCE))) { - return String.format( - "%s=%s,%s", // TODO should be a param - "uid", - // - organizationId, - config.get(GlobalKeysConfig.ORGANIZATION_SOURCE)); + return config + .get(LdapConfigKeys.ORGANIZATION_DN_PATTERN) + .replace("{id}", organizationId) + .replace("{source}", config.get(GlobalKeysConfig.ORGANIZATION_SOURCE)); } else { throw new UnsupportedOperationException( "Organizations feature not configured for this storage"); @@ -127,22 +119,16 @@ protected String getOrganizationDN(String organizationId) { } protected String getUserDN(String username) { - return String.format( - "%s=%s,%s", - // TODO should be a param - "uid", - // - username, - config.get(GlobalKeysConfig.USER_SOURCE)); + return config + .get(LdapConfigKeys.USER_DN_PATTERN) + .replace("{id}", username) + .replace("{source}", config.get(GlobalKeysConfig.USER_SOURCE)); } protected String getAddressDN(String addressId) { - return String.format( - "%s=%s,%s", - // TODO should be a param - "l", - // - addressId, - config.get(GlobalKeysConfig.ADDRESS_SOURCE)); + return config + .get(LdapConfigKeys.ADDRESS_DN_PATTERN) + .replace("{id}", addressId) + .replace("{source}", config.get(GlobalKeysConfig.ADDRESS_SOURCE)); } } diff --git a/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapStoreBeans.java b/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapStoreBeans.java index de3542a3..f0aa5c8a 100644 --- a/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapStoreBeans.java +++ b/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapStoreBeans.java @@ -62,6 +62,21 @@ public class LdapStoreBeans { @Value("${fr.insee.sugoi.ldap.default.group_manager_source_pattern:}") private String defaultGroupManagerSourcePattern; + @Value("${fr.insee.sugoi.ldap.default.organization_dn_pattern:uid={id},{source}}") + private String defaultOrganizationDnPattern; + + @Value("${fr.insee.sugoi.ldap.default.user_dn_pattern:uid={id},{source}}") + private String defaultUserDnPattern; + + @Value("${fr.insee.sugoi.ldap.default.address_dn_pattern:l={id},{source}}") + private String defaultAddressDnPattern; + + @Value("${fr.insee.sugoi.ldap.default.group_dn_pattern:cn={id},{source}}") + private String defaultGroupDnPattern; + + @Value("${fr.insee.sugoi.ldap.default.application_dn_pattern:ou={id},{source}}") + private String defaultApplicationDnPattern; + @Value("${fr.insee.sugoi.ldap.default.vlv.enabled:false}") private String vlvEnabled; @@ -135,6 +150,31 @@ public Map generateConfig(Realm realm, UserStorage user config.put(GlobalKeysConfig.APP_SOURCE, realm.getAppSource()); config.put(GlobalKeysConfig.ORGANIZATION_SOURCE, userStorage.getOrganizationSource()); config.put(GlobalKeysConfig.ADDRESS_SOURCE, userStorage.getAddressSource()); + config.put( + LdapConfigKeys.ORGANIZATION_DN_PATTERN, + userStorage.getProperties().get(LdapConfigKeys.ORGANIZATION_DN_PATTERN) != null + ? userStorage.getProperties().get(LdapConfigKeys.ORGANIZATION_DN_PATTERN).get(0) + : defaultOrganizationDnPattern); + config.put( + LdapConfigKeys.USER_DN_PATTERN, + userStorage.getProperties().get(LdapConfigKeys.USER_DN_PATTERN) != null + ? userStorage.getProperties().get(LdapConfigKeys.USER_DN_PATTERN).get(0) + : defaultUserDnPattern); + config.put( + LdapConfigKeys.ADDRESS_DN_PATTERN, + userStorage.getProperties().get(LdapConfigKeys.ADDRESS_DN_PATTERN) != null + ? userStorage.getProperties().get(LdapConfigKeys.ADDRESS_DN_PATTERN).get(0) + : defaultAddressDnPattern); + config.put( + LdapConfigKeys.GROUP_DN_PATTERN, + userStorage.getProperties().get(LdapConfigKeys.GROUP_DN_PATTERN) != null + ? userStorage.getProperties().get(LdapConfigKeys.GROUP_DN_PATTERN).get(0) + : defaultGroupDnPattern); + config.put( + LdapConfigKeys.APPLICATION_DN_PATTERN, + userStorage.getProperties().get(LdapConfigKeys.APPLICATION_DN_PATTERN) != null + ? userStorage.getProperties().get(LdapConfigKeys.APPLICATION_DN_PATTERN).get(0) + : defaultApplicationDnPattern); config.put( LdapConfigKeys.GROUP_SOURCE_PATTERN, realm.getProperties().get(LdapConfigKeys.GROUP_SOURCE_PATTERN) != null diff --git a/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapWriterStore.java b/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapWriterStore.java index 7e98d8b9..6f6b9088 100644 --- a/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapWriterStore.java +++ b/sugoi-api-ldap-store-provider/src/main/java/fr/insee/sugoi/store/ldap/LdapWriterStore.java @@ -25,7 +25,6 @@ import com.unboundid.ldap.sdk.ResultCode; import com.unboundid.ldap.sdk.extensions.PasswordModifyExtendedRequest; import com.unboundid.util.SubtreeDeleter; -import fr.insee.sugoi.core.configuration.GlobalKeysConfig; import fr.insee.sugoi.core.model.ProviderRequest; import fr.insee.sugoi.core.model.ProviderResponse; import fr.insee.sugoi.core.model.ProviderResponse.ProviderResponseStatus; @@ -474,8 +473,7 @@ public ProviderResponse reinitPassword( .orElseThrow( () -> new UserNotFoundException(config.get(LdapConfigKeys.REALM_NAME), userId)); try { - ldapPoolConnection.modify( - "uid=" + user.getUsername() + "," + config.get(GlobalKeysConfig.USER_SOURCE), mod); + ldapPoolConnection.modify(getUserDN(user.getUsername()), mod); changePasswordResetStatus(userId, changePasswordResetStatus); ProviderResponse response = new ProviderResponse(); response.setStatus(ProviderResponseStatus.OK); @@ -499,8 +497,7 @@ public ProviderResponse initPassword( .orElseThrow( () -> new UserNotFoundException(config.get(LdapConfigKeys.REALM_NAME), userId)); try { - ldapPoolConnection.modify( - "uid=" + user.getUsername() + "," + config.get(GlobalKeysConfig.USER_SOURCE), mod); + ldapPoolConnection.modify(getUserDN(user.getUsername()), mod); changePasswordResetStatus(userId, changePasswordResetStatus); ProviderResponse response = new ProviderResponse(); response.setStatus(ProviderResponseStatus.OK); @@ -527,9 +524,7 @@ public ProviderResponse changePassword( try { PasswordModifyExtendedRequest pmer = new PasswordModifyExtendedRequest( - "uid=" + user.getUsername() + "," + config.get(GlobalKeysConfig.USER_SOURCE), - oldPassword, - newPassword); + getUserDN(user.getUsername()), oldPassword, newPassword); ExtendedResult result = ldapPoolConnection.processExtendedOperation(pmer); if (result.getResultCode().intValue() == ResultCode.INVALID_CREDENTIALS_INT_VALUE @@ -563,8 +558,7 @@ private ProviderResponse changePasswordResetStatus(String userId, boolean isRese .orElseThrow( () -> new UserNotFoundException(config.get(LdapConfigKeys.REALM_NAME), userId)); try { - ldapPoolConnection.modify( - "uid=" + user.getUsername() + "," + config.get(GlobalKeysConfig.USER_SOURCE), mod); + ldapPoolConnection.modify(getUserDN(user.getUsername()), mod); ProviderResponse response = new ProviderResponse(); response.setStatus(ProviderResponseStatus.OK); response.setEntityId(user.getUsername()); diff --git a/sugoi-api-ldap-utils/src/main/java/fr/insee/sugoi/ldap/utils/config/LdapConfigKeys.java b/sugoi-api-ldap-utils/src/main/java/fr/insee/sugoi/ldap/utils/config/LdapConfigKeys.java index 3d9abc08..beea0d98 100644 --- a/sugoi-api-ldap-utils/src/main/java/fr/insee/sugoi/ldap/utils/config/LdapConfigKeys.java +++ b/sugoi-api-ldap-utils/src/main/java/fr/insee/sugoi/ldap/utils/config/LdapConfigKeys.java @@ -26,6 +26,11 @@ public enum LdapConfigKeys implements RealmConfigKeys { GROUP_SOURCE_PATTERN("groupSourcePattern"), GROUP_MANAGER_SOURCE_PATTERN("group_manager_source_pattern"), GROUP_FILTER_PATTERN("groupFilterPattern"), + ORGANIZATION_DN_PATTERN("organization_dn_pattern"), + USER_DN_PATTERN("user_dn_pattern"), + ADDRESS_DN_PATTERN("address_dn_pattern"), + GROUP_DN_PATTERN("group_dn_pattern"), + APPLICATION_DN_PATTERN("application_dn_pattern"), REALM_NAME("realm_name"), VLV_ENABLED("vlvEnabled"), SORT_KEY("sortKey"), diff --git a/sugoi-api-ldap-utils/src/main/java/fr/insee/sugoi/ldap/utils/mapper/GenericLdapMapper.java b/sugoi-api-ldap-utils/src/main/java/fr/insee/sugoi/ldap/utils/mapper/GenericLdapMapper.java index f3d1f04b..e8e3fa43 100644 --- a/sugoi-api-ldap-utils/src/main/java/fr/insee/sugoi/ldap/utils/mapper/GenericLdapMapper.java +++ b/sugoi-api-ldap-utils/src/main/java/fr/insee/sugoi/ldap/utils/mapper/GenericLdapMapper.java @@ -217,25 +217,19 @@ private static Attribute transformSugoiToAttribute( case ORGANIZATION: return new Attribute( ldapAttributeName, - String.format( - "%s=%s,%s", - // TODO should be a param - "uid", - // - ((Organization) sugoiValue).getIdentifiant(), - config.get(GlobalKeysConfig.ORGANIZATION_SOURCE))); + config + .get(LdapConfigKeys.ORGANIZATION_DN_PATTERN) + .replace("{id}", ((Organization) sugoiValue).getIdentifiant()) + .replace("{source}", config.get(GlobalKeysConfig.ORGANIZATION_SOURCE))); case ADDRESS: if (((PostalAddress) sugoiValue).getId() != null && config.get(GlobalKeysConfig.ADDRESS_SOURCE) != null) { return new Attribute( ldapAttributeName, - String.format( - "%s=%s,%s", - // TODO should be a param - "l", - // - ((PostalAddress) sugoiValue).getId(), - config.get(GlobalKeysConfig.ADDRESS_SOURCE))); + config + .get(LdapConfigKeys.ADDRESS_DN_PATTERN) + .replace("{id}", ((PostalAddress) sugoiValue).getId()) + .replace("{source}", config.get(GlobalKeysConfig.ADDRESS_SOURCE))); } else return null; case LIST_HABILITATION: // Assume that if list is empty it's really to set an empty list (delete all @@ -253,7 +247,10 @@ private static Attribute transformSugoiToAttribute( // Assume that if list is empty it's really to set an empty list (delete all // values) // Groups list can be set null if not needed - String genericGroup = "cn={group}" + "," + config.get(LdapConfigKeys.GROUP_SOURCE_PATTERN); + String genericGroup = + config + .get(LdapConfigKeys.GROUP_DN_PATTERN) + .replace("{source}", config.get(LdapConfigKeys.GROUP_SOURCE_PATTERN)); return new Attribute( ldapAttributeName, @@ -263,7 +260,7 @@ private static Attribute transformSugoiToAttribute( group -> genericGroup .replace("{appliname}", group.getAppName()) - .replace("{group}", group.getName())) + .replace("{id}", group.getName())) .collect(Collectors.toList())); case LIST_STRING: // Assume that if list is empty it's really to set an empty list (delete all diff --git a/sugoi-api-ldap-utils/src/test/java/fr/insee/sugoi/ldap/utils/mapper/OrganizationLdapMapperFromObjectTest.java b/sugoi-api-ldap-utils/src/test/java/fr/insee/sugoi/ldap/utils/mapper/OrganizationLdapMapperFromObjectTest.java index 0a314169..e35056b7 100644 --- a/sugoi-api-ldap-utils/src/test/java/fr/insee/sugoi/ldap/utils/mapper/OrganizationLdapMapperFromObjectTest.java +++ b/sugoi-api-ldap-utils/src/test/java/fr/insee/sugoi/ldap/utils/mapper/OrganizationLdapMapperFromObjectTest.java @@ -17,6 +17,7 @@ import com.unboundid.ldap.sdk.Attribute; import fr.insee.sugoi.core.configuration.GlobalKeysConfig; +import fr.insee.sugoi.ldap.utils.config.LdapConfigKeys; import fr.insee.sugoi.model.Organization; import fr.insee.sugoi.model.PostalAddress; import fr.insee.sugoi.model.RealmConfigKeys; @@ -41,6 +42,8 @@ public void setup() { Map config = new HashMap<>(); config.put(GlobalKeysConfig.ORGANIZATION_SOURCE, "ou=organisations,o=insee,c=fr"); config.put(GlobalKeysConfig.ADDRESS_SOURCE, "ou=address,o=insee,c=fr"); + config.put(LdapConfigKeys.ORGANIZATION_DN_PATTERN, "uid={id},{source}"); + config.put(LdapConfigKeys.ADDRESS_DN_PATTERN, "l={id},{source}"); organizationLdapMapper = new OrganizationLdapMapper(config, StoreMappingFixture.getOrganizationStoreMappings()); diff --git a/sugoi-api-ldap-utils/src/test/java/fr/insee/sugoi/ldap/utils/mapper/UserLdapMapperFromObjectTest.java b/sugoi-api-ldap-utils/src/test/java/fr/insee/sugoi/ldap/utils/mapper/UserLdapMapperFromObjectTest.java index 6af90256..36b538e6 100644 --- a/sugoi-api-ldap-utils/src/test/java/fr/insee/sugoi/ldap/utils/mapper/UserLdapMapperFromObjectTest.java +++ b/sugoi-api-ldap-utils/src/test/java/fr/insee/sugoi/ldap/utils/mapper/UserLdapMapperFromObjectTest.java @@ -42,6 +42,8 @@ public void setup() { config.put(GlobalKeysConfig.ORGANIZATION_SOURCE, "ou=organisations,o=insee,c=fr"); config.put(GlobalKeysConfig.ADDRESS_SOURCE, "ou=address,o=insee,c=fr"); config.put(LdapConfigKeys.USER_OBJECT_CLASSES, "top,person"); + config.put(LdapConfigKeys.ORGANIZATION_DN_PATTERN, "uid={id},{source}"); + config.put(LdapConfigKeys.ADDRESS_DN_PATTERN, "l={id},{source}"); userLdapMapper = new UserLdapMapper(config, StoreMappingFixture.getUserStoreMappings());