ForgeRock Identity Platform 5.0 docs

By now you have probably read the news about the ForgeRock Identity Platform 5.0 release.

This major platform update comes with many documentation changes and improvements:

Hope you have no trouble finding what you need.

This blog post was first published @, included here with permission.

ForgeRock Identity Platform 5.0 docs

ForgeRock Logo By now you have probably read the news about the ForgeRock Identity Platform 5.0 release.

This major platform update comes with many documentation changes and improvements:

Hope you have no trouble finding what you need.

OpenDJ: Configuration over REST

ForgeRock LogoIn recent builds of OpenDJ directory server, the REST to LDAP configuration changed quite a bit… for the better.

The draft release notes tell only part of the story:

The changes let you configure multiple endpoints each with multiple versions, resource type inheritance, subresource definitions, and protection with OAuth 2.0. This version of REST to LDAP also brings many minor improvements.

A really cool “minor” improvement is that you can now configure OpenDJ directory server over HTTP. In the draft Admin Guide, you can also find a procedure titled, To Set Up REST Access to Administrative Data.

tl;dr—Directory administrators can configure the server over REST through the /admin/config endpoint, and can read monitoring info under the /admin/monitor endpoint.

Important note: Before you go wild writing a whole new OpenDJ web-based console as a single-page app, keep in mind that the REST to LDAP implementation is still an Evolving interface, so incompatible changes can happen even in minor releases.

Here’s one example using /admin/config:

# This example demonstrates 
# using the /admin/config endpoint
# to create a password policy
# as a directory administrator
# who is also a regular user.
# This requires a nightly build or release 
# from no earlier than late June 2016.
# In order to get this working,
# first set up OpenDJ directory server
# with data from Example.ldif,
# and enable the HTTP connection handler.

# Give Kirsten Vaughan the right
# to read/write the server configuration.
# This command updates privileges, 
# which are explained in the Admin Guide:
 --port 1389 
 --bindDN "cn=Directory Manager" 
 --bindPassword password
dn: uid=kvaughan,ou=People,dc=example,dc=com
changetype: modify
add: ds-privilege-name
ds-privilege-name: config-read
add: ds-privilege-name
ds-privilege-name: config-write

# Give Kirsten access to write password policies.
# This command adds a global ACI.
# Global ACIs are explained in the Admin Guide:
 --port 4444 
 --bindDN "cn=Directory Manager" 
 --bindPassword "password" 
 --add global-aci:"(target="ldap:///cn=Password Policies,cn=config")(targetscope="subtree")(targetattr="*")(version 3.0; acl "Manage password policies"; allow (all) userdn="ldap:///uid=kvaughan,ou=People,dc=example,dc=com";)" 

# Server config-based password policies
# are under the container entry
# /admin/config/password-policies.
# This corresponds to 
# cn=Password Policies,cn=config in LDAP.
# The following are standard common REST operations.
# Common REST is explained and demonstrated
# in the OpenDJ Server Dev Guide.
# In production, of course,
# use HTTPS (as described in the Admin Guide).

# Add a new password policy:
 --user kvaughan:bribery 
 --request POST 
 --header "Content-Type: application/json" 
 --data '{
    "_id": "New Account Password Policy",
    "_schema": "password-policy",
    "password-attribute": "userPassword",
    "force-change-on-add": true,
    "default-password-storage-scheme": "Salted SHA-1"

# Read the new password policy:
# curl --user kvaughan:bribery

# An exercise for the reader:
# Figure out how to set a user's pwd policy over REST.

If you have not yet learned how to use commons REST and OpenDJ REST to LDAP, have a look at the Server Dev Guide chapter, Performing RESTful Operations.

OpenDJ: Uploading a photo over REST

ForgeRock LogoOpenDJ REST to LDAP is based on ForgeRock common REST. ForgeRock common REST handles HTTP multipart requests, so you can upload binary data, such as JPEG photos. This blog entry demonstrates how to upload a JPEG photo as a patch using the curl command.

Before you start, set up OpenDJ server and configure HTTP access to sample directory data, as described in the OpenDJ server dev guide chapter on Performing RESTful Operations. To follow the example below, make sure you have imported data from Example.ldif and that you can read Babs’s resource over HTTP as shown in the documentation.

As of this writing, the default configuration for OpenDJ REST to LDAP does not include a mapping for a user’s jpegPhoto attribute, so you must add a mapping. To add the mapping, edit the "/users" attributes section in the configuration file /path/to/opendj/config/http-config.json to include a "photo" property in user resources. The following line adds a simple mapping from the "photo" property to the jpegPhoto LDAP attribute:

"photo" : { "simple" : { "ldapAttribute" : "jpegPhoto", "isSingleValued" : true } },

