Thursday, 30 June 2016

Dynamic Profiles in OpenAM 13

I recently had cause to play with 'Dynamic Profiles' in OpenAMv13.

If you don't know then Dynamic Profile is a realm based configuration setting that can dynamically create a user profile in the configured DataStore.  This can be useful in many circumstances where the authentication of a user takes place in a service other than the DataStore.  Examples of this include using OpenAM as Social Media/Oauth2 client (allowing users to sign in with Facebook), and SAML Service Provider (e.g. allowing users to sign in with Federated credentials).  Having authenticated the credentials it now might be useful to maintain a profile of that user that is used throughout their interactions with the services protected by OpenAM.
The specific scenario that triggered this investigation (and therefore this blog post!) was one where the user is authenticated by credentials in an Active Directory, but the user profile (DataStore) was a separate OpenDJ instance.

Now before I go any further it is entirely possible to make the AD your DataStore making things simple.  However, there are many occasions where the schema changes needed in a directory to provide the full range of DataStore capabilities are simply not allowed to be applied to an Active Directory due to business or security policy.  Therefore it is necessary to configure a separate DataStore using, say, OpenDJ as well as allow users to authenticate against AD with their AD credentials.

By default OpenAM configures a realm to 'Require' a profile entry in the DataStore for an authenticated user.  Therefore a user profile has to exist in the DataStore in order for authentication to complete.

Now you could provision a set of user profiles into the DataStore using something like OpenIDM to ensure the required profile exists.  Or you can set 'Dynamic' profiles in OpenAM.  This causes OpenAM to dynamically create a profile in the configured DataStore if one does not exist.
So, if we're 'dynamically' creating profiles, what data does OpenAM use to populate the DataStore?   Good question...and one that this post is designed to answer!

First, the basics...

  • Authentication Chain
    As described we want the user to authenticate using their AD credentials.  Therefore we need to define the Authentication Chain to contain an LDAP Authentication Module (or the AD module if you prefer).  Let's assume you can get this working (you may choose to set profile as 'Ignored' whilst you set this up so that authentication can complete without the need for a profile at all).

  • DataStore
    We'll be using OpenDJ as the DataStore.  Let's assume you can setup OpenDJ and configure it as a DataStore.  The OpenAM documentation on Backstage provides guidance on this.

Ok, so now we need to tie these together.  The general idea is that a user logs in with their AD credentials using the Authentication Chain/Module.  OpenAM checks to see if there is an associated user profile record in the DataStore and creates it if not.

Let's first of all consider the Authentication Module.  There are two key properties here:

  1. Attribute Used to Retrieve User Profile
  2. User Creation Attributes

Attribute Used to Retrieve User Profile

In my experience the description and helptext for this is not entirely accurate.  In the scenario I'm describing, OpenAM will retrieve the value of this attribute from the AD/LDAP.  It will then be placed into memory for use later on.  Let's call this 'AttributeX'.  Typically the attribute you want to retrieve is the unique identifier for the user, so might be sAMAccountName or uid, for example.  We'll use the value in 'AttributeX' later when we search for and find the user profile in the DataStore.

User Creation Attributes

This is a list of attribute values to be retrieved from the AD/LDAP authentication source that we wish to pass through to the user profile creation step.
You can specify these attributes either as singletons such as 'sn' or 'mail'.  Or as pipe separated maps such as phoneNum|telephoneNumber.
Again the description of the pipe mapping syntax is confusing.
In actual fact it should be read as:
OpenAM property|AD/LDAP property
i.e. the setting of phoneNum|telephoneNumber would take the value of telephoneNumber from AD/LDAP and store it in an OpenAM property called phoneNum.  We can then use the OpenAM property later when we create the record in the DataStore.
Note that the singleton syntax such as 'sn' will essentially be interpreted as 'sn|sn'.
Also note that the list that appears here is explicit.  If a property is not in this list then the DataStore will not be able to access it during creation.

Ok, so now we know how to extract information from the AD/LDAP that we authenticate against and store it in OpenAM properties.  Now let's look at the DataStore configuration.

There are a couple of things to look at here, but they're mostly in the 'User Configuration' section:
1. LDAP Users Search Attribute
2. LDAP People Container Naming Attribute
3. LDAP People Container Value
4. Create User Attribute Mapping
and, in the Authentication Configuration section,
5. Authentication Naming Attribute

LDAP Users Search Attribute

This is the attribute that will contain the value of 'AttributeX' i.e. the unique key for the user.  Typically in OpenDJ this is often 'uid', but could be something else depending on your configuration.  For example I want a DN of a person record to be something like:
whereas the default might be:
Therefore I need the search attribute to be 'cn' not 'uid'.

LDAP People Container Naming Attribute

As you can see from my desired DN, the container for the 'people' records is 'users' named by the 'cn' property.  Hence the value I specify here is 'cn'.  The default (for OpenDJ) is 'ou' here.

LDAP People Container Value

Again, from the desired DN you can see that I needs to specify 'users' here, whereas the the default is 'people'.

These settings are used to both find a user as well as set the DN when the user is dynamically created.
So, with my settings a user will be created thus:

(The dc=example,dc=com section is defined as the 'LDAP Organization DN' elsewhere in the DataStore config but you should already have that setup correctly!)

Create User Attribute Mapping

Now this is the interesting bit!  This is where the values we retrieved from the AD/LDAP Authentication module and placed in to OpenAM properties can be mapped to attributes in the DataStore.
By default the list contains two singletons: cn and sn

But the helptext says you must specify the list as 'TargetAttributeName=SourceAttributeName' which the defaults don't follow.
Remember that 'AttributeX' we collected?  Well any singleton attribute in this list will take the value of AttributeX...if it was not explicitly defined in the Authentication Module 'User Creation Attributes' map.
i.e. if User Creation Attributes included 'sn' then the value of that would be used for the 'sn' value here.  If there was no mapping then 'sn' here would take the value of AttributeX.
This particular nuance allows you to configure a setting to ensure that attributes that are defined as 'mandatory' in the DataStore to always have an attribute value.  This avoids record creation errors due to missing data constraints defined in the DataStore.
Now, what happens if we use the 'TargetAttributeName=SourceAttributeName' format?  Well, in this case 'TargetAttributeName' refers to the name of the attribute in the DataStore, whereas 'SourceAttributeName' refers to the OpenAM property as specified in Create User Attribute Mapping of the Authentication Module.

Oh, and there's one extra consideration here...
If the OpenAM property name (as defined in Create User Attribute Mapping) exactly matches the name of a DataStore property then you don't need to specify it at all in this list!!

For example, say you want to take the value of 'givenName' from the AD/LDAP and place it in the attribute called givenName in the DataStore then all you need to do is specify 'givenName' in the Authentication Module Create User Attribute Mapping settings.  There is no need to explicitly define it in this list.

However, if you do define it in this list then you must define it as givenName=givenName.
If you were to just specify givenName then it's value will be that of the mysterious 'AttributeX'.

and finally...

Authentication Naming Attribute

For my configuration I set this to 'cn'.  This is a bit of conjecture, but I believe this configuration is used when the DataStore Authentication Module is used.  The DataStore Authn Module will take the username entered by the user on the login page and try to find a user record where the 'cn' equals it.
Now in my scenario this should never happen...I never intend the DataStore to be used as an authentication source...but, as they say, YMMV.

Friday, 3 June 2016

Webinar: Mid-year release of the ForgeRock Identity Platform

We're preparing the ForgeRock Identity platform for a new release imminently. To find out more details, attend this webinar hosted by John Barco, VP of Global Product Marketing: