ForgeRock Identity Platform 7 — adding a custom schema to your shared DS repository

Mark Nienaber
10 min readOct 29, 2020

ForgeRock Identity Platform 7 allows you to use an external Directory Server (DS) as shared repository between ForgeRock Access Management (AM) and ForgeRock Identity Management (IDM). This deployment means no synchronisation is required between IDM and DS. As illustrated in the ForgeRock Platform Setup Guide, and illustrated below, both IDM and AM talk to the same DS instance.

Shared Identity Store

This is very different from the more traditional architecture where IDM’s repository is a database like MySQL, Oracle or PostgreSQL. In the traditional architecture, changes are synced to AM’s repository in a similar method that IDM talks to all its external sources. For more on IDM synchronisation, see here. This approach is represented below.

Traditional Approach with Separate Identity Stores

Custom Schema

More often than not customers require the underlying identity store, such as ForgeRock Directory Services, to be configured with a custom schema. An example is an organisation named ExampleOrg that sells T-Shirts, to allow all customers to maintain their T-Shirt size as part of their identity.

To meet this requirement, you would create a new custom attribute, for example exampleTShirtSize (because it is not a standard LDAP attribute). This attribute is configured in its own custom objectClass, i.e. examplePerson, with whatever rules meet the organisations requirements. By doing this, an organisation defines the rules for its identities i.e. an organisation may require some mandatory and some optional attributes to be maintained for its customers.

In the use case for this article we have the following requirements:

ExampleOrg should maintain an optional attribute named exampletshirtsize for its customers.

The following describes how to configure the custom schema for the underlying shared DS repository, as well as configuring IDM and AM to set and consume these values.

This article does not detail the setup of DS as a shared repository, it is assumed you have already done this following the instructions here.

All sample files referenced in this article can be found here.

ForgeRock Directory Services

Configure Custom Schema

The first step is to create your custom schema and add to your Directory Server.

Either create your custom schema file named 99-user.ldif or modify the existing file.

You can download a copy of this file from here.

To meet the requirements we will create the following:
New Attribute : exampletshirtsize
New Object Class : examplePerson which contains the optional attribute exampletshirtsize

The contents of the schema file will look similar to this (reminder you can get the file here):

dn: cn=schema
objectclass: top
objectclass: ldapSubentry
objectclass: subschema
cn: schema
attributeTypes: ( exampleTShirtSize-oid NAME ‘exampletshirtsize’ DESC ‘T-Shirt Size of User’ EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications X-SCHEMA-FILE ‘99-user.ldif’ )
objectClasses: ( examplePerson-oid NAME ‘examplePerson’ DESC ‘User Object for Example People’ SUP inetOrgPerson STRUCTURAL MAY ( exampletshirtsize ) X-SCHEMA-FILE ‘99-user.ldif’ )

Place the file in the schema folder of your directory server i.e.

opends/ds_instances/idrepo/opendj/db/schema/99-user.ldif

Restart the directory server.

opends/ds_instances/idrepo/opendj/bin/stop-ds

opends/ds_instances/idrepo/opendj/bin/start-ds

Test Custom Schema in DS

We will test the configuration by adding the custom objectclass and attribute to an existing user.

Search for an existing user:

~/opends/ds_instances/idrepo/opendj/bin/ldapsearch -h idrepo1.example.com -p 2636 -D “uid=am-identity-bind-account,ou=admins,ou=identities” -w password -b ou=people,ou=identities -Z -X objectclass=* dn

dn: fr-idm-uuid=dac4d07c-3a74–4401-b28c-ace4ab8f7a26,ou=people,ou=identities

Now that we have the DN we will prepare an LDIF file to add the custom values.

Create a file named testuser.ldif with similar contents to below. A sample file can be found here.

dn: fr-idm-uuid=dac4d07c-3a74–4401-b28c-ace4ab8f7a26,ou=people,ou=identities
changetype: modify
add: objectClass
objectClass: examplePerson
-
add: exampleTShirtSize
exampleTShirtSize: XXL

Now we import this file into the directory server.

~/opends/ds_instances/config/opendj/bin/ldapmodify -h idrepo1.example.com -p 2636 -D “cn=directory manager” -X -Z -w password -f testuser.ldif

# MODIFY operation successful for DN fr-idm-uuid=dac4d07c-3a74–4401-b28c-ace4ab8f7a26,ou=people,ou=identities

Search the user to make sure the attribute is set correctly.

~/opends/ds_instances/config/opendj/bin/ldapsearch -h idrepo1.example.com -p 2636 -D “uid=am-identity-bind-account,ou=admins,ou=identities” -w password -b ou=people,ou=identities -Z -X objectclass=*
dn: ou=people,ou=identities
objectClass: organizationalunit
objectClass: top
ou: people

dn: fr-idm-uuid=dac4d07c-3a74–4401-b28c-ace4ab8f7a26,ou=people,ou=identities
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
...
objectClass: top
objectClass: examplePerson
cn: testuser testuser
employeeNumber: 0
exampletshirtsize: XXL
...

We now have the underlying directory server configured correctly and are able to add in custom attributes to users.

Next step is to configure IDM, which uses DS as a repository to include these customisations.

ForgeRock Identity Management

When IDM is configured to use DS as a repository, you’ll need to take the following steps:

  1. Configure IDM Repository
  2. Configure IDM Managed Object/s

Configure IDM Repository

IDM uses the repo.ds.json file to define communication with the DS repository. This will need to be modified to include the custom objectClass and attribute.

You will find an example of this file here for reference.

In your openidm/config/repo.ds.json file, browse to the user definition i.e. “managed/user” and add in the examplePerson objectClass:

“managed/user” : {
“dnTemplate” : “ou=people,ou=identities”,
“namingStrategy” : {
“type” : “clientDnNaming”,
“dnAttribute” : “fr-idm-uuid”
},
“nativeId” : false,
objectClasses” : [
“person”,
“organizationalPerson”,
“inetOrgPerson”,
“iplanet-am-user-service”,
……
examplePerson

Then under “properties”, add in exampletshirtsize and point it to your newly created ldap attribute:

properties” : {
“_id” : {
“primaryKey” : true,
“type” : “simple”,
“ldapAttribute” : “fr-idm-uuid”
},
“_rev” : {
“type” : “simple”,
“ldapAttribute” : “etag”
},
“userName” : {
“type” : “simple”,
“ldapAttribute” : “uid”
},
…………
“exampletshirtsize” : {
“type” : “simple”,
“ldapAttribute” : “exampletshirtsize”
}

Configure IDM Managed Object

You can configure managed objects via the managed.json file or using the IDM Admin UI, both of these options are described below, choose one method.

Modify the mananged.json file

An example managed.json file can be found here.

Open the openidm/conf/managed.json file and under the “objects” section, browse to the “user” object, then “schema”. This section lists the attributes / properties of the object, we will add in our new property:

schema” : {
“$schema” : “http://forgerock.org/json-schema#",
“type” : “object”,
“title” : “User”,
“description” : null,
“icon” : “fa-user”,
properties” : {
……
“exampletshirtsize” : {
“title” : “Example T-Shirt Tize”,
“type” : “string”,f
“viewable” : true,
“searchable” : false,
“userEditable” : true
}

Important note: in our example there is no requirement for this attribute to be searchable so no indexing is configured.

To define where this attribute appears on the list of all attributes modify the “order”.In this example I’ve placed it close to the top of the list to make things easier to view in this demo.

order” : [
“_id”,
“userName”,
exampletshirtsize”,

Using the Admin UI

It is also possible to achieve the above step through the IDM Admin UI by browsing to Configure -> Managed Objects -> User -> Add Property. Then set the appropriate values.

Add Property with Admin UI

Once you have completed these steps, restart IDM (This is only necessary as we modified the repo.ds.json file).

Test IDM Repository Configuration

We can test that IDM is correctly configured by browsing to Manage -> User.

The value for Example T-Shirt Size should be visible, lets change the value to Medium.

Custom attribute read from repository.

Press Save twice.

A search of our directory server (or IDM) should result in the updated value:

~/opends/ds_instances/config/opendj/bin/ldapsearch -h idrepo1.example.com -p 2636 -D “uid=am-identity-bind-account,ou=admins,ou=identities” -w password -b ou=people,ou=identities -Z -X objectclass=* exampletshirtsize

dn: fr-idm-uuid=dac4d07c-3a74–4401-b28c-ace4ab8f7a26,ou=people,ou=identities
exampletshirtsize: Medium

IDM is configured correctly, the next step is to modify AM so AM has access to these values as required.

ForgeRock Access Management

AM will need to be modified so when communicating with the Identity Repository, it can read the new objectclass and attribute.

This can be achieved via the AM Admin console, or via REST. Both are demonstrated below, however select one that suits.

Configure AM via AM Admin Console

Browse to your AM console and head to Identity Stores.

AM Identity Store Configuration

Select the Identity Store, i.e. OpenDJ, then select the User Configuration Tab

User Configuration

Add examplePerson in the LDAP User Object Class box.

Custom Object Class

Add exampletshirtsize to the LDAP User Attributes box.

Custom Attribute

Press Save Changes.

Configure AM via REST

If you don’t want to use the UI, and/or you want a repeatable process (recommended) you can configure this via REST. To do this you’ll need to GET the value of the Identity Store, then modify the details to include the custom values.

The following is an example to GET the value of the Identity Store:

curl -X GET “https://openam.example.com:8443/secure/json/realm-config/services/id-repositories/LDAPv3ForOpenDS?_queryFilter=true" -H “accept: application/json” -H “X-Requested-With: SwaggerUI”

AM will return a JSON response with the current contents of the identity store.

Modify the contents and send the amended details back to AM via a PUT request. An example is included below, note the inclusion of the custom objectclass as well as attribute.

curl ‘https://openam.example.com:8443/secure/json/realms/root/realm-config/services/id-repositories/LDAPv3ForOpenDS/OpenDJ' -X PUT -H ‘User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:82.0) Gecko/20100101 Firefox/82.0’ -H ‘Accept: application/json, text/javascript, */*; q=0.01’ -H ‘Accept-Language: en-US’ — compressed -H ‘Content-Type: application/json’ -H ‘Accept-API-Version: protocol=2.0,resource=1.0’ -H ‘X-Requested-With: XMLHttpRequest’ -H ‘Cache-Control: no-cache’ -H ‘Origin: https://openam.example.com:8443' -H ‘Connection: keep-alive’ -H ‘Referer: https://openam.example.com:8443/secure/ui-admin/' -H ‘Cookie: amlbcookie=01; iPlanetDirectoryPro=HDd0swkJu8Rm1EnDWaxA7bDdhaA.*AAJTSQACMDEAAlNLABxSWG1YNTZvb1NXanVwamhWRm1LdUdBd1JLTTA9AAR0eXBlAANDVFMAAlMxAAA.*’ — data-raw ‘{
“_id”:”OpenDJ”,
“groupconfig”:{
“sun-idrepo-ldapv3-config-memberurl”:”memberUrl”,
“sun-idrepo-ldapv3-config-group-attributes”:[
“objectclass”,
“dn”,
“cn”,
“uniqueMember”
],
“sun-idrepo-ldapv3-config-group-container-name”:”ou”,
“sun-idrepo-ldapv3-config-groups-search-filter”:”(objectclass=groupOfUniqueNames)”,
“sun-idrepo-ldapv3-config-uniquemember”:”uniqueMember”,
“sun-idrepo-ldapv3-config-group-objectclass”:[
“top”,
“groupofuniquenames”
],
“sun-idrepo-ldapv3-config-groups-search-attribute”:”cn”,
“sun-idrepo-ldapv3-config-group-container-value”:”groups”
},
“userconfig”:{
“sun-idrepo-ldapv3-config-user-objectclass”:[
“pushDeviceProfilesContainer”,
“oathDeviceProfilesContainer”,
“person”,
“top”,
“webauthnDeviceProfilesContainer”,
“sunFMSAML2NameIdentifier”,
“iPlanetPreferences”,
“iplanet-am-user-service”,
“forgerock-am-dashboard-service”,
“inetuser”,
“kbaInfoContainer”,
“deviceProfilesContainer”,
“sunAMAuthAccountLockout”,
“inetorgperson”,
“iplanet-am-auth-configuration-service”,
“organizationalperson”,
“devicePrintProfilesContainer”,
“iplanet-am-managed-person”,
examplePerson
],
“sun-idrepo-ldapv3-config-createuser-attr-mapping”:[
“cn”,
“sn”
],
“sun-idrepo-ldapv3-config-user-attributes”:[
“kbaInfo”,
“iplanet-am-user-account-life”,
“devicePrintProfiles”,
“userPassword”,
“iplanet-am-user-password-reset-question-answer”,
“iplanet-am-user-admin-start-dn”,
“kbaActiveIndex”,
“createTimestamp”,
“kbaInfoAttempts”,
“sunIdentityMSISDNNumber”,
“cn”,
“iplanet-am-user-login-status”,
“sn”,
“iplanet-am-session-get-valid-sessions”,
“iplanet-am-user-failure-url”,
“dn”,
“authorityRevocationList”,
“webauthnDeviceProfiles”,
“inetUserHttpURL”,
“sun-fm-saml2-nameid-info”,
“mail”,
“sun-fm-saml2-nameid-infokey”,
“iplanet-am-user-success-url”,
“iplanet-am-session-max-session-time”,
“pushDeviceProfiles”,
“caCertificate”,
“employeeNumber”,
“iplanet-am-user-alias-list”,
“sunAMAuthInvalidAttemptsData”,
“adminRole”,
“givenName”,
“assignedDashboard”,
“iplanet-am-user-password-reset-force-reset”,
“push2faEnabled”,
“iplanet-am-session-destroy-sessions”,
“oath2faEnabled”,
“userCertificate”,
“iplanet-am-session-max-idle-time”,
“iplanet-am-user-auth-modules”,
“postalAddress”,
“deviceProfiles”,
“iplanet-am-user-password-reset-options”,
“lastEmailSent”,
“iplanet-am-auth-configuration”,
“inetUserStatus”,
“distinguishedName”,
“oathDeviceProfiles”,
“modifyTimestamp”,
“preferredlanguage”,
“manager”,
“preferredtimezone”,
“iplanet-am-session-service-status”,
“uid”,
“iplanet-am-session-max-caching-time”,
“iplanet-am-session-quota-limit”,
“memberOf”,
“preferredLocale”,
“telephoneNumber”,
“iplanet-am-user-auth-config”,
“objectClass”,
exampletshirtsize
],
“sun-idrepo-ldapv3-config-people-container-value”:”people”,
“sun-idrepo-ldapv3-config-auth-kba-index-attr”:”kbaActiveIndex”,
“sun-idrepo-ldapv3-config-inactive”:”Inactive”,
“sun-idrepo-ldapv3-config-users-search-filter”:”(objectclass=inetorgperson)”,
“sun-idrepo-ldapv3-config-auth-kba-attr”:[
“kbaInfo”
],
“sun-idrepo-ldapv3-config-active”:”Active”,
“sun-idrepo-ldapv3-config-people-container-name”:”ou”,
“sun-idrepo-ldapv3-config-users-search-attribute”:”fr-idm-uuid”,
“sun-idrepo-ldapv3-config-auth-kba-attempts-attr”:[
“kbaInfoAttempts”
],
“sun-idrepo-ldapv3-config-isactive”:”inetuserstatus”
},
“ldapsettings”:{
“openam-idrepo-ldapv3-affinity-enabled”:false,
“sun-idrepo-ldapv3-config-connection_pool_min_size”:1,
“sun-idrepo-ldapv3-config-authid”:”uid=am-identity-bind-account,ou=admins,ou=identities”,
“sun-idrepo-ldapv3-config-organization_name”:”ou=identities”,
“sun-idrepo-ldapv3-config-search-scope”:”SCOPE_ONE”,
“openam-idrepo-ldapv3-heartbeat-interval”:10,
“openam-idrepo-ldapv3-proxied-auth-enabled”:false,
“sun-idrepo-ldapv3-config-time-limit”:10,
“openam-idrepo-ldapv3-behera-support-enabled”:true,
“openam-idrepo-ldapv3-contains-iot-identities-enriched-as-oauth2client”:false,
“sun-idrepo-ldapv3-config-ldap-server”:[
“idrepo1.example.com:2636”
],
“sun-idrepo-ldapv3-config-max-result”:1000,
“sun-idrepo-ldapv3-config-connection-mode”:”LDAPS”,
“sun-idrepo-ldapv3-config-connection_pool_max_size”:10,
“openam-idrepo-ldapv3-proxied-auth-denied-fallback”:false,
“openam-idrepo-ldapv3-heartbeat-timeunit”:”SECONDS”
},
“pluginconfig”:{
“sunIdRepoClass”:”org.forgerock.openam.idrepo.ldap.DJLDAPv3Repo”,
“sunIdRepoSupportedOperations”:[
“group=read,create,edit,delete”,
“user=read,create,edit,delete,service”,
“realm=read,create,edit,delete,service”
],
“sunIdRepoAttributeMapping”:[

]
},
“persistentsearch”:{
“sun-idrepo-ldapv3-config-psearch-filter”:”(!(objectclass=frCoreToken))”,
“sun-idrepo-ldapv3-config-psearch-scope”:”SCOPE_SUB”,
“sun-idrepo-ldapv3-config-psearchbase”:”ou=identities”
},
“authentication”:{
“sun-idrepo-ldapv3-config-auth-naming-attr”:”uid”
},
“cachecontrol”:{
“sun-idrepo-ldapv3-dncache-enabled”:true,
“sun-idrepo-ldapv3-dncache-size”:1500
},
“errorhandling”:{
“com.iplanet.am.ldap.connection.delay.between.retries”:1000
},
“_type”:{
“_id”:”LDAPv3ForOpenDS”,
“name”:”OpenDJ”,
“collection”:true
}
}'

Test AM Configuration

We are not modifying the XUI, so to test this, make a REST call to AM and ensure the values are available.

curl -k ‘https://openam.example.com:8443/secure/json/realms/root/users/dac4d07c-3a74-4401-b28c-ace4ab8f7a26' -H ‘User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:83.0) Gecko/20100101 Firefox/83.0’ -H ‘Accept: application/json, text/javascript, */*; q=0.01’ -H ‘Content-Type: application/json’ -H ‘Accept-API-Version: protocol=2.1,resource=4.0’ -H ‘X-Requested-With: XMLHttpRequest’ -H ‘Cache-Control: no-cache’ -H ‘Connection: keep-alive’ -H ‘Cookie: amlbcookie=01; iPlanetDirectoryPro=o3_uynq8mHxxkzGFmd7k0H85bNM.*AAJTSQACMDEAAlNLABxERXBJTENsblN6cEErSWtxVUdmVGVQdWtIU1k9AAR0eXBlAANDVFMAAlMxAAA.*’ | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1128 100 1128 0 0 11139 0 — : — : — — : — : — — : — : — 11168
{
“_id”: “dac4d07c-3a74–4401-b28c-ace4ab8f7a26”,
“_rev”: “-1”,
“realm”: “/”,
“username”: “testuser”,
“fr-idm-uuid”: [
“dac4d07c-3a74–4401-b28c-ace4ab8f7a26”
],
“telephoneNumber”: [
“99911”
],
“mail”: [
testuser@email.com
],
“givenName”: [
“testuser”
],
“objectClass”: [
“iplanet-am-managed-person”,
“inetuser”,
“inetOrgPerson”,
“sunFMSAML2NameIdentifier”,
“devicePrintProfilesContainer”,
“iplanet-am-user-service”,
“iPlanetPreferences”,
“pushDeviceProfilesContainer”,
examplePerson”,
“forgerock-am-dashboard-service”,
“top”,
“kbaInfoContainer”,
“person”,
“organizationalPerson”,
“oathDeviceProfilesContainer”,
“sunAMAuthAccountLockout”,
“webauthnDeviceProfilesContainer”,
“iplanet-am-auth-configuration-service”,
“fr-idm-managed-user-explicit”
],
“exampletshirtsize”: [
“Medium”
],

“dn”: [
“fr-idm-uuid=dac4d07c-3a74–4401-b28c-ace4ab8f7a26,ou=people,ou=identities”
],

That’s it, you’ve successfully configured your ForgeRock Identity Platform 7 shared repository with a custom schema. Well done!

--

--