When finished, save your changes and restart OpenDJ server or at least the HTTP connection handler to have the connection handler reread the updated configuration file:

/path/to/opendj/bin/stop-ds --restart

To test your changes, copy a JPEG photo to picture.jpg in the current directory and patch Babs’s resource to add the photo:

 --request PATCH 
 --form 'json=[{"operation": "add", "field": "/photo", 
         "value": {"$ref":"cid:picture#content"}}];type=application/json' 
 --form 'picture=@picture.jpg;type=image/jpeg' 

The result of the operation should be Babs’s resource with the photo a (potentially very long) base64-encoded string:

	"_id": "bjensen",
	"_rev": "00000000160694db",
	"schemas": ["urn:scim:schemas:core:1.0"],
	"userName": "",
	"displayName": "Barbara Jensen",
	"name": {
		"givenName": "Barbara",
		"familyName": "Jensen"
	"photo": "_9j_4RZJRXhpZg...AA",
	"manager": [{
		"_id": "trigden",
		"displayName": "Torrey Rigden"
	"contactInformation": {
		"telephoneNumber": "+1 408 555 1862",
		"emailAddress": ""
	"meta": {
		"lastModified": "2016-03-22T09:13:23Z"

Notice the curl command form data. When you specify the reference to the content ID, the reference takes the form:


If you want other attributes to hold the filename (picture.jpg) and MIME type (image/jpeg) of the file you upload, you can reference those as well. In the example above, {"$ref":"cid:picture#filename"} gets picture.jpg and {"$ref":"cid:picture#mimetype"} gets image/jpeg.

ForgeRock Common Audit

ForgeRock LogoCommon Audit is another new feature of the ForgeRock platform.

Common Audit is part of the platform-wide infrastructure: a framework to handle audit events using common audit event handlers that are plugged in to the individual products. The handlers record events, logging them for example into files, relational databases, or syslog. Because handlers are pluggable, new handlers can be added to interoperate with your systems that store and analyze audit data.

Each audit event is identified by a unique transaction ID. The IDs can be communicated across the products and recorded for each local event. The transaction ID is the means to track requests as they traverse the platform.

In the current platform, configuring handlers depends on the product. So there are several places in the docs to read about how to configure Common Audit:

In addition, if you want to get the source code for Common Audit, or are interested in trying out new handlers and developments, you can find it on the ForgeRock Stash server. Right now it is in the forgerock-audit git repository. (To access most code on the ForgeRock Stash server, sign in with your ForgeRock credentials. You can sign up if you have not done so.)

What’s new in the ForgeRock platform release

ForgeRock LogoPerhaps you have read yesterday’s news about ForgeRock launching the updated identity platform.

Those of us who spent the last year working on this update are proud of all the new capabilities, from the integration achieved with common components to the depth and breadth of new features across all the products in the platform.

Looking for detailed lists of what’s new? Here are some quick links to each of the products’ release notes:

I’ll drill down on some of those in future posts.

OpenDJ: LDAP Controls

OpenDJ LogoLDAP controls are a standard mechanism for extending basic LDAP operations. For example, you can use a control to ask the LDAP server to sort search results before returning them, or to return search results a few at a time.

OpenDJ directory server supports a fairly long list of controls. Let’s take a look at three of them.

“Only do this if…”

The Assertion Control tells the directory server only to process the operation if a specified assertion is true for the target entry. You can specify the assertion as a filter to match.

As an example, let’s replace Babs Jensen’s street address, but only if it is the one we are expecting. Notice the assertion filter passed to the ldapmodify request. If Babs’s street address is not “500 3rd Street”, the request does not have an effect:

$ ldapmodify 
> --port 1389 
> --bindDN uid=kvaughan,ou=people,dc=example,dc=com 
> --bindPassword bribery 
> --assertionFilter "(street=500 3rd Street)"
dn: uid=bjensen,ou=people,dc=example,dc=com
changetype: modify
replace: street
street: 33 New Montgomery Street

Processing MODIFY request for uid=bjensen,ou=people,dc=example,dc=com
MODIFY operation successful for DN uid=bjensen,ou=people,dc=example,dc=com

“Make the modification, and shut up”

The Permissive Modify Control is handy when you want to make a modification no matter what. It lets you add an attribute that already exists, or delete one that is already gone without getting an error.

As an example, let’s make sure user.0 is a member of a big static group. It doesn’t matter whether user.0 was already a member, but if not, we want to make sure user.0 is added to the group.

$ ldapmodify 
>  --port 1389 
>  --bindDN uid=user.1,ou=people,dc=example,dc=com 
>  --bindPassword password 
>  --control 1.2.840.113556.1.4.1413
dn: cn=Static,ou=Groups,dc=example,dc=com
changetype: modify
add: member
member: uid=user.0,ou=people,dc=example,dc=com

Processing MODIFY request for cn=Static,ou=Groups,dc=example,dc=com
MODIFY operation successful for DN cn=Static,ou=Groups,dc=example,dc=com

“Delete the children, too”

The Subtree Delete Control lets you delete an entire branch of entries.

As an example, let’s delete ou=Groups,dc=example,dc=com and any groups underneath. The user doing this needs an access to use the tree delete control, as in aci: (targetcontrol="1.2.840.113556.1.4.805") (version 3.0; acl "Tree delete"; allow(all) userdn ="ldap:///uid=user.1,ou=people,dc=example,dc=com";).

$ ldapdelete 
>  --port 1389 
>  --bindDN uid=user.1,ou=people,dc=example,dc=com 
>  --bindPassword password 
>  --deleteSubtree 
>  ou=Groups,dc=example,dc=com
Processing DELETE request for ou=Groups,dc=example,dc=com

DELETE operation successful for DN ou=Groups,dc=example,dc=com

As mentioned above, OpenDJ directory server supports many LDAP controls. So does OpenDJ LDAP SDK. If you want to use one in your application, see the Dev Guide chapter on Working With Controls.

OpenDJ directory server & groups

OpenDJ Logo When you think about groups in general, you realize there are different ways to define groups.

You can define a group by:

  • Specifying attributes that all members have in common (“All the software developers in Grenoble”).
  • Listing the members (“Alice, Bob, Carol”).
  • Using a mix of both (all the software developers in Grenoble, plus Alice, Bob, and Carol).

Notice that when you define a group by attributes of its members, the size of the definition is not a function of the size of the group. “All the software developers in Grenoble” remains the same size, even if the number of software developers in Grenoble varies.

Also notice that when you define a group by listing its members, the size of the definition depends mainly on the size of the group. “Alice, Bob, Carol” is about half the size of “Alice, Bob, Carol, Dan, Elena, Francis.”

OpenDJ directory server has groups of each of these types.

  • OpenDJ represents groups as directory entries. The group entry’s objectClass tells you what type of group it is.
  • Dynamic groups work by specifying attributes that all members have in common. They identify members by LDAP URLs, as in memberUrl: ldap:///ou=Developers,ou=People,dc=example,dc=com??sub?l=Grenoble. This is like specifying members by LDAP search, as in ldapsearch --baseDN ou=Product Development,ou=People,dc=example,dc=com "(l=Grenoble)".
    Dynamic group entries have objectClass: groupOfUrls. Dynamic group entries generally remain small even when they have lots of members.
  • Static groups list each member. Static group entries can have several object classes in OpenDJ, including groupOfNames, groupOfUniqueNames, and groupOfEntries.
    For groups with objectClass: groupOfNames and objectClass: groupOfEntries the member attribute is member, as in

    member: uid=Alice,ou=People,dc=example,dc=com
    member: uid=Bob,ou=People,dc=example,dc=com
    member: uid=Carol,ou=People,dc=example,dc=com
    member: cn=Grenoble Developers,ou=Groups,dc=example,dc=com

    As you would expect, static group entries get larger as you add members.

    You can nest groups, too, by adding a group as a member of a group. (For instance, the last member in the example is the Grenoble Developers group.)

    When you delete an entry or rename an entry, you no longer want it referenced in static groups. OpenDJ has a referential integrity plugin to handle this.

  • When you look up a dynamic group in OpenDJ what you get is the memberUrl value. This is fine if you are ready to use the LDAP URL, but not so great if you just wanted a list of the member entries. OpenDJ therefore provides what are called virtual static groups. Virtual static groups have OpenDJ look up the members of a dynamic group, and then return the list when you read the entry.

Checking group membership

In general, avoid reading and writing whole static group entries, because static groups can be large. Why read an entire 1-million-member static group entry over the network when all you want is to do is check membership for one or two entries?

To make it easy to check group membership, OpenDJ provides a virtual attribute, isMemberOf. If you explicitly request isMemberOf with a search, the values OpenDJ returns are the DNs of groups the entry belongs to. Reading isMemberOf is the recommended way to look up group membership.

Updating static groups

You can update a large OpenDJ static group without reading the group, and without checking membership beforehand. Use the Permissive Modify control (--control 1.2.840.113556.1.4.1413).

If you have an application that needs notification of changes to groups, OpenDJ has you covered there as well. Instead of polling group entries, you can use the external change log mechanism. The external change log is related to directory replication.

Where to go from here

This discussion of OpenDJ groups only scratches the surface. To dig a bit deeper, have a look at these sections in the documentation: