Reference
More
Resource Blocks

Resource Blocks

Actor and resource blocks provide a way to organize authorization logic by application type. These blocks are especially useful for expressing role-based access control logic.

The simplest form of a block looks like this:

# Actor block
actor User {}
 
# Resource block
resource Repository {}

Inside of a block, you can declare permissions, roles, and relations and write shorthand rules.

A more complete block looks like this:

resource Repository {
  permissions = ["read", "push"];
  roles = ["contributor", "maintainer"];
  relations = { parent: Organization };
 
  # An actor has the "read" permission if they have the "contributor" role.
  "read" if "contributor";
  # An actor has the "push" permission if they have the "maintainer" role.
  "push" if "maintainer";
 
  # An actor has the "contributor" role if they have the "maintainer" role.
  "contributor" if "maintainer";
 
  # An actor has the "maintainer" role if they have the "owner" role on the "parent" Organization.
  "maintainer" if "owner" on "parent";
}

Once you have declared a block, you can use the built-in Actor and Resource specializers to match all types declared as actors or resources, respectively.

Permission Declarations

You can specify the permissions that are available on a type of actor or resource using the following syntax:

resource Repository {
  permissions = ["read", "push"];
}

Permissions are always strings. You must declare permissions in order to use them in shorthand rules.

Role Declarations

You can specify the roles that are available for an actor or resource type using the following syntax:

resource Repository {
  roles = ["contributor", "maintainer", "admin"];
}

Roles are always strings. You must declare roles in order to use them in shorthand rules.

Relation Declarations

You can specify relations between actor/resource types using the following syntax:

resource Repository {
  relations = { parent: Organization };
}

Relations are key: value pairs where the key is the relation name and the value is the type of the related object. All related objects referenced in a relation declaration must have an actor or resource block. For example, in the above snippet, there must be a corresponding resource Organization {} block since Organization is referenced in Repository's relations.

Shorthand Rules

Shorthand rules are concise rules that you can define inside actor and resource blocks using declared permissions, roles, and relations.

For example,

resource Repository {
  permissions = ["read", "push"];
  roles = ["contributor", "maintainer"];
  relations = { parent: Organization };
 
  # An actor has the "read" permission if they have the "contributor" role.
  "read" if "contributor";
  # An actor has the "push" permission if they have the "maintainer" role.
  "push" if "maintainer";
 
  # An actor has the "contributor" role if they have the "maintainer" role.
  "contributor" if "maintainer";
 
  # An actor has the "maintainer" role if they have the "owner" role on the "parent" Organization.
  "maintainer" if "owner" on "parent";
}

Shorthand Rules Without Relations

A shorthand rule has the basic form:

"result" if "condition";

"result" and "condition" can be permissions or roles that are declared on the same resource.

For example:

resource Repository {
  permissions = ["read", "push"];
  roles = ["contributor", "maintainer"];
 
  "read" if "contributor";  # "contributor" role grants "read" permission
  "push" if "maintainer";  # "maintainer" role grants "push" permission
  "contributor" if "maintainer";  # "maintainer" role grants "contributor" role
}

Shorthand Rules With Relations

If you have declared relations inside a block, you can also write shorthand rules of this form:

"result" if "condition" on "relation";

Here, "result" is a permission or role declared on the current resource type, "condition" is a permission or role declared on a related resource type, and "relation" is the name of the relation linking the two resource types.

This form is used to grant results based on conditions on a related resource or actor. This form is commonly used with "parent" relations. For example:

resource Repository {
  roles = ["contributor", "maintainer"];
  relations = { parent: Organization };
 
  "admin" if "owner" on "parent"  # "owner" role on parent Organization grants the "admin" role
  "contributor" if "member" on "parent"  # "member" role on parent Organization grants "contributor" role
}

The Global block

You can use a global block to represent roles and permissions that apply across the entire application.

These are particularly useful for representing global roles.

The global block has the same syntax as resource blocks, but rather than differentiating blocks by a resource name, there is only a single global block:

global {
  roles = ["admin", "member"];
  permissions = ["invite_member", "create_tenant"];
 
  "create_tenant" if "member";
 
  "member" if "admin";
  "invite_member" if "admin";
}

You can refer to roles and permissions from a global block by using the keyword global before the role/permission you want to use:

resource Organization {
  roles = ["internal_admin"];
  permissions = ["read"];
 
  "internal_admin" if global "admin";
  "read" if "internal_admin";
}

Shorthand Rule Expansion

Shorthand rules are expanded to full Polar rules when they are loaded. The semantics of this expansion are as follows.

Expansion without relation

$x if $y;
=> rule1(actor: Actor, $x, resource: $Type) if rule2(actor, $y, resource);

where rule1 and rule2 are the expansions of $x and $y respectively.

If $x is a permission, then rule1 will be has_permission. If $x is a role, then rule1 will be has_role. The same semantics apply for $y and $rule2.

The resource argument specializer $Type is determined by the enclosing block definition. E.g., if the rule is defined inside of resource Repository {}, then $Type will be Repository.

For example:

resource Repository {
  permissions = ["read"];
  roles = ["contributor"];
 
  "read" if "contributor"; # Shorthand rule
}
 
# Expanded rule
#                            "read"                        if                 "contributor"           ;
#                              \/                                                  \/
has_permission(actor: Actor, "read", resource: Repository) if has_role(actor, "contributor", resource);

Expansion with relation

$x if $y on $z;
=> rule1(actor: Actor, $x, resource: $Type) if rule2(actor, $y, related) and has_relation(resource, $z, related);

where rule1, rule2, and has_relation are the expansions of $x, $y, and $z respectively.

The expansion of $x to rule1 and $y to rule2 follow the same semantics as expansion without relation. $z must always be a declared relation on the enclosing resource type. The has_relation rule is necessary in order to access the related object that rule2 references.

For example:

resource Repository {
  roles = ["admin"];
  relations = {parent: Organization};
 
  "admin" if "owner" on "parent";
}
 
# Expanded rule
#                      "admin"                        if                 "owner"
#                        \/                                                \/
has_role(actor: Actor, "admin", resource: Repository) if has_role(actor, "owner", related) and
#                                             on                        "parent"     ||
#                                                                          \/        ||
                                                 has_relation(resource, "parent", related);

Global block expansion

Global blocks expand to reference an instance of the type Global.

$x if global $y;
=> rule1(actor: Actor, $x, resource: $Type) if rule2(actor, $y);

where rule1 and rule2 are the expansions of $x and $y respectively.

If $x is a permission, then rule1 will be has_permission. If $x is a role, then rule1 will be has_role. The same semantics apply for $y and $rule2.

resource Organization {
  roles = ["internal_admin"];
  permissions = ["read"];
 
  "internal_admin" if global "admin";
  "read" if "internal_admin";
}
 
 
# Expanded rule
#                        "internal_admin"                            if          global "admin" ;
#                              \/                                                         \/
has_role(actor: Actor, "internal_admin", organization: Organization) if has_role(actor, "admin");

Talk to an Oso Engineer

If you'd like to learn more about resource blocks and how to use them within your policy, schedule a 1x1 with an Oso engineer. We're happy to help.

Get started with Oso Cloud →