Policy-based Authorization in ForgeRock

Posted August 5, 2021 by Jatinder Singh ‐ 10 min read

Centralizing and decoupling authorization using Policy-based Access Control (PBAC) in ForgeRock AM

Authz Policies
Policy-based Authorization in ForgeRock

Introduction

As published in the Gartner Hype Cycle for Identity and Access Management 2020 report, Externalized Authorization Management (EAM) is on the rise. Covid-19 has increased the visibility of the IAM landscape, and with that, organizations need to establish more robust authorization practices to prevent breaches and improve compliance and security. This blog post will show you how we can use the ForgeRock Access Manager (AM) Policy Authorization to centralize and decouple authorization. The demo includes step-by-step instructions on how to implement this within AM to protect REST resources. In addition, our GitHub repository provides the companion source code to help you implement this topic in your lab environment.

What is Policy-based Authorization?

A Policy-based Authorization, also known by the name Attribute-based Access Control (ABAC) or Policy-based Access Control (PBAC), is an access control model that establishes who (authenticated identity) can access what (resources) and under what circumstances (conditions). While this way of externalizing authorization is getting much traction in the last few years, it is not new. The policy-based design extends from the eXtensible Access Control Markup Language (XACML), which has been around since 2003, with the draft officially standardized by the OASIS Standard in 2013.

The ForgeRock AM achieves policy-based authorization using a policy document that the policy administrator defines. In a nutshell, the policy document is a criteria set by its administrator that the requester must meet to access a protected resource. The official standard of XACML has many different constructs, but we will primarily discuss the top-level constructs to focus on AM’s interpretation of XACML.

A brief overview of Policy Architecture

As discussed in the XACML standard, the architecture of a Policy-based design requires the following components:

  • Policy Decision Point( PDP);
  • Policy Enforcement Point (PEP);
  • Policy Information Point (PIP);
  • Policy Administration Point (PAP).

While the above components can exist in isolation and be independent services, AM encapsulates all of them as a cohesive unit in a single application. Due to this encapsulation, the diagram below doesn’t reveal the interaction between PDP, PIP, and PAP.

Policy Architecture
Policy Architecture

What are Policy, Policy Set, and Resource Type constructs?

In the XACML standard to manage the authorization policies, a component called PAP is defined, allowing the administration of new and existing policies. Within PAP, policy construction happens using the constructs Policy, Policy Set, and Resource Type. With these constructs, a policy administrator can create a concrete policy. AM provides access to these constructs through REST endpoints /resourcestypes, /applications, and /policies. In addition, the GUI equivalent is available through the Authorization menu in the AM console.

  • Resource Type - A resource type enables an administrator to specify a template for the kind of resources AM should protect. For example, URL-based resources, IoT devices, or the doors of an airport.
  • Policy Set - A policy set allows an administrator to provide a container to group policies based on templates (resource types) included in a policy set. For example, grouping policies for URL-based endpoint (e.g./v1/pets).
  • Policy - A policy defines resources, actions, subject conditions, environmental conditions, and response attributes, allowing AM to determine whether to grant or deny access. A Policy is what consumes a given template within its Policy Set to satisfy concrete authorization requirements. So, for example, a Policy based on a URL-based template, we can state allow Managers to GET access to the /v1/pets endpoint. Similarly, a second policy can allow Employees to GET access to /v1/pets endpoint within regular business hours of 9-5.
Policy Constructs
Policy Constructs

Policy Authorization in Action (Demo)

One of the first steps in the authorization landscape is to talk to the business stakeholders to define authorization requirements. Since I am the everything for this post, we will set the following authorization business requirements:

  • Requirement #1 - Allow Employees GET action to /v1/pets endpoint.
  • Requirement #2 - Allow Managers whose last name starts with “Mc”, POST, PUT and DELETE actions to /v1/pets endpoint.

While the above requirements are simple enough, implementing them, we will learn how access models, including RBAC and ABAC, can be defined using the ForgeRock AM Policy authorization.

My ForgeRock stack for this post includes:

  • AM v7.1.0;
  • DS v7.1.0.

Note The complete Postman collection, including the companion source code, is available under the GitHub section.

Set-up Policy Infrastructure

Since our requirement is to protect URL-based endpoints, AM’s default configuration comes with a URL-based Resource Type that we will use. Therefore, in Step 1 of the demo, we will set up our policy infrastructure to evaluate policies on the protected resources later.

Step 1 - Login as Policy Administrator

Request:

curl --location --request POST 'https://identity.sqoopdata.local:17143/openam/json/realms/root/authenticate' \
--header 'Accept-API-Version: resource=2.1' \
--header 'X-OpenAM-Username: amadmin' \
--header 'X-OpenAM-Password: changeit'

Response:

{
  "tokenId": "U_IXfUMyuosjyolpQ2IC7XfUjxQ.*AAJTSQACMDIAAlNLABwvY3BuMjFKSWZUMzhPNGRIRnc1czRBRFhUUjA9AAR0eXBlAANDVFMAAlMxAAIwMQ..*",
  "successUrl": "/openam/console",
  "realm": "/"
}
Step 2 - Create Policy Set to hold policies

Request:

curl --location --request POST 'https://identity.sqoopdata.local:17143/openam/json/realms/root/realms/pbac/applications/?_action=create' \
--header 'Content-Type: application/json' \
--header 'Accept-API-Version: resource=2.1' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=PRrXH0XU7ABss3Y0Fw45HGUXxDY.*AAJTSQACMDIAAlNLABxCc3NyYWpXb1prU0wwejJsSUl2ckhuRmRBQlE9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
    "name":"PetsPolicySet",
    "resourceTypeUuids":[
        "76656a38-5f8e-401b-83aa-4ccb74ce88d2"
    ],
    "realm":"/pbac",
    "conditions":[
        "AND",
        "Script",
        "OR",
        "NOT",
        "AMIdentityMembership",
        "AuthLevel",
        "AuthScheme",
        "AuthenticateToRealm",
        "AuthenticateToService",
        "IPv4",
        "IPv6",
        "LDAPFilter",
        "LEAuthLevel",
        "OAuth2Scope",
        "ResourceEnvIP",
        "Session",
        "SessionProperty",
        "SimpleTime"
    ],
    "applicationType":"iPlanetAMWebAgentService",
    "description":"Policy AuthZ Demo",
    "resourceComparator":"com.sun.identity.entitlement.URLResourceName",
    "subjects":[
        "AND",
        "OR",
        "NOT",
        "AuthenticatedUsers",
        "Identity",
        "JwtClaim"
    ],
    "entitlementCombiner":"DenyOverride",
    "saveIndex":null,
    "searchIndex":null,
    "attributeNames":[
    ]
}'

Response:

{
    "_id": "PetsPolicySet",
    "_rev": "1628529590564",
    "name": "PetsPolicySet",
    "displayName": null,
    "resourceTypeUuids": [
        "76656a38-5f8e-401b-83aa-4ccb74ce88d2"
    ],
    "description": "Policy AuthZ Demo",
    "attributeNames": [],
    "creationDate": 1628529590564,
    "createdBy": "id=amadmin,ou=user,dc=sqoopdata,dc=local",
    "conditions": [
        "AND",
        "Script",
        "OR",
        "NOT",
        "AMIdentityMembership",
        "AuthLevel",
        "AuthScheme",
        "AuthenticateToRealm",
        "AuthenticateToService",
        "IPv4",
        "IPv6",
        "LDAPFilter",
        "LEAuthLevel",
        "OAuth2Scope",
        "ResourceEnvIP",
        "Session",
        "SessionProperty",
        "SimpleTime"
    ],
    "resourceComparator": "com.sun.identity.entitlement.URLResourceName",
    "lastModifiedDate": 1628529590564,
    "lastModifiedBy": "id=amadmin,ou=user,dc=sqoopdata,dc=local",
    "subjects": [
        "AND",
        "OR",
        "NOT",
        "AuthenticatedUsers",
        "Identity",
        "JwtClaim"
    ],
    "saveIndex": null,
    "searchIndex": null,
    "entitlementCombiner": "DenyOverride",
    "editable": true,
    "applicationType": "iPlanetAMWebAgentService"
}
Step 3 - Create Employee Policy

With the Resource Type and Policy Set in place, let’s create a Policy to implement Requirement 1. The policy request below is an ALLOW policy that will ALLOW users access to /v1/pets who belong to an Employee role. In addition, the policy uses an LDAP Filter environmental condition that constructs a standard LDAPv3 filter to define role relationships. So there you go, we have a basic RBAC in action using Policy-based authorization.

Request:

curl --location --request POST 'https://identity.sqoopdata.local:17143/openam/json/realms/root/realms/pbac/policies?_action=create' \
--header 'Content-Type: application/json' \
--header 'Accept-API-Version: resource=1.0' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=PRrXH0XU7ABss3Y0Fw45HGUXxDY.*AAJTSQACMDIAAlNLABxCc3NyYWpXb1prU0wwejJsSUl2ckhuRmRBQlE9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
    "name": "EmployeePetsPolicy",
    "active": true,
    "description": "Defines Pets Policies for Employees",
    "applicationName": "PetsPolicySet",
    "actionValues": {
        "GET": true
    },
    "resources": [
        "*://*:*/*/v1/pets/*"
    ],
    "subject": {
        "type": "AuthenticatedUsers"
    },
    "condition": {
    "type": "LDAPFilter",
    "ldapFilter": "(&(objectclass=inetorgperson)(ismemberof=cn=Employee,ou=groups,dc=sqoopdata,dc=local))"
    },
    "resourceTypeUuid": "76656a38-5f8e-401b-83aa-4ccb74ce88d2"
}'

Response:

{
    "_id": "EmployeePetsPolicy",
    "_rev": "1628535741884",
    "name": "EmployeePetsPolicy",
    "active": true,
    "description": "Defines Pets Policies for Employees",
    "resources": [
        "*://*:*/*/v1/pets/*"
    ],
    "applicationName": "PetsPolicySet",
    "actionValues": {
        "GET": true
    },
    "subject": {
        "type": "AuthenticatedUsers"
    },
    "condition": {
        "type": "LDAPFilter",
        "ldapFilter": "(&(objectclass=inetorgperson)(ismemberof=cn=Employee,ou=groups,dc=sqoopdata,dc=local))"
    },
    "lastModifiedBy": "id=amadmin,ou=user,dc=sqoopdata,dc=local",
    "lastModifiedDate": "2021-08-09T19:02:21.884Z",
    "createdBy": "id=amadmin,ou=user,dc=sqoopdata,dc=local",
    "creationDate": "2021-08-09T19:02:21.884Z"
}
Step 4 - Create Manager Policy

Creating a manager policy is very similar to the employee policy, with the difference being a modified LDAP Filter environment condition. So, the below is an ALLOW policy that will ALLOW authenticated identities access to POST, DELETE, and PUT actions on the /v1/pets endpoint. The identity must belong to a Manager role, and its sn attribute must start with the letters “mc”. Using an attribute to determine access, we have successfully implemented the ABAC model.

Request:

curl --location --request POST 'https://identity.sqoopdata.local:17143/openam/json/realms/root/realms/pbac/policies?_action=create' \
--header 'Content-Type: application/json' \
--header 'Accept-API-Version: resource=1.0' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=PRrXH0XU7ABss3Y0Fw45HGUXxDY.*AAJTSQACMDIAAlNLABxCc3NyYWpXb1prU0wwejJsSUl2ckhuRmRBQlE9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
    "name": "ManagerPetsPolicy",
    "active": true,
    "description": "Defines Pets Policies for Managers",
    "applicationName": "PetsPolicySet",
    "actionValues": {
        "DELETE": true,
        "POST": true,
        "PUT": true
    },
    "resources": [
        "*://*:*/*/v1/pets/*"
    ],
    "subject": {
        "type": "AuthenticatedUsers"
    },
    "condition": {
        "type": "LDAPFilter",
        "ldapFilter": "(&(objectclass=inetorgperson)(sn=mc*)(ismemberof=cn=Manager,ou=groups,dc=sqoopdata,dc=local))"
    },
    "resourceTypeUuid": "76656a38-5f8e-401b-83aa-4ccb74ce88d2"
}'

Response:

{
    "_id": "ManagerPetsPolicy",
    "_rev": "1628536957837",
    "name": "ManagerPetsPolicy",
    "active": true,
    "description": "Defines Pets Policies for Managers",
    "resources": [
        "*://*:*/*/v1/pets/*"
    ],
    "applicationName": "PetsPolicySet",
    "actionValues": {
        "DELETE": true,
        "POST": true,
        "PUT": true
    },
    "subject": {
        "type": "AuthenticatedUsers"
    },
    "condition": {
        "type": "LDAPFilter",
        "ldapFilter": "(&(objectclass=inetorgperson)(sn=mc*)(ismemberof=cn=Manager,ou=groups,dc=sqoopdata,dc=local))"
    },
    "lastModifiedBy": "id=amadmin,ou=user,dc=sqoopdata,dc=local",
    "lastModifiedDate": "2021-08-09T19:22:37.837Z",
    "createdBy": "id=amadmin,ou=user,dc=sqoopdata,dc=local",
    "creationDate": "2021-08-09T19:22:37.837Z"
}

Evaluate Policies

We will test our policies against the following user vs. role matrix. As prerequisite steps, we have already obtained SSO tokens for all the below three users.

Full NameUserRole
John DoejdoeEmployee
Denny McDonalddmcdonaldManager
Alice CharliealiceManager
Ron SaundersrsaundersAnalyst
Test Policy Evaluation for Requirement #1

Test Case - Check Ron Saunders access to /v1/pets endpoint.

Expected Result - Since Ron Sauders is an Analyst, the server will NOT MATCH ANY POLICY.

Request:

curl --location --request POST 'https://identity.sqoopdata.local:17143/openam/json/realms/root/realms/pbac/policies?_action=evaluate' \
--header 'Content-Type: application/json' \
--header 'Accept-API-Version: resource=2.1' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=PRrXH0XU7ABss3Y0Fw45HGUXxDY.*AAJTSQACMDIAAlNLABxCc3NyYWpXb1prU0wwejJsSUl2ckhuRmRBQlE9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
    "resources": [
        "https://pets.clinic.com:443/app/v1/pets/all"
    ],
    "application": "PetsPolicySet",
    "subject": {
        "ssoToken": "zdq-Ahw436shRW70fYw0rBhydNQ.*AAJTSQACMDIAAlNLABwreS9HKzFoQnNyVjRvdUc0S1JFa3JSVEV3SlE9AAR0eXBlAANDVFMAAlMxAAIwMQ..*"
    }
}'

Response:

[
    {
        "resource": "https://pets.clinic.com:443/app/v1/pets/all",
        "actions": {},
        "attributes": {},
        "advices": {},
        "ttl": 9223372036854775807
    }
]

Test Case - Check John Doe’s access to /v1/pets endpoint.

Expected Result - Since John Doe is an Employee, the server must MATCH POLICY and return ACTIONS ALLOWED.

Request:

curl --location --request POST 'https://identity.sqoopdata.local:17143/openam/json/realms/root/realms/pbac/policies?_action=evaluate' \
--header 'Content-Type: application/json' \
--header 'Accept-API-Version: resource=2.1' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=fcuTuCtv4pD6hyb7ksZosGF02lk.*AAJTSQACMDIAAlNLABxFYlljMGE5S1F4SFI0dkFxZitiWXoydVBkUzg9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
    "resources": [
        "https://pets.clinic.com:443/app/v1/pets/all"
    ],
    "application": "PetsPolicySet",
    "subject": {
        "ssoToken": "_u4wdGguigg3HWYpt0yRsxELEe4.*AAJTSQACMDIAAlNLABxSWjhjSkRHNk5zYnpDZlRPWUs0cVJuUXN6TVE9AAR0eXBlAANDVFMAAlMxAAIwMQ..*"
    }
}'

Response:

[
    {
        "resource": "https://pets.clinic.com:443/app/v1/pets/all",
        "actions": {
            "GET": true
        },
        "attributes": {},
        "advices": {},
        "ttl": 9223372036854775807
    }
]
Test Policy Evaluation for Requirement #2

