Authorization Modeling
This guide introduces a few Building Blocks, which are commonly-seen elements of authorization models, and then presents a few real-world Patterns that combine these Building Blocks in ways applications can use.
Building Blocks
Authorization models often consist of a few independent concepts that can be combined in an Oso policy. Each such concept is called a Building Block.
Roles
Roles are one of the most useful building blocks for talking about authorization. A role represents a named set of permissions given to a specific actor on a specific resource. A single role can extend permissions to multiple actors over multiple resources through the use of Actor and Resource Hierarchies. Roles are a very general building block that can be used to represent any kind of relationship between an actor and a resource, so practically any use case can be modeled using roles.
Resource Hierarchies
It’s common to organize resources into hierarchies, where access flows from resources to their subresources. Three common patterns using this building block are Ownership/Sharing, Org Roles, and Files / Folders. In all of these cases, a resources are grouped beneath other resources. Only the names have been changed.
Actor Hierarchies
It’s often useful to arrange actors into hierarchies. Two common patterns using this building block are Groups and Organizational Charts. In the first case, permissions belonging to a Group are shared by the Group’s members. In the second case, permissions belonging to workers are shared by their managers.
Attributes
Attributes describe information about actors and resources. An attribute on a particular actor or resource provides information about that actor or resource. Information from attributes on a specific actor and on a specific resource can then be used to determine whether to give the actor a specific permission on that resource.
Patterns
Authorization models in real-world applications usually combine a number Building Blocks, and there are some common Patterns of how the Building Blocks go together in applications.
Org Charts
Organizational charts are a common kind of Actor Hierarchy that capture the authority structure of an organization. In this example, managers automatically inherit all the permissions of their subordinates.
Oso Policy
# A manager has permission to do anything their workers can.
has_permission(manager: Actor, action: String, resource: Resource) if
has_relation(manager, "manager", worker) and
has_permission(worker, action, resource);
CLI Example
# This shows that granting a permission to Bob grants Eve permission,
# since Eve is Bob's manager's manager.
oso-cloud tell has_relation User:bob "manager" User:alice
oso-cloud tell has_relation User:alice "manager" User:eve
oso-cloud tell has_role User:bob "reader" Repo:service
oso-cloud authorize User:eve "read" Repo:service
Files/Folders
Collecting files or documents into folders is a common use case for Resource Hierarchies. In this example, anyone having a permission on a container object automatically has that permission on the object’s contents.
Oso Policy
# If you can act on the container, you can act on the contents
has_permission(actor: Actor, action: String, inner: Resource) if
has_relation(outer, "container", inner) and
has_permission(actor, action, outer);
CLI Example
oso-cloud tell has_relation User:bob container Org:oso
oso-cloud tell has_role User:alice reader Org:oso
oso-cloud authorize User:alice read User:bob
Ownership/Sharing
Many applications require a concept of resource ownership. Ownership is a basic example of a Resource Hierarchy, where resources have a specific, globally unique relation to their owners.
This example defines roles and permissions on a shareable resource type: the owner of an Issue
can share it with others by assigning the role "reader"
on the issue. The ability to assign the role is reflected in the "add_reader_role"
permission on the Issue
resource.
Oso Policy
# A shareable resource
resource Issue {
roles = ["reader", "writer", "owner"];
permissions = ["read", "write", "add_reader_role"];
"reader" if "writer";
"writer" if "owner";
"read" if "reader";
"write" if "writer";
"add_reader_role" if "owner";
}
CLI Example
# Bob is the owner of Issue:bug so Bob can give Alice the reader role on Issue:bug
oso-cloud tell has_role User:bob "owner" Issue:bug
oso-cloud authorize User:bob "add_reader_role" Issue:bug
# In reality Bob would do this
oso-cloud tell has_role User:alice "reader" Issue:bug
oso-cloud authorize User:alice "read" Issue:bug
Groups
Groups are named collections of actors, just like roles are named collections of permissions. In this common case of Actor Hierarchies, roles and permissions are assigned directly to a group, and inherited by the group’s members.
Oso Policy
# A group is an actor and membership is a role on the group
actor Group {
roles = ["member"];
}
# Actors inherit permissions from groups
has_permission(actor: Actor, action: String, resource: Resource) if
has_role(actor, "member", group) and
has_permission(group, action, resource);
CLI Example
oso-cloud tell has_role Group:oso-readers reader Org:oso
oso-cloud tell has_role User:alice member Group:oso-readers
oso-cloud authorize User:alice read Org:oso
Org Roles
In a multi-tenant application, resources are often associated with the tenant they belong to. In this special case of Resource Hierarchies, a role on an organization extends to all of its resources.
Oso Policy
# A role on an org extends to the org's resources
has_role(actor: Actor, role: String, resource: Resource) if
has_relation(organization, "container", resource) and
has_role(actor, role, organization);
CLI Example
oso-cloud tell has_relation Repo:service container Org:oso
oso-cloud tell has_role User:alice reader Org:oso
oso-cloud authorize User:alice read Repo:service
Custom Roles
Sometimes an application allows its users to create new roles with customizable permissions. In this case, rather than statically defining the roles in the policy, the application can create these custom roles on the fly by defining what permissions they grant.
Oso Policy
# A custom role is defined by the permissions it grants
has_permission(actor: Actor, action: String, resource: Resource) if
role matches Role and
has_role(actor, role, resource) and
grants_permission(role, action);
CLI Example
# If you have the custom role "viewer", you are allowed to read
oso-cloud tell grants_permission Role:viewer read
# Alice is a viewer
oso-cloud tell has_role User:alice Role:viewer Repo:service
oso-cloud authorize User:alice read Repo:service
Default Roles
In a multi-tenant application, resources are often associated with the tenant they belong to. Users may dynamically decide an organization's default role, which all of the organization's members inherit.
Oso Policy
has_role(actor: Actor, role: String, repo: Repo) if
org matches Org and
has_parent(repo, org) and
has_default_role(org, role) and
has_role(actor, "member", org);
CLI Example
# Members of Oso are writers by default
oso-cloud tell has_default_role Org:oso writer
# Bob is a member of Oso
oso-cloud tell has_role User:bob member Org:oso
oso-cloud tell has_parent Repo:service Org:oso
oso-cloud authorize User:bob write Repo:service
Public Resources
Some resources are public, meaning they can be read by anyone. An attribute on a resource can be used to indicate that it is public.
Oso Policy
has_permission(_: Actor, "read", resource: Resource) if
is_public(resource);
CLI Example
oso-cloud tell is_public Org:oso
oso-cloud authorize Actor:alice read Org:oso
User Statuses
Additional information about a user, such as whether it is an active account, may be used for additional authorization checks. Such information can be provided as an attribute on the user.
Oso Policy
allow(actor: Actor, action: String, resource: Resource) if
is_active(actor) and
has_permission(actor, action, resource);
CLI Example
oso-cloud tell has_permission User:bob delete Repo:legacy
oso-cloud tell is_active User:bob
oso-cloud authorize User:bob delete Repo:legacy
Toggles
A toggle on a resource can turn on and off certain permissions that a specific role has. The state of the toggle can be provided as an attribute on the resource.
Oso Policy
has_permission(actor: Actor, "delete", resource: Resource) if
has_role(actor, "member", resource) and
is_protected(resource, false);
CLI Example
oso-cloud tell has_role User:alice member Repo:legacy
oso-cloud tell is_protected Repo:legacy Boolean:false
oso-cloud authorize User:alice delete Repo:legacy