Structured Rego
  • 07 Apr 2024
  • 12 Minutes to read
  • Dark
    Light
  • PDF

Structured Rego

  • Dark
    Light
  • PDF

Article Summary

About Structured Rego

The Platform uses a structured form of Rego to create Policies.

Rego is a declarative, context-aware language for authoring Policies and supports the Open Policy Agent (OPA).

Policies written in structured Rego can be imported to the Platform, where they will be visible with the UI and a Policy Map will be available. Any Policy available in PlainID administration point can be exported to Structured Rego.

Sample Structured Rego Policy

Following is a sample Policy that is expressed in Rego. In this case, the Policy specifies that Gold member customers and Standard customers (WHO) can view and manage their personal accounts and credit cards (WHAT).


# METADATA
# custom:
#   plainid:
# 	policyId: 08ae32e4-fbf3-4cc8-b3b9-3b4061d1c825
# 	name: Manage personal account and Credit cards
# 	description: Customers can view and manage their account’s credit cards only with MFA
# 	accessType: Allow
package sample_policy
import future.keywords
 
# METADATA
# custom:
#   plainid:
# 	kind: DynamicGroup
# 	name: Gold member customers
dynamic_group(identity) {
	identity.template == "User"
	identity["User_Type"] == "External"
	identity["Membership_Type"] == "Gold"
}
# METADATA
# custom:
#   plainid:
# 	kind: DynamicGroup
# 	name: Standard Customers
dynamic_group1(identity) {
	identity.template == "User"
	identity["User_Type"] == "External"
	identity["Membership_Type"] == "Standard"
}
 
# METADATA
# custom:
#   plainid:
# 	kind: Ruleset
# 	name: User personal bank accounts
# 	description: Any account where the owner is the user currently trying to access
ruleset (asset, identity) {
	asset.template == "Bank Accounts"
	identity.template == "User"
	asset["account_owner"] == identity.Userid
}
 
# METADATA
# custom:
#   plainid:
# 	kind: Action
action(asset) {
	asset.template == "Bank Accounts"
	asset.action in ["Manage", "View"]
}
 
# METADATA
# custom:
#   plainid:
# 	kind: Ruleset
# 	name: User personal credit cards
# 	description: Any credit card where the owner is the user currently trying to access
ruleset(asset, identity) {
	asset.template == "Credit Cards"
	identity.template == "User"
	asset["card_owner"] == identity.Userid
}
 
# METADATA
# custom:
#   plainid:
# 	kind: Action
action(asset) {
	asset.template == "Credit Cards"
	asset.action in ["Manage", "View"]
}

Policy In Structured Rego

The structure of the Policy in Rego and each of its Rules are detailed in the following section as they would appear based on the sample Policy shared above.

Policy METADATA Comment Block

The Policy METADATA Comment Block contains general Details  about the Policy.

Example

# METADATA
# custom:
#   plainid:
# 	policyId: 08ae32e4-fbf3-4cc8-b3b9-3b4061d1c825
# 	name: Manage personal account and Credit cards
# 	description: Customers can view and manage their account’s credit cards only with MFA
# 	accessType: Allow
package sample_policy
import future.keywords
ParameterExplanation
policyIdA unique identifier for the Policy.

The ID is used to refer to this Policy in subsequent API calls when exporting, updating or deleting the Policy.

The ID can contain characters (A-Z), numbers, hyphens, underscores, colons and periods. The ID is case insensitive, and is unique within the Environment.

When creating a new Policy, if this value is not defined, it will be automatically generated.

Policy ID is viewed and can be updated from the Policy Details tab.

nameThe name of the Policy as it will appear within the Platform.

When creating a new Policy, if this value is not defined, the name of the Policy will be the Policy ID.

descriptionOptional free text field as it will appear within the Platform.
accessTypeThe access decision type.

Valid values: Allow or Deny.

Default value if not defined is Allow.

packageEach Policy is represented as a single Rego Module (package).

WHO In Structured Rego

The WHO section represents WHO will get access defined in that Policy. In our sample Policy, we have:

  • WHO (DynamicGroup): Gold member customers
  • WHO (DynamicGroup): Standard  customers

The Rego rule representing the Dynamic Group begins with a METADATA comment section

Example:

# METADATA
# custom:
#   plainid:
# 	kind: DynamicGroup
# 	name: Gold member customers
ParameterExplanation
kindDynamicGroup (Required)
nameThe name of the Dynamic Group as it will appear within the Platform.

If this value is not defined, it will be generated automatically.

In the example below: Gold member customers

The Rule body contains the expressions which define Who access granted by this Dynamic Group.

Example:

dynamic_group(identity) {
	identity.template == "User"
	identity["User_Type"] == "External"
	identity["Membership_Type"] == "Gold"
}
ExpressionExplanation
Identity Template Expression(Required)

Represents the Identity Template this Dynamic Group is associated with.

identity.template == “<Identity Template ID>"

Template ID can be found in Identity Workspace Settings > Details tab under Identity Template ID.

In the example below: User.

Rule ExpressionsAt least one Rule Expression is required.

Represents the set of rules defining WHO can get access.

identity["<Identity Attribute Key>"] <Operator> "<Value>"

The Identity Attribute Key can be found in Identity Workspace Settings > Identity Attributes tab

under the specific Attribute form.

Supported Operators: Equals, Not equals, Contains, Starts with, Ends with, Greater than, Greater or equals, Less than, Less than or equals

The inserted value should be valid for the Attribute type.

In the following example:

identity[“User_Type”] == identity[“Membership_Type”] |

WHAT In Structured Rego

The WHAT in Rego can contain several Rules to specify WHAT Assets and WHAT Actions are part of the Policy. In our sample Policy, we have:

  • WHAT (Ruleset): personal bank accounts
  • WHAT (Ruleset): personal credit card
  • WHAT (Action): view, manage

WHAT For Ruleset

The Rego rule representing the Ruleset begins with a METADATA comment section.

Example:

# METADATA
# custom:
#   plainid:
#     kind: Ruleset
#     name: bank accounts of location
}
ParameterExplanation
kindRuleset (Required)
nameThe name of the Ruleset as it will appear in the Platform.

If this value is not defined, it will be generated automatically.

In the following example: User personal bank accounts

The Rule body contains the expressions which define WHAT access is granted by this Ruleset.

Example 1:

ruleset(asset, identity, requestParams){
	asset.template == "Bank Accounts"
	identity.template == "User"
	asset["account_location"] == identity["User_Location"]
}

Example 2:

ruleset(asset, identity,requestParams){
	asset.template == "Bank Accounts"
	asset["account_owner"] == requestParams["owner"]
	}
}
ExpressionExplanation
Asset Template Expression(Required)

Represents the Asset template this Ruleset is associated with.

asset.template == “<Asset Template ID>"

Template ID Can be found in Authorization Workspace > Assets & Conditions under the Asset Type details tab.

In example 1: Bank Accounts.

Rule ExpressionAt least one Rule Expression is required.

Represents the set of rules defining WHAT assets can be accessed:

asset["<Asset Attribute Key>"] <Operator> "<Value\Identity Attribute Key>"

Asset Attribute Key can be found in Authorization Workspace settings > Asset & Conditions > Asset Type under the specific attribute form

Supported Operators: Equals, Not equals, Contains, Starts with, Ends with, Greater than, Greater or equals, Less than, Less than or equals

The inserted value should be valid for the Attribute type or Dynamic value (Identity Attribute).

Identity Attribute Key can be found in Identity Workspace settings > Identity attributes tab under the specific attribute form.

Identity Template ExpressionOnly required if the Rule expression relates to a Dynamic Value (Identity Attribute).

Represents the Identity Template this Ruleset is associated withidentity.template == “<Identity Template ID>"

Template ID can be found in Identity Workspace Settings > Details tab under Identity Template ID.

In the example below: User.

WHAT For Action

Actions are optional. Asset Templates may or may not have Actions associated with it.

If Actions are associated with the Asset Template and used in Rulesets, then the Actions must be defined as part of the WHAT in the structured Rego.

The Rego rule representing the Action begins with a METADATA comment section.

Example:

# METADATA
# custom:
#   plainid:
# 	kind: Action
ParameterExplanation
kindAction, Required

The Rule body contains the expressions which define WHAT Actions are granted by this Policy.

Example:

action (asset){
        asset.template == "Bank Accounts"
        asset.action in ["Manage", "View"]
}
ExpressionExplanation
Asset Template Expression(Required)

Represents the Asset Template that defined Actions are associated with.

asset.template == “<Asset Template ID>"

Template ID can be found in Authorization Workspace> Assets & Conditions under the Asset Type details tab.

In the example below: Bank Accounts.

Action list expression(Required)

Represents the list of allowed Actions for this Asset Template.

asset.action in ["<Action Name>"]

Action can be found in Authorization Workspace settings > Asset & Conditions >

under the actions tab

In the example below: View, Manage

WHEN in Structured Rego

Conditions are external factors that you may want to use to create more flexible and precise Policies. With Conditions, you can allow or restrict Member actions based on:

  • IP
  • Identity Attributes
  • Request Attributes

The Rego rule representing the Conditions always begins with a METADATA comment section.

WHEN for IP Conditions

IP Conditions allow or restrict users based on a preconfigured IP range.

Example:

# METADATA
# custom:
#   plainid:
#     kind: Condition IP
#     name: Local IP
ParameterExplanation
kindCondition IP (Required)
nameThe name of the IP Condition as it appears in the Platform.
If this value is not defined, it is generated automatically.
Example:
Local IP

The body contains the expressions which define WHEN access is granted or denied by this Condition.

Example 1:

condition_ip(env)
    {
	net.cidr_contains("10.0.0.0/8", env.sourceIp)
}

Example 2:

condition_ip(env)
    {
	not net.cidr_contains("10.0.0.0/8", env.sourceIp)
}
ExpressionExplanation
Condition ExpressionAt least one Condition Expression is required.
This expression represents the IP Range indicating WHEN assets can be accessed or restricted. Referencing the code block examples provided above:

Example 1: Allows access for an IP range
net.cidr_contains("10.0.0.0/8", env.sourceIp)

Example 2: Restricts access for an IP range
not
net.cidr_contains("10.0.0.0/8",env.sourceIp)

Note: Only valid CIDR addresses are supported.

WHEN for Identity Attributes

Identity Attribute Conditions allow or restricts access based on an attribute associated to the Identity - like all users of a specific department; all users within a specific location (country, city, region, etc.), all users with a** specific title**.

Example:

# METADATA
# custom:
#   plainid:
#     kind: Condition Identity
#     name: User Site is USA
ParameterExplanation
kindCondition Identity (Required)
nameThe name of the Identity Condition as it appears in the Platform.
If this value is not defined, it is generated automatically.
Example:
User Site is USA

The body contains the expressions which define WHEN access is granted by this Condition.
Example 1:

    condition_identity(identity)
{
	identity.template == "User"
	identity["Site"] == "USA"
}
ExpressionExplanation
Condition ExpressionAt least one Condition Expression is required.
This expression represents the set of rules defining WHEN assets can be accessed:
identity["Site"] == "USA"

Supported Operators: Equals, Not equals, Contains, Starts with, Ends with, Greater than, Greater or equals, Less than, Less than or equals;

The value should be valid for the specified Attribute type.
Identity Template ExpressionThe Identity Condition consists of Identity Attributes, so the associated Identity Template ID is required.
identity.template == “<Identity Template ID>"

The Template ID can be found in the Identity Workspace Settings > Details tab under Identity Template ID.
Example as referenced in the Example 1 code block:
User

WHEN for Request Attributes

Request Attribute Conditions allow or restrict access based on the value of a specific request parameter.

Example:

# METADATA
# custom:
#   plainid:
#     kind: Condition Request
#     name: MFA Authentication
}
ParameterExplanation
kindCondition Request (Required)
nameThe name of the Request Condition as it appears in the Platform.
If this value is not defined, it is generated automatically.
Example:
MFA Authentication

The body contains the expressions which define WHEN access is granted by this Condition.
Example 1:

condition_request(requestParams, identity)
{
	requestParams["Auth_Method"] == "MFA"
}

Example 2:

condition_request(requestParams, identity)
{
	identity.template == "User"
	requestParams["Auth_Method"] == identity["userMethod"]
}
ExpressionExplanation
Condition ExpressionAt least one Condition Expression is required.
It represents the set of rules defining WHEN assets can be accessed:
requestParams["Auth_Method"] == "MFA"

Supported Operators: Equals, Not equals, Contains, Starts with, Ends with, Greater than, Greater or equals, Less than, Less than or equals;

