Chapter 7. Using Policies to Validate Data

Table of Contents
7.1. Configuring the Default Policy
7.2. Extending the Policy Service
7.3. Disabling Policy Enforcement
7.4. Managing Policies Over REST

OpenIDM provides an extensible policy service that enables you to apply specific validation requirements to various components and properties. 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 can be applied to user passwords, but also to any kind of managed object.

The policy service enables you to do the following:

A default policy applies to all managed objects. You can configure the default policy to suit your requirements, or you can extend the policy service by supplying your own scripted policies.

7.1. Configuring the Default Policy

The default policy is configured in two files:

  • A policy script file (openidm/bin/defaults/script/policy.js) which defines each policy and specifies how policy validation is performed.

  • A policy configuration file (openidm/conf/policy.json) which specifies which policies are applicable to each resource.

7.1.1. Policy Script File

The policy script file defines policy configuration in two parts:

  • A policy configuration object, which defines each element of the policy.

  • A policy implementation function, which describes the requirements that are enforced by that policy.

Together, the configuration object and the implementation function determine whether an object is valid in terms of the policy. The following extract from the policy script file configures a policy that specifies that the value of a property must contain a certain number of capital letters.

...
            {   "policyId" : "at-least-X-capitals",
                "clientValidation": true,
                "policyExec" : "atLeastXCapitalLetters", 
                "policyRequirements" : ["AT_LEAST_X_CAPITAL_LETTERS"]
            },
...

function atLeastXCapitalLetters(fullObject, value, params, property) {
    var reg = /[(A-Z)]/g;
    if (typeof value !== "string" || !value.length || value.match(reg) 
        === null || value.match(reg).length < params.numCaps) {
        return [ { 
                   "policyRequirement" : "AT_LEAST_X_CAPITAL_LETTERS", 
                   "params" : {
                     "numCaps": params.numCaps
                   } 
                  } 
               ];
    }
    return [];
}     
...     
      

To enforce user passwords that contain at least one capital letter, the previous policy ID is applied to the appropriate resource and the required number of capital letters is defined in the policy configuration file, as described in Section 7.1.2, “Policy Configuration File”.

7.1.1.1. Policy Configuration Object

Each element of the policy is defined in a policy configuration object. The structure of a policy configuration object is as follows:

{   "policyId" : "minimum-length",
    "clientValidation": true,
    "policyExec" : "propertyMinLength",
    "policyRequirements" : ["MIN_LENGTH"]
}
      
"policyId" - a unique ID that enables the policy to be referenced by component objects.
"clientValidation" - indicates whether the policy decision can be made on the client. When "clientValidation": true, the source code for the policy decision function is returned when the client requests the requirements for a property.
"policyExec" - the name of the function that contains the policy implementation. For more information, see Section 7.1.1.2, “Policy Implementation Function”.
"policyRequirements" - an array containing the policy requirement ID of each requirement that is associated with the policy. Typically, a policy will validate only one requirement, but it can validate more than one.

7.1.1.2. Policy Implementation Function

Each policy ID has a corresponding policy implementation function that performs the validation. Functions take the following form:

function <name>(fullObject, value, params, propName) {	
	<implementation_logic>
}
        
fullObject is the full resource object that is supplied with the request.
value is the value of the property that is being validated.
params refers to the "params" array that is specified in the property's policy configuration.
propName is the name of the property that is being validated.

The following example shows the implementation function for the "required" policy.

function required(fullObject, value, params, propName) {
    if (value === undefined) {
        return [ { "policyRequirement" : "REQUIRED" } ];
    }
    return [];
}          
        

7.1.2. Policy Configuration File

The policy configuration file includes a pointer to the policy script, and the configured policies for each component resource. The following extract of the default policy configuration file shows how the at-least-X-capitals policy is applied to user passwords, with a default value of 1.

{
    "type" : "text/javascript",
    "file" : "bin/defaults/script/policy.js",
    "resources" : [
        {
            "resource" : "managed/user/*",
            "properties" : [
...
                {
                    "name" : "password",
                    "policies" : [
                        {
                            "policyId" : "required"
                        },
                        {
                            "policyId" : "not-empty"
                        },
                        {
                            "policyId" : "at-least-X-capitals",
                            "params" : {
                                "numCaps" : 1
                            }
                        },
                ...
           }
       ]
}     
      

The configuration file includes the following properties:

  • "type" - specifies the type of policy service. Currently, only "text/javascript" is supported.

  • "file" - provides the path to the policy script file, relative to the OpenIDM installation directory.

  • "resources" provides an array of resource objects, in JSON format, that are subject to the policy service. Resource objects are identified by the "resource" parameter, which indicates the URI and supports wildcard syntax. For example, "managed/user/*" indicates that the policy applies to all objects under /managed/user. Each resource has the following properties:

    "name" - the name of the property to which the policy is applied.
    "policyID" - the ID of the policy that is applied to that property.
    "params" - any specific parameters that apply to that policy ID.

You can specify that a particular policy does not apply to users with specific OpenIDM roles by setting the "exceptRoles" parameter of the policy ID. For example, the following extract from policy.json specifies that the reauthorization required policy definition does not apply to users with roles openidm-admin, or opendim-reg.

...      
    {
        "policyId" : "re-auth-required",
        "params" : {
            "exceptRoles" : [
                "openidm-admin",
                "openidm-reg"
            ]
        }
    }      
...      
      

7.2. Extending the Policy Service

You can extend the policy service by adding your own scripted policies in openidm/script and referencing them in the policy configuration file (conf/policy.json). Avoid manipulating the default policy script file (in bin/defaults/script) as doing so might result in interoperability issues in a future release. To reference additional policy scripts, set the "additionalFiles" property in conf/policy.json.

The following example creates a custom policy that rejects properties with null values. The policy is defined in a script named mypolicy.js.

var policy = {   "policyId" : "notNull",
       "policyExec" : "notNull",  
       "policyRequirements" : ["NOT_NULL"]
}

addPolicy(policy);

function notNull(fullObject, value, params, property) {
   if (value == null) {
       return [ {"policyRequirement": "NOT_NULL"}];
   }
   return [];
} 
    

The policy is referenced in the policy configuration file as follows:

{
    "type" : "text/javascript",
    "file" : "bin/defaults/script/policy.js",
    "additionalFiles" : ["script/mypolicy.js"],
    "resources" : [
        {
...
    

You can also configure policies for managed object properties as part of the property definition in the conf/managed.json file. For example, the following extract of a managed.json file shows a policy configuration for the password property.

...
"properties" : [
    {
        "name" : "password",
        "encryption" : {
            "key" : "openidm-sym-default"
        },
        "scope" : "private"
        "policies" : [
            {
                "policyId" : "required"
            },
            {
                "policyId" : "not-empty"
            },
            {
                "policyId" : "at-least-X-capitals",
                "params" : {
                "numCaps" : 1
                }
            }
        ]
    },
...
    

7.3. Disabling Policy Enforcement

Policy enforcement refers to the automatic validation of data in the repository when it is created, updated, or patched. In certain situations you might want to disable policy enforcement temporarily. You might, for example, want to import existing data that does not meet the validation requirements with the intention of cleaning up this data at a later stage.

You can disable policy enforcement by setting openidm.policy.enforcement.enabled to false in the conf/boot/boot.properties file. This setting disables policy enforcement in the back-end only, and has no impact on direct policy validation calls to the Policy Service (which the user interface makes to validate input fields). So, with policy enforcement disabled, data added directly over REST is not subject to validation, but data added with the UI is still subject to validation.

Disabling policy enforcement permanently in a production system is not recommended.

7.4. Managing Policies Over REST

You can manage the policy service over the REST interface, by calling the REST endpoint http://localhost:8080/openidm/policy, as shown in the following examples.

7.4.1. Listing the Defined Policies

The following REST call displays a list of all the defined policies:

$ curl
 --header "X-OpenIDM-Username: openidm-admin"
 --header "X-OpenIDM-Password: openidm-admin"
 --request GET
 "http://localhost:8080/openidm/policy"

The policy objects are returned in JSON format, with one object for each defined policy ID, for example:

{
  "resources": [
    {
      "resource": "managed/user/*",
      "properties": [
      {
        "policies": [
          {
            "policyId": "required",
            "policyFunction": "function required(fullObject, value, params, propName) {
              if (value === undefined) {
              return [{"policyRequirement":"REQUIRED"}];
                      }
              return [];
            }",
            "policyRequirements": [
              "REQUIRED"
            ]
          },
...

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

$ curl
 --header "X-OpenIDM-Username: openidm-admin"
 --header "X-OpenIDM-Password: openidm-admin"
 --request GET
 "http://localhost:8080/openidm/policy/managed/user/*"
{
    "resource": "managed/user/*",
    "properties": [
        {
            "policies": [
                {
                    "policyId": "required",
                    "policyFunction": "
                        \nfunction required(fullObject, value, params, propName) {
                            \n    if (value === undefined) {
                            \n        return [{\"policyRequirement\":\"REQUIRED\"}];
                            \n    }
                            \n    return [];
                            \n}
                            \n",
                    "policyRequirements": [
                        "REQUIRED"
                    ]
                },
                {
                    "policyId": "not-empty",
                    "policyFunction": "
                    \nfunction notEmpty(fullObject, value, params, property) {
                    \n    if (typeof (value) !== \"string\" || !value.length) {
                    \n        return [{\"policyRequirement\":\"REQUIRED\"}];
                    \n    } else {
                    \n        return [];
                    \n    }
                    \n}
                    \n",
                    "policyRequirements": [
                        "REQUIRED"
                    ]
                },
                {
                    "policyId": "unique",
                    "policyRequirements": [
                        "UNIQUE"
                    ]
                },
...
}

7.4.2. Validating Objects and Properties Over REST

Use the validateObject action to verify that an object adheres to the requirements of a policy.

The following example verifies that a new managed user object is acceptable in terms of the policy requirements.

$ curl
 --header "X-OpenIDM-Username: openidm-admin"
 --header "X-OpenIDM-Password: openidm-admin"
 --request POST      
 --data '{"familyName":"Jones",
          "givenName":"Bob",
          "_id":"bjones",
          "phoneNumber":"0827878921",
          "passPhrase":null,
          "email":"bjones@example.com",
          "accountStatus":"active",
          "roles":"admin",
          "userName":"bjones@example.com",
          "password":"123"}' 
 "http://localhost:8080/openidm/policy/managed/user/bjones?_action=validateObject"

{"result":false,
 "failedPolicyRequirements":[
    {"policyRequirements":[
         {"policyRequirement":"AT_LEAST_X_CAPITAL_LETTERS",
             "params":{"numCaps":1}
         },
         {"policyRequirement":"MIN_LENGTH",
             "params":{"minLength":8}
         }
         ],
      "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 Barbara Jensen's new password (12345) is acceptable.

$ curl
 --header "X-OpenIDM-Username: openidm-admin"
 --header "X-OpenIDM-Password: openidm-admin"
 --request POST
 --data '{ "password" : "12345" }' 
 "http://localhost:8080/openidm/policy/managed/user/bjensen?_action=validateProperty"
 
{
    "result": false,
    "failedPolicyRequirements": [
        {
            "policyRequirements": [
                {
                    "policyRequirement": "AT_LEAST_X_CAPITAL_LETTERS",
                    "params": {
                        "numCaps": 1
                    }
                },
                {
                    "policyRequirement": "MIN_LENGTH",
                    "params": {
                        "minLength": 8
                    }
                }
            ],
            "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 does fulfil the policy requirements returns a true result, for example:

$ curl
 --header "X-OpenIDM-Username: openidm-admin"
 --header "X-OpenIDM-Password: openidm-admin"
 --request POST
 --data '{ "password" : "1NewPassword" }' 
 "http://localhost:8080/openidm/policy/managed/user/bjensen?_action=validateProperty"
 
{
    "result": true,
    "failedPolicyRequirements": []
}