Policies

Each policy specifies how we want to allow/deny someone to access to some resources.

Policy decision

Each policy could be either positive or negative.
When a policy is positive, it means if the policy matches in you authorization request, we allow to do the operation.
If the policy is negative, it means we will deny user from doing the operation when the policy matches.

note

One negative policy could make result of authorization negative totally.

  • To create/update a positive policy, set its decision value ot 1
  • To create/update a negative policy, set its decision value to 2

Policy features

  • A policy Could be per specific user using user_id field in creation or update policy API.

  • A policy Could be per specific role using role_code field.

  • A policy Could be per resource type (e.g., all products) using res_type field.

  • A policy Could be per specific resource (e.g., product with id:4) using res_id field.

  • A policy can contain a tree, in this case, policy will be matched just when tree path in the request matches with the policy tree path.
    Set a policy tree path using the tree param.

  • A policy can contain a condition.
    A condition could filter policies based on the user's attrs, resource's attrs and context params which we send in our authorization request. Condition should follow expr language definition

  • A policy can contain user_scopes to deny access if the user's token does not contain either one of scopes which is specified in the user_scopes field. The user's token does not need to be jwt to support this feature. It supports every token that shield generates for users.

Examples

Note

You will learn about tree and condition evaluation in the next section.

The following examples could be used as payload of our requests to create or update a policy.

  • Allow user with id abc to write on product with id 4
{
  "service_id": "{{ your service id}}",
  "name": "my test policy",
  "res_type": "product",
  "res_id": "4",
  "user_id": "abc",
  "scopes": [
    "write"
  ],
  "decision": 1
}
  • Allow all reporters to read all project statuses.
 {
  "service_id": "{{ your service id}}",
  "name": "my test policy",
  "res_type": "project",
  "role_code": "reporter",
  "scopes": [
    "read"
  ],
  "decision": 1
} 
  • Allow all reporters to read all project statuses in their own cities using Tree:
 {
  "service_id": "{{ your service id}}",
  "name": "my test policy",
  "res_type": "project",
  "role_code": "reporter",
  "scopes": [
    "read"
  ],
  "decision": 1,
  "tree": {
    "key": "dc",
    "values": [
      "abc.com"
    ],
    "branches": [
      {
        "key": "state",
        "values": [
          "{res.state}"
        ]
      }
    ]
  },
  "is_resource_based": true
} 

INFO

In this type of policies (which we have a tree), we need to send a path param in our Authorization requests too. The tree in the authorization request must match with the tree in the policy. Read more about it in the Tree evaluation section please.

  • Allow all reporters to read all project statuses in their own cities using Condition:
 {
  "service_id": "{{ your service id}}",
  "name": "my test policy",
  "res_type": "project",
  "role_code": "reporter",
  "scopes": [
    "read"
  ],
  "decision": 1,
  "condition": "res.attrs.state==user.attrs.state",
  "is_resource_based": true
} 
  • Allow all product owners to see products
{
  "service_id": "{{ your service id}}",
  "name": "my test policy",
  "res_type": "product",
  "scopes": [
    "read","write"
  ],
  "decision": 1,
  "condition": "res.attrs.owner_id==user.id",
  "is_resource_based": true
}
  • Allow all admins to read and write on all projects
 {
  "service_id": "{{ your service id}}",
  "name": "my test policy",
  "res_type": "project",
  "role_code": "reporter",
  "scopes": [
    "read"
  ],
  "decision": 1
} 
  • Deny if a user doesn't have api_read user scope, to access to every project with resource scope of read.

WARNING

user's scope and resource scope are two different things.

  • resource's scopes are the scopes that you define per your resource to specify the operations that user can do on the resource.
  • user scopes are the scopes in the user's token (e.g., user's jwt token,...) that we granted at the client at token generation time (in any oauth2 flow which the client requests a user token)

Note

This is a good policy to evaluate client's granted scopes in addition to the user permissions.

{
  "service_id": "{{ your service id}}",
  "name": "my test policy",
  "res_type": "project",
  "scopes": [
    "read"
  ],
  "user_scopes": [
    "api_read"
  ],
  "decision": 2
}

Tree

A tree is a hierarchically structured object which we use in our policies to match the policy with a group of users under some categories. (e.g., to allow all users in specific city).

Each value in the tree could be dynamic. We can set resource's attribute, user's attribute or context params.

  • To set user attr as value, use {user.attr_name}. Change attr_name to the real attr name.

  • To set resource attr as value, use {res.attr_name}. attr_name should be changed to the attribute's name.

  • To set context param as value, use {ctx.param_name}. param_name should be changed to the real parameter's name.

Each tree structure is as following:

{
  "key": "{branch's key}",
  "values": [
    "{branch's value}"
  ],
  "branches": [
    {
      "key": "{child branches' key}",
      "values": [
        "{child branches' value}"
      ],
      "branches": []
    }
  ]
}

For example:

{
  "key": "dc",
  "values": [
    "abc.com"
  ],
  "branches": [
    {
      "key": "state",
      "values": [
        "{res.state}"
      ]
    }
  ]
}

Tree evaluation

if the path param (which is a tree-path) in the authorization request is a path of tree in the policy, so policy matches in the authorization request.

Examples

  • path: a=b,c=d is match with the following tree:
{
  "key": "a",
  "values": [
    "b"
  ],
  "branches": [
    {
      "key": "c",
      "values": [
        "d"
      ]
    },
    {
      "key": "e",
      "values": [
        "f"
      ]
    }
  ]
}
  • path a=b,c=d isn't match with the following tree (because of the hi key):
{
  "key": "hi",
  "values": [
    "b"
  ],
  "branches": [
    {
      "key": "c",
      "values": [
        "d"
      ]
    }
  ]
}

Tip

To allow a value in the policy tree match with any value in the tree-path of authorization request, set its value to *

  • Path state=fars,city=fasa is match with all the following trees:
{
  "key": "state",
  "values": [
    "*"
  ]
}
{
  "key": "state",
  "values": [
    "fars"
  ]
}
{
  "key": "state",
  "values": [
    "fars"
  ],
  "branches": [
    {
      "key": "city",
      "values": [
        "fasa"
      ]
    }
  ]
}
  • Path state=fars,city=fasa is not match with none of the following trees:
{
  "key": "state",
  "values": [
    "tehran"
  ]
}
{
  "key": "state",
  "values": [
    "fars"
  ],
  "branches": [
    {
      "key": "city",
      "values": [
        "shiraz"
      ]
    }
  ]
} 

Condition

The condition result specifies whether we should apply the policy or not. If the condition returns true, so we apply the policy (if other sections of policy are good too, e.g., the scope,...)

If it returns false, we skip the policy.

Condition syntax follows expr language

Condition context

The context that we provide to each condition it as following:

ctx: {your provided context data in authorization check}
user: {user_data}
res_type: {resource's type in authorization check}
res: {provided resource in authorization check}
  • User: user data in the condition is as following:
id: {user_id}
roles: {array of user's roles}
scopes: {array of user's available scopes}
attrs: {map of user's attributes}

For example:

id: user-1
roles: ["admin","reporter"]
scopes: ["profile","api_read"]
attrs: {
    "age":"20"
}
  • Resource : It contains the resource's data that you want to check authorization on it. Its structure is as following:
id: {res_id}
type: {res_type},
attrs: {map of resource's attributes}

For example:

id: project-4
type: project
attrs: {
    "owner_id":"user-1",
    "status":"active"
}

Examples

  • Condition to check if a (string literal) is equal to b (string literal)
a==b
  • Condition to check of state of the project(as our resource in the policy) is equal to user's state:
res.attrs.state==user.attrs.state
  • Condition to check if state of the user is not tehran:
 user.attrs.state==tehran
  • Condition to check if user's state is tehran or fars.
user.attrs.state in ['tehran','fars']

Last Updated: