Store multiple passwords for managed users
This sample demonstrates how to set up multiple passwords for managed users and how to synchronize separate passwords to different external resources.
|
You cannot run this sample through the admin UI. To make the sample work with the admin UI, set the
|
Configure the multiple passwords sample
This sample assumes the following scenario:
-
The managed/user repository is the source system.
-
There are two target LDAP servers—
ldapandldap2.For the purposes of this sample, the two servers are represented by two separate organizational units on a single PingDS (DS) instance.
-
Managed user objects have two additional password fields, each mapped to one of the two LDAP servers.
-
Both LDAP servers have a requirement for a password history policy, but the history size differs for the two policies.
The sample shows how to extend the password history policy described in Password history policy to apply to multiple password fields.
-
The value of a managed user’s
passwordfield is used by default for the additional passwords unless the CREATE, UPDATE, or PATCH requests on the managed user explicitly contain a value for these additional passwords.
The sample includes several customized configuration files in the samples/multiple-passwords/conf/ directory. These customizations are crucial to the sample functionality and are described in detail in the following list:
provisioner.openicf-ldap.json-
Configures the connection to the first LDAP directory.
provisioner.openicf-ldap2.json-
Configures the connection to the second LDAP directory.
sync.json-
Provides the mappings from the IDM managed user repository to the respective LDAP servers. The file includes two mappings:
-
A mapping from IDM managed users to the LDAP user objects at the
system/ldap/accountendpoint. This endpoint represents theou=Peoplesubtree. -
A mapping from IDM managed users to the LDAP user objects at the
system/ldap2/accountendpoint. This endpoint represents theou=Customerssubtree.
Both mappings include an explicit mapping from
ldapPasswordandldap2PasswordtouserPasswordin the standard property mappings. Because these passwords are encrypted, a transform script is defined which usesopenidm.decrypt()to set the value on the target object. -
managed.json-
Contains a customized schema for managed users that includes the additional password fields.
This file has been customized as follows:
-
The schema includes an
ldapPasswordfield that is mapped to the accounts in thesystem/ldap/accountstarget. This field is subject to the standard policies associated with thepasswordfield of a managed user. In addition, theldapPasswordmust contain two capital letters instead of the usual one capital letter requirement. -
The schema includes an
ldap2Passwordfield that is mapped to the accounts in thesystem/ldap2/accountstarget. This field is subject to the standard policies associated with thepasswordfield of a managed user. In addition, theldap2Passwordmust contain two numbers instead of the usual one number requirement. -
A custom password history policy (
"policyId" : "is-new") applies to each of the two mapped password fieldsldapPassword, andldap2Password.
-
router.json-
A scripted filter on
managed/userandpolicy/managed/userthat populates the values of the additional password fields with the value of the mainpasswordfield if the additional fields are not included in the request content.
The sample includes the following customized scripts in the script directory:
-
onCreate-user-custom.jsandonUpdate-user-custom.jsare used for validation of the password history policy when a user is created or updated. -
pwpolicy.jsis an additional policy script for the password history policy. -
set-additional-passwords.jspopulates the values of the additional password fields with the value of the mainpasswordfield if the additional fields are not included in the request content.
Password history policy
The sample includes a custom password history policy. Although the sample demonstrates the history of password attributes only, you can use this policy to enforce history validation on any managed object property.
The following configuration changes set up the password history policy:
-
A
fieldHistoryproperty is added to managed users. The value of this field is a map of field names to a list of historical values for that field. These lists of values are used by the policy to determine if a new value has previously been used.The
fieldHistoryproperty is not accessible over REST by default, and cannot be modified. -
The
onCreate-user-custom.jsscript performs the standardonCreatetasks for a managed user object but also stores the initial value of each of the fields for which IDM should keep a history. The script is passed the following configurable properties:historyFieldsa list of the fields to store history on.
historySizethe number of historical fields to store.
-
The
onUpdate-user-custom.jsscript compares the old and new values of the historical fields on update events to determine if the values have changed. When a new value is detected, it is stored in the list of historical values for that field.This script also contains logic to deal with the comparison of encrypted field values. The script is passed the following configurable properties:
historyFieldsa list of the fields to store history on.
historySizethe number of historical fields to store.
-
The
pwpolicy.jsscript contains the additional policy definition for the password history policy. This script compares the new field value with the list of historical values for each field.The policy configuration (
policy.json) references this script in itsadditionalFileslist, so that the policy service loads the policy definition. The new policy takes ahistoryLengthparameter, which indicates the number of historical values to enforce the policy on. This number must not exceed thehistorySizespecified in theonCreateandonUpdatescripts. -
The
ldapPasswordandldap2Passwordfields in the managed user schema have been updated with the policy. For the purposes of this sample thehistorySizehas been set to 2 forldapPasswordand to 4 forldap2Password.
LDAP server configuration
-
Set up DS using
/path/to/openidm/samples/multiple-passwords/data/Example.ldif. -
Perform an
ldapsearchon the LDAP directory, and take note of the organizational units:/path/to/opendj/bin/ldapsearch \ --port 1636 \ --useSSL \ --usePkcs12TrustStore /path/to/opendj/config/keystore \ --trustStorePassword:file /path/to/opendj/config/keystore.pin \ --hostname localhost \ --baseDN "dc=example,dc=com" \ --bindDN uid=admin \ --bindPassword password \ "ou=*" \ ou dn: ou=People,dc=example,dc=com ou: People dn: ou=Customers,dc=example,dc=com ou: people ou: Customers
The organizational units,
ou=Peopleandou=Customers, represent the two different target LDAP systems that our mappings point to.
Show multiple accounts
This section starts IDM with the sample configuration, then creates a user with multiple passwords, adhering to the different policies in the configured password policy. The section tests that the user was synchronized to two separate LDAP directories, with the different required passwords, and that the user can bind to each of these LDAP directories.
-
Prepare IDM as described in Prepare IDM, then start the server with the configuration for the multiple passwords sample:
cd /path/to/openidm/ ./startup.sh -p samples/multiple-passwords
-
Create a user, jdoe, providing individual values for each of the different password fields, that comply with the three different password policies:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --header "Content-Type: application/json" \ --request POST \ --data '{ "userName": "jdoe", "givenName": "John", "sn": "Doe", "displayName": "John Doe", "mail": "john.doe@example.com", "password": "Secretpw1", "ldapPassword": "S3cretPw", "ldap2Password": "Secr3tpw1" }' \ "http://localhost:8080/openidm/managed/user?_action=create" { "_id": "5ce188f6-252b-429e-aad1-4d8754d77de5", "_rev": "00000000d2d76089", "userName": "jdoe", "givenName": "John", "sn": "Doe", "displayName": "John Doe", "mail": "john.doe@example.com", "ldapPassword": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "lkackh...", "data": "T0mljk...", "keySize": 16, "purpose": "idm.password.encryption", "iv": "ehSMbdNn...", "mac": "PssPOsW..." } } }, "ldap2Password": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "lSzMTU54...", "data": "UWlQo5Ws...", "keySize": 16, "purpose": "idm.password.encryption", "iv": "ehSMbdN...", "mac": "PssPOs..." } } }, "accountStatus": "active", "effectiveRoles": [], "effectiveAssignments": [], "roles": [] }The user has been created with three different passwords that comply with three distinct password policies. The passwords have been encrypted as defined in the
managed.jsonfile.In this example, the user has been created with ID 5ce188f6-252b-429e-aad1-4d8754d77de5. You will need the user ID when you update the entry later in this procedure. -
As a result of implicit synchronization, two separate LDAP accounts should have been created for user jdoe on our two simulated LDAP servers. For more information about implicit synchronization, refer to Synchronization types.
-
Query the IDs in the LDAP directory as follows:
curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --request GET \ "http://localhost:8080/openidm/system/ldap/account?_queryId=query-all-ids" { "result": [ { "_id": "00452010-a164-4065-9f84-3e4636a3ee20", }, { "_id": "e5b35587-2d7c-4faa-b3e5-962f5a4ada5c", } ], ... }jdoe has two entries—one in
ou=Peopleand one inou=Customers. -
To verify the passwords propagated correctly, perform an LDAP search, bound using each of the jdoe accounts, against the rootDSE.
For the following commands, make sure to enter 2 or 3 at the following prompt:
Do you trust this server certificate? 1) No 2) Yes, for this session only 3) Yes, also add it to a truststore 4) View certificate details Enter choice [1]: 2
/path/to/opendj/bin/ldapsearch \ --hostname localhost \ --port 1636 \ --useSSL \ --bindDN uid=jdoe,ou=People,dc=example,dc=com \ --bindPassword S3cretPw \ --searchScope base \ --baseDN "" "(objectClass=*)" dn: objectClass: top objectClass: ds-root-dse
/path/to/opendj/bin/ldapsearch \ --hostname localhost \ --port 1636 \ --useSSL \ --bindDN uid=jdoe,ou=Customers,dc=example,dc=com \ --bindPassword Secr3tpw1 \ --searchScope base \ --baseDN "" "(objectClass=*)" dn: objectClass: top objectClass: ds-root-dse
-
Patch jdoe’s managed user entry (
5ce188f6-252b-429e-aad1-4d8754d77de5) to change hisldapPassword:curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --header "Content-Type: application/json" \ --request PATCH \ --data '[ { "operation": "replace", "field": "ldapPassword", "value": "TTestw0rd" } ]' \ "http://localhost:8080/openidm/managed/user/5ce188f6-252b-429e-aad1-4d8754d77de5" { "_id": "5ce188f6-252b-429e-aad1-4d8754d77de5", "_rev": "000000001298f6a6", "userName": "jdoe", "givenName": "John", "sn": "Doe", "displayName": "John Doe", ... "ldapPassword": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "Vlco8e...", "data": "INj9lk...", "keySize": 16, "purpose": "idm.password.encryption", "iv": "ehSMbdNn...", "mac": "PssPOsW..." } } }, ... } -
To verify the password change propagated correctly, perform an LDAP search, bound using jdoe from the People organizational unit, against the rootDSE.
For the following command, make sure to enter
2or3at the following prompt:Do you trust this server certificate? 1) No 2) Yes, for this session only 3) Yes, also add it to a truststore 4) View certificate details Enter choice [1]: 2
/path/to/opendj/bin/ldapsearch \ --hostname localhost \ --port 1636 \ --useSSL \ --bindDN uid=jdoe,ou=People,dc=example,dc=com \ --bindPassword TTestw0rd \ --searchScope base \ --baseDN "" "(objectClass=*)" dn: objectClass: top objectClass: ds-root-dse
Show the password history policy
This section demonstrates the password history policy by patching jdoe’s managed user entry, changing his ldapPassword multiple times.
-
Send the following patch requests, changing the value of jdoe’s
ldapPasswordeach time:curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --header "Content-Type: application/json" \ --request PATCH \ --data '[ { "operation": "replace", "field": "ldapPassword", "value": "TTestw0rd1" } ]' \ "http://localhost:8080/openidm/managed/user/5ce188f6-252b-429e-aad1-4d8754d77de5" { "_id": "5ce188f6-252b-429e-aad1-4d8754d77de5", "_rev": "00000000a92657c7", "userName": "jdoe", "givenName": "John", "sn": "Doe", "displayName": "John Doe", "mail": "john.doe@example.com", ... "ldapPassword": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "TjolL7...", "data": "Unbalo...", "keySize": 16, "purpose": "idm.password.encryption", "iv": "ehSMbdNn...", "mac": "PssPOsW..." } } }, ... }curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --header "Content-Type: application/json" \ --request PATCH \ --data '[ { "operation": "replace", "field": "ldapPassword", "value": "TTestw0rd2" } ]' \ "http://localhost:8080/openidm/managed/user/5ce188f6-252b-429e-aad1-4d8754d77de5" { "_id": "5ce188f6-252b-429e-aad1-4d8754d77de5", "_rev": "00000000dc6160c8", "userName": "jdoe", "givenName": "John", "sn": "Doe", "displayName": "John Doe", ... "ldapPassword": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "Ynio9n...", "data": "R0ol2b...", "keySize": 16, "purpose": "idm.password.encryption", "iv": "ehSMbdNn...", "mac": "PssPOsW..." } } }, ... }curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --header "Content-Type: application/json" \ --request PATCH \ --data '[ { "operation": "replace", "field": "ldapPassword", "value": "TTestw0rd3" } ]' \ "http://localhost:8080/openidm/managed/user/5ce188f6-252b-429e-aad1-4d8754d77de5" { "_id": "5ce188f6-252b-429e-aad1-4d8754d77de5", "_rev": "00000000a92657c7", "userName": "jdoe", "givenName": "John", "sn": "Doe", "displayName": "John Doe", ... "ldapPassword": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "9kilajT...", "data": "Hnkja98...", "keySize": 16, "purpose": "idm.password.encryption", "iv": "ehSMbdNn...", "mac": "PssPOsW..." } } }, ... }User jdoe now has a history of
ldapPasswordvalues, that containsTTestw0rd3,TTestw0rd2,TTestw0rd1, andTTestw0rd, in that order. -
The history size for the
ldapPasswordpolicy is set to 2. To demonstrate the history policy, attempt to patch jdoe’s entry with a password value that was used in his previous 2 password changes:TTestw0rd2:curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --header "Content-Type: application/json" \ --request PATCH \ --data '[ { "operation": "replace", "field": "ldapPassword", "value": "TTestw0rd2" } ]' \ "http://localhost:8080/openidm/managed/user/5ce188f6-252b-429e-aad1-4d8754d77de5" { "code": 403, "reason": "Forbidden", "message": "Failed policy validation", "detail": { "result": false, "failedPolicyRequirements": [ { "policyRequirements": [ { "policyRequirement": "IS_NEW" } ], "property": "ldapPassword" } ] } }The password change fails the
IS_NEWpolicy requirement. -
Change jdoe’s
ldapPasswordto a value not used in the previous two updates:curl \ --header "X-OpenIDM-Username: openidm-admin" \ --header "X-OpenIDM-Password: openidm-admin" \ --header "Accept-API-Version: resource=1.0" \ --header "Content-Type: application/json" \ --request PATCH \ --data '[ { "operation": "replace", "field": "ldapPassword", "value": "TTestw0rd" } ]' \ "http://localhost:8080/openidm/managed/user/5ce188f6-252b-429e-aad1-4d8754d77de5" { "_id": "5ce188f6-252b-429e-aad1-4d8754d77de5", "_rev": "00000000792afa08", "userName": "jdoe", "givenName": "John", "sn": "Doe", "displayName": "John Doe", ... "ldapPassword": { "$crypto": { "type": "x-simple-encryption", "value": { "cipher": "AES/CBC/PKCS5Padding", "stableId": "openidm-sym-default", "salt": "Ivmal5...", "data": "0mkywe...", "keySize": 16, "purpose": "idm.password.encryption", "iv": "ehSMbdNn...", "mac": "PssPOsW..." } } }, ... }The password change succeeds.