Friday, 1 July 2016

Custom Stages to User Self-Service

Commons Project

One of the great features of the OpenAMv13 and OpenIDMv4 releases is actually not a feature of those products at all.  I'm talking about the Self-Service capability which is actually part of the 'COMMONS' project at ForgeRock.  As the name suggests, the functionality on 'Commons' may be found in more than one of the final products.  Commons includes capabilities such as audit logging, the CREST framework, the UI framework, as well as the User Self-Service functionality.

Self-Service Configuration

Now there is lots of good documentation about how to make use of the User Self-Service functionality as exposed through the OpenAM and OpenIDM products.
For example, for OpenAM: https://backstage.forgerock.com/#!/docs/openam/13/admin-guide#chap-usr-selfservices, and for OpenIDM: https://backstage.forgerock.com/#!/docs/openidm/4/integrators-guide#ui-configuring.
Whilst the end-user functionality is the same, the way you configure the functionality in OpenAM and OpenIDM is slightly different.  This is the OpenIDM configuration view:



One thing you might notice from the documentation is that the User Self-Service flows (Registration, Forgotten Password, and Forgotten Username) have the ability to use custom 'plugins' - sometimes called 'stages'.  However, another thing you might notice is a distinct lack of documentation on how to go about creating such a plugin/stage and adding it to your configuration.

Note that there is an outstanding JIRA logged for ForgeRock to provide documentation (https://bugster.forgerock.org/jira/browse/OPENIDM-5630) but, in the meantime, this post attempts to serve as an interim.  But, I'm only going to use OpenIDM as the target here, not OpenAM.

Fortunately, the JIRA linked above highlights that within the code base there already exists the code for a custom module, and a sample configuration.  So, in this post I'll explain the steps required to build, deploy, and configure that pre-existing sample.

The easiest way to do this is to get the code of the Standalone Self-Service application!

Get the Standalone Self-Service code

(*** EDIT : the code referenced below is now available in the forgerock-selfservice-public repo of the LEGACYCOMMONS project: https://stash.forgerock.org/projects/LEGACYCOMMONS/repos/forgerock-selfservice-public/browse ***)

You'll need to head over the 'self-service' repository in the 'COMMONS' project of the ForgeRock Stash server: https://stash.forgerock.org/projects/COMMONS/repos/forgerock-selfservice/browse
(You may need to register, but go ahead, it's free!)
If you're following the instructions in this post, and are targeting OpenIDMv4  (as opposed to any later releases) then you'll specifically want v1.0.3 of this SelfService repository.
i.e
https://stash.forgerock.org/projects/COMMONS/repos/forgerock-selfservice/browse?at=refs%2Ftags%2F1.0.3

 Now download the code to your local drive so we can build it.

Compile and run it

You can see that the 'readme.txt' provides the details of how to compile this project.  Note that this will compile the full 'Commons Self-Service' functionality, including the custom stage, in a standalone harness.

Once it's built you can browse to the web site and play with the functionality.  Any users registered are held in memory of this harness, and therefore flushed each time you stop the jetty container.


It's also worth noting that the readme.txt instructs you to enter email username and password.  These are used to connect to the configured email service of this test harness in order to actually send registration emails.  (The implementations in OpenAM and OpenIDM will use the configured email services for those products).  By default, the configured email service is gmail.  And, by default, gmail stops this type of activity unless you change your gmail account settings.  However, you may instead choose to run a dummy SMTP service to capture the sent emails.  One such utility that I'll use here is FakeSMTP: https://nilhcem.github.io/FakeSMTP/
So, once you have an accessible SMTP service, you might now need to change the email service config of the User Self-Service functionality.  You find this for the test harness - assuming the mvn build has worked successfully - here:

forgerock-selfservice-example/target/classes/config.json

If you're running FakeSMTP on port 2525, then this might look like:
{
  "mailserver": {
    "host": "localhost",
    "port": "2525"
  }
}

Now when you run the example webapp emails will be delivered to FakeSMTP (and will ignore whatever you use for username and password)



So, go ahead, register a user. The first thing you should see is a "Math Problem" stage.  Eh? What? Where did that come from? Well, that's the custom stage!!  Yes, this standalone version of Self-Service includes the custom stage!

Step1. Math Problem
Assuming you can add 12 and 4 together complete the 'puzzle'.  Then follow the remaining steps of the registration (noting that the email gets delivered to FakeSMTP, where you can open it and click the link to complete the registration).
Step 2. Email Verification
Email link
Step 3. Register details
Step 4. KBA
Success!

Inspect the configuration

Now, if we take a look at the 'Registration Stage' configuration for this example app, which we can find here:
forgerock-selfservice-example/target/classes/registration.json
we will see it begins like this:
{
  "stageConfigs": [
    {
      "class" : "org.forgerock.selfservice.custom.MathProblemStageConfig",
      "leftValue" : 12,
      "rightValue" : 4
    },
    {
      "name" : "emailValidation",
      "emailServiceUrl": "/email",
      "from": "info@admin.org", 
...

Brilliant!  That first item in the stageConfigs array is the "Math Problem" with it's configuration (i.e. which two numbers to sum!) The remaining array items reference 'name' which are the native/in-built modules and their necessary configuration.
So, what we've achieved so far is:

  • Compiled the custom stage (Math Problem)
  • Compiled standalone version of Common User Self-Service
  • Tested a Registration flow that includes the custom stage, along with some native stages.

And what's left to do?

  • Deploy and configure the custom stage in OpenIDM

OpenIDM Deployment

Simply copy the custom stage JAR file to the 'bundle' directory of your OpenIDM deployment
e.g. 
cp forgerock-selfservice-custom-stage/target/forgerock-selfservice-custom-stage-1.0.3.jar <openidm>/bundle

And update the 'selfservice-registration.json' file in your 'conf' folder.
This is OpenIDM's name for the 'registration.json' file of the standalone SelfService app, so use the same configuration snippet you saw in the standalone app.

It seems that this method will not allow you to see the custom stage in the Admin UI for OpenIDM. Happily, changes made within the Admin UI do not remove the custom stage from the file. However, be warned that if you disable, then re-enable SelfService Registration there is no guarantee that custom stage will be added into the re-created 'selfservice-registration.json' file in the correct place.

So, with User Self Registration enabled, and the custom stage included in the config, when a user tries to register they will be prompted for the calculation at the appropriate point in the flow.
Custom Stage in OpenIDM flow!



Exercise for the Reader!

As you can see I have use the pre-supplied custom stage.  Showing one approach to building, deploying and configuring it.  If you need a custom stage to do something different, then you'll have to investigate the interfaces that need implementing in order to develop a custom stage.



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:
cn=<userid>,cn=users,dc=example,dc=com
whereas the default might be:
uid=<userid>,ou=people,dc=example,dc=com
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:
cn=<userid>,cn=users,dc=example,dc=com

(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:
https://go.forgerock.com/ForgeRock-Identity-Platform-Whats-New_RegisterNow.html

Tuesday, 10 May 2016

OpenIDM: Allocate from a Pool

The problem

During a recent Proof of Concept we had a need to allocate 'serial numbers' of hardware tokens to users as the user was created.  The serial number would come from a 'pool' of available tokens and should be allocated automatically on user creation.  It should also be possible to remove serial numbers from users which would be returned to the pool.

The solution

We decided to create a custom managed object in OpenIDM to hold the 'pool' of token serial numbers.  We could then leverage the new (in v4) relationship model to allocate serial numbers to users.
So we needed a few bits to this jigsaw:

  1. Define the custom managed object
  2. Populate the new object with the available serial numbers
  3. Modify the user creation logic to retrieve an available serial number and associate it with the user

Define the custom managed object

We were using FrejaId tokens, so we defined a 'managed object' like this:
SerialNo is just defined as a string and uses the OOTB box policy validation capability to ensure uniqueness (take a look at the 'managed user' object and its 'userName' property to see how validation policies can be applied to properties).  Also, it is defined as 'Viewable' and 'Searchable'.

AssignedUser is defined as a 'reverse relationship' which matches up with a property on the managed user object called 'AssignedSerialNo'.
We therefore also have to modify the 'managed user' object to include the 'AssignedSerialNo' property which is defined as a reverse relationship to the 'AssignedUser' property on on the FrejaToken object.

Populate the token object with SerialNos

There are several ways you could achieve this depending on the source of the information.  I just had a list of serial numbers from the 3rd party hardware vendor.  The easiest way for me was to create a command line script that iterated through the list and called the OpenIDM REST API for the new managed object.  So I created a file called 'tokens.csv' with each SerialNo on a new line.  There was no need for a header to be included.  The script then used this file as the source of input to a REST call to populate the object.  This was a bash script saved in 'tokenimport.sh' and required the CSV file as an input parameter when it runs e.g.:
#!/bin/bash
# tokenimport.sh
#  import the specified token serial no to OpenIDM
E_NOARGS=85
if [ -z "$1" ]   # Exit if no argument given.
then
  echo "Usage: `basename $0` file"
  exit $E_NOARGS
fi
cat "$1" | while read line;
           do {
             curl --header 'X-OpenIDM-Username: openidm-admin' --header 'X-OpenIDM-Password: openidm-admin'  --header 'Content-Type: application/json' --header 'If-None-Match: *' --data '{"SerialNo":"'"$line"'"}' --request PUT http://localhost:8080/openidm/managed/FrejaToken/"$line"
           }
           done
exit 0

You can run this multiple times.  The 'unique' policy validation constraint on the managed object 'SerialNo' property will stop duplicates being saved.  Also note I'm using a 'PUT' request and specifying the _id of the item being created.  (The _id being the same as the 'SerialNo').
Note also that I'm using the default OpenIDM-Admin username and password, and running this script on the same machine as OpenIDM.

Modify user creation logic

As you may be aware OpenIDM provides 'hooks' in many places to allow custom logic to be triggered at various points.  One such place is when a specific instance of a managed object is created.  In fact, the out-of-the-box definition for a managed user object includes an 'onCreate' script.  This script is called onCreate-user-set-default-fields.js  The script exists in openidm/bin/defaults/script/ui.  You should never ever modify this script...but you can make a copy of it and modify the copy!  So take a copy and place it in:
openidm/script/ui
(you may need to create the 'ui' directory)
Then add this to the bottom of the file:
// allocate first available freja token
var availableTokens = openidm.query('managed/FrejaToken', {_queryFilter:'true'}, ['*', 'AssignedUser']).result.filter(function (token) { return !(token.AssignedUser)});
if ((availableTokens) && (availableTokens.length > 0)) {
  object.AssignedSerialNo = {"_ref":"managed/FrejaToken/" + availableTokens[0]._id};
}

The first line queries all tokens, and filters out the tokens that are already assigned.  The availableTokens array therefore contains all available tokens.
We then check that there are actually tokens in the array, and then get the first one, saving it as a 'relationship' to the 'AssignedSerialNo' property of the managed user object that we created earlier.

Note that as this is defined as a 'reverse property' you would also see that the 'AssignedUser' property of the FrejaToken is now automagically populated.  Which also means that the next time this query is run then this token will already be 'allocated' and therefore not exist in the availableTokens array.

Deallocation

Because of the 'reverse property' relationship, deallocation is straightforward.  You can remove the link between the user and SerialNo using REST calls or the User Interface.  This means the token becomes available again.  Also, if you delete a user then the relationship is removed, returning the assigned SerialNo to the pool.

Wednesday, 4 May 2016

OpenIDM: Sequential Number Generator

The problem 

Working on a Proof of Concept recently the customer wanted numbers to be allocated to new users in sequence. They wanted the sequence to start at 300000000 and increase by 1 each time a new user was created. This was to 'guarantee' uniqueness of the number allocated to each user.

The solution

I decided to make use of the generic repository object capability of OpenIDM. This is a little known feature but something I've blogged about in the past (See: http://yaunap.blogspot.co.uk/2015/09/data-driven-preferences-list-in-openidm.html) But just storing a number wasn't enough...I needed some logic to increment the number and store the result, as well as an API in order to 'GetNextId' when a new user was created.
So there are several parts to this:
  1. Repository Object 
  2. Script to read stored number, increment it, store it and return it 
  3. Custom endpoint in OpenIDM to expose the script to clients 

Repository object 

Creating the repository object is straightforward. I called my object 'counter'. It will have a single field/value called 'lastid' seeded with 300000000 (note limitation below):
curl --header "X-OpenIDM-Username: openidm-admin" --header "X-OpenIDM-Password: openidm-admin"  --header "Content-Type: application/json" --data '{"lastid":"300000000" }' --request POST http://localhost:8080/openidm/repo/counter?_action=create

Script

The script is also fairly simple:
(function getNextId () {
  //read last number from custom repo object, add 1, store it back, return it!
  var  lastid = (openidm.read('repo/counter/lastid'));
  var nextid = Number(lastid['lastid']) + 1;
  lastid['lastid'] = nextid;
  openidm.update('repo/counter/lastid',null,lastid);
  return {nextid:nextid.toString()};
}());
This needs to be saved to a new .js file in the 'script' directory under the OpenIDM deployment directory. e.g. openidm/script/getNextId.js

Custom Endpoint

The custom endpoint is a simple definition (creating custom endpoints is well documented in the Integrators Guide):
{
    "type" : "text/javascript",
    "file" : "script/getNextId.js"
} 

 This needs to be saved to a .json file in the 'conf' directory. The naming convention is important. It must start with 'endpoint-'. Anything after the '-' will be the name of API you can access over HTTP. Therefore a file called:
openidm/conf/endpoint-getnextid.json
will be addressable thus:
curl -H "X-OpenIDM-Username: openidm-admin" -H "X-OpenIDM-Password: openidm-admin" 'http://localhost:8080/openidm/endpoint/getnextid'


Limitation: 

The endpoint only handles javascript numbers with values less than 2147483648 If you want larger numbers (i.e. starting at 3000000000) then the logic will need to support some way of handling the larger number.

Counter Reset:

To reset the counter, use:
curl --header "X-OpenIDM-Username: openidm-admin" --header "X-OpenIDM-Password: openidm-admin"  --header "Content-Type: application/json" --data '{"lastid":"300000000" }' --header "If-Match: *" --request PUT http://localhost:8080/openidm/repo/counter/lastid

Tuesday, 22 September 2015

Data Driven ‘preferences list’ in OpenIDM

My colleague over at idmonsters has done a great job of describing how to add ‘marketing’ preferences to a user’s profile in OpenIDM.  This requirement is fairly typical as it allows user’s (and administrators) to control things like contact preferences as mandated by law.  The method presented at http://idmonsters.blogspot.co.uk/2015/09/implementing-user-marketing-preferences.html uses config files to determine the set of preferences that should be available for management on a user’s profile.  I decided to take this a step further and make this data-driven.  The list of preferences should be populated via REST call.  This goes beyond a fairly standard set of ‘contact preferences’ and allows more flexibility to ask the user for, say, brand preferences, seating preferences, colour choices, disability needs, channel preferences etc.  i.e. the sorts of ‘choices’ where the available list might change based on the changing drivers of the business.

In the approach presented here I used the generic repository object capability of OpenIDM to store the list of preferences.  This is an often overlooked feature that allows any data to be stored in the OpenIDM repository and accessed via a REST call.  These generic objects are not ‘managed objects’ and therefore can’t participate directly in synchronisation mappings.  They also don’t have a user interface in order to define the behaviour, nor a user interface to manage data – it is all done through REST.  You could of course populate generic objects as part of a Master Data Management strategy that sees the master of this data being elsewhere, or maybe the generic object is the master…anyway, I digress…in this scenario the generic object provides a handy way to store and retrieve simple data sets (the ‘preferences’) over REST.

Prepare Generic Repository Object

Populate Data

So, let’s begin by populating the data.  Fire up your favourite REST client and formulate a ‘PUT’ request.  I typically use Postman, but CURL etc will work just as well.  The parameters we’ll use are:
Method: PUT
Headers:
  • X-OpenIDM-Username: <user> (e.g. openidm-admin) 
  • X-OpenIDM-Password: <password> (e.g. openidm-admin) 
  • Content-Type: application/json 
  • If-None-Match: *
Data:
{"name":"London", "type":"location"}
URL: http://openam.example.com/openidm/repo/custom/preferences/london

Just look at the URL for a moment.  The /repo/custom element tells OpenIDM that we’re planning on storing a generic repository object.  The name of the object is the next element i.e. ‘preferences’.  Then the last element is the _id of the item we’re going to store in this object.  (I find it helps to think of the ‘object’ as a table and the items as rows).
The ‘Data’ will be the values stored as the item in the object.  In this case it is essentially two columns: name and type.

Let’s add some more data using the same Method and Headers, but with the following Data and URLs:

Data:
{"name":"Manchester", "type":"location"}
URL: http://openam.example.com/openidm/repo/custom/preferences/manchester

Data:
{"name":"Glasgow", "type":"location"}
URL: http://openam.example.com/openidm/repo/custom/preferences/glasgow

So far we’ve added three preferences of type ‘location’.  You may not need to include the ‘type’ in your model, or you may need more complex Data.  But for now we’ll add more preferences with a different type:

Data:
{"name":"Bus", "type":"transport"}
URL: http://openam.example.com/openidm/repo/custom/preferences/bus

Data:
{"name":"Underground", "type":"transport"}
URL: http://openam.example.com/openidm/repo/custom/preferences/underground

Data:
{"name":"Taxi", "type":"transport"}
URL: http://openam.example.com/openidm/repo/custom/preferences/taxi

When it comes to the User Interface, we’ll display all of these preferences in a single list, but you should be able to extrapolate what’s going on here to provide multiple/hierarchical lists if you desire.

Configure Access to REST endpoints

By default, OpenIDM restricts access to the REST API for generic repository objects to administrators only.  So, having added the data we now need to configure OpenIDM to allow any logged in user to retrieve this list from the REST API as they’ll be calling this when they access the User Interface.
In ‘access.js’ find the section controlling access to the ‘repo’ endpoint and permit the ‘openidm-authorized’ (for user self management) and ‘openidm-reg’ (for user self registration) roles to access it.  Of course, in production, you may want to be more granular in your permissions.  It should now look like:

        // Disallow command action on repo
        {
            "pattern"   : "repo",
            "roles"     : "openidm-admin,openidm-authorized,openidm-reg",
            "methods"   : "*", // default to all methods allowed
            "actions"   : "*", // default to all actions allowed
            "customAuthz" : "disallowCommandAction()"
        },
        {
            "pattern"   : "repo/*",
            "roles"     : "openidm-admin,openidm-authorized,openidm-reg",
            "methods"   : "*", // default to all methods allowed
            "actions"   : "*", // default to all actions allowed
            "customAuthz" : "disallowCommandAction()"
        },

Configure Profile functionality

Configure permissions to allow users to update their Preferences profile attribute 

Also, by default, OpenIDM restricts the list of profile properties that a user can modify themselves.  An admin can get/set any old property put users are restricted to the list defined in access.js.  So whilst we’re in the file let’s allow users to get/set the ‘preferences’ property of their profile:
var allowedPropertiesForManagedUser =   "preferences,userName,password,mail,givenName,sn,telephoneNumber," +
                                        "postalAddress,address2,city,stateProvince,postalCode,country,siteImage," +
                                        "passPhrase,securityAnswer,securityQuestion";
If we were purely using the REST API to manage/retrieve the information about a user, then we could stop here.  But this article is really about configuring the OpenIDM native UI in order to manage these preferences.

Retrieve Preferences from REST endpoint

Now we need to create a PreferencesDelegate.js file.  This will be used by the UI to call the REST API of the ‘preferences’ generic object in order to retrieve the data to show.  The easiest way to do this is make a copy of the ‘RolesDelegate.js’ and edit that.  (I’m going to make all my UI changes in the ‘default’ branch of the structure rather than make use of the ‘extension’ branch.  The recommendation is to use the extension branch to avoid files being overwritten on upgrade but in my case I have made a copy of each of the files I changed in case I need to revert).
So, make a copy of: 

ui/default/enduser/public/org/forgerock/openidm/ui/user/delegates/RoleDelegate.js 
as
ui/default/enduser/public/org/forgerock/openidm/ui/user/delegates/PreferencesDelegate.js 
Edit PreferencesDelegate.js so that the path to the REST endpoint is the ‘preferences’ repository object rather than the ‘roles’ managed object.  There will be four lines to change that should now look like:
define("org/forgerock/openidm/ui/user/delegates/PreferencesDelegate", [
var obj = new AbstractDelegate(constants.host + "/openidm/repo/custom/preferences");
obj.getAllPreferences = function () {
r._id = "repo/custom/preferences/" + r._id;

User Interface Modification 

Now we’ll modify the UI. There are 4 logical areas to change, with each area needing a changes to both an html template as well as javascript files:

  1. AdminUserRegistration: Administrator functionality to register a new user 
  2. AdminUserProfile: Administrator functionality to modify an existing user’s profile 
  3. UserRegistration: User self-registration 
  4. UserProfile: User modification of their own profile 

 We’ll take each one step by step, although you may not need to implement all items depending on your needs.

AdminUserRegistration

JS file:
ui/default/enduser/public/org/forgerock/openidm/ui/admin/users/AdminUserRegistrationView.js

  1. Include the PreferencesDelegate.js file we created earlier.  Add this after the RoleDelegate reference:
    "org/forgerock/openidm/ui/user/delegates/RoleDelegate",
    "org/forgerock/openidm/ui/user/delegates/PreferencesDelegate"
  2. Ensure the delegate reference is passed as a function parameter called preferenceDelegate:
    ], function(AbstractView, validatorsManager, uiUtils, userDelegate, eventManager, constants, conf, router, roleDelegate, preferenceDelegate)
    
  3. In the formSubmit event ensure that any selected preferences are saved. Add this after the ‘data.roles = …’ line:
    data.preferences = this.$el.find("input[name=preferences]:checked").map(function(){return $(this).val();}).get();
    
  4. In the render event, call the preferences REST API (using the delegate) and add it to the ‘data’ object that will be used to populate the UI. Make the event look like this:
            …
            render: function() {
               $.when(
                roleDelegate.getAllRoles(),
                preferenceDelegate.getAllPreferences()
               ).then(_.bind(function (roles, preferences) {
    
                    var managedRoleMap = _.chain(roles.result)
                                          .map(function (r) { return [r._id, r.name || r._id]; })
                                          .object()
                                          .value();
    
                    this.data.roles = _.extend({}, conf.globalData.userRoles, managedRoleMap);
    
                    var preferenceMap = _.chain(preferences.result)
                                          .map(function (r) { return [r._id, r.name || r._id]; })
                                          .object()
                                          .value();
    
                    this.data.preferences = preferenceMap;
    
                    this.parentRender(function() {
                    …
Note that I’m populating preferences solely from the REST endpoint.  If you look at the ‘roles’ block in this file you’ll see that this is merging ‘managed roles’ with roles configured in a configuration file.  Maybe you’ll want some preferences to be managed by configuration, if so you can replicate the ‘extend’ method in the roles functionality.  E.g:
this.data.preferences = _.extend({}, conf.globalData.userPreferences, preferenceMap);

HTML Template:
ui/default/enduser/public/templates/admin/AdminUserRegistrationTemplate.html
The template specifies the layout of the registration page and is easy enough to work out. You need to decide where to put the preferences list…I chose to locate it ‘after’ (i.e. below) the Password validation rules section. So, add the following block:
            <div class="group-field-block" >
                <label class="light align-right">{{t "Preferences"}}</label>
                {{checkbox preferences "preferences"}}
            </div>

Note the {{t “Preferences”}} item.  This controls the label value in the UI.  If you want to make this globalised then follow the format of the other blocks and update the translation.json file.  I’ll leave that as an exercise for the reader! This block uses the ‘checkbox’ helper function to display the supplied object (the 2nd parameter: preferences), using the name and _id of the items with the object, as checkboxes.  The HTML element it creates is given the ‘name’ of the 3rd parameter (“preferences”).  Note that the JS file relies on the HTML element name in order to work out where to populate that data and which data to submit when the profile is saved. When logged in as an administrator, the ‘Add User’ screen should now look like this:
Now if you add a user, selecting preferences, you’ll be able to see the data stored by retrieving the user in a REST call.
e.g.
http://openam.example.com/openidm/managed/user?_queryId=query-all
might return something similar to this:
        {
            "mail": "jim@example.com",
            "sn": "Bean",
            "userName": "jim",
            "stateProvince": "",
            "preferences": [
                "repo/custom/preferences/bus",
                "repo/custom/preferences/london",
                "repo/custom/preferences/taxi"
            ]
        }


AdminUserProfile

Now let’s edit the Administrator’s functionality to manage a user so that it also includes the preferences functionality.

JS file:
ui/default/enduser/public/org/forgerock/openidm/ui/admin/users/AdminUserProfileView.js

  1. Add the PreferencesDelegate.js file as per AdminUserRegistration above
  2. Add the preferenceDelegate function as per AdminUserRegistration above
  3. Add the data.preferences line as per AdminUserRegistration above
  4. Modify the render event as follows:
    render: function(userName, callback) {
                userName = userName[0].toString();
    
                $.when(
                    userDelegate.getForUserName(userName),
                    roleDelegate.getAllRoles(),
                    preferenceDelegate.getAllPreferences()
                ).then(
                    _.bind(function(user, roles, preferences) {
    
                        var managedRoleMap = _.chain(roles.result)
                            .map(function (r) { return [r._id, r.name || r._id]; })
                            .object()
                            .value();
    
                        var preferenceMap = _.chain(preferences.result)
                            .map(function (r) { return [r._id, r.name || r._id]; })
                            .object()
                            .value();
    
                        this.editedUser = user;
                        this.data.user = user;
                        this.data.roles = _.extend({}, conf.globalData.userRoles, managedRoleMap);
                        this.data.preferences = preferenceMap; 
                     this.data.profileName = user.givenName + ' ' + user.sn;
       …
    
  5. Modify the reloadData event to ensure the UI is able to highlight the preferences already selected against the user’s profile. Add this after the similar roles block:
        _.each(this.editedUser.preferences, _.bind(function(v) {
          this.$el.find("input[name=preferences][value='"+v+"']").prop('checked', true);
        }, this));
    

HTML file:
ui/default/enduser/public/templates/admin/AdminUserProfileTemplate.html

As per the AdminUserRegistration template add a new UI block for the preferences. In this case I created a whole new section after (i.e. below) the Country and State items:
<div class="clearfix">
  <div class="group-field-block col2">
    <label class="light align-right">{{t "Preferences"}}</label>
    {{checkbox preferences "preferences"}}
  </div>
</div>

An Administrator managing a user’s profile should now have a screen that looks like this:


UserRegistration

This allows the user to self-register their own profile.
JS file:
ui/default/enduser/public/org/forgerock/openidm/ui/user/UserRegistrationView.js
  1. Add the PreferencesDelegate.js file as per AdminUserRegistration above
  2. Add the preferenceDelegate function as per AdminUserRegistration above
  3. Modfiy the formSubmit event by adding the data.preferences line:
    … 
    var data = form2js(this.$el.attr("id")), element;
    data.preferences = this.$el.find("input[name=preferences]:checked").map(function(){return $(this).val();}).get();
    delete data.terms;
    …
    
  4. Modify the render event as follows:
            render: function(args, callback) {
    
            $.when(preferenceDelegate.getAllPreferences()
            ).then(
              _.bind(function(preferences){
    
                conf.setProperty("gotoURL", null);
    
                var preferenceMap = _.chain(preferences.result)
                            .map(function (r) { return [r._id, r.name || r._id]; })
                            .object()
                            .value();
                this.data.preferences = preferenceMap;
    
                this.parentRender(_.bind(function() {
    
                    …
                    }, this));
                }, this));
              }, this));
            },
    
HTML file:
ui/default/enduser/public/templates/user/UserRegistrationTemplate.html

Add a new UI block for the preferences.  I added this below the fieldset defining the user profile properties:
<fieldset class="fieldset col0">
    <div class="group-field-block">
        <label for="marketing" class="light align-right">Preferences</label>
        <div class="float-left separate-message">
             {{checkbox preferences "preferences"}}
        </div>
     </div>
</fieldset>

Now, having enabled user self-registration (https://backstage.forgerock.com/#!/docs/openidm/3.1.0/integrators-guide#ui-self-registration) the user should see a screen like this:
The user can now self-register, including preferences information.


UserProfile

To allow the user to modify their own profile preferences once they have been registered carry out the following changes.

This requires two JS files updating along with the HTML template
JS file:
ui/default/enduser/public/org/forgerock/commons/ui/user/profile/UserProfileView.js
This is a fairly simple change replicating the formSubmit event handling of the Admin side:

                …
                this.data = form2js(this.el, '.', false);
                this.data.preferences = this.$el.find("input[name=preferences]:checked").map(function(){return $(this).val();}).get();
                
                // buttons will be included in this structure, so remove those.
                                    …
JS file:
ui/default/enduser/public/org/forgerock/openidm/ui/user/profile/UserProfileView.js

This looks like fairly complex set of changes in order to introduce the PreferencesDelegate and call it at the appropriate point in order to populate the preferences data element, as well as select the items in the list that the user has already added to their profile. But, as it turns out, we end up making this file look like the AdminUserProfile to call the getAllPreferences function.

  1. Add the PreferencesDelegate reference as per Step 1 of the Admin side
  2. Add the preferenceDelegate function reference as per Step 2 of the Admin side
  3. Change the obj.render assignment as follows:
    obj.render = function(args, callback) {
         $.when(preferenceDelegate.getAllPreferences()
         ).then(
           _.bind(function(preferences){
                var preferenceMap = _.chain(preferences.result)
                            .map(function (r) { return [r._id, r.name || r._id]; })
                            .object()
                            .value();
                obj.data.preferences = preferenceMap;
    
            if(conf.globalData.userComponent && conf.globalData.userComponent === "repo/internal/user"){
                obj.data.adminUser = true;
            } else {
                obj.data.adminUser = false;
            }
    
            this.parentRender(function() {
                var self = this,
              …
                    });
    
                    this.reloadData();
    
                   _.each(conf.loggedUser.preferences, _.bind(function(v) {
                       this.$el.find("input[name=preferences][value='"+v+"']").prop('checked', true);
                   }, this));
    
                    if(callback) {
                        callback();
                    }
    
                }, this));
            });
           },this));
        };
    
HTML Template:
ui/default/enduser/public/templates/user/UserProfileTemplate.html

Add the same block as per the AdminUserProfile.  I added this after (i.e. below) the {{/if}} section that wraps the Address Details block:
        <div class="clearfix">
            <div class="group-field-block col2" >
                <label class="light align-right">{{t "Preferences"}}</label>
                {{checkbox preferences "preferences"}}
            </div>
        </div>
When the user edits their profile they should get a screen that looks like this:

Summary

You’re done!  You now have a list preferences populated from a REST endpoint (which happens to be an OpenIDM generic repository object) that are selectable by users and admins alike.

Monday, 24 August 2015

Windows 2012 R2, ADFS3, WIF4.5 and OpenAM v12

Recently I was involved in a Proof of Concept that required OpenAM v12 to be the authentication service to an ASP.net application that relied on Integrated Windows Authentication.  Normally, you might use the IIS Policy Agent for this scenario because it supports Impersonation.  However, this also requires that OpenAM's DataStore is configured as the Active Directory.  In the PoC, OpenAM was not able to use the AD as the DataStore and was instead depending on an OpenDJ DataStore.  To resolve this I made use of Windows Identity Framework 4.5 as part of Windows 2012 R2 because this has the ability to Impersonate a user based on Windows Identity Claims.  These claims are provided to WIF4.5 via ADFS 3 (it's not formally called ADFS 3 - but it is the version that comes with 2012 R2 that everyone refers to as ADFS 3).  The claims were passed-through ADFS having being initially generated by OpenAM v12.

I decided to write this up as a series of wiki articles providing step-by-step guidance because I realised that some people might come at this knowing Windows/ADFS well, but limited experience with OpenAM.  Similarly, some readers might be very familiar with OpenAM, but have limited Windows experience.  So the series of articles is designed such that you can skip various parts if you already have the necessary components.  Bear in mind that SSL is a 'must' - even for a PoC - so you need to be aware of how to exchange and trust self-signed certificates across the different servers.  In my PoC I used different operating systems (CentOS for the OpenAM instance) so the articles also explain how to exchange self-signed certificates.

The articles can be found here:
https://wikis.forgerock.org/confluence/display/openam/ADFS+3+%28Windows+2012+R2%29+and+OpenAM+12