Identity Cloud

Object modeling

Guide to creating and managing objects in ForgeRock® Identity Cloud.

Identity Cloud provides a default schema for typical managed object types, such as users and roles. This section shows you how to change and add to the managed object schema, how to establish relationships between objects, and how to use policies to validate objects. You will also learn how to access Identity Cloud objects using queries.

ForgeRock Identity Platform™ serves as the basis for our simple and comprehensive Identity and Access Management solution. We help our customers deepen their relationships with their customers, and improve the productivity and connectivity of their employees and partners. For more information about ForgeRock and about the platform, see https://www.forgerock.com.

The ForgeRock Common REST API works across the platform to provide common ways to access web resources and collections of resources.

Managed objects

These topics describe how to work with managed object types:

The Identity Cloud object model includes other object types besides managed objects. For more information, see Data models and objects reference.

Identity Cloud managed object schema

Managed objects and their properties are defined in the default Identity Cloud managed object schema.

The default schema includes these types of managed objects:

Each managed object type contains properties for storing information about objects of that type. For example, the user object type has properties for storing usernames, passwords, email addresses, and so forth.

You can extend the default Identity Cloud managed object schema to meet your needs:

To store custom data for users, you do not extend the default Identity Cloud managed object schema. Instead, use one of the generic extension attributes provided in the default user object schema. For a list of generic extension attributes, see the tables here.

The IDM admin UI depends on the presence of the default managed object types and the default properties nested within them. If you remove any of these schema elements, and you use the IDM admin UI to configure Identity Cloud, you must modify the IDM admin UI code accordingly. For example, if you remove the assignment object type from the managed object configuration, the UI will throw exceptions wherever it queries this schema element.

Create and modify object types

If the managed object types provided in the default configuration are not sufficient for your deployment, you can create new managed object types. The easiest way to create a new managed object type is to use the IDM admin UI, as follows:

  1. Select Configure > Managed Objects > New Managed Object.

  2. On the New Managed Object page, enter a name and readable title for the object, make optional changes, as necessary, and click Save. The readable title specifies what the object will be called in the UI.

  3. On the Properties tab, specify the schema for the object type (the properties that make up the object).

  4. On the Scripts tab, specify any scripts that will be applied on events associated with that object type. For example, scripts that will be run when an object of that type is created, updated, or deleted.

You can also create a new managed object type by editing your managed object schema.

Example: Phone object created using the IDM admin UI
{
    "name": "Phone",
    "schema": {
        "$schema": "http://forgerock.org/json-schema#",
        "type": "object",
        "properties": {
            "brand": {
                "description": "The supplier of the mobile phone",
                "title": "Brand",
                "viewable": true,
                "searchable": true,
                "userEditable": false,
                "policies": [],
                "returnByDefault": false,
                "pattern": "",
                "isVirtual": false,
                "type": [
                    "string",
                    "null"
                ]
            },
            "assetNumber": {
                "description": "The asset tag number of the mobile device",
                "title": "Asset Number",
                "viewable": true,
                "searchable": true,
                "userEditable": false,
                "policies": [],
                "returnByDefault": false,
                "pattern": "",
                "isVirtual": false,
                "type": "string"
            },
            "model": {
                "description": "The model number of the mobile device, such as 6 plus, Galaxy S4",
                "title": "Model",
                "viewable": true,
                "searchable": false,
                "userEditable": false,
                "policies": [],
                "returnByDefault": false,
                "pattern": "",
                "isVirtual": false,
                "type": "string"
            }
        },
        "required": [],
        "order": [
            "brand",
            "assetNumber",
            "model"
        ]
    }
}

Every managed object type has a name and a schema that describes the properties associated with that object. The name can only include the characters a-z, A-Z, 0-9, and _ (underscore). You can add any arbitrary properties to the schema.

Avoid using the dash character in property names (like last-name) because dashes in names make JavaScript syntax more complex. Rather use "camel case" (lastName). If you cannot avoid dash characters, write source['last-name'] instead of source.last-name in your JavaScript.

Also, managed object properties that contain an underscore (_) are reserved for internal use. Do not create new properties that contain underscores, and do not include these properties in update requests.

A property definition typically includes the following fields:

title

The name of the property, in human-readable language, used to display the property in the UI.

description

A brief description of the property.

viewable

Specifies whether this property is viewable in the object’s profile in the UI. Boolean, true or false (true by default).

searchable

Specifies whether this property can be searched in the UI. A searchable property is visible within the Managed Object data grid in the End User UI.

Boolean, true or false (false by default).

Do not modify the searchable setting on properties in the Identity Cloud managed object schema.
userEditable

Specifies whether users can edit the property value in the UI. This property applies in the context of the End User UI, where users are able to edit certain properties of their own accounts. Boolean, true or false (false by default).

isProtected

Specifies whether reauthentication is required if the value of this property changes.

For certain properties, such as passwords, changing the value of the property should force an end user to reauthenticate. These properties are referred to as protected properties. Depending on how the user authenticates (which authentication module is used), the list of protected properties is added to the user’s security context. For example, if a user logs in with the login and password of their managed user entry (MANAGED_USER authentication module), their security context will include this list of protected properties. The list of protected properties is not included in the security context if the user logs in with a module that does not support reauthentication (such as through a social identity provider).

pattern

Any specific pattern to which the value of the property must adhere. For example, a property whose value is a date might require a specific date format.

policies

Any policy validation that must be applied to the property. For more information on managed object policies, see Default policy for managed objects.

required

Specifies whether the property must be supplied when an object of this type is created. Boolean, true or false.

The required policy is assessed only during object creation, not when an object is updated. You can effectively bypass the policy by updating the object and supplying an empty value for that property. To prevent this inconsistency, set both required and notEmpty to true for required properties. This configuration indicates that the property must exist, and must have a value.
type

The data type for the property value; can be string, array, boolean, integer, number, object, Resource Collection, or null.

If any user might not have a value for a specific property (such as a telephoneNumber), you must include null as one of the property types. You can set a null property type in the IDM admin UI (Configure > Managed Objects > User, select the property, and under the Details tab, Advanced Options, set Nullable to true).

You can also set a null property type in your managed object configuration by setting "type" : '[ "string","null" ]' for that property (where string can be any other valid property type. This information is validated by the policy service, as described in Validate Managed Object Data Types.

If you’re configuring a data type of array through the IDM admin UI, you’re limited to two values.

isVirtual

Specifies whether the property takes a static value, or whether its value is calculated "on the fly" as the result of a script. Boolean, true or false.

returnByDefault

For non-core attributes (virtual attributes and relationship fields), specifies whether the property will be returned in the results of a query on an object of this type if it is not explicitly requested. Virtual attributes and relationship fields are not returned by default. Boolean, true or false. When the property is in an array within a relationship, always set to false.

relationshipGrantTemporalConstraintsEnforced

For attributes with relationship fields. Specifies whether this relationship should have temporal constraints enforced. Boolean, true or false. For more information about temporal constraints, see Use temporal constraints to restrict effective roles.

default

Specifies a default value if the object is created without passing a value. Default values are available for the following data types, and arrays of those types:

  • boolean

  • number

  • object

  • string

Identity Cloud assumes all default values are valid for the schema.

Default values

You can specify default values in the Identity Cloud managed object schema. If you omit a default value when creating an object, the default value is automatically applied to the object. You can have default values for the following data types, and arrays of those types:

  • boolean

  • number

  • object

  • string

For example, the default Identity Cloud managed object schema includes a default value that makes accountStatus:active, which effectively replaces the onCreate script that was previously used to achieve the same result. The following excerpt from the Identity Cloud managed object schema displays the default value for accountStatus:

"accountStatus" : {
    "title" : "Status",
    "description" : "Status",
    "viewable" : true,
    "type" : "string",
    "searchable" : true,
    "userEditable" : false,
    "usageDescription" : "",
    "isPersonal" : false,
    "policies" : [
        {
            "policyId": "regexpMatches",
            "params": {
                "regexp": "^(active|inactive)$"
            }
        }
    ],
    "default" : "active"
}

Identity Cloud assumes all default values are valid for the schema. Although Identity Cloud skips policy validation for objects with default values, you can force validation on property values.

Virtual properties

Properties can be derived from other properties within an object. This lets computed and composite values be created in the object. Such derived properties are named virtual properties. The value of a virtual property can be calculated in two ways:

  • Using a script called by the onRetrieve script hook. This script then calculates the current value of the virtual property based on the related properties.

  • Using a query to identify the relationship fields to traverse to reach the managed objects whose state is included in the virtual property, and the fields in these managed objects to include in the value of the virtual property.

    These properties are called relationship-derived virtual properties.

Do not extend the Identity Cloud default managed object schema for the user object type by adding a new virtual property. Instead, use one of the generic extension attributes provided in the default user object schema. For a list of generic extension attributes, see the tables here.

Virtual Properties Using onRetrieve Scripts

The onRetrieve script hook lets you run a script when the object is retrieved. In the case of virtual properties, this script gets the data from related properties and uses it to calculate a value for the virtual property. For more information about running scripts on managed objects, see Run scripts on managed objects.

Relationship-Derived Virtual Properties

Virtual properties can be calculated by Identity Cloud based on relationships and relationship notifications. This means that, rather than calculating the current state when retrieved, the managed object that contains the virtual property is notified of changes in a related object, and the virtual property is recalculated when this notification is received. To configure virtual properties to use relationship notifications, there are two areas that need to be configured:

  • The related managed objects must be configured to use relationship notifications. This lets Identity Cloud know where to send notifications of changes in related objects.

  • To calculate the value of a virtual property, you must configure which relationships to check, and in which order, a notification of a change in a related object is received. You configure this using the queryConfig property.

The queryConfig property tells Identity Cloud the sequence of relationship fields it should traverse in order to calculate (or recalculate) a virtual property, and which fields it should return from that related object. This is done using the following fields:

  • referencedRelationshipFields is an array listing a sequence of relationship fields connecting the current object with the related objects you want to calculate the value of the virtual property from. The first field in the array is a relationship field belonging to the same managed object as the virtual property. The second field is a relationship in the managed object referenced by the first field, and so on.

    For example, the referencedRelationshipFields for effectiveAssignments is ["roles","assignments"]. The first field refers to the roles relationship field in managed/realm-name_user, which references the managed/realm-name_role object. It then refers to the assignments relationship in managed/realm-name_role, which references the managed/realm-name_assignment object. Changes to either related object (managed/realm-name_role or managed/realm-name_assignment) will cause the virtual property value to be recalculated, due to the notify, notifySelf, and notifyRelationships configurations on managed user, role, and assignment. These configurations ensure that any changes in the relationships between a user and their roles, or their roles, and their assignments, as well as any relevant changes to the roles or assignments themselves, such as the modification of temporal constraints on roles, or attributes on assignments, will be propagated to connected users, so their effectiveRoles and effectiveAssignments can be recalculated and potentially synced.

  • referencedObjectFields is an array of object fields that should be returned as part of the virtual property. If this property is not included, the returned properties will be a reference for the related object. To return the entire related object, use *.

  • flattenProperties is a boolean that specifies whether relationship-derived virtual properties should be returned as plain fields rather than as JSON objects with an _id and a _rev. This property is false by default.

    With flattenProperties set to false, and referencedObjectFields set to name, the response to a query on a user’s effectiveAssignments might look something like this:

    "effectiveAssignments": [
        {
            "name": "MyFirstAssignment",
            "_id": "02b166cc-d7ed-46b7-813f-5ed103145e76",
            "_rev": "2"
        },
        {
            "name": "MySecondAssignment",
            "_id": "7162ddd4-591a-413e-a30b-3a5864bee5ec",
            "_rev": "0"
        }
    ]

    With flattenProperties set to true, and referencedObjectFields set to name, the response to the same query looks like this:

    "effectiveAssignments": [
        "MyFirstAssignment",
        "MySecondAssignment"
    ]

    Setting flattenProperties to true also lets singleton relationship-derived virtual properties be initialized to null.

Using queryConfig, the virtual property is recalculated when it receives a notice that changes occurred in the related objects. This can be significantly more efficient than recalculating whenever an object is retrieved, while still ensuring the state of the virtual property is correct.

When you change which fields to return using referencedObjectFields, changes are not reflected until there is a change in the related object that would trigger the virtual property to be recalculated (as specified by the notify, notifySelf, and notifyRelationships configurations). The calculated state of the virtual property is still correct, but since a change is necessary for the state to be updated, the returned fields will still be based on the previous configuration.

Run scripts on managed objects

Before implementing a script, it’s highly recommended that you validate the script over REST. Use scripts in a test environment before deploying them to a production environment.

A number of script hooks let you manipulate managed objects using scripts. Scripts can be triggered during various stages of the lifecycle of the managed object, and are defined in the managed object schema.

You can trigger scripts when a managed object is created (onCreate), updated (onUpdate), retrieved (onRetrieve), deleted (onDelete), validated (onValidate), or stored in the repository (onStore). You can also trigger a script when a change to a managed object triggers an implicit synchronization operation (onSync).

Post-action scripts let you manipulate objects after they are created (postCreate), updated (postUpdate), and deleted (postDelete).

The following sample schema runs a script to check that a role has no members before attempting to delete the role:

{
    "name" : "role",
    "onDelete" : {
        "type" : "text/javascript",
        "file" : "roles/onDelete-roles.js"
},

Track user metadata

Some self-service features, such as progressive profile completion, privacy and consent, and terms and conditions acceptance, rely on user metadata that tracks information related to a managed object state. Such data might include when the object was created, or the date of the most recent change, for example. This metadata is not stored within the object itself, but in a separate resource location.

Because object metadata is stored outside the managed object, state change situations (such as the time of an update) are separate from object changes (the update itself). This separation reduces unnecessary synchronization to targets when the only data that has changed is metadata. Metadata is not returned in a query unless it is specifically requested. Therefore, the volume of data that is retrieved when metadata is not required, is reduced.

To specify which metadata you want to track for an object, add a meta stanza to the object definition in your managed object configuration. The following default configuration tracks the createDate and lastChanged date for managed user objects:

{
  "objects" : [
    {
      "name" : "user",
      ...
      "schema" : {
        ...
      },
      "meta" : {
        "property" : "_meta",
        "resourceCollection" : "internal/usermeta",
        "trackedProperties" : [
          "createDate",
          "lastChanged"
        ]
      },
      ...
    },
    ...
  ]
}

If you are not using the self-service features that require metadata, you can remove the meta stanza from the user object in your managed object schema. Preventing the creation and tracking of metadata where it is not required will improve performance in that scenario.

The metadata configuration includes the following properties:

property

The property that will be dynamically added to the managed object schema for this object.

resourceCollection

The resource location in which the metadata will be stored.

Adjust your repository to match the location you specify here. It’s recommended that you use an internal object path and define the storage in your repo.jdbc.json or repo.ds.json file.

For a JDBC repository, metadata is stored in the metaobjects table by default. The metaobjectproperties table is used for indexing.

For a DS repository, metadata is stored under ou=usermeta,ou=internal,dc=openidm,dc=forgerock,dc=com by default.

User objects stored in a DS repository must include the ou specified in the preceding dnTemplate attribute.

trackedProperties

The properties that will be tracked as metadata for this object. In the previous example, the createDate (when the object was created) and the lastChanged date (when the object was last modified) are tracked.

You cannot search on metadata and it is not returned in the results of a query unless it is specifically requested. To return all metadata for an object, include _fields=,_meta/* in your request. The following example returns a user entry without requesting the metadata:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/bjensen"
{
  "_id": "bjensen",
  "_rev": "000000000444dd1a",
  "mail": "bjensen@example.com",
  "givenName": "Barbara",
  "sn": "Jensen",
  "description": "Created By CSV",
  "userName": "bjensen",
  "telephoneNumber": "1234567",
  "accountStatus": "active",
  "effectiveRoles": [],
  "effectiveAssignments": []
}

The following example returns the same user entry, with their metadata:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/bjensen?_fields=,_meta/*"
{
  "_id": "bjensen",
  "_rev": "000000000444dd1a",
  "mail": "bjensen@example.com",
  "givenName": "Barbara",
  "sn": "Jensen",
  "description": "Created By CSV",
  "userName": "bjensen",
  "telephoneNumber": "1234567",
  "accountStatus": "active",
  "effectiveRoles": [],
  "effectiveAssignments": []
  "_meta": {
    "_ref": "internal/usermeta/284273ff-5e50-4fa4-9d30-4a3cf4a5f642",
    "_refResourceCollection": "internal/usermeta",
    "_refResourceId": "284273ff-5e50-4fa4-9d30-4a3cf4a5f642",
    "_refProperties": {
      "_id": "30076e2e-8db5-4b4d-ab91-5351d2da4620",
      "_rev": "000000001ad09f00"
    },
    "createDate": "2018-04-12T19:53:19.004Z",
    "lastChanged": {
      "date": "2018-04-12T19:53:19.004Z"
    },
    "loginCount": 0,
    "_rev": "0000000094605ed9",
    "_id": "284273ff-5e50-4fa4-9d30-4a3cf4a5f642"
  }
}

Apart from the createDate and lastChanged shown previously, the request also returns the loginCount. This property is stored by default for all objects, and increments with each login request based on password or social authentication. If the object for which metadata is tracked is not an object that "logs in," this field will remain 0.

The request also returns a _meta property that includes relationship information. Identity Cloud uses the relationship model to store the metadata. When the meta stanza is added to the user object definition, the attribute specified by the property ("property" : "_meta", in this case) is added to the schema as a uni-directional relationship to the resource collection specified by resourceCollection. In this example, the user object’s _meta field is stored as an internal/usermeta object. The _meta/_ref property shows the full resource path to the internal object where the metadata for this user is stored.

Users

In the IDM admin UI, user identities are referred to as user managed objects, also known as managed users.

You can retrieve, add, change, and delete managed users:

  • By using the Identities > Manage option in the IDM admin UI.

  • By using one of the Manage > realm-name_user options in the IDM admin UI.

  • By using the REST interface at the context path /openidm/managed/realm-name_user.

Managed users examples

The following examples show how to retrieve, add, change, and delete users over the REST interface. For a reference of all managed user endpoints and actions, see the Managed users endpoint.

When viewing users with the IDM admin UI, the User List page supports specialized filtering with the Advanced Filter option. This page can help you build user queries, such as the ones in these examples.

Some examples in this documentation use client-assigned IDs (such as bjensen and scarter) when creating objects because it makes the examples easier to read. If you create objects using the IDM admin UI, they are created with server-assigned IDs (such as 55ef0a75-f261-47e9-a72b-f5c61c32d339). Generally, immutable server-assigned UUIDs are used in production environments.

Retrieve the IDs of all managed users in the repository

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=true&_fields=_id"
{
  "result": [
    {
      "_id": "bjensen",
      "_rev": "0000000079b78ace"
    },
    {
      "_id": "scarter",
      "_rev": "0000000070e587a7"
    },
    ...
  ],
  ...
}

Query managed users for a specific user

The _queryFilter requires double quotes, or the URL-encoded equivalent (%22), around the search term. This example uses the URL-encoded equivalent:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=userName+eq+%22scarter%22"
{
  "result": [
    {
      "_id": "scarter",
      "_rev": "0000000070e587a7",
      "userName": "scarter",
      "givenName": "Sam",
      "sn": "Carter",
      "telephoneNumber": "12345678",
      "active": "true",
      "mail": "scarter@example.com",
      "accountStatus": "active",
      "effectiveAssignments": [],
      "effectiveRoles": []
    }
  ],
  ...
}

This example uses single quotes around the URL to avoid conflicts with the double quotes around the search term:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=userName+eq+"scarter"'
{
  "result": [
    {
      "_id": "scarter",
      "_rev": "0000000070e587a7",
      "userName": "scarter",
      "givenName": "Sam",
      "sn": "Carter",
      "telephoneNumber": "12345678",
      "active": "true",
      "mail": "scarter@example.com",
      "accountStatus": "active",
      "effectiveAssignments": [],
      "effectiveRoles": []
    }
  ],
  ...
}

Retrieve a managed user by their ID

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter"
{
  "_id": "scarter",
  "_rev": "0000000070e587a7",
  "userName": "scarter",
  "givenName": "Sam",
  "sn": "Carter",
  "telephoneNumber": "12345678",
  "active": "true",
  "mail": "scarter@example.com",
  "accountStatus": "active",
  "effectiveAssignments": [],
  "effectiveRoles": []
}

Add a user with a specific user ID

curl \
--header "Content-Type: application/json" \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "If-None-Match: *" \
--request PUT \
--data '{
  "userName": "bjackson",
  "sn": "Jackson",
  "givenName": "Barbara",
  "mail": "bjackson@example.com",
  "telephoneNumber": "082082082",
  "password": "Passw0rd"
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/bjackson"
{
  "_id": "bjackson",
  "_rev": "0000000055c185c5",
  "userName": "bjackson",
  "sn": "Jackson",
  "givenName": "Barbara",
  "mail": "bjackson@example.com",
  "telephoneNumber": "082082082",
  "accountStatus": "active",
  "effectiveAssignments": [],
  "effectiveRoles": []
}

Add a user with a system-generated ID

curl \
--header "Content-Type: application/json" \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request POST \
--data '{
  "userName": "pjensen",
  "sn": "Jensen",
  "givenName": "Pam",
  "mail": "pjensen@example.com",
  "telephoneNumber": "082082082",
  "password": "Passw0rd"
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_action=create"
{
  "_id": "9d92cdc8-8b22-4037-a344-df960ea66194",
  "_rev": "00000000a4bf9006",
  "userName": "pjensen",
  "sn": "Jensen",
  "givenName": "Pam",
  "mail": "pjensen@example.com",
  "telephoneNumber": "082082082",
  "accountStatus": "active",
  "effectiveAssignments": [],
  "effectiveRoles": []
}

Update a user

This example checks whether user bjensen exists, then replaces her telephone number with the new data provided in the request body:

curl \
--header "Content-Type: application/json" \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request POST \
--data '[
  {
    "operation": "replace",
    "field": "/telephoneNumber",
    "value": "0763483726"
  }
]' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_action=patch&_queryFilter=userName+eq+'bjackson'"
{
  "userName": "bjackson",
  "sn": "Jackson",
  "givenName": "Barbara",
  "mail": "bjackson@example.com",
  "telephoneNumber": "0763483726",
  "accountStatus": "active",
  "effectiveAssignments": [],
  "effectiveRoles": [],
  "_rev": "000000008c0f8617",
  "_id": "bjackson"
}

Delete a user

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request DELETE \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/bjackson"
{
  "_id": "bjackson",
  "_rev": "000000008c0f8617",
  "userName": "bjackson",
  "sn": "Jackson",
  "givenName": "Barbara",
  "mail": "bjackson@example.com",
  "telephoneNumber": "0763483726",
  "accountStatus": "active",
  "effectiveAssignments": [],
  "effectiveRoles": []
}

Relationships between objects

Relationships are references between managed objects. Roles and Organizations are implemented using relationships.

The default Identity Cloud schema defines several user properties as relationships. Adding properties to the user schema is not supported, so you cannot define additional user properties as relationships.

Create a relationship between two objects

In the default Identity Cloud schema, several user properties are defined as relationships. For example, the manager relationship. Relationships let you reference one managed object from another, using the _ref* relationship properties. Three properties make up a relationship reference:

  • _refResourceCollection specifies the container of the referenced object (for example, managed/realm-name_user).

  • _refResourceId specifies the ID of the referenced object. This is generally a system-generated UUID, such as 9dce06d4-2fc1-4830-a92b-bd35c2f6bcbb. For clarity, this section uses client-assigned IDs such as bjensen and psmith.

  • _ref is a derived path that is a combination of _refResourceCollection and a URL-encoded _refResourceId.

For example, imagine that you are creating a new user, psmith, and that psmith’s manager will be bjensen. You would add psmith’s user entry, and reference bjensen’s entry with the _ref property, as follows:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "If-None-Match: *" \
--header "Content-Type: application/json" \
--request PUT \
--data '{
  "sn":"Smith",
  "userName":"psmith",
  "givenName":"Patricia",
  "displayName":"Patti Smith",
  "description" : "psmith - new user",
  "mail" : "psmith@example.com",
  "phoneNumber" : "0831245986",
  "password" : "Passw0rd",
  "manager" : {"_ref" : "managed/realm-name_user/bjensen"}
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/psmith"
{
  "_id": "psmith",
  "_rev": "00000000ec41097c",
  "sn": "Smith",
  "userName": "psmith",
  "givenName": "Patricia",
  "displayName": "Patti Smith",
  "description": "psmith - new user",
  "mail": "psmith@example.com",
  "phoneNumber": "0831245986",
  "accountStatus": "active",
  "effectiveRoles": [],
  "effectiveAssignments": []
}

Note that relationship information is not returned by default. To show the relationship in psmith’s entry, you must explicitly request her manager entry, as follows:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/psmith?_fields=manager"
{
  "_id": "psmith",
  "_rev": "00000000ec41097c",
  "manager": {
    "_ref": "managed/realm-name_user/bjensen",
    "_refResourceCollection": "managed/realm-name_user",
    "_refResourceId": "bjensen",
    "_refProperties": {
      "_id": "ffc6f0f3-93db-4939-b9eb-1f8389a59a52",
      "_rev": "0000000081aa991a"
    }
  }
}

If a relationship changes, you can query the updated relationship state when any referenced managed objects are queried. So, after creating user psmith with manager bjensen, a query on bjensen’s user entry will show a reference to psmith’s entry in her reports property (because the reports property is configured as the reversePropertyName of the manager property). The following query shows the updated relationship state for bjensen:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/bjensen?_fields=reports"
{
  "_id": "bjensen",
  "_rev": "0000000057b5fe9d",
  "reports": [
    {
      "_ref": "managed/realm-name_user/psmith",
      "_refResourceCollection": "managed/realm-name_user",
      "_refResourceId": "psmith",
      "_refProperties": {
        "_id": "ffc6f0f3-93db-4939-b9eb-1f8389a59a52",
        "_rev": "0000000081aa991a"
      }
    }
  ]
}

Identity Cloud maintains referential integrity by deleting the relationship reference, if the object referred to by that relationship is deleted. In our example, if bjensen’s user entry is deleted, the corresponding reference in psmith’s manager property is removed.

Configure relationship change notification

A relationship exists between two managed objects. By default, when a relationship changes (when it is created, updated, or deleted), the managed objects on either side of the relationship are not notified of that change. This means that the state of each object with respect to that relationship field is not recalculated until the object is read. This default behavior improves performance, especially in the case where many objects are affected by a single relationship change.

For roles, a special kind of relationship, change notification is configured by default. The purpose of this default configuration is to notify managed users when any of the relationships that link users, roles, and assignments are manipulated. For more information about relationship change notification in the specific case of managed roles, see Roles and relationship change notification.

To change the default configuration, or to set up notification for other relationship changes, use the notify* properties in the relationship definition, as described in this section.

A relationship exists between an origin object and a referenced object. These terms reflect which managed object is specified in the URL (for example managed/realm-name_user/psmith), and which object is referenced by the relationship (_ref*) properties. For more information about the relationship properties, see Create a relationship between two objects.

In the previous example, a PUT on managed/realm-name_user/psmith with "manager" : {_ref : "managed/realm-name_user/bjensen"}, causes managed/realm-name_user/psmith to be the origin object, and managed/realm-name_user/bjensen to be the referenced object for that relationship, as shown in the following illustration:

Illustration shows the origin and referenced objects in a relationship
Figure 1. Relationship Objects

Note that for the reverse relationship (a PUT on managed/realm-name_user/bjensen with "reports" : [{_ref = "managed/realm-name_user/psmith"}]) managed/realm-name_user/bjensen would be the origin object, and managed/realm-name_user/psmith would be the referenced object.

By default, when a relationship changes, neither the origin object nor the referenced object is notified of the change. So, with the PUT on managed/realm-name_user/psmith with "manager" : {_ref : "managed/realm-name_user/bjensen"}, neither psmith’s object nor bjensen’s object is notified.

Auditing is not tied to relationship change notification and is always triggered when a relationship changes. Therefore, relationship changes are audited, regardless of the notify and notifySelf properties.

To configure relationship change notification, set the notify and notifySelf properties in your managed object schema. These properties specify whether objects that reference relationships are notified of a relationship change:

notifySelf

Notifies the origin object of the relationship change.

In our example, if the manager definition includes "notifySelf" : true, and if the relationship is changed through a URL that references psmith, then psmith’s object would be notified of the change. For example, for a CREATE, UPDATE or DELETE request on the psmith/manager, psmith would be notified, but the managed object referenced by this relationship (bjensen) would not be notified.

If the relationship were manipulated through a request to bjensen/reports, then bjensen would only be notified if the reports relationship specified "notifySelf" : true.

notify

Notifies the referenced object of the relationship change. Set this property on the resourceCollection of the relationship property.

In our example, assume that the manager definition has a resourceCollection with a path of managed/realm-name_user, and that this object specifies "notify" : true. If the relationship changes through a CREATE, UPDATE, or DELETE on the URL psmith/manager, then the reference object (managed/realm-name_user/bjensen) would be notified of the change to the relationship.

notifyRelationships

This property controls the propagation of notifications out of a managed object when one of its properties changes through an update or patch, or when that object receives a notification through one of these fields.

The notifyRelationships property takes an array of relationships as a value; for example, "notifyRelationships" : ["relationship1", "relationship2"]. The relationships specified here are fields defined on the managed object type (which might itself be a relationship).

Notifications are propagated according to the recipient’snotifyRelationships configuration. If a managed object type is notified of a change through one if its relationship fields, the notification is done according to the configuration of the recipient object. To illustrate, look at the attributes property in the default managed/realm-name_assignment object:

{
    "name" : "assignment",
    "schema" : {
        ...
        "properties" : {
            ...
            "attributes" : {
                "description" : "The attributes operated on by this assignment.",
                "title" : "Assignment Attributes",
                ...
                "notifyRelationships" : ["roles"]
            },
...

This configuration means that if an assignment is updated or patched, and the assignment’s attributes change in some way, all the roles connected to that assignment are notified. Because the role managed object has "notifyRelationships" : ["members"] defined on its assignments field, the notification that originated from the change to the assignment attribute is propagated to the connected roles, and then out to the members of those roles.

So, the role is notified through its assignments field because an attribute in the assignment changed. This notification is propagated out of the members field because the role definition has "notifyRelationships" : ["members"] on its assignments field.

By default, roles, assignments, and members use relationship change notification to ensure that relationship changes are accurately provisioned.

For example, the default user object includes a roles property with notifySelf set to true:

{
   "name" : "user",
   ...
   "schema" : {
       ...
       "properties" : {
           ...
           "roles" : {
               "description" : "Provisioning Roles",
               ...
               "items" : {
                   "type" : "relationship",
                   ...
                   "reverseRelationship" : true,
                   "reversePropertyName" : "members",
                   "notifySelf" : true,
                   ...
               }
...

In this case, notifySelf indicates the origin or user object. If any changes are made to a relationship referencing a role through a URL that includes a user, the user will be notified of the change. For example, if there is a CREATE on managed/realm-name_user/psmith/roles which specifies a set of references to existing roles, user psmith will be notified of the change.

Similarly, the role object includes a members property. That property includes the following schema definition:

{
    "name" : "role",
    ...
    "schema" : {
        ...
        "properties" : {
            ...
            "members" : {
                ...
                "items" : {
                    "type" : "relationship",
                    ...
                    "properties" : {
                        ...
                        "resourceCollection" : [
                            {
                                "notify" : true,
                                "path" : "managed/realm-name_user",
                                "label" : "User",
                                ...
                            }
                        ]
                    }
...

Notice the "notify" : true setting on the resourceCollection. This setting indicates that if the relationship is created, updated, or deleted through a URL that references that role, all objects in that resource collection (in this case, managed/realm-name_user objects) that are identified as members of that role must be notified of the change.

  • To notify an object at the end of a relationship that the relationship has changed (using the notify property), the relationship must be bidirectional ("reverseRelationship" : true).

    When an object is notified of a relationship state change (create, delete, or update), part of that notification process involves calculating the changed object state with respect to the changed relationship field. For example, if a managed user is notified that a role has been created, the user object calculates its base state, and the state of its roles field, before and after the new role was created. This before and after state is then reconciled. An object that is referenced by a forward (unidirectional) relationship does not have a field that references that relationship; the object is "pointed-to", but does not "point-back". Because this object cannot calculate its before and after state with respect to the relationship field, it cannot be notified.

    Similarly, relationships that are notified of changes to the objects that reference them must be bidirectional relationships.

    If you configure relationship change notification on a unidirectional relationship, Identity Cloud throws an exception.

  • You cannot configure relationship change notification in the IDM admin UI; you must update the managed object schema directly.

Validate relationships between objects

Optionally, you can specify that a relationship between two objects must be validated when the relationship is created. For example, you can indicate that a user cannot reference a role, if that role does not exist.

When you create a new relationship type, validation is disabled by default, because it involves an expensive query to the relationship that is not always required.

To configure validation of a referenced relationship, set "validate": true in the managed object schema. The default schema enables validation for the following relationships:

  • For user objects—roles, managers, and reports

  • For role objects—members and assignments

  • For assignment objects—roles

Query relationships bidirectionally

In most cases, relationships between two objects in the Identity Cloud schema are defined in both directions. For example, a relationship between a user and his manager might indicate a reverse relationship between the manager and her direct report. Reverse relationships are particularly useful for queries. You might want to query jdoe’s user entry to discover who his manager is, or query bjensen’s user entry to discover all the users who report to bjensen.

A reverse relationship is part of the relationship definition. Consider the following sample excerpt of the default managed object configuration:

"reports" : {
    "description" : "Direct Reports",
    "title" : "Direct Reports",
    ...
    "type" : "array",
    "returnByDefault" : false,
    "items" : {
        "type" : "relationship",
        "reverseRelationship" : true,
        "reversePropertyName" : "manager",
        "validate" : true,
        ...
    }
...

The reports property is a relationship between users and managers. So, you can refer to a managed user’s reports by referencing the reports. However, the reports property is also a reverse relationship ("reverseRelationship" : true) which means that you can list all users that reference that report.

You can list all users whose manager property is set to the currently queried user.

The reverse relationship includes an optional resourceCollection that lets you query a set of objects, based on specific fields:

"resourceCollection" : [
    {
        "path" : "managed/realm-name_user",
        "label" : "User",
        "query" : {
            "queryFilter" : "true",
            "fields" : [
                "userName",
                "givenName",
                "sn"
            ]
        }
    }
]

The path property of the resourceCollection points to the set of objects to be queried. If this path is not in the local repository, the link expansion can incur a significant performance cost. Although the resourceCollection is optional, the same performance cost is incurred if the property is absent.

The query property indicates how you will query this resource collection to configure the relationship. In this case, "queryFilter" : "true", indicates that you can search on any of the properties listed in the fields array when you are assigning a manager to a user or a new report to a manager.

Grant relationships conditionally

Relationships can be granted dynamically, based on a specified condition. In order to conditionally grant a relationship, the schemas for the resources you are creating a relationship between need to be configured to support conditional association. To do this, three fields in the schema are used:

conditionalAssociation

Boolean. This property is applied to the resourceCollection for the grantor of the relationship. For example, the members relationship on managed/realm-name_role specifies that there is a conditional association with the managed/realm-name_user resource:

"resourceCollection" : [
    {
        "notify" : true,
        "conditionalAssociation" : true,
        "path" : "managed/realm-name_user",
        "label" : "User",
        "query" : {
            "queryFilter" : "true",
            "fields" : [
                "userName",
                "givenName",
                "sn"
            ]
        }
    }
]
conditionalAssociationField

This property is a string, specifying the field used to determine whether a conditional relationship is granted. The field is applied to the resourceCollection of the grantee of the relationship. For example, the roles relationship on managed/realm-name_user specifies that the conditional association with managed/realm-name_role is defined by the condition field in managed/realm-name_role:

"resourceCollection" : [
    {
        "path" : "managed/realm-name_role",
        "label" : "Role",
        "conditionalAssociationField" : "condition",
        "query" : {
            "queryFilter" : "true",
            "fields" : [
                "name"
            ]
        }
    }
]

The field name specified will usually be condition if you are using default schema, but can be any field that evaluates a condition and has been flagged as isConditional.

isConditional

Boolean. This is applied to the field you wish to check to determine whether membership in a relationship is granted. Only one field on a resource can be marked as isConditional. For example, in the relationship between managed/realm-name_user and managed/realm-name_role, conditional membership in the relationship is determined by the query filter specified in the managed/realm-name_role condition field:

"condition" : {
    "description" : "A conditional filter for this role",
    "title" : "Condition",
    "viewable" : false,
    "searchable" : false,
    "isConditional" : true,
    "type" : "string"
}

Conditions support both properties and virtual properties derived from other relationships, if the query property has been configured. Conditions are a powerful tool for dynamically creating relationships between two objects. An example of conditional relationships in use is covered in Grant a Role Based on a Condition.

View relationships over REST

By default, information about relationships is not returned as the result of a GET request on a managed object. You must explicitly include the relationship property in the request, for example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/psmith?_fields=manager"
{
  "_id": "psmith",
  "_rev": "0000000014c0b68d",
  "manager": {
    "_ref": "managed/realm-name_user/bjensen",
    "_refResourceCollection": "managed/realm-name_user",
    "_refResourceId": "bjensen",
    "_refProperties": {
      "_id": "42418f09-ad6c-4b77-bf80-2a12d0c44678",
      "_rev": "00000000288b921e"
    }
  }
}

To obtain more information about the referenced object (psmith’s manager, in this case), you can include additional fields from the referenced object in the query, using the syntax object/property (for a simple string value) or object/*/property (for an array of values).

The following example returns the email address and contact number for psmith’s manager:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/psmith?_fields=manager/mail,manager/telephoneNumber"
{
  "_id": "psmith",
  "_rev": "0000000014c0b68d",
  "manager": {
    "_rev": "000000005bac8c10",
    "_id": "bjensen",
    "telephoneNumber": "12345678",
    "mail": "bjensen@example.com",
    "_ref": "managed/realm-name_user/bjensen",
    "_refResourceCollection": "managed/realm-name_user",
    "_refResourceId": "bjensen",
    "_refProperties": {
      "_id": "42418f09-ad6c-4b77-bf80-2a12d0c44678",
      "_rev": "00000000288b921e"
    }
  }
}

To query all the relationships associated with a managed object, query the reference (*_ref) property of that object. For example, the following query shows all the objects that are referenced by psmith’s entry:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/psmith?_fields=*_ref"
{
  "_id": "psmith",
  "_rev": "0000000014c0b68d",
  "reports": [],
  "manager": {
    "_ref": "managed/realm-name_user/bjensen",
    "_refResourceCollection": "managed/realm-name_user",
    "_refResourceId": "bjensen",
    "_refProperties": {
      "_id": "42418f09-ad6c-4b77-bf80-2a12d0c44678",
      "_rev": "00000000288b921e"
    }
  },
  "roles": [],
  "_meta": {
    "_ref": "internal/usermeta/601a3086-8c64-4966-b33c-7a213b13d859",
    "_refResourceCollection": "internal/usermeta",
    "_refResourceId": "601a3086-8c64-4966-b33c-7a213b13d859",
    "_refProperties": {
      "_id": "9de71bd7-1e1b-462e-b565-ac0a7d2f9269",
      "_rev": "0000000037f79a00"
    }
  },
  "authzRoles": [],
  "_notifications": [
    {
      "_ref": "internal/notification/3000bb64-4619-490a-8c4b-50ae7ca6b20c",
      "_refResourceCollection": "internal/notification",
      "_refResourceId": "3000bb64-4619-490a-8c4b-50ae7ca6b20c",
      "_refProperties": {
        "_id": "f54b6f84-7d3f-4486-a7c1-676fca03eeab",
        "_rev": "00000000748da107"
      }
    }
  ]
}

To expand that query to show all fields within each relationship, add a wildcard as follows:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/psmith?_fields=*_ref/*"

Which outputs the following:

{
  "_id": "psmith",
  "_rev": "0000000014c0b68d",
  "reports": [],
  "manager": {
    "_rev": "000000005bac8c10",
    "_id": "bjensen",
    "userName": "bjensen",
    "givenName": "Babs",
    "sn": "Jensen",
    "telephoneNumber": "12345678",
    "active": "true",
    "mail": "bjensen@example.com",
    "accountStatus": "active",
    "effectiveAssignments": [],
    "effectiveRoles": [],
    "_ref": "managed/realm-name_user/bjensen",
    "_refResourceCollection": "managed/realm-name_user",
    "_refResourceId": "bjensen",
    "_refProperties": {
      "_id": "42418f09-ad6c-4b77-bf80-2a12d0c44678",
      "_rev": "00000000288b921e"
    }
  },
  "roles": [],
  "_meta": {
    "_rev": "0000000079e86d8d",
    "_id": "601a3086-8c64-4966-b33c-7a213b13d859",
    "createDate": "2020-07-29T08:52:20.061794Z",
    "lastChanged": {
      "date": "2020-07-29T11:52:16.424167Z"
    },
    "loginCount": 0,
    "_ref": "internal/usermeta/601a3086-8c64-4966-b33c-7a213b13d859",
    "_refResourceCollection": "internal/usermeta",
    "_refResourceId": "601a3086-8c64-4966-b33c-7a213b13d859",
    "_refProperties": {
      "_id": "9de71bd7-1e1b-462e-b565-ac0a7d2f9269",
      "_rev": "0000000037f79a00"
    }
  },
  "authzRoles": [],
  "_notifications": [
    {
      "_rev": "00000000d93a6598",
      "_id": "3000bb64-4619-490a-8c4b-50ae7ca6b20c",
      "notificationType": "info",
      "message": "Your profile has been updated.",
      "createDate": "2020-07-29T11:52:16.517200Z",
      "_ref": "internal/notification/3000bb64-4619-490a-8c4b-50ae7ca6b20c",
      "_refResourceCollection": "internal/notification",
      "_refResourceId": "3000bb64-4619-490a-8c4b-50ae7ca6b20c",
      "_refProperties": {
        "_id": "f54b6f84-7d3f-4486-a7c1-676fca03eeab",
        "_rev": "00000000748da107"
      }
    }
  ]
}

Metadata is implemented using the relationships mechanism so when you request all relationships for a user (with _ref/), you will also get all the metadata for that user, if metadata is being tracked. For more information, see Track user metadata.

Roles

The managed role object is a default managed object type that uses the relationships mechanism. You should understand how relationships work before you read about Identity Cloud roles.

In the ForgeRock Identity Cloud®, the default role object is named alpha_role.

Identity Cloud role types

Identity Cloud supports two types of roles:

  • Provisioning roles : used to specify how objects are provisioned to an external system.

    Provisioning roles are created as managed roles, at the context path openidm/managed/realm-name_role/role-name, and are granted to managed users as values of the user’s roles property.

  • Authorization roles : used to specify the authorization rights of a managed object internally, within Identity Cloud.

    Authorization roles are created as internal roles, at the context path openidm/internal/role/role-name, and are granted to managed users as values of the user’s authzRoles property.

Provisioning roles and authorization roles use relationships to link the role to the managed object to which it applies. Authorization roles can also be granted statically, during authentication, with the defaultUserRoles property.

Managed roles

For information about internal authorization roles, and how Identity Cloud controls authorization to its own endpoints, see Authorization and roles.

Managed roles are defined like any other managed object, and are granted to users through the relationships mechanism. A managed role can be granted manually, as a static value of the user’s roles attribute, or dynamically, as a result of a condition or script. For example, a user might be granted a role such as sales-role dynamically, if that user is in the sales organization.

A user’s roles attribute takes an array of references as a value, where the references point to the managed roles. For example, if user bjensen has been granted two roles (employee and supervisor), the value of bjensen’s roles attribute would look something like the following:

"roles": [
  {
    "_ref": "managed/realm-name_role/employee",
    "_refResourceCollection": "managed/realm-name_role",
    "_refResourceId": "employee",
    "_refProperties": {
      "_grantType": "",
      "_id": "bb399428-21a9-4b01-8b74-46a7ac43e0be",
      "_rev": "00000000e43e9ba7"
    }
  },
  {
    "_ref": "managed/realm-name_role/supervisor",
    "_refResourceCollection": "managed/realm-name_role",
    "_refResourceId": "supervisor",
    "_refProperties": {
      "_grantType": "",
      "_id": "9f7d124b-c7b1-4bcf-9ece-db4900e37c31",
      "_rev": "00000000e9c19d26"
    }
  }
]

The _refResourceCollection is the container that holds the role. The _refResourceId is the ID of the role. The _ref property is a resource path that is derived from the _refResourceCollection and the URL-encoded _refResourceId. _refProperties provides more information about the relationship.

Some of the examples in this documentation set use client-assigned IDs (such as bjensen and scarter) for the user objects because it makes the examples easier to read. If you create objects using the IDM admin UI, they are created with server-assigned IDs (such as 55ef0a75-f261-47e9-a72b-f5c61c32d339). This particular example uses a client-assigned role ID that is the same as the role name. All other examples in this chapter use server-assigned IDs. Generally, immutable server-assigned UUIDs are used for all managed objects in production environments.

Manipulate roles

These sections show the REST calls to create, read, update, and delete managed roles, and to grant roles to users. For information about using roles to provision users to external systems, see Use assignments to provision users.

Create a role

Using the IDM admin UI

  1. From the navigation bar, click Manage > Role.

  2. On the Roles page, click New Role.

  3. On the New Role page, enter a name and description, and click Save.

  4. Optionally, do any of the following, and click Save:

    • To restrict the role grant to a set time period, enable Temporal Constraint, and set the Timezone Offset, Start Date, and End Date.

    • To define a query filter that dynamically grants the role to members, enable Condition, and define the query.

Using REST

To create a role, send a PUT or POST request to the /openidm/managed/realm-name_role context path. The following example creates a managed role named employee:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "name": "employee",
  "description": "Role granted to workers on the company payroll"
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role?_action=create"
{
  "_id": "5790220a-719b-49ad-96a6-6571e63cbaf1",
  "_rev": "0000000079c6644f",
  "name": "employee",
  "description": "Role granted to workers on the company payroll"
}

By default, the role name must be unique. To change this behavior, adjust the policy validation on the role property in your managed object schema.

This employee role has no corresponding assignments. Assignments are what enables the provisioning logic to the external system. Assignments are created and maintained as separate managed objects, and are referred to within role definitions. For more information about assignments, see Use assignments to provision users.

List roles

To list all managed roles over REST, query the openidm/managed/realm-name_role endpoint. The following example shows the employee role that you created in the previous example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role?_queryFilter=true"
{
  "result": [
    {
      "_id": "5790220a-719b-49ad-96a6-6571e63cbaf1",
      "_rev": "0000000079c6644f",
      "name": "employee",
      "description": "Role granted to workers on the company payroll"
    }
  ],
  ...
}

To display all configured managed roles in the IDM admin UI, select Manage > Role.

If you have a large number of roles, select Advanced Filter to build a more complex query filter to display only the roles you want.

Grant roles to a user

You grant roles to users through the relationship mechanism. Relationships are essentially references from one managed object to another; in this case, from a user object to a role object. For more information about relationships, see Relationships between objects.

You can grant roles statically or dynamically.

To grant a role statically, you must do one of the following:

  • Update the value of the user’s roles property to reference the role.

  • Update the value of the role’s members property to reference the user.

Dynamic role grants use the result of a condition or script to update a user’s list of roles.

Grant roles statically

Grant a role to a user statically using the REST interface or the IDM admin UI as follows:

Using REST

Use one of the following methods to grant a role to a user over REST:

  • Add the user as a role member. The following example adds user scarter as a member of the role (5790220a-719b-49ad-96a6-6571e63cbaf1):

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
      "_ref":"managed/realm-name_user/scarter",
      "_refProperties":{}
    }' \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/5790220a-719b-49ad-96a6-6571e63cbaf1/members?_action=create"
    {
      "_id": "4c32ae53-abed-45f8-bc84-c367e2b0e194",
      "_rev": "00000000c67a99ce",
      "_ref": "managed/realm-name_user/scarter",
      "_refResourceCollection": "managed/realm-name_user",
      "_refResourceId": "scarter",
      "_refProperties": {
        "_id": "4c32ae53-abed-45f8-bc84-c367e2b0e194",
        "_rev": "00000000c67a99ce"
      }
    }
    This preferred method does not incur an unnecessary performance cost when working with a role that contains many members.
  • Update the user’s roles property to refer to the role.

    The following example grants the employee role (5790220a-719b-49ad-96a6-6571e63cbaf1) to user scarter:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request PATCH \
    --data '[
      {
        "operation": "add",
        "field": "/roles/-",
        "value": {"_ref" : "managed/realm-name_role/5790220a-719b-49ad-96a6-6571e63cbaf1"}
      }
    ]' \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter"
    {
      "_id": "scarter",
      "_rev": "000000003be825ce",
      "mail": "scarter@example.com",
      "givenName": "Steven",
      "sn": "Carter",
      "description": "Created By CSV",
      "userName": "scarter",
      "telephoneNumber": "1234567",
      "accountStatus": "active",
      "effectiveRoles": [
        {
          "_ref": "managed/realm-name_role/5790220a-719b-49ad-96a6-6571e63cbaf1"
        }
      ],
      "effectiveAssignments": []
    }

    Note that scarter’s effectiveRoles attribute has been updated with a reference to the new role. For more information about effective roles and effective assignments, see Effective roles and effective assignments.

    When you update a user’s existing roles array, use the - special index to add the new value to the set. For more information, see Set semantic arrays in Patch Operation: Add.

  • Update the role’s members property to refer to the user.

    The following sample command makes scarter a member of the employee role:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request PATCH \
    --data '[
      {
        "operation": "add",
        "field": "/members/-",
        "value": {"_ref" : "managed/realm-name_user/scarter"}
      }
    ]' \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/5790220a-719b-49ad-96a6-6571e63cbaf1"
    {
      "_id": "5790220a-719b-49ad-96a6-6571e63cbaf1",
      "_rev": "0000000079c6644f",
      "name": "employee",
      "description": "Role granted to workers on the company payroll"
    }

    The members property of a role is not returned by default in the output. To show all members of a role, you must specifically request the relationship properties (*_ref) in your query. The following example lists the members of the employee role (currently only scarter):

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request GET \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/5790220a-719b-49ad-96a6-6571e63cbaf1?_fields=*_ref,name"
    {
      "_id": "5790220a-719b-49ad-96a6-6571e63cbaf1",
      "_rev": "0000000079c6644f",
      "name": "employee",
      "assignments": [],
      "members": [
        {
          "_ref": "managed/realm-name_user/scarter",
          "_refResourceCollection": "managed/realm-name_user",
          "_refResourceId": "scarter",
          "_refProperties": {
            "_id": "7ad15a7b-6806-487b-900d-db569927f56d",
            "_rev": "0000000075e09cbf"
          }
        }
      ]
    }
  • You can replace an existing role grant with a new one by using the replace operation in your patch request.

    The following command replaces scarter’s entire roles entry (that is, overwrites any existing roles) with a single entry, the reference to the employee role (ID 5790220a-719b-49ad-96a6-6571e63cbaf1):

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request PATCH \
    --data '[
      {
        "operation": "replace",
        "field": "/roles",
        "value": [
          {"_ref":"managed/realm-name_role/5790220a-719b-49ad-96a6-6571e63cbaf1"}
        ]
      }
    ]' \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter"
    {
      "_id": "scarter",
      "_rev": "00000000da112702",
      "mail": "scarter@example.com",
      "givenName": "Steven",
      "sn": "Carter",
      "description": "Created By CSV",
      "userName": "scarter",
      "telephoneNumber": "1234567",
      "accountStatus": "active",
      "effectiveRoles": [
        {
          "_ref": "managed/realm-name_role/5790220a-719b-49ad-96a6-6571e63cbaf1"
        }
      ],
      "effectiveAssignments": []
    }
Using the IDM admin UI

Use one of the following UI methods to grant a role to a user:

Update the user entry:

  1. Select Manage > User, and select the user to whom you want to grant the role.

  2. Select the Provisioning Roles tab, and select Add Provisioning Roles.

  3. Select the role from the drop-down list, and select Add.

Update the role entry:

  1. Select Manage > Role, and select the role that you want to grant.

  2. Select the Role Members tab, and select Add Role Members.

  3. Select the user from the drop-down list, and select Add.

Grant Roles Dynamically

Grant a role dynamically by using one of the following methods:

  • Use a condition, expressed as a query filter, in the role definition. If the condition is true for a particular member, that member is granted the role. Conditions can be used in both managed and internal roles.

  • Use a custom script to define a more complex role-granting strategy.

Grant a Role Based on a Condition

A role that is granted based on a defined condition is called a conditional role. To create a conditional role, include a query filter in the role definition.

To create a conditional role by using the IDM admin UI, select Condition on the role Details page, then define the query filter that will be used to assess the condition.

To create a conditional role over REST, include the query filter as a value of the condition property in the role definition. The following example creates a role, fr-employee, that will be granted only to those users who live in France (whose country property is set to FR):

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "name": "fr-employee",
  "description": "Role granted to employees resident in France",
  "condition": "/country eq \"FR\""
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role?_action=create"
{
  "_id": "eb18a2e2-ee1e-4cca-83fb-5708a41db94f",
  "_rev": "000000004085704c",
  "name": "fr-employee",
  "description": "Role granted to employees resident in France",
  "condition": "/country eq \"FR\""
}

When a conditional role is created or updated, Identity Cloud automatically assesses all managed users, and recalculates the value of their roles property, if they qualify for that role. When a condition is removed from a role, that is, when the role becomes an unconditional role, all conditional grants are removed. So, users who were granted the role based on the condition, have that role removed from their roles property.

When a conditional role is defined in an existing data set, every user entry (including the mapped entries on remote systems) must be updated with the assignments implied by that conditional role. The time that it takes to create a new conditional role is impacted by the following items:

  • The number of managed users affected by the condition.

  • The number of assignments related to the conditional role.

  • The average time required to provision updates to all remote systems affected by those assignments.

In a data set with a very large number of users, creating a new conditional role can therefore incur a significant performance cost when you create it. Ideally, you should set up your conditional roles at the beginning of your deployment to avoid performance issues later.

Query a User’s Roles

To query user roles over REST, query the user’s roles property. The following example shows that scarter has been granted two roles—an employee role, and an fr-employee role:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter/roles?_queryFilter=true&_fields=_ref/*,name"
{
  "result": [
    {
      "_id": "5a023862-654d-4d7f-b9d0-7c151b8dede5",
      "_rev": "00000000baa999c1",
      "_refResourceCollection": "managed/realm-name_role",
      "_refResourceId": "b8783543-869a-4bd4-907e-9c1d89f826ae",
      "_refResourceRev": "0000000027a959cf",
      "name": "employee",
      "_ref": "managed/realm-name_role/b8783543-869a-4bd4-907e-9c1d89f826ae",
      "_refProperties": {
        "_id": "5a023862-654d-4d7f-b9d0-7c151b8dede5",
        "_rev": "00000000baa999c1"
      }
    },
    {
      "_id": "b281ffdf-477e-4211-a112-84476435bab2",
      "_rev": "00000000d612a248",
      "_refResourceCollection": "managed/realm-name_role",
      "_refResourceId": "01ee6191-75d8-4d4b-9291-13a46592c57a",
      "_refResourceRev": "000000000cb0794d",
      "name": "fr-employee",
      "_ref": "managed/realm-name_role/01ee6191-75d8-4d4b-9291-13a46592c57a",
      "_refProperties": {
        "_grantType": "conditional",
        "_id": "b281ffdf-477e-4211-a112-84476435bab2",
        "_rev": "00000000d612a248"
      }
    }
  ],
  ...
}

Note that the fr-employee role indicates a _grantType of conditional. This property indicates how the role was granted to the user. If no _grantType is listed, the role was granted statically.

Querying a user’s roles in this way does not return any roles that would be in effect as a result of a custom script, or of any temporal constraint applied to the role. To return a complete list of all the roles in effect at a specific time, query the user’s effectiveRoles property, as follows:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter?_fields=effectiveRoles"

Alternatively, to check which roles have been granted to a user, either statically or dynamically, look at the user’s entry in the IDM admin UI:

  1. Select Manage > User, then select the user whose roles you want to see.

  2. Select the Provisioning Roles tab.

  3. If you have a large number of managed roles, use the Advanced Filter option on the Role List page to build a custom query.

Delete a User’s Roles

To remove a statically granted role from a user entry, do one of the following:

  • Update the value of the user’s roles property to remove the reference to the role.

  • Update the value of the role’s members property to remove the reference to that user.

A delegated administrator must use PATCH to add or remove relationships.

Roles that have been granted as the result of a condition can only be removed when the condition is changed or removed, or when the role itself is deleted.

Over REST

Use one of the following methods to remove a role grant from a user:

  • DELETE the role from the user’s roles property, including the reference ID (the ID of the relationship between the user and the role) in the delete request.

    The following example removes the employee role from user scarter. The role ID is b8783543-869a-4bd4-907e-9c1d89f826ae, but the ID required in the DELETE request is the reference ID (5a023862-654d-4d7f-b9d0-7c151b8dede5):

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request DELETE \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter/roles/5a023862-654d-4d7f-b9d0-7c151b8dede5"
    {
      "_id": "5a023862-654d-4d7f-b9d0-7c151b8dede5",
      "_rev": "00000000baa999c1",
      "_ref": "managed/realm-name_role/b8783543-869a-4bd4-907e-9c1d89f826ae",
      "_refResourceCollection": "managed/realm-name_role",
      "_refResourceId": "b8783543-869a-4bd4-907e-9c1d89f826ae",
      "_refProperties": {
        "_id": "5a023862-654d-4d7f-b9d0-7c151b8dede5",
        "_rev": "00000000baa999c1"
      }
    }
  • PATCH the user entry to remove the role from the array of roles, specifying the value of the role object in the JSON payload.

    When you remove a role in this way, you must include the entire object in the value, as shown in the following example:
    curl \
    --header "Content-type: application/json" \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request PATCH \
    --data '[
      {
        "operation" : "remove",
        "field" : "/roles",
        "value" : {
          "_ref": "managed/realm-name_role/b8783543-869a-4bd4-907e-9c1d89f826ae",
          "_refResourceCollection": "managed/realm-name_role",
          "_refResourceId": "b8783543-869a-4bd4-907e-9c1d89f826ae",
          "_refProperties": {
            "_id": "5a023862-654d-4d7f-b9d0-7c151b8dede5",
            "_rev": "00000000baa999c1"
          }
        }
      }
    ]' \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter"
    {
      "_id": "scarter",
      "_rev": "000000007b78257d",
      "mail": "scarter@example.com",
      "givenName": "Steven",
      "sn": "Carter",
      "description": "Created By CSV",
      "userName": "scarter",
      "telephoneNumber": "1234567",
      "accountStatus": "active",
      "effectiveRoles": [
        {
          "_ref": "managed/realm-name_role/01ee6191-75d8-4d4b-9291-13a46592c57a"
        }
      ],
      "effectiveAssignments": [],
      "preferences": {
        "updates": false,
        "marketing": false
      },
      "country": "France"
    }
  • DELETE the user from the role’s members property, including the reference ID (the ID of the relationship between the user and the role) in the delete request.

    The following example first queries the members of the employee role, to obtain the ID of the relationship, then removes bjensen’s membership from that role:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request GET \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/b8783543-869a-4bd4-907e-9c1d89f826ae/members?_queryFilter=true"
    {
      "result": [
        {
          "_id": "a5a4bf94-6425-4458-aae4-bbd6ad094f72",
          "_rev": "00000000c25d994a",
          "_ref": "managed/realm-name_user/bjensen",
          "_refResourceCollection": "managed/realm-name_user",
          "_refResourceId": "bjensen",
          "_refProperties": {
            "_id": "a5a4bf94-6425-4458-aae4-bbd6ad094f72",
            "_rev": "00000000c25d994a"
          }
        }
      ],
      ...
    }
    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request DELETE \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/b8783543-869a-4bd4-907e-9c1d89f826ae/members/a5a4bf94-6425-4458-aae4-bbd6ad094f72"
    {
      "_id": "a5a4bf94-6425-4458-aae4-bbd6ad094f72",
      "_rev": "00000000c25d994a",
      "_ref": "managed/realm-name_user/bjensen",
      "_refResourceCollection": "managed/realm-name_user",
      "_refResourceId": "bjensen",
      "_refProperties": {
        "_id": "a5a4bf94-6425-4458-aae4-bbd6ad094f72",
        "_rev": "00000000c25d994a"
      }
    }

Using the IDM admin UI

Use one of the following methods to remove a user’s roles:

Method 1:

  1. Select Manage > User, and select the user whose roles you want to remove.

  2. Select the Provisioning Roles tab, select the role that you want to remove, then select Remove Selected Provisioning Roles.

Method 2:

  1. Select Manage > Role, and select the role whose members you want to remove.

  2. Select the Role Members tab, select the members that you want to remove, then select Remove Selected Role Members.

Delete a Role Definition

To delete a role over the REST interface, simply delete that managed object. The following command deletes the employee role created in the previous section:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request DELETE \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/b8783543-869a-4bd4-907e-9c1d89f826ae"
{
  "_id": "b8783543-869a-4bd4-907e-9c1d89f826ae",
  "_rev": "0000000027a959cf",
  "privileges": [],
  "name": "employee",
  "description": "All employees"
}

You cannot delete a role that is currently granted to users. If you attempt to delete a role that is granted to a user (either over the REST interface, or by using the IDM admin UI), Identity Cloud returns an error. The following example attempts to remove a role that is still granted to a user:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request DELETE \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/01ee6191-75d8-4d4b-9291-13a46592c57a"
{
  "code": 409,
  "reason": "Conflict",
  "message": "Cannot delete a role that is currently granted"
}

To delete a role through the IDM admin UI, select Manage > Role, select the role you want to remove, then click Delete Selected.

Use temporal constraints to restrict effective roles

Temporal constraints restrict the period that a role is effective. You can apply temporal constraints to managed and internal roles, and to role grants (for individual users).

For example, you might want a role, contractors-2020, to apply to all contract employees for the year 2020. In this case, you would set the temporal constraint on the role. Alternatively, you might want to assign a contractors role that applies to an individual user only for the period of their contract of employment.

The following examples show how to set temporal constraints on role definitions, and on individual role grants.

Add a Temporal Constraint to a Role

When you create a role, you can include a temporal constraint in the role definition that restricts the validity of the role, regardless of how that role is granted. Temporal constraints are expressed as a time interval in ISO 8601 date and time format. For more information on this format, see the ISO 8601 standard.

The following example adds a contractor role over the REST interface. The role is effective from March 1st, 2020 to August 31st, 2020:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "name": "contractor",
  "description": "Role granted to contract workers for 2020",
  "temporalConstraints": [
    {
      "duration": "2020-03-01T00:00:00.000Z/2020-08-31T00:00:00.000Z"
    }
  ]
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role?_action=create"
{
  "_id": "ed761370-b24f-4e21-8e58-a3230942da67",
  "_rev": "000000007429750e",
  "name": "contractor",
  "description": "Role granted to contract workers for 2020",
  "temporalConstraints": [
    {
      "duration": "2020-03-01T00:00:00.000Z/2020-08-31T00:00:00.000Z"
    }
  ]
}

This example specifies the time zone as Coordinated Universal Time (UTC) by appending Z to the time. If no time zone information is provided, the time zone is assumed to be local time. To specify a different time zone, include an offset (from UTC) in the format ±hh:mm. For example, an interval of 2020-03-01T00:00:00.000-07:00/2020-08-31T00:00:00.000-07:00 specifies a time zone that is seven hours behind UTC.

When the period defined by the constraint has ended, the role object remains in the repository, but the effective roles script will not include the role in the list of effective roles for any user.

The following example assumes that user scarter has been granted a role contractor-march. A temporal constraint has been included in the contractor-march role definition, specifying that the role should be applicable only during the month of March 2020. At the end of this period, a query on scarter’s entry shows that his roles property still includes the contractor-march role (with ID 0face495-772d-4d36-a30d-8594618aba0d), but his effectiveRoles property does not:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter?_fields=_id,userName,roles,effectiveRoles"
{
  "_id": "scarter",
  "_rev": "00000000e5fdeb51",
  "userName": "scarter",
  "effectiveRoles": [],
  "roles": [
    {
      "_ref": "managed/realm-name_role/0face495-772d-4d36-a30d-8594618aba0d",
      "_refResourceCollection": "managed/realm-name_role",
      "_refResourceId": "0face495-772d-4d36-a30d-8594618aba0d",
      "_refProperties": {
        "_id": "5f41d5a5-19b4-4524-a4b1-445790ff14da",
        "_rev": "00000000cb339810"
      }
    }
  ]
}

The role is still in place but is no longer effective.

To restrict the period during which a role is valid by using the IDM admin UI, select Temporal Constraint on the role Details tab, then select a timezone offset relative to GMT and the start and end dates for the required period.

Add a Temporal Constraint to a Role Grant

To restrict the validity of a role for individual users, apply a temporal constraint at the grant level, rather than as part of the role definition. In this case, the temporal constraint is taken into account per user, when the user’s effective roles are calculated. Temporal constraints that are defined at the grant level can be different for each user who is a member of that role.

To apply a temporal constraint to a grant over the REST interface, include the constraint as one of the _refProperties of the relationship between the user and the role. The following example assumes a contractor role, with ID ed761370-b24f-4e21-8e58-a3230942da67. The command adds user bjensen as a member of that role, with a temporal constraint that specifies that she be a member of the role for one year only, from January 1st, 2020 to January 1st, 2021:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request PATCH \
--data '[
  {
    "operation": "add",
    "field": "/members/-",
    "value": {
      "_ref": "managed/realm-name_user/bjensen",
      "_refProperties": {
        "temporalConstraints": [{"duration": "2020-01-01T00:00:00.000Z/2021-01-01T00:00:00.000Z"}]
      }
    }
  }
]' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/ed761370-b24f-4e21-8e58-a3230942da67"
{
  "_id": "ed761370-b24f-4e21-8e58-a3230942da67",
  "_rev": "000000007429750e",
  "name": "contractor",
  "description": "Role granted to contract workers for 2020",
  "temporalConstraints": [
    {
      "duration": "2020-03-01T00:00:00.000Z/2020-08-31T00:00:00.000Z"
    }
  ]
}

A query on bjensen’s roles property shows that the temporal constraint has been applied to this grant:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/bjensen/roles?_queryFilter=true"
{
  "result": [
    {
      "_id": "40600260-111d-4695-81f1-450365025784",
      "_rev": "00000000173daedb",
      "_ref": "managed/realm-name_role/ed761370-b24f-4e21-8e58-a3230942da67",
      "_refResourceCollection": "managed/realm-name_role",
      "_refResourceId": "ed761370-b24f-4e21-8e58-a3230942da67",
      "_refProperties": {
        "temporalConstraints": [
          {
            "duration": "2020-01-01T00:00:00.000Z/2021-01-01T00:00:00.000Z"
          }
        ],
        "_id": "40600260-111d-4695-81f1-450365025784",
        "_rev": "00000000173daedb"
      }
    }
  ],
  ...
}

To restrict the period during which a role grant is valid using the IDM admin UI, set a temporal constraint when you add the member to the role.

For example, to specify that bjensen be added to a Contractor role only for the period of her employment contract, select Manage > Role, select the Contractor role, then select Add Role Members. On the Add Role Members screen, select bjensen from the list, then enable the Temporal Constraint, and specify the start and end date of her contract.

Use assignments to provision users

Authorization roles control access to Identity Cloud itself. Provisioning roles define rules for how attribute values are updated on external systems. These rules are configured through assignments that are attached to a provisioning role definition. The purpose of an assignment is to provision an attribute or set of attributes, based on an object’s role membership.

The synchronization mapping configuration between two resources provides the basic account provisioning logic (how an account is mapped from a source to a target system). Role assignments provide additional provisioning logic that is not covered in the basic mapping configuration. The attributes and values that are updated by using assignments might include membership in external groups, access to specific external resources, and so on. A set of assignments can collectively represent a role.

Assignment objects are created, updated, and deleted like any other managed object, and are attached to a role by using the relationships mechanism, in much the same way as a role is granted to a user. Assignments are stored in the repository and are accessible at the context path /openidm/managed/realm-name_assignment.

This section describes how to manipulate assignments over the REST interface, and by using the IDM admin UI. When you have created an assignment, and attached it to a role definition, all user objects that reference that role definition will, as a result, reference the corresponding assignment in their effectiveAssignments attribute.

If you have mapped roles and assignments to properties on a target system, and you are preloading the result set into memory, make sure that your targetQuery returns the mapped property. For example, if you have mapped a specific role to the ldapGroups property on the target system, the target query must include the ldapGroups property when it returns the object.

The following mapping excerpt indicates that the target query must return the _id of the object as well as its ldapGroups property:

"targetQuery": {
    "_queryFilter": true,
    "_fields": "_id,ldapGroups"
}

For more information about preloading the result set for reconciliation operations, see Improve Reconciliation Query Performance.

Create an Assignment

You can create assignments over the REST interface or using the IDM admin UI:

Over REST

To create a new assignment over REST, send a PUT or POST request to the /openidm/managed/realm-name_assignment context path.

The following example creates a new managed assignment named employee. The JSON payload in this example shows the following:

  • The assignment is applied for the mapping managedUser_systemLdapAccounts, so attributes will be updated on the external LDAP system specified in this mapping.

  • The name of the attribute on the external system whose value will be set is employeeType, and its value will be set to Employee.

  • When the assignment is applied during a sync operation, the attribute value Employee is added to any existing values for that attribute. When the assignment is removed (if the role is deleted, or if the user is no longer a member of that role), the attribute value Employee is removed from the values of that attribute.

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
      "name": "employee",
      "description": "Assignment for employees.",
      "mapping": "managedUser_systemLdapAccounts",
      "attributes": [
        {
          "name": "employeeType",
          "value": [
            "Employee"
          ],
          "assignmentOperation": "mergeWithTarget",
          "unassignmentOperation": "removeFromTarget"
        }
      ]
    }' \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_assignment?_action=create"
    {
      "_id": "1a6a3af3-024f-4cf1-b4f6-116b98053816",
      "_rev": "00000000b2329649",
      "name": "employee",
      "description": "Assignment for employees.",
      "mapping": "managedUser_systemLdapAccounts",
      "attributes": [
        {
          "name": "employeeType",
          "value": [
            "Employee"
          ],
          "assignmentOperation": "mergeWithTarget",
          "unassignmentOperation": "removeFromTarget"
        }
      ]
    }

    Note that at this stage, the assignment is not linked to any role, so no user can make use of the assignment. You must add the assignment to a role, as described in Add an Assignment to a Role.

Using the IDM admin UI
  1. Select Manage > Assignment > New Assignment.

  2. Enter a name and description for the new assignment.

  3. Select the mapping to which the assignment should apply. The mapping indicates the target resource, that is, the resource on which the attributes specified in the assignment will be adjusted.

  4. Select Save to add the assignment.

  5. Select the Attributes tab and select the attribute or attributes whose values will be adjusted by this assignment. The attribute you select here will determine what is displayed next:

    • Regular text field—specify what the value of the attribute should be, when this assignment is applied.

    • Item button—specify a managed object type, such as an object, relationship, or string.

    • Properties button—specify additional information, such as an array of role references.

  6. Select the assignment operation from the dropdown list:

    • Merge With Target: the attribute value will be added to any existing values for that attribute. This operation merges the existing value of the target object attribute with the value(s) from the assignment. If duplicate values are found (for attributes that take a list as a value), each value is included only once in the resulting target. This assignment operation is used only with complex attribute values like arrays and objects, and does not work with strings or numbers.

    • Replace Target: the attribute value will overwrite any existing values for that attribute. The value from the assignment becomes the authoritative source for the attribute.

  7. Select the unassignment operation from the drop-down list:

    • Remove From Target: the attribute value is removed from the system object when the user is no longer a member of the role, or when the assignment itself is removed from the role definition.

    • No Operation: removing the assignment from the user’s effectiveAssignments has no effect on the current state of the attribute in the system object.

  8. Select the Events tab to specify any scriptable events associated with this assignment.

    The assignment and unassignment operations described in the previous step operate at the attribute level. That is, you specify what should happen with each attribute affected by the assignment when the assignment is applied to a user, or removed from a user.

    The scriptable On assignment and On unassignment events operate at the assignment level, rather than the attribute level. Define scripts here to apply additional logic or operations that should be performed when a user (or other object) receives or loses an entire assignment. This logic can be anything that is not restricted to an operation on a single attribute.

    For information about the variables available to these scripts, see Variables available to role assignment scripts.

  9. Select the Roles tab to attach this assignment to an existing role definition.

Add an Assignment to a Role

After you have created a role, and an assignment, you create a relationship between the assignment and the role, in much the same way as a user references a role.

Update a role definition to include one or more assignments over the REST interface, or using the IDM admin UI:

Over REST

Update the role definition to include a reference to the ID of the assignment in the assignments property of the role. The following example adds the employee assignment (ID 1a6a3af3-024f-4cf1-b4f6-116b98053816) to an existing employee role (ID 2243f5f8-ed75-4c3b-b4b3-058d5c58fbb4):

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request PATCH \
--data '[
  {
    "operation": "add",
    "field": "/assignments/-",
    "value": { "_ref": "managed/realm-name_assignment/1a6a3af3-024f-4cf1-b4f6-116b98053816" }
  }
]' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/2243f5f8-ed75-4c3b-b4b3-058d5c58fbb4"
{
  "_id": "2243f5f8-ed75-4c3b-b4b3-058d5c58fbb4",
  "_rev": "00000000e85263c7",
  "privileges": [],
  "name": "employee",
  "description": "Roll granted to all permanent employees"
}

To check that the assignment was added successfully, query the role’s assignments property:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role/2243f5f8-ed75-4c3b-b4b3-058d5c58fbb4/assignments?_queryFilter=true&_fields=_ref/*,name,assignments"
{
  "result": [
    {
      "_id": "d15822f0-05bc-464a-927d-8e5018a234d3",
      "_rev": "0000000010eea343",
      "_refResourceCollection": "managed/realm-name_assignment",
      "_refResourceId": "1a6a3af3-024f-4cf1-b4f6-116b98053816",
      "_refResourceRev": "00000000b2329649",
      "name": "employee",
      "_ref": "managed/realm-name_assignment/1a6a3af3-024f-4cf1-b4f6-116b98053816",
      "_refProperties": {
        "_id": "d15822f0-05bc-464a-927d-8e5018a234d3",
        "_rev": "0000000010eea343"
      }
    }
  ],
  ...
}

Note that the assignments property references the assignment that you created in the previous step.

To remove an assignment from a role definition, remove the reference to the assignment from the role’s assignments property.

Using the IDM admin UI
  1. Select Manage > Role, and select the role to which you want to add an assignment.

  2. Select the Managed Assignments tab, and select Add Managed Assignments.

  3. Select the assignment that you want to add to the role, then select Add.

Delete an Assignment

Delete assignments over the REST interface, or using the IDM admin UI:

Over REST

To delete an assignment over the REST interface, simply delete that object. The following example deletes the employee assignment created in the previous example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request DELETE \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_assignment/1a6a3af3-024f-4cf1-b4f6-116b98053816"
{
  "_id": "1a6a3af3-024f-4cf1-b4f6-116b98053816",
  "_rev": "00000000b2329649",
  "name": "employee",
  "description": "Assignment for employees.",
  "mapping": "managedUser_systemLdapAccounts",
  "attributes": [
    {
      "name": "employeeType",
      "value": [
        "Employee"
      ],
      "assignmentOperation": "mergeWithTarget",
      "unassignmentOperation": "removeFromTarget"
    }
  ]
}

You can delete an assignment, even if it is referenced by a managed role. When the assignment is removed, any users to whom the corresponding roles were granted will no longer have that assignment in their list of effectiveAssignments. For more information about effective roles and effective assignments, see Effective roles and effective assignments.

Using the IDM admin UI

To delete an assignment using the IDM admin UI, select Manage > Assignment.

Select the assignment you want to remove, then select Delete.

Effective roles and effective assignments

Effective roles and effective assignments are virtual properties of a user object. Their values are calculated by Identity Cloud, using relationships between related objects to know when to recalculate when changes occur. The relationships between objects are configured using the notify, notifySelf, and notifyRelationships settings for managed/realm-name_user, managed/realm-name_role, and managed/realm-name_assignment. Which related objects to traverse for calculation is configured using queryConfig. Calculation or recalculation is triggered when the roles or assignments for a managed user are added, removed, or changed, including by changes from temporal constraints, and notification of that change is sent to the related objects.

The following excerpt of the Identity Cloud managed object schema shows how these two virtual properties are constructed for each managed user object:

"effectiveRoles" : {
    "type" : "array",
    "title" : "Effective Roles",
    "description" : "Effective Roles",
    "viewable" : false,
    "returnByDefault" : true,
    "isVirtual" : true,
    "queryConfig" : {
        "referencedRelationshipFields" : ["roles"]
    },
    "usageDescription" : "",
    "isPersonal" : false,
    "items" : {
        "type" : "object",
        "title" : "Effective Roles Items"
    }
},
"effectiveAssignments" : {
    "type" : "array",
    "title" : "Effective Assignments",
    "description" : "Effective Assignments",
    "viewable" : false,
    "returnByDefault" : true,
    "isVirtual" : true,
    "queryConfig" : {
        "referencedRelationshipFields" : ["roles", "assignments"],
        "referencedObjectFields" : ["*"]
    },
    "usageDescription" : "",
    "isPersonal" : false,
    "items" : {
        "type" : "object",
        "title" : "Effective Assignments Items"
    }
}

When a role references an assignment, and a user references the role, that user automatically references the assignment in its list of effective assignments.

effectiveRoles uses the roles relationship to calculate the grants that are currently in effect, including any qualified by temporal constraints.

effectiveAssignments uses the roles relationship, and the assignments relationship for each role, to calculate the current assignments in effect for that user. The synchronization engine reads the calculated value of the effectiveAssignments attribute when it processes the user. The target system is updated according to the configured assignmentOperation for each assignment.

When a user’s roles or assignments are updated, Identity Cloud calculates the effectiveRoles and effectiveAssignments for that user based on the current value of the user’s roles property, and the assignments property of any roles referenced by the roles property. The previous set of examples showed the creation of a role employee that referenced an assignment employee and was granted to user bjensen. Querying that user entry would show the following effective roles and effective assignments:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/bjensen?_fields=userName,roles,effectiveRoles,effectiveAssignments"
{
  "_id": "ca8855fd-a404-42c7-88b7-02f8a8a825b2",
  "_rev": "0000000081eebe1a",
  "userName": "bjensen",
  "effectiveRoles": [
    {
      "_refResourceCollection": "managed/realm-name_role",
      "_refResourceId": "2243f5f8-ed75-4c3b-b4b3-058d5c58fbb4"
      "_ref": "managed/realm-name_role/2243f5f8-ed75-4c3b-b4b3-058d5c58fbb4"
    }
  ],
  "effectiveAssignments": [
    {
      "name": "employee",
      "description": "Assignment for employees.",
      "mapping": "managedUser_systemLdapAccounts",
      "attributes": [
        {
          "assignmentOperation": "mergeWithTarget",
          "name": "employeeType",
          "unassignmentOperation": "removeFromTarget",
          "value": [
            "employee"
          ]
        }
      ],
      "_rev": "0000000087d5a9a5",
      "_id": "46befacf-a7ad-4633-864d-d93abfa561e9"
      "_refResourceCollection": "managed/realm-name_assignment",
      "_refResourceId": "46befacf-a7ad-4633-864d-d93abfa561e9",
      "_ref": "managed/realm-name_assignment/46befacf-a7ad-4633-864d-d93abfa561e9"
    }
  ],
  "roles": [
    {
      "_ref": "managed/realm-name_role/2243f5f8-ed75-4c3b-b4b3-058d5c58fbb4",
      "_refResourceCollection": "managed/realm-name_role",
      "_refResourceId": "2243f5f8-ed75-4c3b-b4b3-058d5c58fbb4",
      "_refProperties": {
        "_id": "93552530-10fa-49a4-865f-c942dffd2801",
        "_rev": "0000000081ed9f2b"
      }
    }
  ]
}

In this example, synchronizing the managed/realm-name_user repository with the external LDAP system defined in the mapping populates user bjensen’s employeeType attribute in LDAP with the value employee.

Roles and relationship change notification

Before you read this section, see Configure relationship change notification to understand the notify and notifyRelationships properties, and how change notification works for relationships in general. In the case of roles, the change notification configuration exists to ensure that managed users are notified when any of the relationships that link users, roles, and assignments are manipulated (that is, created, updated, or deleted).

Consider the situation where a user has role R. A new assignment A is created that references role R. Ultimately, we want to notify all users that have role R so that their reconciliation state will reflect any attributes in the new assignment A. We achieve this notification with the following configuration:

In the managed object schema, the assignment object definition has a roles property that includes a resourceCollection. The path of this resource collection is managed/realm-name_role and "notify" : true for the resource collection:

{
    "name" : "assignment",
    "schema" : {
        ...
        "properties" : {
            ...
            "roles" : {
                ...
                "items" : {
                    ...
                    "resourceCollection" : [
                        {
                            "notify" : true,
                            "path" : "managed/realm-name_role",
                            "label" : "Role",
                            "query" : {
                                "queryFilter" : "true",
                                "fields" : [
                                    "name"
                                ]
                            }
                        }
                    ...
}

With this configuration, when assignment A is created, with a reference to role R, role R is notified of the change. However, we still need to propagate that notification to any users who are members of role R. To do this, we configure the role object as follows:

{
    "name" : "role",
    "schema" : {
        ...
        "properties" : {
            ...
            "assignments" : {
                ...
                "notifyRelationships" : ["members"]
            }
    ...
}

When role R is notified of the creation of a new relationship to assignment A, the notification is propagated through the assignments property. Because "notifyRelationships" : ["members"] is set on the assignments property, the notification is propagated across role R to all members of role R.

Managed role script hooks

Like any other managed object, you can use script hooks to configure role behavior. The default role configuration includes an onDelete hook that calls a script to prevent the role from being deleted if it is currently assigned to users:

{
    "name" : "role",
    "onDelete" : {
        "type" : "text/javascript",
        "file" : "roles/onDelete-roles.js"
    },
    ...

Map roles to external groups

A user’s access to Identity Cloud is based on one or more authorization roles. Authorization roles are cumulative, and are calculated for a user in the following order:

  1. Roles set specifically in the user’s userRoles property

  2. Group roles — based on group membership in an external system

    Group roles are controlled with the following properties in the authentication configuration:

    • groupMembership: the property on the external system that represents group membership. In a DS directory server, that property is ldapGroups by default. In an Active Directory server, the property is memberOf by default. For example:

      "groupMembership" : "ldapGroups"

      Note that the value of the groupMembership property must be the ICF property name defined in the provisioner file, rather than the property name on the external system.

    • groupRoleMapping: a mapping between an Identity Cloud role and a group on the external system. Setting this property ensures that if a user authenticates through pass-through authentication, they are given specific Identity Cloud roles depending on their membership in groups on the external system. In the following example, users who are members of the group cn=admins,ou=Groups,dc=example,dc=com are given the internal openidm-admin role when they authenticate:

      "groupRoleMapping" : {
          "internal/role/openidm-admin" : ["cn=admins,ou=Groups,dc=example,dc=com"]
      }
    • groupComparisonMethod: the method used to check whether the authenticated user’s group membership matches one of the groups mapped to an Identity Cloud role (in the groupRoleMapping property).

      The groupComparisonMethod can be one of the following:

      • equals: a case-sensitive equality check

      • caseInsensitive: a case-insensitive equality check

      • ldap: a case-insensitive and whitespace-insensitive equality check. Because LDAP directories do not take case or whitespace into account in group DNs, you must set the groupComparisonMethod if you are using pass-through authentication with an LDAP directory.

To control access to external systems, use provisioning roles and assignments, as described in Use assignments to provision users.

Groups

The managed group object is a default managed object type, and is defined like any other managed object type. Users are made members of groups through the relationships mechanism. You should understand how relationships work before you read about Identity Cloud groups.

A group can be assigned to a user manually, as a static value of the user’s groups attribute, or dynamically, as a result of a condition or script. For example, a user might be assigned to a group such as sales dynamically, if that user is in the sales organization.

A user’s groups attribute takes an array of references as a value, where the references point to the managed groups. For example, if user bjensen has been assigned to two groups (employees and supervisors), the value of bjensen’s groups attribute would look something like the following:

"groups": [
    {
        "_ref": "managed/group/supervisors",
        "_refResourceCollection": "managed/group",
        "_refResourceId": "supervisors",
        "_refProperties": {
            "_id": "61315165-9269-4944-8db9-98f681c6b0a9",
            "_rev": "00000000586a94fd"
        }
    },
    {
        "_ref": "managed/group/employees",
        "_refResourceCollection": "managed/group",
        "_refResourceId": "employees",
        "_refProperties": {
            "_id": "2a965519-5788-428c-92d1-19fac497db8f",
            "_rev": "000000001e1793bc"
        }
    }
]

The _refResourceCollection is the container that holds the group. The _refResourceId is the ID of the group. The _ref property is a resource path that is derived from the _refResourceCollection and the URL-encoded _refResourceId. _refProperties provides more information about the relationship.

Some of the examples in this documentation set use client-assigned IDs (such as bjensen and scarter) for the user objects because it makes the examples easier to read. If you create objects using the IDM admin UI, they are created with server-assigned IDs (such as 55ef0a75-f261-47e9-a72b-f5c61c32d339). This particular example uses a client-assigned group ID that is the same as the group name. Generally, immutable server-assigned UUIDs are used for all managed objects in production environments.

Manage groups

These sections show the REST calls to create, read, update, and delete groups, and to assign groups to users.

Create a group

Using the IDM admin UI

  1. From the navigation bar, click Manage > Group.

  2. On the Groups page, click New Group.

  3. On the New Group page, enter a name and description, and click Save.

Using REST

To create a group, send a PUT or POST request to the /openidm/managed/group context path. The following example creates a group named employees, with ID employees:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "_id": "employees",
  "name": "employees",
  "description": "Group that includes temporary and permanent employees"
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/group?_action=create"
{
  "_id": "employees",
  "_rev": "0000000067dba53b",
  "name": "employees",
  "description": "Group that includes temporary and permanent employees"
}

The following example creates a group with a system-generated ID:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "name": "employees",
  "description": "Group that includes temporary and permanent employees"
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/group"
{
  "_id": "444940e2-e0e8-4f02-98d5-30b3e74dbb98",
  "_rev": "0000000067dba53b",
  "name": "employees",
  "description": "Group that includes temporary and permanent employees"
}

List groups

To list groups over REST, query the openidm/managed/group endpoint. The following example shows the employees group that you created in the previous example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/group?_queryFilter=true"
{
  "result": [
    {
      "_id": "employees",
      "_rev": "0000000067dba53b",
      "name": "employees",
      "description": "Group that includes temporary and permanent employees"
    }
  ],
  ...
}

To list the managed groups in the IDM admin UI, select Manage > Group.

If you have a large number of groups, select Advanced Filter to build a more complex query filter to display only the groups you want.

Add users to a group

You add users to a group through the relationship mechanism. Relationships are essentially references from one managed object to another; in this case, from a user object to a group object. For more information about relationships, see Relationships between objects.

You can add group members statically or dynamically.

To add members statically, you must do one of the following:

  • Update the value of the user’s groups property to reference the group.

  • Update the value of the groups’s members property to reference the user.

Dynamic groups use the result of a condition or script to update a user’s list of groups.

Add group members statically

Add a user to a group statically, using the REST interface or the IDM admin UI as follows:

Using REST

Use one of these methods to add group members over REST:

  • Add the user as a group member. The following example adds user scarter as a member of the employees group:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
      "_ref":"managed/realm-name_user/scarter",
      "_refProperties":{}
    }' \
    "https://tenant-name.forgeblocks.com/openidm/managed/group/employees/members?_action=create"
    {
      "_id": "105cdbdb-a3c2-489b-9082-6e1aaff16d31",
      "_rev": "000000008a2c93f9",
      "_ref": "managed/realm-name_user/scarter",
      "_refResourceCollection": "managed/realm-name_user",
      "_refResourceId": "scarter",
      "_refProperties": {
        "_id": "105cdbdb-a3c2-489b-9082-6e1aaff16d31",
        "_rev": "000000008a2c93f9"
      }
    }
    This is the preferred method as it does not incur an unnecessary performance cost for groups with many members.
  • Update the user’s groups property.

    The following example adds the employees group to user scarter:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request PATCH \
    --data '[
      {
        "operation": "add",
        "field": "/groups/-",
        "value": {"_ref" : "managed/group/employees"}
      }
    ]' \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter"
    {
      "_id": "scarter",
      "_rev": "00000000788ed06a",
      "mail": "scarter@example.com",
      "givenName": "Steven",
      "sn": "Carter",
      "description": "Example User",
      "userName": "scarter",
      "telephoneNumber": "1234567",
      "accountStatus": "active",
      "memberOfOrgIDs": [],
      "effectiveRoles": [],
      "effectiveAssignments": []
    }

    When you update a user’s existing groups array, use the - special index to add the new value to the set. For more information, see Set semantic arrays in Patch Operation: Add.

  • Update the group’s members property to refer to the user.

    The following sample command makes scarter a member of the employees group:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request PATCH \
    --data '[
      {
        "operation": "add",
        "field": "/members/-",
        "value": {"_ref" : "managed/realm-name_user/scarter"}
      }
    ]' \
    "https://tenant-name.forgeblocks.com/openidm/managed/group/employees"
    {
      "_id": "employees",
      "_rev": "0000000067dba53b",
      "name": "employees",
      "description": "Group that includes temporary and permanent employees"
    }

    The members property of a group is not returned by default in the output. To show all members of a group, you must specifically request the relationship properties (*_ref) in your query. The following example lists the members of the employees group:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request GET \
    "https://tenant-name.forgeblocks.com/openidm/managed/group/employees?_fields=*_ref,name"
    {
      "_id": "employees",
      "_rev": "0000000067dba53b",
      "name": "employees",
      "members": [
        {
          "_ref": "managed/realm-name_user/scarter",
          "_refResourceCollection": "managed/realm-name_user",
          "_refResourceId": "scarter",
          "_refProperties": {
            "_id": "9a3c7b7f-29a8-4b56-99c3-698fd0cd6b65",
            "_rev": "00000000ae8194c1"
          }
        },
        {
          "_ref": "managed/realm-name_user/bjensen",
          "_refResourceCollection": "managed/realm-name_user",
          "_refResourceId": "bjensen",
          "_refProperties": {
            "_id": "2a965519-5788-428c-92d1-19fac497db8f",
            "_rev": "000000001e1793bc"
          }
        }
      ]
    }
Using the UI

Use one of the following UI methods to add members to a group:

  • Update the user entry:

    1. Select Manage > User and select the user that you want to add.

    2. Select the Groups tab and click Add Groups.

    3. Select the group from the dropdown list and click Add.

  • Update the group entry:

    1. Select Manage > Group and select the group to which you want to add members.

    2. Select the Members tab and click Add Members.

    3. Select the user from the dropdown list and click Add.

Add group members dynamically

To add a member to a group dynamically, use a condition, expressed as a query filter, in the group definition. If the condition is true for a particular member, that member is added to the group.

A group whose membership is based on a defined condition is called a conditional group. To create a conditional group, include a query filter in the group definition.

Properties that are used as the basis of a conditional group query must be configured as searchable and must be indexed in the repository configuration. To configure a property as searchable, update its definition in your managed object configuration. For more information, see [objects-guide:creating-modifying-managed-objects].

To create a conditional group over REST, include the query filter as a value of the condition property in the group definition. The following example creates a group, fr-employees, whose members will be only users who live in France (whose country property is set to FR):

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "name": "fr-employees",
  "description": "Group for employees resident in France",
  "condition": "/country eq \"FR\""
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/group?_action=create"
{
  "_id": "fr-employees",
  "_rev": "00000000092faa7f",
  "condition": "/country eq \"FR\"",
  "name": "fr-employees",
  "description": "Group for employees resident in France"
}

When a conditional group is created or updated, Identity Cloud assesses all managed users, and recalculates the value of their groups property, if they are members of that group. When a condition is removed from a group, that is, when the group becomes an unconditional group, all members are removed from the group. So, users who became members based on the condition, have that group removed from their groups property.

When a conditional group is defined in an existing data set, every user entry (including the mapped entries on remote systems) must be updated with the relationships implied by that conditional group. The time that it takes to create a new conditional group is impacted by the number of managed users affected by the condition.

In a data set with a very large number of users, creating a new conditional group can therefore incur a significant performance cost when you create it. Ideally, you should set up your conditional groups at the beginning of your deployment to avoid performance issues later.

Query a user’s group memberships

To list a user’s groups, query their groups property. The following example shows that scarter is a member of two groups — the employees group, and the supervisors group:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter/groups?_queryFilter=true&_fields=_ref/*,name"
{
  "result": [
    {
      "_id": "9a3c7b7f-29a8-4b56-99c3-698fd0cd6b65",
      "_rev": "00000000ae8194c1",
      "_refResourceCollection": "managed/group",
      "_refResourceId": "employees",
      "_refResourceRev": "0000000067dba53b",
      "name": "employees",
      "_ref": "managed/group/employees",
      "_refProperties": {
        "_id": "9a3c7b7f-29a8-4b56-99c3-698fd0cd6b65",
        "_rev": "00000000ae8194c1"
      }
    },
    {
      "_id": "431f7a46-a5ca-4ad6-9d0e-666d5fad1be6",
      "_rev": "000000002df096de",
      "_refResourceCollection": "managed/group",
      "_refResourceId": "supervisors",
      "_refResourceRev": "00000000f220b6bf",
      "name": "supervisors",
      "_ref": "managed/group/supervisors",
      "_refProperties": {
        "_id": "431f7a46-a5ca-4ad6-9d0e-666d5fad1be6",
        "_rev": "000000002df096de"
      }
    },
    ...
  ]
}

To view a user’s group membership in the IDM admin UI:

  1. Select Manage > User, then select the user whose groups you want to see.

  2. Select the Groups tab.

  3. If you have a large number of groups, use the Advanced Filter option on the Group List page to build a custom query.

Remove a member from a group

To remove a static group membership from a user entry, do one of the following:

  • Update the value of the user’s groups property to remove the reference to the role.

  • Update the value of the groups’s members property to remove the reference to that user.

You can use both of these methods over REST, or use the IDM admin UI.

A delegated administrator must use PATCH to add or remove relationships.

Conditional group membership can only be removed when the condition is changed or removed, or when the group itself is deleted.

Using REST

Use one of the following methods to remove member from a group:

  • DELETE the group from the user’s groups property, including the reference ID (the ID of the relationship between the user and the group) in the delete request.

    The following example removes the employees group from user scarter. Note that ID required in the DELETE request is not the ID of the group but the reference _id of the relationship:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request DELETE \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter/groups/9a3c7b7f-29a8-4b56-99c3-698fd0cd6b65"
    {
      "_id": "9a3c7b7f-29a8-4b56-99c3-698fd0cd6b65",
      "_rev": "00000000ae8194c1",
      "_ref": "managed/group/employees",
      "_refResourceCollection": "managed/group",
      "_refResourceId": "employees",
      "_refProperties": {
        "_id": "9a3c7b7f-29a8-4b56-99c3-698fd0cd6b65",
        "_rev": "00000000ae8194c1"
      }
    }
  • PATCH the user entry to remove the group from the array of groups, specifying the value of the group object in the JSON payload.

    When you remove a group in this way, you must include the entire object in the value, as shown in the following example:
    curl \
    --header "Content-type: application/json" \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request PATCH \
    --data '[
      {
        "operation" : "remove",
        "field" : "/groups",
        "value" : {
          "_ref": "managed/group/employees",
          "_refResourceCollection": "managed/group",
          "_refResourceId": "employees",
          "_refProperties": {
            "_id": "9a3c7b7f-29a8-4b56-99c3-698fd0cd6b65",
              "_rev": "00000000ae8194c1"
          }
        }
      }
    ]' \
    "https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter"
    {
      "_id": "scarter",
      "_rev": "000000007631d204",
      "mail": "scarter@example.com",
      "givenName": "Steven",
      "sn": "Carter",
      "description": "Created By CSV",
      "userName": "scarter",
      "telephoneNumber": "1234567",
      "accountStatus": "active",
      "memberOfOrgIDs": [],
      "effectiveRoles": [],
      "effectiveAssignments": []
    }
  • DELETE the user from the group’s members property, including the reference ID (the ID of the relationship between the user and the role) in the delete request.

    The following example first queries the members of the employees group, to obtain the ID of the relationship, then removes bjensen’s membership from that group:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request GET \
    "https://tenant-name.forgeblocks.com/openidm/managed/group/employees/members?_queryFilter=true&_fields=_ref/*,name"
    {
      "result": [
        {
          "_id": "a5a4bf94-6425-4458-aae4-bbd6ad094f72",
          "_rev": "00000000c25d994a",
          "_ref": "managed/realm-name_user/bjensen",
          "_refResourceCollection": "managed/realm-name_user",
          "_refResourceId": "bjensen",
          "_refProperties": {
            "_id": "a5a4bf94-6425-4458-aae4-bbd6ad094f72",
            "_rev": "00000000c25d994a"
          }
        }
      ],
      ...
    }
    
    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --request DELETE \
    "https://tenant-name.forgeblocks.com/openidm/managed/group/employees/members/a5a4bf94-6425-4458-aae4-bbd6ad094f72"
    {
      "_id": "a5a4bf94-6425-4458-aae4-bbd6ad094f72",
      "_rev": "00000000c25d994a",
      "_ref": "managed/realm-name_user/bjensen",
      "_refResourceCollection": "managed/realm-name_user",
      "_refResourceId": "bjensen",
      "_refProperties": {
        "_id": "a5a4bf94-6425-4458-aae4-bbd6ad094f72",
        "_rev": "00000000c25d994a"
      }
    }

Using the UI

Use one of the following methods to remove a group member:

  • Select Manage > User and select the user whose group or groups you want to remove.

    Select the Groups tab, select the group that you want to remove, then select Remove Selected Groups.

  • Select Manage > Group, and select the group whose members you want to remove.

    Select the Members tab, select the member or members that that you want to remove, then select Remove Selected Members.

Delete a group

To delete a group over the REST interface, simply delete that managed object. The following command deletes the employees group created in the previous section:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request DELETE \
"https://tenant-name.forgeblocks.com/openidm/managed/group/employees"
{
  "_id": "employees",
  "_rev": "0000000067dba53b",
  "name": "employees",
  "description": "Group that includes temporary and permanent employees"
}

To delete a group using the IDM admin UI, select Manage > Group, select the group you want to remove, then Delete Selected.

Organizations

Organization objects let you arrange and manage users in hierarchical trees. Organizations also let you give users fine-grained administrative privileges to various parts of the tree, based on their location in that tree. For example, an administrator of one organization might have full access to the users within that organization, but no access to the users in an adjacent organization.

An organization object (defined in the Identity Cloud managed object schema) has an array of admins, an array of owners, and an array of members. These relationship properties enable the hierarchical organization model.

Users and organizations have a set of relationship-derived virtual properties used by the delegated administration filters to provide the visibility and access constraints that underpin the organization model. Users have the ids of all the organizations of which they are members, and organizations have the ids of all their admin and owner users.

Only Identity Cloud administrative users can create top-level organizations. Within organizations, there are various levels of privileges, depending on how a user is related to the organization.

The following diagram gives a high-level overview of how privileges are assigned to various entities in the organization hierarchy:

org-model1
  • An organization owner can manipulate all organizations, members, and admins in their ownership area. The ownership area includes any part of the tree in or beneath the organization that they own. So, in the preceding image, the owner of Org A can do the following anywhere within their ownership area (the pale green region):

    • Add and update members.

      Organization owners only have access to the members in their ownership area. So, an owner can create a new user as a member of their organization, but cannot add an existing managed user to their organization if that user is outside of their ownership area (that is, in any part of the tree not in or beneath the organization that they own).
    • Add and update sub-organizations, such as Org B and Org C.

    • Give an organization member the admin privilege for the parent organization or any sub-organizations.

      An organization owner cannot create additional owners in their root organization.

      An organization owner does not have to be a member of the organization. If the organization owner is a member of the organization, that owner is automatically in the administrative area of any admins of that organization, and can therefore be manipulated by an organization admin. To avoid accidentally giving organization admins privileges over an organization owner, do not make the owner a member of the organization.
  • An organization admin has control over their administrative area. The administrative area includes any part of the tree in or beneath the organization that they administer. In the preceding diagram, the administrative area of Admin A is shaded red. The administrative areas of Admins B and C are shaded grey. An admin can do the following in their administrative area:

    • Add and update members.

      Organization admins only have access to the members in their administrative area. So, an admin can create a new user as a member of their organization, but cannot add an existing managed user to their organization if that user is outside of their administrative area (that is, in any part of the tree not in or beneath the organization that they administer).
    • Add and update sub-organizations of the organization they administer.

      Notice that Admin B and C are outside of the administrative area of Admin A. An organization admin cannot create additional admins in their administrative areas.

      An organization admin must be a member of the organization, so must either be an existing member of the organization, or must be given the memberOfOrg relationship at the time they are created.

  • Organization members are regular users, with no special privileges in the organization hierarchy.

    Managed users have a memberOfOrgIDs virtual property that lists the organizations to which the user belongs (either directly, or through any parent organizations).

  • Parent and child organizations are essentially relationships between an organization and existing organizations in the tree.

Manage organizations over REST

Identity Cloud provides RESTful access to managed organizations, at the context path /openidm/managed/realm-name_organization. You can add, change, and delete organizations by using the IDM admin UI or over the REST interface. To use the IDM admin UI, select Manage > Organization.

The following examples show how to add, change, and delete organizations over the REST interface. For a reference of all managed organization endpoints and actions, see Managed organizations.

Add an organization

Only Identity Cloud administrators can create top level organizations.

curl \
--header "Content-Type: application/json" \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "If-None-Match: *" \
--request PUT \
--data '{
  "name": "example-org"
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization/example-org"
{
  "_id": "example-org",
  "_rev": "00000000bc9871c8",
  "adminIDs": [],
  "ownerIDs": [],
  "parentAdminIDs": [],
  "parentIDs": [],
  "parentOwnerIDs": [],
  "name": "example-org"
}
Add an organization owner

Identity Cloud administrators can create owners for an organization. This example makes bjensen the owner of the organization created previously. The example assumes that the managed user bjensen already exists:

curl \
--header "Content-Type: application/json" \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request POST \
--data '{"_ref":"managed/realm-name_user/bjensen"}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization/example-org/owners?_action=create"
{
  "_id": "fcb0f4d0-dad2-4138-a80c-62407a8e831e",
  "_rev": "00000000496d9920",
  "_ref": "managed/realm-name_user/bjensen",
  "_refResourceCollection": "managed/realm-name_user",
  "_refResourceId": "bjensen",
  "_refProperties": {
    "_id": "fcb0f4d0-dad2-4138-a80c-62407a8e831e",
    "_rev": "00000000496d9920"
  }
}
List an owner’s organizations

This example lists the organizations of which bjensen is an owner:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/bjensen/ownerOfOrg?_queryFilter=true"
{
  "result": [
    {
      "_id": "fcb0f4d0-dad2-4138-a80c-62407a8e831e",
      "_rev": "00000000496d9920",
      "_ref": "managed/realm-name_organization/example-org",
      "_refResourceCollection": "managed/realm-name_organization",
      "_refResourceId": "example-org",
      "_refProperties": {
        "_id": "fcb0f4d0-dad2-4138-a80c-62407a8e831e",
        "_rev": "00000000496d9920"
      }
    }
  ],
  ...
}
Add an organization member

Organization owners can create members in the organizations that they own. In this example bjensen creates user scarter and makes him a member of the organization created previously:

curl \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: bjensen" \
--header "X-OpenIDM-Password: Th3Password" \
--header "Accept-API-Version: resource=1.0" \
--request PUT \
--data '{
  "userName": "scarter",
  "sn": "Carter",
  "givenName": "Steven",
  "mail": "scarter@example.com",
  "password": "Th3Password",
  "memberOfOrg": [{"_ref": "managed/realm-name_organization/example-org"}]
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/scarter"
{
  "_id": "scarter",
  "_rev": "00000000eac81c23"
}
List the members of an organization

Organization owners can view the members of the organizations that they own. In this example, bjensen lists the members of example-org:

curl \
--header "X-OpenIDM-Username: bjensen" \
--header "X-OpenIDM-Password: Th3Password" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization/example-org/members?_queryFilter=true"
{
  "result": [
    {
      "_id": "b71e8dd9-6224-466f-9630-4358a69c69fd",
      "_rev": "0000000038ea999e",
      "_ref": "managed/realm-name_user/scarter",
      "_refResourceCollection": "managed/realm-name_user",
      "_refResourceId": "scarter",
      "_refProperties": {
        "_id": "b71e8dd9-6224-466f-9630-4358a69c69fd",
        "_rev": "0000000038ea999e"
      }
    }
  ],
  ...
}
Add an organization admin

Organization owners can create admins of the organizations that they own. An organization admin must be a member of the organization. In this example, bjensen makes scarter an admin of example-org:

curl \
--header 'Content-Type: application/json' \
--header "Accept-API-Version: resource=1.0" \
--header 'X-OpenIDM-Username: bjensen' \
--header 'X-OpenIDM-Password: Th3Password' \
--request PATCH \
--data '[
    {
        "operation": "add",
        "field": "/admins/-",
        "value": {
            "_ref": "managed/realm-name_user/scarter"
        }
    }
]' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization/example-org"
{
  "_id": "example-org",
  "_rev": "000000009c248a4a",
  "adminIDs": [
    "scarter"
  ],
  "ownerIDs": [
    "bjensen"
  ],
  "parentAdminIDs": [],
  "parentIDs": [],
  "parentOwnerIDs": [],
  "name": "example-org"
}
List an admin’s organizations

An organization owner, or admin, can only see the organizations that they own or administer. In this example, the admin scarter lists the organizations, and sees only those of which he is an admin:

curl \
--header "X-OpenIDM-Username: scarter" \
--header "X-OpenIDM-Password: Th3Password" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization?_queryFilter=true"
{
  "result": [
    {
      "_id": "example-org",
      "_rev": "000000009c248a4a",
      "adminIDs": [
        "scarter"
      ],
      "ownerIDs": [
        "bjensen"
      ],
      "parentAdminIDs": [],
      "parentIDs": [],
      "parentOwnerIDs": [],
      "name": "example-org"
    }
  ],
  ...
}
Add a member as an organization admin

Organization admins can also add members to the organizations they administer. In this example, the organization admin, scarter, creates a new member, jsanchez, and makes her a member of example-org:

curl \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: scarter" \
--header "X-OpenIDM-Password: Th3Password" \
--header "Accept-API-Version: resource=1.0" \
--request PUT \
--data '{
  "userName": "jsanchez",
  "sn": "Sanchez",
  "givenName": "Juanita",
  "mail": "jsanchez@example.com",
  "password": "Th3Password",
  "memberOfOrg": [{"_ref": "managed/realm-name_organization/example-org"}]
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/jsanchez"
{
  "_id": "jsanchez",
  "_rev": "00000000f9341bd6"
}
List a member’s organizations

Organization owners and admins can list the organizations of which a user is a member, as long as those organizations are owned or administrated by them. In this example, scarter lists the organizations of which jsanchez is a member:

curl \
--header "X-OpenIDM-Username: scarter" \
--header "X-OpenIDM-Password: Th3Password" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/jsanchez?_fields=memberOfOrg"
{
  "_id": "jsanchez",
  "_rev": "00000000f9341bd6",
  "memberOfOrg": [
    {
      "_ref": "managed/realm-name_organization/example-org",
      "_refResourceCollection": "managed/realm-name_organization",
      "_refResourceId": "example-org",
      "_refProperties": {
        "_id": "078d14b2-e5f1-4b21-9801-041138e691f4",
        "_rev": "00000000ac2e9927"
      }
    }
  ]
}

The organization established by the previous set of examples can be represented as follows:

example-org

In this organization, both bjensen and scarter can create and delete sub-organizations, also known as child organizations, of example-org, and can create and delete members within these child organizations.

The following example shows how to add and delete child organizations over the REST interface:

Add a child organization

Organization owners and admins can create and manage child organizations of the organizations that they own or administer. In this example, the organization owner, bjensen, creates a new organization named example-child-org, and makes it a child organization of example-org:

curl \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: bjensen" \
--header "X-OpenIDM-Password: Th3Password" \
--header "Accept-API-Version: resource=1.0" \
--header "If-None-Match: *" \
--request PUT \
--data '{
  "name": "example-child-org",
  "parent": {"_ref": "managed/realm-name_organization/example-org"}
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization/example-child-org"
{
  "_id": "example-child-org",
  "_rev": "00000000db852a9d"
}

The organization model is based on delegated administration. As with delegated administration, you cannot explicitly change the relationship endpoints. So, for example, so you cannot create, update, delete, or patch relationship edges. The following type of request is therefore not possible with the organization model:

curl \
--header "Content-Type: application/json" \
--header "X-OpenIDM-Username: bjensen" \
--header "X-OpenIDM-Password: Th3Password" \
--header "Accept-API-Version: resource=1.0" \
--header "If-None-Match: *" \
--request PUT \
--data '{
"name": "example-child-org",
"parent": {"_ref": "managed/realm-name_organization/example-org"}
}' \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization/children?_action=create"

When creating a child organization, custom attributes need to be added to the access flags for the owner-view-update-delete-orgs and owner-create-orgs privileges. Once these attributes are added, you can create a child organization and the custom attributes will be visible.

For more information on adding and updating privileges that apply to managed organizations, see Server configuration

List an owner’s organizations and child organzations

Organization owners and admins have access to any organizations that are child organizations of their own orgs. In this example, admin scarter lists his visible organizations again:

curl \
--header "X-OpenIDM-Username: scarter" \
--header "X-OpenIDM-Password: Th3Password" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization?_queryFilter=true"
{
  "result": [
    {
      "_id": "example-org",
      "_rev": "000000009c248a4a",
      "adminIDs": [
        "scarter"
      ],
      "ownerIDs": [
        "bjensen"
      ],
      "parentAdminIDs": [],
      "parentIDs": [],
      "parentOwnerIDs": [],
      "name": "example-org"
    },
    {
      "_id": "example-child-org",
      "_rev": "00000000db852a9d",
      "adminIDs": [],
      "ownerIDs": [],
      "parentAdminIDs": [
        "scarter"
      ],
      "parentIDs": [
        "example-org"
      ],
      "parentOwnerIDs": [
        "bjensen"
      ],
      "name": "example-child-org"
    }
  ],
  ...
}

Notice that scarter can now see the example-child-org that bjensen created in the previous example.

Organizations in high latency environments

The relationship-derived virtual properties that support the organization model are generally calculated in response to relationship signals that travel down the organization tree hierarchy. Imagine, for example, that a new root organization is added to an existing organization hierarchy (or that a new admin or owner is added to the root of an existing organization hierarchy). The relationship signals that trigger relationship-derived virtual property calculation are propagated down the organization hierarchy, and to all members of the organizations in this hierarchy. This, in turn, updates their relationship-derived virtual property state.

If there are many thousands of members of the organizations in the hierarchy, this operation can take a long time to complete. It is therefore best practice to grow an organization hierarchy downwards, adding new organizations as leaves to an existing hierarchy, and adding new admins and members to the leaves in the hierarchy tree. This is preferable to growing the hierarchy upwards, starting with the leaves, and growing the hierarchy up towards the root.

If you must add a new root to an existing organization hierarchy with many organizations and many members, or a new admin or owner to an organization near the top of the hierarchy, rather perform this request over the command-line, using the examples provided in the previous section.

Use policies to validate data

Identity Cloud provides an extensible policy service that lets you apply specific validation requirements to various components and properties. This chapter describes the policy service, and provides instructions on configuring policies for managed objects.

The policy service provides a REST interface for reading policy requirements and validating the properties of components against configured policies. Objects and properties are validated automatically when they are created, updated, or patched. Policies are generally applied to user passwords, but can also be applied to any managed or system object, and to internal user objects.

The policy service lets you accomplish the following tasks:

  • Read the configured policy requirements of a specific component.

  • Read the configured policy requirements of all components.

  • Validate a component object against the configured policies.

  • Validate the properties of a component against the configured policies.

The router service limits policy application to managed and internal user objects. To apply policies to additional objects, such as the audit service, modify your project’s router configuration. For more information about the router service, see Script triggers defined in the router configuration.

A configurable default policy applies to all managed objects.

Default policy for managed objects

Policies applied to managed objects are configured in two places:

  • A policy script that defines each policy and specifies how policy validation is performed.

  • A managed object policy element, defined in your managed object schema, that specifies which policies are applicable to each managed resource. For more information, see Policy Configuration Element.

    The policy configuration determines which policies apply to resources other than managed objects. The default policy configuration includes policies that are applied to internal user objects, but you can extend the configuration to apply policies to system objects.

Default policy reference

Identity Cloud includes the following default policies and parameters:

Policy Id Parameters

required

The property is required; not optional.

not-empty

The property can’t be empty.

not-null

The property can’t be null.

unique

The property must be unique.

valid-username

Tests for uniqueness and internal user conflicts.

no-internal-user-conflict

Tests for internal user conflicts.

regexpMatches

Matches a regular expression.

regexp

flags

The regular expression pattern.

valid-type

Tests for the specified types.

types

valid-query-filter

Tests for a valid query filter.

valid-array-items

Tests for valid array items.

valid-date

Tests for a valid date.

valid-formatted-date

Tests for a valid date format.

valid-time

Tests for a valid time.

valid-datetime

Tests for a valid date and time.

valid-duration

Tests for a valid duration format.

valid-email-address-format

Tests for a valid email address.

valid-name-format

Tests for a valid name format.

valid-phone-format

Tests for a valid phone number format.

at-least-X-capitals

The property must contain the minimum specified number of capital letters.

numCaps

Minimum number of capital letters.

at-least-X-numbers

The property must contain the minimum specified number of numbers.

numNums

Minimum number of numbers.

validNumber

Tests for a valid number.

minimumNumber

The property value must be greater than the minimum.

minimum

The minimum value.

maximumNumber

The property value must be less than the maximum.

maximum

The maximum value.

minimum-length

The property’s minimum string length.

minLength

The minimum string length.

maximum-length

The property’s maximum string length.

maxLength

The maximum string length.

cannot-contain-others

The property cannot contain values of the specified fields.

disallowedFields

A comma-separated list of the fields to check against. For example, the default managed user password policy specifies userName,givenName,sn as disallowed fields.

cannot-contain-characters

The property cannot contain the specified characters.

forbiddenChars

A comma-separated list of disallowed characters. For example, the default managed user userName policy specifies / as a disallowed character.

cannot-contain-duplicates

The property cannot contain duplicate characters.

mapping-exists

A sync mapping must exist for the property.

valid-permissions

Tests for valid permissions.

valid-accessFlags-object

Tests for valid access flags.

valid-privilege-path

Tests for a valid privilege path.

valid-temporal-constraints

Tests for valid temporal constraints.

Policy Configuration Element

Properties defined in the Identity Cloud managed object schema can include a policies element that specifies how policy validation should be applied to that property. The following excerpt of the default managed object configuration shows how policy validation is applied to the password and _id properties of a managed/realm-name_user object.

You can only declare policies on top-level managed object attributes. Nested attributes (those within an array or object) cannot have policy declared on them.
{
    "name" : "user",
    "schema" : {
        "id" : "http://jsonschema.net",
        "properties" : {
            "_id" : {
                "description" : "User ID",
                "type" : "string",
                "viewable" : false,
                "searchable" : false,
                "userEditable" : false,
                "usageDescription" : "",
                "isPersonal" : false,
                "policies" : [
                    {
                        "policyId" : "cannot-contain-characters",
                        "params" : {
                            "forbiddenChars" : [
                                "/"
                            ]
                        }
                    }
                ]
            },
            "password" : {
                "title" : "Password",
                "description" : "Password",
                "type" : "string",
                "viewable" : false,
                "searchable" : false,
                "userEditable" : true,
                "encryption" : {
                    "purpose" : "idm.password.encryption"
                },
                "scope" : "private",
                "isProtected": true,
                "usageDescription" : "",
                "isPersonal" : false,
                "policies" : [
                    {
                        "policyId" : "minimum-length",
                        "params" : {
                            "minLength" : 8
                        }
                    },
                    {
                        "policyId" : "at-least-X-capitals",
                        "params" : {
                            "numCaps" : 1
                        }
                    },
                    {
                        "policyId" : "at-least-X-numbers",
                        "params" : {
                            "numNums" : 1
                        }
                    },
                    {
                        "policyId" : "cannot-contain-others",
                        "params" : {
                            "disallowedFields" : [
                                "userName",
                                "givenName",
                                "sn"
                            ]
                        }
                    }
                ]
            }
        }
    }
}

Note that the policy for the _id property references the function cannot-contain-characters, which is defined in the policy. The policy for the password property references the functions minimum-length, at-least-X-capitals, at-least-X-numbers, and cannot-contain-others, that are defined in the policy. The parameters that are passed to these functions (number of capitals required, and so forth) are specified in the same element.

Validate Managed Object Data Types

The type property of a managed object specifies the data type of that property, for example, array, boolean, number, null, object, or string. For more information about data types, see the JSON Schema Primitive Types section of the JSON Schema standard.

The type property is subject to policy validation when a managed object is created or updated. Validation fails if data does not match the specified type, such as when the data is an array instead of a string. The default valid-type policy enforces the match between property values and the type defined in the managed object schema.

Identity Cloud supports multiple valid property types. For example, you might have a scenario where a managed user can have more than one telephone number, or a null telephone number (when the user entry is first created and the telephone number is not yet known). In such a case, you could specify the accepted property type as follows in your managed object schema:

"telephoneNumber" : {
    "type" : "string",
    "title" : "Telephone Number",
    "description" : "Telephone Number",
    "viewable" : true,
    "userEditable" : true,
    "pattern" : "^\\+?([0-9\\- \\(\\)])*$",
    "usageDescription" : "",
    "isPersonal" : true,
    "policies" : [
        {
            "policyId" : "minimum-length",
            "params" : {
                "minLength" : 1
            }
        },
        {
            "policyId": "maximum-length",
            "params": {
                "maxLength": 255
            }
        }
    ]
}

In this case, the valid-type policy checks the telephone number for an accepted type and pattern, either for a real telephone number or a null entry.

Configure Policy Validation Using the IDM admin UI

To configure policy validation for a managed object type using the IDM admin UI, update the configuration of the object type—a high-level overview:

  1. Go to the managed object, and edit or create a property.

  2. Click the Validation tab, and add the policy.

Show Me
createPolicyUI
  1. From the navigation bar, click Configure > Managed Objects.

  2. On the Managed Objects page, edit or create a managed object.

  3. On the Managed Object NAME page, do one of the following:

    • To edit an existing property, click the property.

    • To create a property, click Add a Property, enter the required information, and click Save.

      • Now click the property.

  4. From the Validation tab, click Add Policy.

  5. In the Add/Edit Policy window, enter information in the following fields, and click Add or Save:

    Policy Id

    Refers to the unique PolicyId.

    For a list of the default policies, see Policy Reference.

    Parameter Name

    Refers to the parameters for the PolicyId. For a list of the default policy parameters, see Policy Reference.

    Value

    The parameter’s value to validate.

Be cautious when using Validation Policies. If a policy relates to an array of relationships, such as between a user and multiple devices, Return by Default should always be set to false. You can verify this in your managed object schema. Any managed object that has items of "type" : "relationship", must also have "returnByDefault" : false.

Extend the policy service

You can extend the policy service by adding policies that are applied only under certain conditions.

Add conditional policy definitions

You can extend the policy service to support policies that are applied only under specific conditions. To apply a conditional policy to managed objects, add the policy to your project’s managed object schema. To apply a conditional policy to other objects, add it to your project’s policy configuration.

The following managed object configuration shows a sample conditional policy for the password property of managed user objects. The policy indicates that sys-admin users have a more lenient password policy than regular employees:

{
    "objects" : [
        {
            "name" : "user",
            ...
                "properties" : {
                ...
                    "password" : {
                        "title" : "Password",
                        "type" : "string",
                        ...
                        "conditionalPolicies" : [
                            {
                                "condition" : {
                                    "type" : "text/javascript",
                                    "source" : "(fullObject.org === 'sys-admin')"
                                },
                                "dependencies" : [ "org" ],
                                "policies" : [
                                    {
                                        "policyId" : "max-age",
                                        "params" : {
                                            "maxDays" : ["90"]
                                        }
                                    }
                                ]
                            },
                            {
                                "condition" : {
                                    "type" : "text/javascript",
                                    "source" : "(fullObject.org === 'employees')"
                                },
                                "dependencies" : [ "org" ],
                                "policies" : [
                                    {
                                        "policyId" : "max-age",
                                        "params" : {
                                            "maxDays" : ["30"]
                                        }
                                    }
                                ]
                            }
                        ],
                        "fallbackPolicies" : [
                            {
                                "policyId" : "max-age",
                                "params" : {
                                    "maxDays" : ["7"]
                                }
                            }
                        ]
                    }
                    ...
}

There are two distinct scripted conditions (defined in the condition elements). The first condition asserts that the user object, contained in the fullObject argument, is a member of the sys-admin org. If that assertion is true, the max-age policy is applied to the password attribute of the user object, and the maximum number of days that a password may remain unchanged is set to 90.

The second condition asserts that the user object is a member of the employees org. If that assertion is true, the max-age policy is applied to the password attribute of the user object, and the maximum number of days that a password may remain unchanged is set to 30.

In the event that neither condition is met (the user object is not a member of the sys-admin org or the employees org), an optional fallback policy can be applied. In this example, the fallback policy also references the max-age policy and specifies that for such users, their password must be changed after 7 days.

The dependencies field prevents the condition scripts from being run at all, if the user object does not include an org attribute.

Manage policies over REST

Manage the policy service over the REST interface at the openidm/policy endpoint.

List the defined policies

The following REST call displays a list of all the policies defined in Identity Cloud that are not associated with a specific resource. The policy objects are returned in JSON format, with one object for each defined policy ID:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/policy"
{
  "_id": "",
  "resources": [
    ...
    {
      "resource": "internal/user/*",
      "properties": [
        {
          "name": "_id",
          "policies": [
            {
              "policyId": "cannot-contain-characters",
              "params": {
                "forbiddenChars": [ "/" ]
              },
              "policyFunction": "\nfunction (fullObject, value, params, property) {\n    ...",
              "policyRequirements": [
                "CANNOT_CONTAIN_CHARACTERS"
              ]
            }
          ],
          "policyRequirements": [
            "CANNOT_CONTAIN_CHARACTERS"
          ]
        }
        ...
      ]
      ...
    }
  ]
}

To display the policies that apply to a specific resource, include the resource name in the URL. For example, the following REST call displays the policies that apply to managed users:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/policy/managed/realm-name_user/"
{
  "_id": "",
  "resource": "managed/realm-name_user/*",
  "properties": [
    {
      "policyRequirements": [
        "VALID_TYPE",
        "CANNOT_CONTAIN_CHARACTERS"
      ],
      "fallbackPolicies": null,
      "name": "_id",
      "policies": [
        {
          "policyRequirements": [
            "VALID_TYPE"
          ],
          "policyId": "valid-type",
          "params": {
            "types": [
              "string"
            ]
          }
        },
        {
          "policyId": "cannot-contain-characters",
          "params": {
            "forbiddenChars": [ "/" ]
          },
          "policyFunction": "...",
          "policyRequirements": [
            "CANNOT_CONTAIN_CHARACTERS"
          ]
        }
      ],
      "conditionalPolicies": null
    }
    ...
  ]
}

Validate objects and properties over REST

To verify that an object adheres to the requirements of all applied policies, include the validateObject action in the request.

The following example verifies that a new managed user object is acceptable, in terms of the policy requirements. Note that the ID in the URL (test in this example) is ignored—the action simply validates the object in the JSON payload:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "sn": "Jones",
  "givenName": "Bob",
  "telephoneNumber": "0827878921",
  "passPhrase": null,
  "mail": "bjones@example.com",
  "accountStatus": "active",
  "userName": "bjones@example.com",
  "password": "123"
}' \
"https://tenant-name.forgeblocks.com/openidm/policy/managed/realm-name_user/test?_action=validateObject"
{
  "result": false,
  "failedPolicyRequirements": [
    {
      "policyRequirements": [
        {
          "policyRequirement": "MIN_LENGTH",
          "params": {
            "minLength": 8
          }
        }
      ],
      "property": "password"
    },
    {
      "policyRequirements": [
        {
          "policyRequirement": "AT_LEAST_X_CAPITAL_LETTERS",
          "params": {
            "numCaps": 1
          }
        }
      ],
      "property": "password"
    }
  ]
}

The result (false) indicates that the object is not valid. The unfulfilled policy requirements are provided as part of the response - in this case, the user password does not meet the validation requirements.

Use the validateProperty action to verify that a specific property adheres to the requirements of a policy.

The following example checks whether a user’s new password (12345) is acceptable:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "password": "12345"
}' \
"https://tenant-name.forgeblocks.com/openidm/policy/managed/realm-name_user/9dce06d4-2fc1-4830-a92b-bd35c2f6bcbb?_action=validateProperty"
{
  "result": false,
  "failedPolicyRequirements": [
    {
      "policyRequirements": [
        {
          "policyRequirement": "MIN_LENGTH",
          "params": {
            "minLength": 8
          }
        }
      ],
      "property": "password"
    },
    {
      "policyRequirements": [
        {
          "policyRequirement": "AT_LEAST_X_CAPITAL_LETTERS",
          "params": {
            "numCaps": 1
          }
        }
      ],
      "property": "password"
    }
  ]
}

The result (false) indicates that the password is not valid. The unfulfilled policy requirements are provided as part of the response - in this case, the minimum length and the minimum number of capital letters.

Validating a property that fulfills the policy requirements returns a true result, for example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "password": "1NewPassword"
}' \
"https://tenant-name.forgeblocks.com/openidm/policy/managed/realm-name_user/9dce06d4-2fc1-4830-a92b-bd35c2f6bcbb?_action=validateProperty"
{
  "result": true,
  "failedPolicyRequirements": []
}

Validate field removal

To validate field removal, specify the fields to remove when calling the policy validateProperty action. You cannot remove fields that:

  • Are required in the required schema array.

  • Have a required policy.

  • Have a default value.

The following example validates the removal of the fields description and givenName:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request POST \
--data '{
  "_remove": [ "description", "givenName" ]
}' \
"https://tenant-name.forgeblocks.com/openidm/policy/managed/realm-name_user/ca5a3196-2ed3-4a76-8881-30403dee70e9?_action=validateProperty"

Because givenName is a required field, Identity Cloud returns a failed policy validation:

{
  "result": false,
  "failedPolicyRequirements": [
    {
      "policyRequirements": [
        {
          "policyRequirement": "REQUIRED"
        }
      ],
      "property": "givenName"
    }
  ]
}

Force validation of default values

Identity Cloud does not perform policy validation for default values specified in the managed objects schema. It may be necessary to force validation when validating properties for an object that does not yet exist. To force validation, include forceValidate=true in the request URL.

Validate properties to unknown resource paths

To perform a validateProperty action to a path that is unknown (*), such as managed/realm-name_user/* or managed/realm-name_user/userDoesntExistYet, the payload must include:

  • An object field that contains the object details.

  • A properties field that contains the properties to be evaluated.

Pre-registration validation example

A common use case for validating properties for unknown resources is prior to object creation, such as during pre-registration.

  1. Always pass the object and properties content in the POST body because Identity Cloud has no object to look up.

  2. Use any placeholder id in the request URL, as * has no special meaning in the API.

    This example uses a conditional policy for any object with the description test1:

    "password" : {
        ...
        "conditionalPolicies" : [
            {
                "condition" : {
                    "type" : "text/javascript",
                    "source" : "(fullObject.description === 'test1')"
                },
                "dependencies" : [ "description" ],
                "policies" : [
                    {
                        "policyId" : "at-least-X-capitals",
                        "params" : {
                            "numCaps" : 1
                        }
                    }
                ]
            }
        ],
  3. Using the above conditional policy, you could perform a validateProperty action to managed/realm-name_user/* with the request:

    curl \
    --header "Authorization: Bearer *token*" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
      "object": {
        "description": "test1"
      },
      "properties": {
        "password": "passw0rd"
      }
    }' \
    "https://tenant-name.forgeblocks.com/openidm/policy/managed/realm-name_user/*?_action=validateProperty"
    {
      "result": false,
      "failedPolicyRequirements": [
        {
          "policyRequirements": [
            {
              "params": {
                "numCaps": 1
              },
              "policyRequirement": "AT_LEAST_X_CAPITAL_LETTERS"
            }
          ],
          "property": "password"
        }
      ]
    }

Access data objects

Access data objects using scripts

Identity Cloud’s uniform programming model means that all objects are queried and manipulated in the same way, using the Resource API. The URL or URI that is used to identify the target object for an operation depends on the object type. For more information about scripts and the objects available to scripts, see Scripting.

You can use the Resource API to obtain managed, system, configuration, and repository objects, as follows:

val = openidm.read("managed/realm-name_organization/mysampleorg")
val = openidm.read("system/mysystem/account")
val = openidm.read("config/custom/mylookuptable")
val = openidm.read("repo/custom/mylookuptable")

For information about constructing an object ID, see URI Scheme.

You can update entire objects with the update() function, as follows:

openidm.update("managed/realm-name_organization/mysampleorg", rev, object)
openidm.update("system/mysystem/account", rev, object)

You can apply a partial update to a managed or system object by using the patch() function:

openidm.patch("managed/realm-name_organization/mysampleorg", rev, value)

The create(), delete(), and query() functions work the same way.

Access data objects using the REST API

Identity Cloud provides RESTful access to data objects through the ForgeRock Common REST API. To access objects over REST, you can use a browser-based REST client, such as the Simple REST Client for Chrome, or RESTClient for Firefox. Alternatively, you can use the curl command-line utility.

For a comprehensive overview of the REST API, see the REST API reference.

To obtain a managed object through the REST API, depending on your security settings and authentication configuration, perform an HTTP GET on the corresponding URL, for example https://tenant-name.forgeblocks.com/openidm/managed/realm-name_organization/mysampleorg.

By default, the HTTP GET returns a JSON representation of the object.

In general, you can map any HTTP request to the corresponding openidm.method call. The following example shows how the parameters provided in an openidm.query request correspond with the key-value pairs that you would include in a similar HTTP GET request:

Reading an object using the Resource API:

openidm.query("managed/realm-name_user", { "_queryFilter": "true" }, ["userName","sn"])

Reading an object using the REST API:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=true&_fields=userName,sn"

Access data objects by remote proxy

You can proxy REST requests to a remote IDM instance using the /external/idm/factoryPid endpoint. This lets you treat any other IDM instance as a resource within the one you are managing. You can then use it in a sync mapping, call actions on it, use it within scripts, or use it in any other way that you might use a resource in IDM. You can call any endpoint in the remote IDM system using this proxy.

A few situations where this feature may be useful include:

  • Situations where a development or testing environment has data that needs to be synced into the production environment.

  • Situations where data is deployed in geographically diverse data centers and changes need to be kept in sync with one another.

  • Situations where a new instance needs to sync data between existing on-premises and cloud instances.

This feature does not support liveSync/implicit sync from the remote IDM resources. This means that you will be limited to using recon when it comes to pulling data from a remote system.

Prerequisites

To connect to a remote instance over SSL, you must import the remote instance’s server certificate into your local instance’s truststore. For further information, see Configure a self-managed SSL certificate.

Use in a sync mapping

To use the remote IDM proxy in a synchronization mapping, add the mapping to your mapping configuration. For example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--data '{
  "mappings": [
    {
      "name": "onprem_user_to_fidc_alpha_user",
      "source": "external/idm/65/managed/user",
      "target": "external/idm/fidc/managed/alpha_user"
    }
  ]
}' \
--request PUT \
"https://tenant-name.forgeblocks.com/openidm/config/sync"

To verify the changes, get the mapping configuration. For example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/config/sync"

Authentication

Authentication against the remote IDM instance is supported via basic authentication, or bearer token authentication when IDM is configured to use rsFilter. The authentication strategy determines some of the parameters required for the request.

Property Required? Definition

enabled

No

The enable state of the service. Default is true.

scope

No

The requested OAuth2 scope(s).

scopeDelimiter

No

The scope delimiter to use. Defaults to space.

authtype

Yes

The authentication strategy to use. Either basic or bearer.

instanceUrl

Yes

The URL of the remote instance to relay the request to.

userName

With basic auth

The basic authentication user name.

password

With basic auth

The basic authentication password.

clientId

With bearer auth

The clientId used to request an access token from the token endpoint.

clientSecret

With bearer auth

The client secret used to request an access token from the token endpoint.

tokenEndpoint

With bearer auth

The OAuth2 token endpoint.

Examples

Basic authentication

{
  "enabled" : true,
  "authType" : "basic",
  "instanceUrl" : "https://localhost:8443/openidm/",
  "userName" : "openidm-admin",
  "password" : "openidm-admin"
}

Bearer/Oauth2 authentication

{
  "enabled" : true,
  "authType" : "bearer",
  "instanceUrl" : "https://fr-platform.iam.example.com/openidm/",
  "clientId" : "idm-provisioning",
  "clientSecret" : "password",
  "scope" : [ ],
  "tokenEndpoint" : "https://fr-platform.iam.example.com/am/oauth2/realms/root/access_token",
  "scopeDelimiter" : " "
}

Sample request

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/external/idm/platform/managed/user?_queryFilter=true'
{
  "result": [{
  "_id": "95b2b43c-621e-4bca-8a97-efc768f17751",
  "_rev": "00000000f20217df",
  "userName": "testUser",
  "accountStatus": "active",
  "givenName": "Test",
  "sn": "User",
  "mail": "testUser@test.com"
}],
  "resultCount": 1,
  "pagedResultsCookie": null,
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}

Use in a script

openidm.query("external/idm/fidc/managed/alpha_user", {"_queryFilter": "userName eq 'bjensen'"});

Define and call data queries

An advanced query model enables you to define queries and to call them over the REST or Resource API. The following types of queries are supported, on both managed, and system objects:

  • Common filter expressions

  • Parameterized, or predefined queries

Special characters in queries

JavaScript query invocations are not subject to the same URL-encoding requirements as GET requests. Because JavaScript supports the use of single quotes, it is not necessary to escape the double quotes from most examples in this guide. Make sure to protect against pulling in data that could contain special characters, such as double-quotes ("). The following example shows one method of handling special characters:

"correlationQuery" : {
  "type" : "text/javascript",
  "source" : "var qry = {'_queryFilter': org.forgerock.util.query.QueryFilter.equalTo('uid', source.userName).toString()}; qry"
}

Common filter expressions

The ForgeRock REST API defines common filter expressions that enable you to form arbitrary queries using a number of supported filter operations. This query capability is the standard way to query data if no predefined query exists, and is supported for all managed and system objects.

Common filter expressions are useful in that they do not require knowledge of how the object is stored and do not require additions to the repository configuration.

Common filter expressions are called with the _queryFilter keyword. The following example uses a common filter expression to retrieve managed user objects whose user name is Smith:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=userName+eq+"smith"'

The filter is URL encoded in this example. The corresponding filter using the resource API would be:

openidm.query("managed/realm-name_user", { "_queryFilter" : '/userName eq "smith"' });

Note that, this JavaScript invocation is internal and is not subject to the same URL-encoding requirements that a GET request would be. Also, because JavaScript supports the use of single quotes, it is not necessary to escape the double quotes in this example.

Construct queries

The openidm.query function lets you query managed and system objects. The query syntax is openidm.query(id, params), where id specifies the object on which the query should be performed, and params provides the parameters that are passed to the query (the _queryFilter). For example:

var equalTo = org.forgerock.util.query.QueryFilter.equalTo;
queryParams = {
    "_queryFilter": equalTo("uid", value).toString()
};
openidm.query("managed/realm-name_user", queryParams)

Over the REST interface, the query filter is specified as _queryFilter=filter, for example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=userName+eq+"Smith"'

In _queryFilter expressions, string values must use double-quotes. Numeric and boolean expressions should not use quotes.

When called over REST, you must URL encode the filter expression. The following examples show the filter expressions using the resource API and the REST API, but do not show the URL encoding, to make them easier to read.

The filter expression is constructed from the building blocks shown in this section. In these expressions the simplest json-pointer is a field of the JSON resource, such as userName or id. A JSON pointer can, however, point to nested elements.

You can also use the negation operator (!) in query construction. For example, a _queryFilter=!(userName+eq+"jdoe") query would return every userName except for jdoe.

Comparison expressions

You can use comparison query filters for objects and object array properties that:

Equal a Specified Value

This is the associated JSON comparison expression: json-pointer eq json-value.

Example 1. Example 1
"_queryFilter" : '/givenName eq "Dan"'

The following REST call returns the user name and given name of all managed users whose first name (givenName) is "Dan":

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=givenName+eq+"Dan"&_fields=userName,givenName'
{
  "result": [
    {
      "givenName": "Dan",
      "userName": "dlangdon"
    },
    {
      "givenName": "Dan",
      "userName": "dcope"
    },
    {
      "givenName": "Dan",
      "userName": "dlanoway"
    }
  ],
  ...
}
Example 2. Example 2
"_queryFilter" : "/stringArrayField eq 'foo'"

The following REST call returns role entries where a value within the stringArrayField array equals "foo":

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_role?_queryFilter=stringArrayField+eq+"foo"'
{
  "result": [
    {
      "_id": "admin2",
      "_rev": "0",
      "name": "admin2",
      "stringArrayField": [
        "foo",
        "bar"
      ]
    }
  ],
  ...
}
Contain a Specified Value

This is the associated JSON comparison expression: json-pointer co json-value.

Example 3. Example
"_queryFilter" : '/givenName co "Da"'

The following REST call returns the user name and given name of all managed users whose first name (givenName) contains "Da":

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=givenName+co+"Da"&_fields=userName,givenName'
{
  "result": [
    {
      "givenName": "Dave",
      "userName": "djensen"
    },
    {
      "givenName": "David",
      "userName": "dakers"
    },
    {
      "givenName": "Dan",
      "userName": "dlangdon"
    },
    {
      "givenName": "Dan",
      "userName": "dcope"
    },
    {
      "givenName": "Dan",
      "userName": "dlanoway"
    },
    {
      "givenName": "Daniel",
      "userName": "dsmith"
    },
    ...
  ],
  "resultCount": 10,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}
Start With a Specified Value

This is the associated JSON comparison expression: json-pointer sw json-value.

Example 4. Example
"_queryFilter" : '/sn sw "Jen"'

The following REST call returns the user names of all managed users whose last name (sn) starts with "Jen":

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=sn+sw+"Jen"&_fields=userName'
{
  "result": [
    {
      "userName": "bjensen"
    },
    {
      "userName": "djensen"
    },
    {
      "userName": "cjenkins"
    },
    {
      "userName": "mjennings"
    }
  ],
  "resultCount": 4,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}
Are Less Than a Specified Value

This is the associated JSON comparison expression: json-pointer lt json-value.

Example 5. Example
"_queryFilter" : '/employeeNumber lt 5000'

The following REST call returns the user names of all managed users whose employeeNumber is lower than 5000:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=employeeNumber+lt+5000&_fields=userName,employeeNumber'
{
  "result": [
    {
      "employeeNumber": 4907,
      "userName": "jnorris"
    },
    {
      "employeeNumber": 4905,
      "userName": "afrancis"
    },
    {
      "employeeNumber": 3095,
      "userName": "twhite"
    },
    {
      "employeeNumber": 3921,
      "userName": "abasson"
    },
    {
      "employeeNumber": 2892,
      "userName": "dcarter"
    },
    ...
  ],
  "resultCount": 4999,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}
Are Less Than or Equal to a Specified Value

This is the associated JSON comparison expression: json-pointer le json-value.

Example 6. Example
"_queryFilter" : '/employeeNumber le 5000'

The following REST call returns the user names of all managed users whose employeeNumber is 5000 or less:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=employeeNumber+le+5000&_fields=userName,employeeNumber'
{
  "result": [
    {
      "employeeNumber": 4907,
      "userName": "jnorris"
    },
    {
      "employeeNumber": 4905,
      "userName": "afrancis"
    },
    {
      "employeeNumber": 3095,
      "userName": "twhite"
    },
    {
      "employeeNumber": 3921,
      "userName": "abasson"
    },
    {
      "employeeNumber": 2892,
      "userName": "dcarter"
    },
    ...
  ],
  "resultCount": 5000,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}
Are Greater Than a Specified Value

This is the associated JSON comparison expression: json-pointer gt json-value

Example 7. Example
"_queryFilter" : '/employeeNumber gt 5000'

The following REST call returns the user names of all managed users whose employeeNumber is higher than 5000:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=employeeNumber+gt+5000&_fields=userName,employeeNumber'
{
  "result": [
    {
      "employeeNumber": 5003,
      "userName": "agilder"
    },
    {
      "employeeNumber": 5011,
      "userName": "bsmith"
    },
    {
      "employeeNumber": 5034,
      "userName": "bjensen"
    },
    {
      "employeeNumber": 5027,
      "userName": "cclarke"
    },
    {
      "employeeNumber": 5033,
      "userName": "scarter"
    },
    ...
  ],
  "resultCount": 1458,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}
Are Greater Than or Equal to a Specified Value

This is the associated JSON comparison expression: json-pointer ge json-value.

Example 8. Example
"_queryFilter" : '/employeeNumber ge 5000'

The following REST call returns the user names of all managed users whose employeeNumber is 5000 or greater:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=employeeNumber+ge+5000&_fields=userName,employeeNumber'
{
  "result": [
    {
      "employeeNumber": 5000,
      "userName": "agilder"
    },
    {
      "employeeNumber": 5011,
      "userName": "bsmith"
    },
    {
      "employeeNumber": 5034,
      "userName": "bjensen"
    },
    {
      "employeeNumber": 5027,
      "userName": "cclarke"
    },
    {
      "employeeNumber": 5033,
      "userName": "scarter"
    },
    ...
  ],
  "resultCount": 1457,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}

Although specific system endpoints also support EndsWith and ContainsAllValues queries, such queries are not supported for managed objects and have not been tested with all supported ICF connectors.

Presence expressions

The following examples show how you can build filters using a presence expression, shown as pr. The presence expression is a filter that returns all records with a given attribute.

A presence expression filter evaluates to true when a json-pointer pr matches any object in which the json-pointer is present, and contains a non-null value. Consider the following expression:

"_queryFilter" : '/mail pr'

The following REST call uses that expression to return the mail addresses for all managed users with a mail property:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=mail+pr&_fields=mail'
{
  "result": [
    {
      "mail": "jdoe@exampleAD.com"
    },
    {
      "mail": "bjensen@example.com"
    }
  ],
  "resultCount": 2,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}

Depending on the connector, you can apply the presence filter on system objects. The following query returns the email address of all users in a CSV file who have the email attribute in their entries:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/system/csvfile/account?_queryFilter=email+pr&_fields=email'
{
  "result": [
    {
      "_id": "bjensen",
      "email": "bjensen@example.com"
    },
    {
      "_id": "scarter",
      "email": "scarter@example.com"
    }
  ],
  "resultCount": 2,
  "pagedResultsCookie": "MA%3D%3D",
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}

Not all connectors support the presence filter. In most cases, you can replicate the behavior of the presence filter with an "equals" (eq) query such as _queryFilter=email+eq"*"

Literal expressions

A literal expression is a boolean:

  • true matches any object in the resource.

  • false matches no object in the resource.

For example, you can list the _id of all managed objects as follows:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=true&_fields=_id'
{
  "result": [
    {
      "_id": "d2e29d5f-0d74-4d04-bcfe-b1daf508ad7c"
    },
    {
      "_id": "709fed03-897b-4ff0-8a59-6faaa34e3af6"
    }
  ],
  "resultCount": 2,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}

In expression clause

Identity Cloud provides limited support for the in expression clause. You can use this clause for queries on singleton string properties or arrays. in query expressions are not supported through the IDM admin UI or for use by delegated administrators.

The in operator is shorthand for multiple OR conditions.

The following example command includes escaped characters. For readability, the non-escaped URL syntax is:

https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_pageSize=1000&_fields=userName&_queryFilter=/userName+in+'["user4a","user3a"]'
curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_pageSize=1000&_fields=userName&_queryFilter=userName%20in%20'%5B%22user4a%22%2C%22user3a%22%5D'"
{
  "result": [
    {
      "_id": "e32f9a3d-0039-4cb0-82d7-347cb808672e",
      "_rev": "000000000ae18357",
      "userName": "user3a"
    },
    {
      "_id": "120625c5-cfe7-48e7-b66a-6a0a0f9d2901",
      "_rev": "000000005ad98467",
      "userName": "user4a"
    }
  ],
  "resultCount": 2,
  "pagedResultsCookie": null,
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}

Filter expanded relationships

You can use _queryFilter to directly filter expanded relationships from a collection, such as authzRoles. The following example queries the manager-int authorization role of a user:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/b70293db-8743-45a7-9215-1ca8fd8a0073/authzRoles?_queryFilter=name+eq+'manager-int'&_fields=*"
{
  "result": [
    {
      "_id": "b1d78144-7029-4135-8e73-85efe0a40b6b",
      "_rev": "00000000d4b8ab97",
      "_ref": "internal/role/c0a38233-c0f2-477d-8f18-f5485b7d002f",
      "_refResourceCollection": "internal/role",
      "_refResourceId": "c0a38233-c0f2-477d-8f18-f5485b7d002f",
      "_refProperties": {
        "_grantType": "",
        "_id": "b1d78144-7029-4135-8e73-85efe0a40b6b",
        "_rev": "00000000d4b8ab97"
      },
      "name": "manager-int",
      "description": "manager-int-desc",
      "temporalConstraints": null,
      "condition": null,
      "privileges": null
    }
  ],
  "resultCount": 1,
  "pagedResultsCookie": null,
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}

Complex expressions

You can combine expressions using the boolean operators and, or, and ! (not). The following example queries managed user objects located in London, with last name Jensen:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user/?_queryFilter=city+eq+"London"and+sn+eq"Jensen"&_fields=userName,givenName,sn'
{
  "result": [
    {
      "sn": "Jensen",
      "givenName": "Clive",
      "userName": "cjensen"
    },
    {
      "sn": "Jensen",
      "givenName": "Dave",
      "userName": "djensen"
    },
    {
      "sn": "Jensen",
      "givenName": "Margaret",
      "userName": "mjensen"
    }
  ],
  "resultCount": 3,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}

Page query results

The common filter query mechanism supports paged query results for managed objects, and for some system objects, depending on the system resource. There are two ways to page objects in a query:

  • Using a cookie based on the value of a specified sort key.

  • Using an offset that specifies how many records should be skipped before the first result is returned.

These methods are implemented with the following query parameters:

_pagedResultsCookie

Opaque cookie used by the server to keep track of the position in the search results. The format of the cookie is a base-64 encoded version of the value of the unique sort key property. The value of the returned cookie is URL-encoded to prevent values such as + from being incorrectly translated.

You cannot page results without sorting them (using the _sortKeys parameter). If you do not specify a sort key, the _id of the record is used as the default sort key. At least one of the specified sort key properties must be a unique value property, such as _id.

For paged searches on generic mappings, you should sort on the _id property, because this is the only property that is stored outside of the JSON blob. If you sort on something other than _id, the search will incur a performance hit because Identity Cloud effectively has to pull the entire result set, and then sort it.

The server provides the cookie value on the first request. You should then supply the cookie value in subsequent requests until the server returns a null cookie, meaning that the final page of results has been returned.

The _pagedResultsCookie parameter is supported only for filtered queries, that is, when used with the _queryFilter parameter. You cannot use the _pagedResultsCookie with a _queryId.

The _pagedResultsCookie and _pagedResultsOffset parameters are mutually exclusive, and cannot be used together.

Paged results are enabled only if the _pageSize is a non-zero integer.

_pagedResultsOffset

Specifies the index within the result set of the number of records to be skipped before the first result is returned. The format of the _pagedResultsOffset is an integer value. When the value of _pagedResultsOffset is greater than or equal to 1, the server returns pages, starting after the specified index.

This request assumes that the _pageSize is set, and not equal to zero.

For example, if the result set includes 10 records, the _pageSize is 2, and the _pagedResultsOffset is 6, the server skips the first 6 records, then returns 2 records, 7 and 8. The _remainingPagedResults value would be 2, the last two records (9 and 10) that have not yet been returned.

If the offset points to a page beyond the last of the search results, the result set returned is empty.

_pageSize

An optional parameter indicating that query results should be returned in pages of the specified size. For all paged result requests other than the initial request, a cookie should be provided with the query request.

The default behavior is not to return paged query results. If set, this parameter should be an integer value, greater than zero.

When a _pageSize is specified, and non-zero, the server calculates the totalPagedResults, in accordance with the totalPagedResultsPolicy, and provides the value as part of the response. If a count policy is specified (_totalPagedResultsPolicy=EXACT, The totalPagedResults returns the total result count. If no count policy is specified in the query, or if _totalPagedResultsPolicy=NONE, result counting is disabled, and the server returns a value of -1 for totalPagedResults. The following example shows a query that requests two results with a totalPagedResultsPolicy of EXACT:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=true&_pageSize=2&_totalPagedResultsPolicy=EXACT"
{
  "result": [
    {
      "_id": "adonnelly",
      "_rev": "0",
      "userName": "adonnelly",
      "givenName": "Abigail",
      "sn": "Donnelly",
      "telephoneNumber": "12345678",
      "active": "true",
      "mail": "adonnelly@example.com",
      "accountStatus": "active",
      "effectiveRoles": [],
      "effectiveAssignments": []
    },
    {
      "_id": "bjensen",
      "_rev": "0",
      "userName": "bjensen",
      "givenName": "Babs",
      "sn": "Jensen",
      "telephoneNumber": "12345678",
      "active": "true",
      "mail": "bjensen@example.com",
      "accountStatus": "active",
      "effectiveRoles": [],
      "effectiveAssignments": []
    }
  ],
  "resultCount": 2,
  "pagedResultsCookie": "eyIvX2lkIjoiYm11cnJheSJ9",
  "totalPagedResultsPolicy": "EXACT",
  "totalPagedResults": 22,
  "remainingPagedResults": -1
}

The totalPagedResults and _remainingPagedResults parameters are not supported for all queries. Where they are not supported, their returned value is always -1. In addition, counting query results using these parameters is not currently supported for a ForgeRock Directory Services (DS) repository.

Requesting the total result count (with _totalPagedResultsPolicy=EXACT) incurs a performance cost on the query.

Queries that return large data sets will have a significant impact on heap requirements, particularly if they are run in parallel with other large data requests. To avoid out of memory errors, analyze your data requirements, set the heap configuration appropriately, and modify access controls to restrict requests on large data sets.

Sort query results

For common filter query expressions, you can sort the results of a query using the _sortKeys parameter. This parameter takes a comma-separated list as a value and orders the way in which the JSON result is returned, based on this list.

The _sortKeys parameter is not supported for predefined queries.

  • Pagination using _pageSize is recommended if you intend to use _sortKeys. If you do not paginate your query, the data you are querying must be indexed in DS.

  • When viewing data that is persisted in DS and sorted by un-indexed _sortKeys, the _pageSize parameter must be less than or equal to the index-entry-limit as configured in DS (default value is 4000).

The following query returns all users with the givenName Dan, and sorts the results alphabetically, according to surname (sn):

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/system/ldap/account?_queryFilter=givenName+eq+"Dan"&_fields=givenName,sn&_sortKeys=sn'
{
  "result": [
    {
      "sn": "Cope",
      "givenName": "Dan"
    },
    {
      "sn": "Langdon",
      "givenName": "Dan"
    },
    {
      "sn": "Lanoway",
      "givenName": "Dan"
    }
  ],
  "resultCount": 3,
  "pagedResultsCookie": null,
  "remainingPagedResults": -1
}

When you query a relationship field, fields that belong to the related object are not available as _sortKeys. For example, if you query a list of a manager’s reports, you cannot sort by the reports' last names. This is because the available _sortKeys are based on the object being queried, which, in the case of relationships, is actually a list of references to other objects, not the objects themselves.

Recalculate virtual property values in queries

For managed objects Identity Cloud includes an onRetrieve script hook that enables you to recalculate property values when an object is retrieved as the result of a query. To use the onRetrieve trigger, the query must include the executeOnRetrieve parameter, for example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/managed/realm-name_user?_queryFilter=sn+eq+"Jensen"&executeOnRetrieve=true'

If a query includes executeOnRetrieve, the query recalculates virtual property values, based on the current state of the system. The result of the query will be the same as a read on a specific object, because reads always recalculate virtual property values.

If a query does not include executeOnRetrieve, the query returns the virtual properties of an object, based on the value that is persisted in the repository. Virtual property values are not recalculated.

For performance reasons, executeOnRetrieve is false by default.

Virtual properties that use queryConfig for calculation instead of an onRetrieve script are not recalculated by executeOnRetrieve. These properties are recalculated only when there is a change (such as adding or removing a role affecting effectiveRoles, or a temporal constraint being triggered or changed).

Import bulk data

The bulk import facility lets you import large numbers of external entries over REST. You import entries from a comma-separated values (CSV) file, to a specified managed object type in the Identity Cloud repository. Bulk import works as follows:

  • Loads bulk CSV entries and stores them temporarily (in the Identity Cloud repository) as JSON objects

  • Creates a temporary mapping between those entries and the managed object store in the repository

  • Performs a reconciliation between the JSON objects and the objects in the repository

The bulk import mechanism assumes that the CSV file is the authoritative data source. If you run an import more than once, the import overwrites all of the properties of the managed object (including timestamps) with the values in the CSV file.

To import bulk CSV entries into the repository, using the REST API, follow these steps:

Generate a CSV Template

The first time you upload entries, you must generate a CSV template. The template is essentially an empty CSV file with one header row that matches the managed object type to which you are importing. In most cases, you will be importing data that fits the managed/realm-name_user object model, but you can import any managed object type, such as roles and assignments.

To generate the CSV template, send a GET request to the openidm/csv/template endpoint. The following request generates a CSV template for the managed user object type:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/csv/template?resourceCollection=managed/realm-name_user&_fields=header&_mimeType='text/plain'"
{
  "_id": "template",
  "header": "\"userName\",\"givenName\",\"sn\",\"mail\",\"description\",\"accountStatus\",\"telephoneNumber\",
 \"postalAddress\",\"city\",\"postalCode\",\"country\",\"stateProvince\",\"preferences/updates\",
 \"preferences/marketing\""
}

The template is generated based on the specified resourceCollection, and includes a single header row. The names of each header column are derived from the schema of the managed object type. The template includes only a subset of managed user properties that can be represented by CSV fields.

Only the following managed object properties are included in the header row:

  • Properties of type string, boolean, and number

  • Properties that do not start with an underscore (such as _id or _rev)

    If you are importing entries to managed/realm-name_user, the bulk import facility assumes that self-service password reset is enabled. This is because the import does not support upload of hashed passwords.

  • Properties whose scope is not private

Set the parameters _fields=header and _mimeType=text/csv to download the template as a CSV file.

When you have generated the template, export your external data to CSV format, using the headers in the generated template.

Upload a CSV File

The default maximum file size for bulk import is 50MBytes. If you need to import a number of records that exceeds this size, divide the data into chunks and import each file separately.

When you have a CSV file, with the structure of the template generated in the previous example, upload the file to the Identity Cloud repository with the following request:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--form upload=@/path/to/example-users.csv \
--request POST \
"https://tenant-name.forgeblocks.com/upload/csv/managed/realm-name_user?uniqueProperty=userName"
{
  "importUUIDs": [
    "3ebd514f-bdd7-491f-928f-21b72f44e381"
  ]
}
--form (-F)

This option causes curl to POST data using the Content-Type multipart/form-data, which lets you upload binary files. To indicate that the form content is a file, prefix the file name with an @ sign.

To import more than one file at once, specify multiple --form options, for example:

--form upload=@/path/to/example-users-a-j.csv \
--form upload=@/path/to/example-users-k-z.csv \
uniqueProperty (required)

This parameter lets you correlate existing entries, based on a unique value field. This is useful if you need to upload the same file a number of times (for example, if data in the file changes, or if some entries in the file contained errors). You can specify any unique value property here. You can also correlate on more than one property by specifying multiple, comma-delimited unique properties.

A successful upload generates an array of importUUIDs. You need these UUIDs to perform other operations on the import records.

Note that the endpoint (upload/csv) is not an Identity Cloud endpoint.

Query Bulk Imports

A query on the csv/metadata endpoint returns the import ID, the data structure (header fields in the CSV file), a recon ID, and a number of fields indicating the status of the import:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"https://tenant-name.forgeblocks.com/openidm/csv/metadata/?_queryFilter=true"
{
  "result": [
    {
      "_id": "3ebd514f-bdd7-491f-928f-21b72f44e381",
      "_rev": "000000003e8ef4f7",
      "header": [
        "userName",
        "givenName",
        "sn",
        "mail",
        "description",
        "accountStatus",
        "country"
      ],
      "reconId": "2e2cf41a-c4b8-4dda-9d92-6e0af65a15fe-6528",
      "filename": "example-users.csv",
      "resourcePath": "managed/realm-name_user",
      "total": 1000,
      "success": 1000,
      "failure": 0,
      "created": 1000,
      "updated": 0,
      "unchanged": 0,
      "begin": "2020-04-17T16:31:02.955Z",
      "end": "2020-04-17T16:31:09.861Z",
      "cancelled": false,
      "importDeleted": false,
      "tempRecords": 0,
      "purgedTempRecords": true,
      "purgedErrorRecords": false,
      "authId": "openidm-admin",
      "authzComponent": "internal/user"
    },
    {
      "_rev": "00000000d4392fc8"
    }
  ],
  ...
}
Query Imports To a Specific Object Type

Use a query filter to restrict your query to imports to a specific managed object type. The following example queries uploads to the managed user object:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
'https://tenant-name.forgeblocks.com/openidm/csv/metadata/?_queryFilter=/resourcePath+eq+"managed/realm-name_user"'
{
  "result": [
    {
      "_id": "82d9a643-8b03-4cec-86fc-3e09c4c2f01c",
      "_rev": "000000009b3ff60b",
      "header": [
        "userName",
        "givenName",
        "sn",
        "mail",
        "description",
        "accountStatus",
        "country"
      ],
      "reconId": "417dae3b-c939-4191-acbf-6eb1b9e802af-53335",
      "filename": "example-users.csv",
      "resourcePath": "managed/realm-name_user",
      "total": 1001,
      "success": 1000,
      "failure": 1,
      "created": 0,
      "updated": 0,
      "unchanged": 1000,
      "begin": "2020-04-20T13:12:03.028Z",
      "end": "2020-04-20T13:12:05.222Z",
      "cancelled": false,
      "importDeleted": false,
      "tempRecords": 0,
      "purgedTempRecords": true,
      "purgedErrorRecords": false,
      "authId": "openidm-admin",
      "authzComponent": "internal/user"
    }
  ],
  ...
}
Handle Failed Import Records

The previous example showed the statistics that are returned when you query bulk imports. One of these fields is "failure": 0,. If the import was unsuccessful for any records, this failure field will have a positive value. You can then download the failed records, examine the failures and correct them in the CSV file, then run the import again.

To download failed records, send a GET request to the endpoint export/csvImportFailures/importUUID:

curl \
--header "Authorization: Bearer *token*" \
--request GET \
--header "Accept-API-Version: resource=1.0" \
"https://tenant-name.forgeblocks.com/export/csvImportFailures/82d9a643-8b03-4cec-86fc-3e09c4c2f01c"
userName,  givenName,  sn,      mail,     ...,  _importError
emacheke,  Edward,     Macheke, emacheke, ...,  "{code=403, reason=Forbidden, message=Policy validation
   failed, detail={result=false, failedPolicyRequirements=[{policyRequirements=[
   {policyRequirement=VALID_EMAIL_ADDRESS_FORMAT}], property=mail}]}}"

The output indicates the failed record or records, and the reason for the failure, in the _importError field. In this example, the import failed because of a policy validation error—the email address is not the correct format.

Identity Cloud does not scan for possible CSV injection attacks on uploaded files. Do not edit the downloaded CSV file with Microsoft Excel, as this can expose your data to CSV injection.

Cancel an Import in Progress

Cancel an import that is in progress by sending a POST request to the openidm/csv/metadata/importUUID endpoint, with the cancel action. You might want to cancel an import if the import is taking too long, or if you have noticed problems with the import data, for example:

curl \
--header "Authorization: Bearer *token*" \
--header "Accept-API-Version: resource=1.0" \
--request POST \
"https://tenant-name.forgeblocks.com/openidm/csv/metadata/92971c92-67bb-4ae7-b41b-96d249b0b2aa/?_action=cancel"
{
  "status": "OK"
}
HTTP Request Timeout

By default, the timeout for the bulk import servlets is 30 seconds (or 30000 milliseconds). This parameter is set in your resolver/boot.properties file, as follows:

openidm.servlet.timeoutMillis=30000

If you are importing a very large number of records, you might need to increase the HTTP request timeout to prevent requests timing out.

In test environments, you can set this parameter to 0 to disable the request timeout. You should not disable the timeout in a production environment because no timeout can lead to DDoS attacks where thousands of slow HTTP connections are made.

For a list of all REST endpoints related to bulk import, see Bulk import.

Appendix A: Data models and objects reference

You can customize a variety of objects that can be addressed via a URL or URI. Identity Cloud can perform a common set of functions on these objects, such as CRUDPAQ (create, read, update, delete, patch, action, and query).

Depending on how you intend to use them, different object types are appropriate.

Table 1. Object Types
Object Type Intended Use Special Functionality

Managed objects

Serve as targets and sources for synchronization, and to build virtual identities.

Provide appropriate auditing, script hooks, declarative mappings and so forth in addition to the REST interface.

Configuration objects

Ideal for look-up tables or other custom configuration, which can be configured externally like any other system configuration.

Adds file view, REST interface, and so forth

Repository objects

The equivalent of arbitrary database table access. Appropriate for managing data purely through the underlying data store or repository API.

Persistence and API access

System objects

Representation of target resource objects, such as accounts.

 

Audit objects

Houses audit data in the repository.

 

Links

Defines a relation between two objects.

 

Managed objects

Managed objects and their properties are defined in the Identity Cloud managed object schema.

The default schema includes these types of managed objects:

Each managed object type contains properties for storing information about objects of that type. For example, the user object type has properties for storing usernames, passwords, email addresses, and so forth.

In the IDM admin UI, managed objects represent the identity-related data managed by Identity Cloud. In Identity Cloud, managed objects are stored in a DS repository.

Data consistency

Single-object operations are consistent within the scope of the operation performed, limited by the capabilities of the underlying data store. Bulk operations have no consistency guarantees. Identity Cloud does not expose any transactional semantics in the managed object access API.

For information on conditional header access through the REST API, see Conditional Operations.

Managed object triggers

Triggers are user-definable functions that validate or modify object or property state.

State triggers

Managed objects are resource-oriented. A set of triggers is defined to intercept the supported request methods on managed objects. Such triggers are intended to perform authorization, redact, or modify objects before the action is performed. The object being operated on is in scope for each trigger, meaning that the object is retrieved by the data store before the trigger is fired.

If retrieval of the object fails, the failure occurs before any trigger is called. Triggers are executed before any optimistic concurrency mechanisms are invoked. The reason for this is to prevent a potential attacker from getting information about an object (including its presence in the data store) before authorization is applied.

onCreate

Called upon a request to create a new object. Throwing an exception causes the create to fail.

postCreate

Called after the creation of a new object is complete.

onRead

Called upon a request to retrieve a whole object or portion of an object. Throwing an exception causes the object to not be included in the result. This method is also called when lists of objects are retrieved via requests to its container object; in this case, only the requested properties are included in the object. Allows for uniform access control for retrieval of objects, regardless of the method in which they were requested.

onUpdate

Called upon a request to store an object. The oldObject and newObject variables are in-scope for the trigger. The oldObject represents a complete object, as retrieved from the data store. The trigger can elect to change newObject properties. If, as a result of the trigger, the values of the oldObject and newObject are identical (that is, update is reverted), the update ends prematurely, but successfully. Throwing an exception causes the update to fail.

postUpdate

Called after an update request is complete.

onDelete

Called upon a request to delete an object. Throwing an exception causes the deletion to fail.

postDelete

Called after an object is deleted.

onSync

Called when a managed object is changed, and the change triggers an implicit synchronization operation. The implicit synchronization operation is triggered by calling the sync service, which attempts to go through all the configured managed-system mappings. The sync service returns either a response or an error. For both the response and the error, the script that is referenced by the onSync hook is called.

You can use this hook to inject business logic when the sync service either fails or succeeds to synchronize all applicable mappings. For an example of how the onSync hook is used to revert partial successful synchronization operations, see Synchronization Failure Compensation.

Object storage triggers

An object-scoped trigger applies to an entire object. Unless otherwise specified, the object itself is in scope for the trigger.

onValidate

Validates an object prior to its storage in the data store. If an exception is thrown, the validation fails and the object is not stored.

onStore

Called just prior to when an object is stored in the data store. Typically used to transform an object just prior to its storage (for example, encryption).

Property storage triggers

A property-scoped trigger applies to a specific property within an object. Only the property itself is in scope for the trigger. No other properties in the object should be accessed during execution of the trigger. Unless otherwise specified, the order of execution of property-scoped triggers is intentionally left undefined.

onValidate

Validates a given property value after its retrieval from and prior to its storage in the data store. If an exception is thrown, the validation fails and the property is not stored.

onRetrieve

Called on all requests that return a single object: read, create, update, patch, and delete.

onRetrieve is called on queries only if executeOnRetrieve is set to true in the query request parameters. If executeOnRetrieve is not passed, or if it is false, the query returns previously persisted values of the requested fields. This behavior avoids performance problems when executing the script on all results of a query.

onStore

Called before an object is stored in the data store. Typically used to transform a given property before its object is stored.

Storage Trigger Sequences

Triggers are executed in the following order:

Object Retrieval Sequence
  1. Retrieve the raw object from the data store

  2. The executeOnRetrieve boolean is used to check whether property values should be recalculated. The sequence continues if the boolean is set to true.

  3. Call object onRetrieve trigger

  4. Per-property within the object, call property onRetrieve trigger

Object Storage Sequence
  1. Per-property within the object:

    • Call property onValidate trigger

    • Call object onValidate trigger

  2. Per-property trigger within the object:

    • Call property onStore trigger

    • Call object onStore trigger

    • Store the object with any resulting changes to the data store

Managed Object Encryption

Sensitive object properties can be encrypted prior to storage, typically through the property onStore trigger. The trigger has access to configuration data, which can include arbitrary attributes that you define, such as a symmetric encryption key. Such attributes can be decrypted during retrieval from the data store through the property onRetrieve trigger.

Managed Object Configuration

Configuration of managed objects is provided through an array of managed object configuration objects.

{
  "objects": [ managed-object-config object, ... ]
}
objects

array of managed-object-config objects, required

Specifies the objects that the managed object service manages.

Managed-Object-Config Object Properties

Specifies the configuration of each managed object.

{
  "name"      : string,
  "actions"   : script object,
  "onCreate"  : script object,
  "onDelete"  : script object,
  "onRead"    : script object,
  "onRetrieve": script object,
  "onStore"   : script object,
  "onSync"    : script object,
  "onUpdate"  : script object,
  "onValidate": script object,
  "postCreate": script object,
  "postDelete": script object,
  "postUpdate": script object,
  "schema"    : {
    "id"        : urn,
    "icon"      : string,
    "mat-icon"  : string,
    "order"     : [ list of properties],
    "properties": { property-configuration objects },
    "$schema"   : "http://json-schema.org/draft-03/schema",
    "title"     : "User",
    "viewable"  : true
  }
}
name

string, required

The name of the managed object. Used to identify the managed object in URIs and identifiers.

actions

script object, optional

A custom script that initiates an action on the managed object. For more information, see Register custom scripted actions.

onCreate

script object, optional

A script object to trigger when the creation of an object is being requested. The object to be created is provided in the root scope as an object property. The script can change the object. If an exception is thrown, the create aborts with an exception.

onDelete

script object, optional

A script object to trigger when the deletion of an object is being requested. The object being deleted is provided in the root scope as an object property. If an exception is thrown, the deletion aborts with an exception.

onRead

script object, optional

A script object to trigger when the read of an object is being requested. The object being read is provided in the root scope as an object property. The script can change the object. If an exception is thrown, the read aborts with an exception.

onRetrieve

script object, optional

A script object to trigger when an object is retrieved from the repository. The object that was retrieved is provided in the root scope as an object property. The script can change the object. If an exception is thrown, then object retrieval fails.

onStore

script object, optional

A script object to trigger when an object is about to be stored in the repository. The object to be stored is provided in the root scope as an object property. The script can change the object. If an exception is thrown, then object storage fails.

onSync

script object, optional

A script object to trigger when a change to a managed object triggers an implicit synchronization operation. The script has access to the syncResults object, the request object, the state of the object before the change (oldObject) and the state of the object after the change (newObject). The script can change the object.

onUpdate

script object, optional

A script object to trigger when an update to an object is requested. The old value of the object being updated is provided in the root scope as an oldObject property. The new value of the object being updated is provided in the root scope as a newObject property. The script can change the newObject. If an exception is thrown, the update aborts with an exception.

onValidate

script object, optional

A script object to trigger when the object requires validation. The object to be validated is provided in the root scope as an object property. If an exception is thrown, the validation fails.

postCreate

script object, optional

A script object to trigger after an object is created, but before any targets are synchronized.

postDelete

script object, optional

A script object to trigger after a delete of an object is complete, but before any further synchronization. The value of the deleted object is provided in the root scope as an oldObject property.

postUpdate

script object, optional

A script object to trigger after an update to an object is complete, but before any targets are synchronized. The value of the object before the update is provided in the root scope as an oldObject property. The value of the object after the update is provided in the root scope as a newObject property.

schema

json-schema object, optional

The schema to use to validate the structure and content of the managed object, and how the object is displayed in the UI. The schema-object format is defined by the JSON Schema specification.

The schema property includes the following additional elements:

icon

string, optional

The name of the Font Awesome icon to display for this object in the UI. Only applies to standalone Identity Cloud.

mat-icon

string, optional

The name of the Material Design Icon to display for this object in the UI. Only applies to Identity Cloud as part of the ForgeRock Identity Platform.

id

urn, optional

The URN of the managed object, for example, urn:jsonschema:org:forgerock:openidm:managed:api:Role.

order

list of properties, optional

The order in which properties of this managed object are displayed in the UI.

properties

list of property configuration objects, optional

A list of property specifications. For more information, see Property Configuration Properties.

$schema

url, optional

Link to the JSON schema specification.

title

string, optional

The title of this managed object in the UI.

viewable

boolean, optional

Whether this object is visible in the UI.

Property Configuration Properties

Each managed object property, identified by its property-name, can have the following configurable properties:

"property-name" : {
  "description"     : string,
  "encryption"      : property-encryption object,
  "isPersonal"      : boolean true/false,
  "isProtected"     : boolean true/false,
  "isVirtual"       : boolean true/false,
  "items"           : {
     "id"                  : urn,
     "properties"          : property-config object,
     "resourceCollection"  : property-config object,
     "reversePropertyName" : string,
     "reverseRelationship" : boolean true/false,
     "title"               : string,
     "type"                : string,
     "validate"            : boolean true/false,
  },
  "onRetrieve"      : script object,
  "onStore"         : script object,
  "onValidate"      : script object,
  "pattern"         : string,
  "policies"        : policy object,
  "required"        : boolean true/false,
  "returnByDefault" : boolean true/false,
  "scope"           : string,
  "searchable"      : boolean true/false,
  "secureHash"      : property-hash object,
  "title"           : string,
  "type"            : data type,
  "usageDescription": string,
  "userEditable"    : boolean true/false,
  "viewable"        : boolean true/false,
}
description

string, optional

A brief description of the property.

encryption

property-encryption object, optional

Specifies the configuration for encryption of the property in the repository. If omitted or null, the property is not encrypted.

isPersonal

boolean, true/false

Designed to highlight personally identifying information. By default, isPersonal is set to true for userName and postalAddress.

isProtected

boolean, true/false

Specifies whether reauthentication is required if the value of this property changes.

isVirtual

boolean, true/false

Specifies whether the property takes a static value, or whether its value is calculated dynamically as the result of a script.

The most recently calculated value of a virtual property is persisted by default. The persistence of virtual property values allows Identity Cloud to compare the new value of the property against the last calculated value, and therefore to detect change events during synchronization.

Virtual property values are not persisted by default if you are using an explicit mapping.

items

property-configuration object, optional

For array type properties, defines the elements in the array. items can include the following sub-properties:

id

urn, optional

The URN of the property, for example, urn:jsonschema:org:forgerock:openidm:managed:api:Role:members:items.

properties

property configuration object, optional

A list of properties, and their configuration, that make up this items array. For example, for a relationship type property:

"properties" : {
    "_ref" : {
        "description" : "References a relationship from a managed object",
        "type" : "string"
    },
    "_refProperties" : {
        "description" : "Supports metadata within the relationship",
        ...
    }
}
resourceCollection

property configuration object, optional

The collection of resources (objects) on which this relationship is based (for example, managed/realm-name_user objects).

reversePropertyName

string, optional

For relationship type properties, specifies the corresponding property name in the case of a reverse relationship. For example, a roles property might have a reversePropertyName of members.

reverseRelationship

boolean, true or false.

For relationship type properties, specifies whether the relationship exists in both directions.

title

string, optional

The title of array items, as displayed in the UI, for example Role Members Items.

type

string, optional

The array type, for example relationship.

validate

boolean, true/false

For reverse relationships, specifies whether the relationship should be validated.

onRetrieve

script object, optional

A script object to trigger once a property is retrieved from the repository. That property may be one of two related variables: property and propertyName. The property that was retrieved is provided in the root scope as the propertyName variable; its value is provided as the property variable. If an exception is thrown, then object retrieval fails.

onStore

script object, optional

A script object to trigger when a property is about to be stored in the repository. That property may be one of two related variables: property and propertyName. The property that was retrieved is provided in the root scope as the propertyName variable; its value is provided as the property variable. If an exception is thrown, then object storage fails.

onValidate

script object, optional

A script object to trigger when the property requires validation. The value of the property to be validated is provided in the root scope as the property property. If an exception is thrown, validation fails.

pattern

string, optional

Any specific pattern to which the value of the property must adhere. For example, a property whose value is a date might require a specific date format. Patterns specified here must follow regular expression syntax.

policies

policy object, optional

Any policy validation that must be applied to the property.

required

boolean, true/false

Specifies whether the property must be supplied when an object of this type is created.

returnByDefault

boolean, true/false

For virtual properties, specifies whether the property will be returned in the results of a query on an object of this type if it is not explicitly requested. Virtual attributes are not returned by default.

scope

string, optional

Specifies whether the property should be filtered from HTTP/external calls. The value can be either "public" or "private". "private" indicates that the property should be filtered, "public" indicates no filtering. If no value is set, the property is assumed to be public and thus not filtered.

searchable

boolean, true/false

Specifies whether this property can be used in a search query on the managed object. A searchable property is visible in the End User UI. False by default.

Do not modify the searchable setting on properties in the Identity Cloud managed object schema.
secureHash

property-hash object, optional

Specifies the configuration for hashing of the property value in the repository. If omitted or null, the property is not hashed.

title

string, required

A human-readable string, used to display the property in the UI.

type

data type, required

The data type for the property value; can be String, Array, Boolean, Number, Object, or Resource Collection.

usageDescription

string, optional

Designed to help end users understand the sensitivity of a property such as a telephone number.

userEditable

boolean, true/false

Specifies whether users can edit the property value in the UI. This property applies in the context of the End User UI, in which users are able to edit certain properties of their own accounts. False by default.

viewable

boolean, true/false

Specifies whether this property is viewable in the object’s profile in the UI. True by default.

Script Object Properties

{
  "type"  : "text/javascript",
  "source": string
}
type

string, required

Identity Cloud supports "text/javascript".

source, file

string, required (only one, source or file is required)

Specifies the source code of the script to be executed (if the keyword is "source"), or a pointer to the file that contains the script (if the keyword is "file").

Property Encryption Object

{
  "cipher": string,
  "key"   : string
}
cipher

string, optional

The cipher transformation used to encrypt the property. If omitted or null, the default cipher of "AES/CBC/PKCS5Padding" is used.

key

string, required

The alias of the key in the Identity Cloud cryptography service keystore used to encrypt the property.

Property Hash Object

{
    "algorithm" : string,
    "type" : string
}
algorithm

string, required

The algorithm that should be used to hash the value.

type

string, optional

The type of hashing. Currently only salted hash is supported. If this property is omitted or null, the default "salted-hash" is used.

Accessing Managed Objects Through the REST API

Identity Cloud exposes all managed object functionality through the REST API unless you configure a policy to prevent such access. In addition to the common REST functionality of create, read, update, delete, patch, and query, the REST API also supports patch by query. For more information, see the REST API reference.

System objects

System objects are pluggable representations of objects on external systems. They follow the same RESTful resource based design principles as managed objects. There is a default implementation for the ICF framework, which allows any connector object to be represented as a system object.

Link objects define relations between source objects and target objects, usually relations between managed objects and system objects. The link relationship is established by provisioning activity that either results in a new account on a target system, or a reconciliation or synchronization scenario that takes a LINK action.

Copyright © 2010-2022 ForgeRock, all rights reserved.