Skip to main content
Resource blocks define custom types and provide built-in features for writing authorization logic in Polar. They:
  • Declare types that can be referenced in rules and facts (including facts stored as data).
  • Provide built-in declarations for permissions, roles, and relations, along with shorthand rules for RBAC and ReBAC.
Polar defines two abstract categories for resource blocks:
TypeDescription
resourceAn object to which actions can be applied.
actorAn entity that can be granted permissions.
Each type you declare must be labeled as actor or resource. Example minimal blocks:
actor User {}  
resource Repository {}  
Example with features and shorthand rules:
resource Repository {
  permissions = ["read", "push"];
  roles = ["contributor", "maintainer"];
  relations = { parent: Organization };

  # Shorthand rules

  ## 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 "internal_admin" role
  ## on the "parent" Organization.
  "maintainer" if "internal_admin" on "parent";
}

Global Block

A policy can define one global block to store permissions and roles accessible from any other resource. This is typically used for global roles.
global {
  roles = ["admin", "member"];
  permissions = ["invite_member", "create_tenant"];

  "create_tenant" if "member";

  "member" if "admin";
  "invite_member" if "admin";
}
Reference global values using the global keyword:
resource Organization {
  roles = ["internal_admin"];
  permissions = ["read"];

  "internal_admin" if global "admin";
  "read" if "internal_admin";
}

Permissions, Roles, and Relations

These declarations simplify writing RBAC and ReBAC shorthand rules.
FeatureDescription
permissionsActions that Actors can perform on this resource.
rolesRoles an Actors can have in relation to this resource.
relationsLinks from this resource to other resources.
Rules for all three:
  • Values must be String literals, e.g. "read" (case-sensitive).
  • Each can be declared at most once per block, i.e. you cannot declare roles twice.
  • All values across permissions, roles and relations must be unique within the block, e.g. you can’t declare roles with the value "writer" and also declare a relations named "writer".
  • A feature must be declared before it can be referenced in shorthan

Permissions

Permissions define the actions that Actors can perform on a resource. They are declared as an array of strings.
resource Repository {
  permissions = ["read", "push"];
}
Rules that use permission values expect a String matching one of the values declared in permissions. For example:
has_permission(actor:Actor, permission:String, resource:Resource)...

Roles

roles define Actors in relation to a resource. They are declared as an array of strings.
resource Repository {
  roles = ["contributor", "maintainer", "admin"];
}
Rules and facts that use role values expect a String matching one of the values declared in roles. For example:
has_role(actor:Actor, role:String, resource:Resource)...

Relations

Roles classify the relationship between this and others resources. relation declaration must be key-value pairs, whose:
  • Key is an unquoted string (i.e. an identifier)
  • Value is the type of the related object
For example:
resource Repository {
  relations = { parent: Organization };
}
Rules and facts that use relation values expect String for the relation name (e.g. "parent"), and a value of the related type (e.g. Organization). For example:
has_relation(resource: Repository, relation: String, parent: Organization)...

Shorthand Rules

If a resource block declares permissions, roles, or relations, you can write shorthand rules:
resource Repository {
  permissions = ["read", "push"];
  roles = ["contributor", "maintainer"];
  relations = { parent: Organization };

  # Shorthand rules

  # 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 "internal_admin" role
  # on the "parent" Organization.
  "maintainer" if "internal_admin" on "parent";
}
A shorthand rule has the basic form:
<role or permission> if <expression>;
The left-hand side of the if statement is the result that is granted if the right-hand side evaluates to true. Shorthand rules expand into full rules when loaded.

Left-hand side expressions

The left-hand side can be:
  • A role ("contributor").
  • A permission ("read").
  • The role or permission keywords (any role or permission).
Each of these expands to a rule head that defines what is being granted. For example, in the shorthand rule "read" if "contributor";, the left-hand side "read" expands to:
has_permission(actor: Actor, "read", resource: Repository) if ...
Because this shorthand rule is defined in the Repository resource block, the resource type in the expanded rule is Repository.

Right-hand side expressions

The right-hand side of the if statement is an expression that specifies the conditions under which the left-hand side is true. For most expression types (roles, permissions, or relations), the shorthand rule will only succeed if additional supporting statements—facts or other rules—are present in the policy. These statements must follow the schema of the expanded predicate for that expression (referred to below as $rhs_pred) Examples of valid right-hand sides include:
  • A role ("contributor"). This grants specific permissions to a role ("read" if "contributor").
  • A permission ("read"). This groups common permissions to reduce repetition ("read.issues" if "read").
  • A role or permission on a related resource ("reader" on "parent"). This grants roles or permissions across resource types—assigning a role or permission on one resource to a role on a related resource. The granted role or permission must be declared on the related resource type, and the relation must be declared on the current resource type.
  • Rule call syntax (is_public(resource)). This calls a rule with arguments to check an attribute or condition. The syntax is a rule name followed by a list of arguments. Arguments can be primitive values (strings "open" or numbers 0) or the keyword resource, which refers to the resource declared in the enclosing resource block.
Right-hand side expressions can be combined with and and or.

Statements supporting right-hand side expressions

Right-hand side expressions other than rule calls (e.g. roles) require additional statements (i.e. facts or additional rules) to succeed.

Expansion Semantics

Shorthand rules are expanded to full Polar rules when they are loaded.

Without relation

$lhs if $rhs;
=> $lhs_pred(actor: Actor, $lhs, resource: $Type) if $rhs_pred(actor, $rhs, resource);
In the expanded form, $lhs_pred and $rhs_pred are derived from the shorthand expression types of $lhs and $rhs:
Shorthand expression typeExpanded predicate
Permissionhas_permission
Rolehas_role
The $Type in the resource position is the type declared by the enclosing resource block. For example, in resource Repository {...}, $Type is Repository. For this shorthand rule to be usable in authorization checks, your policy must also define supporting statements—facts or rules—that match the schema of the right-hand side predicate. 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);
To satisfy this rule, include supporting facts such as:
has_role(User{"alice"}, "contributor", Repository{"anvils"})

With relation

$lhs if $rhs on $rel;
=> $lhs_pred(actor: Actor, $lhs, resource: $Type) if
  $rhs_pred(actor, $rhs, related) and has_relation(resource, $rel, related);
The values of $lhs_pred and $rhs_pred follow the same semantics as expansion without relation. $rel must be a declared relation on the current resource type. The has_relation rule is required to access the related object that $rhs_pred references. For this shorthand rule to be usable in authorization checks, your policy must also define supporting statements that match the schema of the right-hand side predicate. 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);
To satisfy this rule, include supporting facts such as:
has_role(User{"alice"}, "owner", Organization{"acme"});
has_relation(Repository{"anvils"}, "parent", Organization{"acme"});

Global block expansion

A global block expands to rules that reference the Global type.
$lhs if global $rhs;
=> $lhs_pred(actor: Actor, $lhs, resource: $Type) if $rhs_pred(actor, $rhs);
$lhs_pred and $rhs_pred depend on the shorthand expression type of $lhs and $rhs:
Shorthand expression typeExpanded predicate
Permissionhas_permission
Rolehas_role
  • To satisfy this rule, add supporting facts matching the right-hand side schema.
  • The schema of the right-hand side’s expanded predicate differs from non-global expansions. global right-hand side predicates do not expect a resource value.
For example:
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");
Supporting facts:
has_role(User{"alice"}, "admin");

Referencing resources

Whether declared as an actororresource, refer to a resource by its type name (e.g., ResourceName`) followed by a string identifier in curly braces:
ResourceName{"ident"}
This format is called object literal syntax.

Polymorphism

Polar applies polymorphism within resource blocks: The abstract type Actor works with any type declared in an actor block when used in has_role or has_permission. Actor is a subtype of Resource. Any variable of type Resource can also be an Actor.
Because all actors are also resources, we use the term “resource blocks” to cover both.
For details, see extends documentation.

actor vs. resource

Actor is a subtype of Resource, meaning any Actor can also be used where a Resource is expected.
- Actor: something that can be assigned roles or permissions (e.g., User, ServiceAccount).
- Resource: something you grant permissions to (e.g., Repository, Document).
Choose based on shorthand rule expansion:
  • If it needs to appear as the first argument of has_role or has_permission, make it an actor.
  • Otherwise, make it a resource.