Test Case - Check Denny McDonald access to /v1/pets endpoint.

Expected Result - Since Denny McDonald is a Manager, the server must MATCH POLICY and return ACTIONS ALLOWED.

Request:

curl --location --request POST 'https://identity.sqoopdata.local:17143/openam/json/realms/root/realms/pbac/policies?_action=evaluate' \
--header 'Content-Type: application/json' \
--header 'Accept-API-Version: resource=2.1' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=PRrXH0XU7ABss3Y0Fw45HGUXxDY.*AAJTSQACMDIAAlNLABxCc3NyYWpXb1prU0wwejJsSUl2ckhuRmRBQlE9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
    "resources": [
        "https://pets.clinic.com:443/app/v1/pets/all"
    ],
    "application": "PetsPolicySet",
    "subject": {
        "ssoToken": "sAxFGSegOzhPDDbv0KbeYoBHlZg.*AAJTSQACMDIAAlNLABxUYklydHBHV0N2TDB1L2pINUxZeWtteEZCbWc9AAR0eXBlAANDVFMAAlMxAAIwMQ..*"
    }
}'

Response:

[
    {
        "resource": "https://pets.clinic.com:443/app/v1/pets/all",
        "actions": {
            "DELETE": true,
            "POST": true,
            "PUT": true
        },
        "attributes": {},
        "advices": {},
        "ttl": 9223372036854775807
    }
]

Test Case - Check Alice who is also a Manager access to /v1/pets endpoint.

Expected Result - Since the manager policy requires the sn to start with the letters “Mc”, the server will not MATCH ANY POLICY and return empty actions.

Request:

curl --location --request POST 'https://identity.sqoopdata.local:17143/openam/json/realms/root/realms/pbac/policies?_action=evaluate' \
--header 'Content-Type: application/json' \
--header 'Accept-API-Version: resource=2.1' \
--header 'Cookie: amlbcookie=01; iPlanetDirectoryPro=GTp5MD3J3tq237M0xM7VTW1Aq2E.*AAJTSQACMDIAAlNLABxTdTFCMFMvVFhPbWhGT2U4VW9HY0ZnVmI0TDg9AAR0eXBlAANDVFMAAlMxAAIwMQ..*' \
--data-raw '{
    "resources": [
        "https://pets.clinic.com:443/app/v1/pets/all"
    ],
    "application": "PetsPolicySet",
    "subject": {
        "ssoToken": "Y4pZG7g4oW0qm8wd9vENJaFwhBY.*AAJTSQACMDIAAlNLABxXZnRJK0ZjY1VzYklUY2lkdjB0ZUVQM1NvaXc9AAR0eXBlAANDVFMAAlMxAAIwMQ..*"
    }
}'

Response:

[
    {
        "resource": "https://pets.clinic.com:443/app/v1/pets/all",
        "actions": {},
        "attributes": {},
        "advices": {},
        "ttl": 9223372036854775807
    }
]

Common Issues

Issue - Policy evalution yields empty actions with 200 response code.

Explanation 1 - A response with empty actions could mean AM couldn’t match the access request with any configured policies. Check your access request and ensure it satisfies at least one policy.

Explanation 2 - If you use an LDAP Filter environmental condition in your policy, you must configure the Policy Configuration service to point to the correct Identity repository. By default, AM uses the Config repository configured in this service.

Example Response:

[
    {
        "resource": "https://pets.clinic.com:443/app/v1/pets/all",
        "actions": {},
        "attributes": {},
        "advices": {},
        "ttl": 9223372036854775807
    }
]

GitHub Source Code

The companion source code for this blog post includes AM configuration for the Authorization and Postman collection. You can find the source code at our repository hosted at Github here.

Summary

We did what we set out to do! Using ForgeRock AM, we decoupled authorization by centralizing it at AM using Policies.

Using Policy-based authorization, we successfully implemented ABAC and RBAC access models to protect REST endpoints.

If you find yourself going down this path, you can use our companion source code and postman collection as a reference implementation.

If you enjoyed reading this post but require further help with your authorization requirements, we would be thrilled to speak with you. You can reach us at contact@sqoopdata.com.