The value should be valid for the specified Attribute type or Dynamic value (Identity Attribute).


The Identity Attribute Key can be found in the Identity Workspace settings > Identity attributes tab under the specific attribute form.

Compare Example 2 to the following Identity Attribute value:
requestParams["Auth_Method"] == identity["userMethod"]
Identity Template ExpressionThis expression is only required if the Rule expression relates to a Dynamic Value (Identity Attribute).
It represents the Identity Template the Condition is associated with.
identity.template == “<Identity Template ID>"

The Template ID can be found in the Identity Workspace Settings > Details tab under Identity Template ID.
Example as shown in Example 2 code block:
User

Structured Rego Guidelines

Each Policy is represented as a single Rego Module (package) and is constructed using the following syntax rules:

  • Each Policy file begins with a # METADATA comment block. This block contains the metadata for the Policy (Policy Name, Policy ID, Description and Access Type (Allow/Deny), etc.)
  • Each Rule within Rego begins with an expression that specifies which Identity Template or Asset Template is being used (see Policy Structure in Rego for details).
  • All Rego Rules are preceded by a # METADATA comment block, which contains the metadata about the building block, including its type (Dynamic Group, Ruleset, etc., and optionally other information such as name, to find the Asset Template ID.
  • The expressions must conform to a strict format, wherein a value from the source is compared to a value (or a set of values) appearing directly in the expression, using an operator such as ==, <, > etc.
  • Multiple statements may exist within a Rule. When there is more than one statement within a Rule, the statements are evaluated with an AND relationship between them.
  • Multiple Rego Rules may exist for one building block. When there is more than one Rego Rule for a Building Block, the Rules are evaluated with an OR relationship between them
  • Each such expression is a comparison between an Attribute and a fixed value, or between two Attributes.
  • Attributes, which are defined as part of the Template (either an Identity Template in the case of Dynamic Groups and an Asset Template in the case of Rulesets), can source their values either from the Authorization Request itself, or from an external data source. Depending on the type of the Building Block, different operators are allowed in an expression. For details, please see the PlainID Policy Authoring documentation.
  • Annotated code is validated to contain only expressions that use supported operators, and only access attributes that exist in the relevant Identity or Asset Templates.

Structured Rego Ordering Best Practices

To import a Policy into PlainID, these blocks and their parameters must be included in the Policy file:

  • Policy metadata
  • Dynamic Groups and Rulesets
    • Actions (optional)
    • Conditions (optional)

Note: All Structured Rego files include these constant parameters:

  • METADATA
  • custom
  • plainid

When a Policy is exported from PlainID as a Rego file, the Policy Structured Rego is organized in the following manner:

  • Policy metadata Block:

    • policyId
    • name
    • description (optional)
    • accessType
  • Dynamic Group Block (sorted alphabetically by name):

    • kind
    • name
    • dynamic_group(identity)
      • identity.template
      • identity[{“IdentityAttName”}]
  • Ruleset Block (sorted alphabetically by Asset Type Name and their associated Rulesets):

    • kind
    • name
    • ruleset(asset, identity)
      • asset.template
      • identity.template (optional)
      • asset[{“AssetAttName”}]
  • Action Block (sorted alphabetically by Asset Type Name and their associated Action):

    • kind
    • name
    • action(asset)
      • asset.template
      • asset.action in[{“ActionName”}]
  • Condition Block (sorted alphabetically by type and their associated Condition):

    • Type: Condition IP

      • kind
      • name
      • condition_ip(env)
      • net.cidr_contains ({“IPRange”}, env.sourceIp)
    • Type: Condition Request

      • kind
      • name
      • condition_request(requestParams, identity)
      • identity.template (optional)
      • requestParams[{“RequestAtt”}]
    • Type: Condition Identity

      • kind
      • name
      • condition_identity(identity)
      • identity.template
      • identity[{“IdentityAttName”}]

It is recommended to keep Rego files in the order returned by the Export API (or the code output of the Import API) to ensure that changes in the Policy via the console do not result in major differences in code. If the Rego files are managed in a source control system (such as Git), keeping the export order will result in minimal diffs between versions.

Important

When a Rego file is imported by PlainID, any code that isn't annotated with a PlainID-formatted #METADATA block is skipped.


Was this article helpful?

What's Next