Model Your App's Authorization
In Oso, you'll write your model models in the Polar. But you don't need to start from scratch!
This guide contains a selection of patterns that commonly appear in real-world policies. You can use these opinionated patterns as a starting point, but you're never locked into them. The building blocks that compose these patterns are flexible enough to accommodate whatever logic your application requires.
This guide will follow our usual model of GitCloud — a source code collaboration platform modeled after GitHub and GitLab. See the example application here (opens in a new tab).
Patterns
Let's start by looking at some of the most common patterns in real-world authorization policies.
Multitenancy
Summary: Your app may host many companies' data. Much of that data will be resources, like documents or code. In that case, it's essential to restrict access to only members of the organization that those resources belong to.
You associate users with one or many organizations through roles, and
grant permissions to those roles, like "read" if "member"
.
Challenges: Choosing the right roles is a design problem that requires careful thought. You need a small number of meaningful roles in your app. A good starting place is to stick with "admin" and "member".
In GitCloud: All users have roles in the organizations they belong to. Users have the "admin" role in organizations they create and "member" in organizations they join.
Use Cases: Nearly all B2B apps start with organization roles. This is one of the simplest ways to build multitenant applications.
Advanced Uses:
Users may have roles in many different organizations, like in GitCloud.
Thankfully, Oso Cloud supports this by default!
You can assign users to any number of organizations when inserting has_role
facts.
Oso Policy
actor User { }
resource Organization {
roles = ["admin", "member"];
permissions = ["read", "read_repository", "add_member", "create_repository"];
# role hierarchy:
# admins inherit all permissions that members have
"member" if "admin";
# org-level permissions
"read" if "member";
"add_member" if "admin";
# permission to create a repository in the organization
"create_repository" if "admin";
# permissions on child resources
"read_repository" if "member";
"delete_repository" if "admin";
}
CLI Example
oso-cloud tell has_role User:alice member Organization:acme
oso-cloud authorize User:alice read Organization:acme
oso-cloud authorize User:alice read_repository Organization:acme
When starting out with multitenant roles, it's best to keep it simple
and grant users broad permissions on the organization. For example,
in the above we granted members "read_repository"
permission at the
organization level.
However, when you need to grant more granular access, you'll want to introduce
resources for those permissions, and instead grant members "read"
permission
on the repository. This will make it possible to grant users access on specific
repositories:
actor User { }
resource Organization {
roles = ["admin", "member"];
permissions = ["read", "add_member", "create_repository"];
"member" if "admin";
"read" if "member";
"add_member" if "admin";
"create_repository" if "admin";
}
resource Repository {
permissions = ["read", "update", "delete"];
roles = ["member", "admin"];
relations = {
organization: Organization,
};
role if role on "organization";
"member" if "admin";
"read" if "member";
permission if "admin";
}
CLI Example
oso-cloud tell has_role User:alice member Organization:acme
oso-cloud tell has_relation Repositiory:anvil organization Organization:acme
oso-cloud authorize User:alice read Repository:anvil
Sharing
Summary: Often, someone will need to grant access on a resource to a specific person. To achieve this, we'll define roles on that resource. We'll often also want to use roles to control who is allowed to share a resource.
Challenges: When resources are shared with lots of users, you'll often want to be able to see who a resource is shared with. You can use Oso Cloud to query for these users.
In GitCloud: Repository admins can invite users as external collaborators on a repository. We do this by assigning those users roles on the repository.
Use Cases: Collaborative apps use this pattern. For instance, a Google doc has one owner and can be shared with other users or groups. Apps with file-sharing features will need to use this pattern too.
Advanced Uses: You may need to share a resource with different levels of permission (like "share and grant reading permission" and "share and grant editing permission"). Thankfully, this is very straightforward to implement!
Oso Policy
actor User { }
resource Repository {
roles = ["reader", "admin"];
permissions = ["read", "invite"];
"read" if "reader";
"invite" if "admin";
}
CLI Example
# Bob is an admin of Repository:anvil so Bob can give Alice the reader role on Repository:anvil
oso-cloud tell has_role User:bob "admin" Repository:anvil
oso-cloud authorize User:bob "invite" Repository:anvil
# The app would insert this on Bob's behalf
oso-cloud tell has_role User:alice "reader" Repository:anvil
oso-cloud authorize User:alice "read" Repository:anvil
Ownership
Summary: Many applications will want to grant additional permissions to the "owner" of a resource. This might be some fixed piece of application-specific data, like the person who opened an issue, or wrote a comment. Or ownership might be something that can be transferred between users. You can implement these scenarios by treating ownership as a kind of role.
Challenges: Storing ownership data in Oso Cloud requires you to insert these facts whenever resources are created. If that's unrealistic in your application, you can instead send ownership information as a Context Fact in a request.
In GitCloud: Most users are allowed to open issues in a GitCloud Repository. However, to close an issue, you must either be a maintainer of the repository, or the person who opened it.
This latter case is an example of ownership.
Use Cases: Many collaborative apps use this pattern to associate data with the person who created it.
Advanced Uses: You might want to be able to transfer ownership. In that case, you would perform a fact update to remove the ownership role from one person and assign it to the other.
Oso Policy
actor User { }
resource Issue {
roles = ["reader", "admin", "creator"];
permissions = ["read", "comment", "close"];
"read" if "reader";
"comment" if "reader";
"close" if "creator";
"close" if "admin";
"reader" if "admin";
}
CLI Example
# Alice created issue #537
oso-cloud tell has_role User:alice "creator" Issue:537
# Alice can close her own issue
oso-cloud authorize User:alice "close" Issue:537
Groups
Summary: If your customer has a lot of users, it'll be easier for them if they can grant access to entire groups of users, rather than assigning them individually.
Groups are an example of an actor hierarchy. With Oso, you'll model a hierarchy that allows members of a group to inherit the group's role assignments.
Challenges: A user needs to inherit all roles from the groups that user belongs to. Make sure that your users have both their individual roles and the roles of their groups.
In GitCloud: Members in a GitCloud Organization can be assigned to teams. Those teams can be granted permissions on repositories. Each member of a team then has that team's permissions on those repositories.
Use Cases: Groups are useful for collaborative apps like Notion and Google Docs. Any app with an idea of a "team" will probably need groups. Content management systems may need groups, too.
Advanced Uses: You may need nested groups, where a group is itself part of a group. You can do this by writing recursive rules.
Sometimes, you'll assign group membership by a user's external identity, like in Google Groups. You can do this by using Context Facts.
Oso Policy
actor User { }
# A group is a kind of actor
actor Group { }
resource Repository {
roles = ["reader"];
permissions = ["read"];
"read" if "reader";
}
# Actors inherit roles from groups
has_role(user: User, role: String, resource: Resource) if
group matches Group and
has_group(user, group) and
has_role(group, role, resource);
# Nested groups
has_group(user: User, group: Group) if
g matches Group and
has_group(user, g) and
has_group(g, group);
CLI Example
oso-cloud tell has_role Group:anvil-readers member Repository:anvil
oso-cloud tell has_group User:alice Group:anvil-readers
oso-cloud authorize User:alice read Repository:anvil
Files/Folders
Summary: If you're granted access to a folder, by default you'll often also have access to that folder's contents. In this example, anyone with a role on a folder also has the same role on any file/folder it contains.
Challenges: Your users may end up with many different ways to access a file. A role on the file itself, a role on the folder that the file is contained in, a role on the organization that owns the file, and so on. For debugging, your system needs a way to display what roles are granting a user access to a particular file.
In GitCloud: GitCloud source code is organized into files, folders, and repositories. Suppose we want to implement file-level access control on source code, then we would want the ability to inherit access based on the access you have on parent folders or the repository.
Use Cases: Folders let users assign fine-grained permissions in bulk. This is useful for expense reporting apps, reporting, analytics, and project management. CRM apps may have several folders per customer.
Advanced Uses: You may find you need folders inside of other folders, like a filesystem. Implementing this requires recursive rules.
Oso Policy
actor User { }
resource Repository {
roles = ["reader", "maintainer"];
}
resource Folder {
roles = ["reader", "writer"];
relations = {
repository: Repository,
folder: Folder,
};
"reader" if "reader" on "repository";
"writer" if "maintainer" on "repository";
role if role on "folder";
}
resource File {
permissions = ["read", "write"];
roles = ["reader", "writer"];
relations = {
folder: Folder,
};
role if role on "folder";
"read" if "reader";
"write" if "writer";
}
You can either spell out all the role implications in the shorthand syntax:
"reader" if "reader" on "folder";
or by writing a single shorthand rule that implies all roles from the parent folder:
role if role on "folder"
CLI Example
# Set up the file structure
oso-cloud tell has_relation File:test.py "folder" Folder:tests
oso-cloud tell has_relation Folder:tests "folder" Folder:python
oso-cloud tell has_relation Folder:python "repository" Repository:anvil-py
# Check file permissions
oso-cloud tell has_role User:alice reader Repository:anvil-py
oso-cloud authorize User:alice read File:test.py
Org Charts
Summary: Many companies need their authorization policy to correspond to their org chart. Organizational charts are a kind of actor hierarchy. In this example, managers automatically inherit all the permissions of their subordinates.
Challenges: Sometimes organizations have complex relationships beyond "manager" and "reportee." Org charts aren't always perfect trees, either. You can fill these cases in with other authorization patterns.
In GitCloud: GitCloud doesn't currently use this pattern. If we were to implement a feature like "managers can see analytics on PRs their employees have submitted," we'd use the org chart pattern.
Use Cases: Human resources systems, recruiting and applicant tracking systems, and customer relationship management systems all require this pattern.
Oso Policy
actor User { }
resource Repository { }
# 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" Repository:anvil
oso-cloud authorize User:eve "read" Repository:anvil
Custom Roles
Summary: Some applications need to allow their users to create new roles with customizable permissions. In that case, rather than statically defining the roles in the policy, the application can create these custom roles on the fly.
Challenges: The UI is the challenge here. Make sure it's clear where a user's permissions come from.
Custom roles typically associate permissions with a single resource -- normally an organization. If you want to allow users to specify granular permissions on child resources (e.g. repository-level permissions), you will need to use resource hierarchies to imply those permissions.
In GitCloud: GitCloud doesn't currently use this pattern.
Use Cases: Whenever your users need to define their own app's access patterns, consider custom roles. For instance, Discord uses custom roles to allow server admins to define moderator roles for their own communities.
Oso Policy
actor User { }
resource Organization { }
# A custom role is defined by the permissions it grants
has_permission(actor: Actor, action: String, org: Organization) if
role matches Role and
has_role(actor, role, org) 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 Organization:acme
oso-cloud authorize User:alice read Organization:acme
Default Roles
Summary: Many multitenant applications will use resource hierarchies to imply roles on child resources. Although those implications are normally hard-coded, some applications will give users the ability to customize this.
You can achieve this by having a configurable Attribute on the organization which determines what role a user inherits.
In GitCloud: Organization admins can choose what repository role a member will inherit by default.
Use Cases: Any application making use of resource hierarchies and resource-specific roles at multiple levels of the hierarchy might use this.
Advanced Uses: You might also want to capture that members by default have no role.
Oso Policy
actor User {}
resource Organization {
roles = ["member", "admin"];
permissions = ["set_default_role"];
"set_default_role" if "admin";
}
# Editors of a repository can edit it
resource Repository {
roles = ["reader", "editor", "admin"];
permissions = ["write"];
relations = { organization: Organization };
"write" if "editor";
}
has_role(actor: Actor, role: String, repo: Repository) if
org matches Organization and
has_organization(repo, "organization", org) and
has_default_role(org, role) and
has_role(actor, "member", org);
CLI Example
# Members of Acme are editors by default
oso-cloud tell has_default_role Organization:acme editor
# Bob is a member of Acme
oso-cloud tell has_role User:bob member Organization:acme
oso-cloud tell has_relation Repository:anvil organization Organization:acme
oso-cloud authorize User:bob write Repository:anvil
Public or Private Resources
Summary: Some resources are public, meaning they can be read by anyone. A "public" attribute on a resource can be used to indicate that it is public.
In GitCloud: The owner of a repository can mark it "public." Public repositories are visible to anyone, but private repositories are only visible to users in the owner's organization.
Use Cases: Apps with shared or collaborative resources—like documents, code, or comments—may need to use a "public" flag.
Oso Policy
actor User { }
resource Repository { }
has_permission(_: Actor, "read", repo: Repository) if
is_public(repo);
CLI Example
oso-cloud tell is_public Repository:anvil
oso-cloud authorize User:alice read Repository:anvil
Toggles
Summary: 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.
In GitCloud: GitCloud's "public" flag is a case of a toggle—users can change the public status of repositories they own.
Use Cases: Many apps have "protected" documents or files flagged "important". Document-sharing apps, human resource apps, and infrastructure apps may use this pattern.
Oso Policy
actor User { }
resource Repository { }
has_permission(actor: Actor, "delete", repo: Repository) if
has_role(actor, "member", repo) and
is_protected(repo, false);
CLI Example
oso-cloud tell has_role User:alice member Repository:legacy
oso-cloud tell is_protected Repository:legacy Boolean:false
oso-cloud authorize User:alice delete Repository:legacy
Global Roles
Summary: Give users roles that span the entire application (regardless of resource). This is common for internal users of your application and purely internal applications.
In GitCloud:
GitCloud internal users have read
permissions on all Organizations
to
provide technical support.
Use Cases: In a B2B app, the company's internal users may require access to resources belonging to organizations that use the app.
Oso Policy
actor User { }
global {
roles = ["admin"];
}
resource Organization {
roles = ["admin", "member", "internal_admin"];
permissions = ["read", "write"];
# internal roles
"internal_admin" if global "admin";
"read" if "internal_admin";
"member" if "admin";
"read" if "member";
"write" if "admin";
}
CLI Example
oso-cloud tell has_role User:alice admin
oso-cloud authorize User:alice read Organization:acme
Impersonation
Summary: Users can impersonate other users, which means they inherit some subset of the permissions that the other user has.
In GitCloud: Customer support reps can get read-only access to all organizations a user belongs to.
Use Cases: This pattern is useful in any system where some users need to troubleshoot or oversee other users. For example: customer support, HR, and payroll systems.
Oso Policy
actor User {
permissions = ["impersonate"];
"impersonate" if global "support";
}
global {
roles = ["support"];
}
resource Organization {
roles = ["admin", "member"];
permissions = ["read", "write"];
"member" if "admin";
"read" if "member";
"write" if "admin";
}
# a user can do anything some other user can do
# if they are allowed to impersonate that user and
# are currently impersonating them
allow(user: User, action, resource: Resource) if
other_user matches User and
has_permission(user, "impersonate", other_user) and
is_impersonating(user, other_user) and
has_permission(other_user, action, resource);
# we need to specify the default allow rule here
# because we added our own custom one above
allow(user: User, action, resource: Resource) if
has_permission(user, action, resource);
test "global support users can read user organizations via impersonation" {
setup {
has_role(User{"alice"}, "support");
has_role(User{"bob"}, "admin", Organization{"acme"});
has_role(User{"charlie"}, "member", Organization{"bar"});
is_impersonating(User{"alice"}, User{"bob"});
}
# bob can read as a member
assert allow(User{"bob"}, "read", Organization{"acme"});
# alice can impersonate bob
assert allow(User{"alice"}, "impersonate", User{"bob"});
# alice can read via Bob by impersonating bob
assert allow(User{"alice"}, "read", Organization{"acme"});
# charlie can read as a member
assert allow(User{"charlie"}, "read", Organization{"bar"});
# alice cannot read because alice is not impersonating charlie
assert_not allow(User{"alice"}, "read", Organization{"bar"});
}
CLI Example
oso-cloud tell has_role User:alice support
oso-cloud tell has_role User:bob admin Organization:acme
oso-cloud authorize User:alice read Organization:acme -c "is_impersonating User:alice User:bob"
Advanced Uses: There may be several ways to define who has permission to impersonate another user. Common ones include:
From a relationship with the user:
actor User {
permissions = ["impersonate"];
relations = { manager: User };
"impersonate" if "manager";
}
Or from a role on the organization:
actor User {
permissions = ["impersonate"];
}
resource Organization {
roles = ["member", "admin"];
}
# Organization admins can impersonate members
has_permission(user: User, "impersonate", other_user: User) if
org matches Organization and
has_role(user, "admin", org) and
has_role(other_user, "member", org);
The is_impersonating
fact can be included as either a context fact,
or by persisting the fact. In the former case, when a user "ends" an impersonation session, the application
stops sending the impersonation context fact. In the latter case, you need to delete the fact.
The former should be preferred when you want impersonation to be restricted to only a single application/service, whereas the latter is better when you want the impersonation session to be shared.
Entitlements
Summary: Users' permissions are determined by the features they have paid for.
In GitCloud: Basic organizations can only create a maximum of 5 repositories; a user can create a repository if they are an organization admin and the organization has remaining quota.
Use Cases: Applications with differentiated subscription tiers or paid features.
Oso Policy
actor User { }
resource Organization {
roles = ["admin", "member"];
permissions = ["create_repository"];
"member" if "admin";
}
resource Plan {
roles = ["subscriber"];
relations = { subscribed_organization: Organization };
"subscriber" if role on "subscribed_organization";
}
resource Feature {
relations = { plan: Plan };
}
declare plan_quota(Plan, Feature, Integer);
declare quota_used(Organization, Feature, Integer);
plan_quota(Plan{"pro"}, Feature{"repository"}, 10);
plan_quota(Plan{"basic"}, Feature{"repository"}, 0);
has_quota_remaining(org: Organization, feature: Feature) if
has_quota(org, feature, quota) and
quota_used(org, feature, used) and
used < quota;
has_quota(org: Organization, feature: Feature, quota: Integer) if
plan matches Plan and
has_relation(plan, "subscribed", org) and
plan_quota(plan, feature, quota);
has_permission(user: User, "create_repository", org: Organization) if
has_role(user, "member", org) and
has_quota_remaining(org, Feature{"repository"});
test "members can create repositorys if they have quota" {
setup {
quota_used(Organization{"apple"}, Feature{"repository"}, 5);
quota_used(Organization{"netflix"}, Feature{"repository"}, 10);
quota_used(Organization{"amazon"}, Feature{"repository"}, 0);
has_relation(Plan{"pro"}, "subscribed", Organization{"apple"});
has_relation(Plan{"pro"}, "subscribed", Organization{"netflix"});
has_relation(Plan{"basic"}, "subscribed", Organization{"amazon"});
has_role(User{"alice"}, "member", Organization{"apple"});
has_role(User{"bob"}, "member", Organization{"netflix"});
has_role(User{"charlie"}, "member", Organization{"amazon"});
}
assert has_quota_remaining(Organization{"apple"}, Feature{"repository"});
# Apple has quota remaining, so all good
assert allow(User{"alice"}, "create_repository", Organization{"apple"});
# Netflix has used all quota
assert_not allow(User{"bob"}, "create_repository", Organization{"netflix"});
# Amazon doesn't have any quota left
assert_not allow(User{"charlie"}, "create_repository", Organization{"amazon"});
}
Building Blocks
The patterns above are made from a combination of common authorization components called building blocks. Now, we'll discuss these building blocks in more detail. Once you've learned how to use them, you can construct authorization models tailored to your application's needs.
Actors and Resources
All authorization comes down to "who can act on what".
Actors are the who. Most of the time, this is a User
, but you might also
want to model actions taken by internal ServiceAccount
s, or distinguish
external users from InternalUser
s.
In Oso, you add actors to your model with an actor block:
actor User { }
Resources are the what. Typically, resources are your data models (or application objects) that actors are interacting with.
For example, in GitCloud, users interact with the data models: Organization
, Repository
, Issue
, etc.
Most of our authorization models revolve around determining whether a User
is allowed to perform some action
on one of these.
In Oso, you add resources to your model with resource blocks:
resource Repository {
...
}
You'll see your policy's actor and resource types show up when adding data to Oso Cloud.
For example, to tell Oso that the actor "alice" (a User
) is an admin, we
use the tell
API:
oso-cloud tell is_admin User:alice
Roles
Roles are groups of permissions that can be assigned to users. Most apps have roles. You've seen roles like "member," "owner," or "admin" in the patterns above and in other apps. In authorization terms, a role is a set of permissions given to a specific actor (like a user) on a specific resource (like a repository). A user with the "member" role may only have "read" and "write" permissions, but an "admin" may also have a "delete" permission.
Roles are a very general building block that you can use to represent many relationships between actors and resources. Most authorization use cases can be modeled using roles, so it's the first building block you should reach for.
In Oso, you'll typically set up roles via resource blocks. Let's look at a sample policy to see how to define roles, permissions, and the relationships between them.
resource Repository {
permissions = ["read", "edit", "delete"];
roles = ["reader", "editor", "admin"];
# permission assignments
"read" if "reader";
"edit" if "editor";
# give a role all permissions
permission if "admin";
# role implications
"reader" if "editor";
}
Permission and Role Definitions
Define permissions by adding them to resource blocks within your policy.
permissions = ["read", "edit", "delete"];
Define roles in a similar fashion.
roles = ["reader", "editor", "admin"];
Permission assignments
Assign permissions to roles by using the shorthand syntax seen below:
"edit" if "editor";
You can also write the equivalent long-form instead of defining the permission within the resource block.
has_permission(actor: Actor, "edit", repo: Repository) if
has_role(actor, "editor", repo);
Note: Rules in long-form syntax must be defined outside of resource blocks.
Role implications
Roles can imply other roles, which means that anyone with a role will additionally have all permissions granted by the implied role.
Add role implications by using the shorthand syntax seen below:
"editor" if "admin";
You can also write the equivalent long-form instead of defining the role within the resource block.
has_role(actor: Actor, "editor", repo: Repository) if
has_role(actor, "admin", repo);
Note: Rules in long-form syntax must be defined outside of resource blocks.
Role assignments
Assigning roles to users is done by adding data to Oso. For example, to assign Alice the reader role on the foo
repo:
oso-cloud tell has_role User:alice reader Repository:foo
In Oso, role assignments use the has_role
fact, which is a built-in convention in the Polar language.
While you hypothetically could assign roles using your own facts like my_has_role(User:alice, "reader", Repository:foo)
,
you would not be able to use resource block syntax to define permission assignments.
Resource Hierarchies
Resources often exist in hierarchies.
In these cases, access flows from parent resources to their subresources.
For example, in GitCloud your permissions on an Issue
are often determined by your role on the parent Repository
.
You create resource hierarchies in Oso by using resource blocks. Let's look at a sample policy to see how.
resource Organization {
...
}
resource Repository {
...
# declares that repositories have an "organization"
# relationship with organizations
relations = {
organization: Organization,
};
# role implication using the parent organization
"reader" if "member" on "organization";
role if "admin" on "organization";
}
The pieces that make up resource hierarchies are:
Assign permissions with relations
Let authorization logic flow through the resource hierarchy by using the shorthand syntax seen above:
"reader" if "member" on "organization";
You can also write the equivalent long-form instead of defining the relationship-based role within the resource block.
has_role(actor: Actor, "reader", repo: Repository) if
org matches Organization and
has_relation(repo, "organization", org) and
has_role(actor, "member", org);
Note: Rules in long-form syntax must be defined outside of resource blocks.
Inserting relationship data
Create the hierarchy in Oso by telling us the relevant data:
oso-cloud tell has_relation Repository:anvil organization Organization:acme
Rather than store all relationship data in Oso Cloud, you can also provide it at authorization-time by using Context Facts.
Actor Hierarchies
Like resources, actors also have hierarchies. If you're a member of a working group, you'll have all of the permissions that the group has.
Create actor hierarchies by relating multiple actors:
actor User { }
actor Group { }
has_role(user: User, role: String, resource: Resource) if
group matches Group and
has_group(user, group) and
has_role(group, role, resource);
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.
In Oso, attributes are represented by having arbitrary facts.
You've already seen how to add facts about roles and relationships, but you can tell Oso anything you want (as long as it's used in your policy somewhere)!
For example, we could say that users can only comment on open issues that they can read:
has_permission(user: User, "comment", issue: Issue) if
issue_status(issue, "open") and
has_permission(user, "read", issue);
Here, the issue_status(Issue, String)
facts are a way to communicate that
attribute about the issue to Oso.
Finally, we need to tell Oso about that piece of data:
oso-cloud tell issue_status Issue:490 open
For attributes that reflect application state, it is often better to keep these stored in the application and provide to Oso Cloud at authorization-time by using Context Facts
However, if you need this application state to be shared between multiple services for authorization, then Oso Cloud is a good place to store the state.
Talk to an Oso Engineer
If you'd like to learn more about using Oso Cloud in your app or have any questions about this guide, schedule a 1x1 with an Oso engineer. We're happy to help.