Pick a language:
Build Attribute-Based Access Control (ABAC)
Build Attribute-Based Access Control (ABAC) While role-based access control (RBAC) emphasizes granting permissions based on roles, you may also wish to grant permissions or roles based on attributes of actors or resources. With Oso, you can use attribute-based logic alongside roles.
Grant permissions with attributes Granting users permissions based on attributes is simple with Oso. Let’s say your policy contains the following resource block:
main.polar# ... resource Repository { permissions = ["read"]; roles = ["contributor", "admin"]; "read" if "contributor"; } The block contains a role-based rule that grants the "read" permission to actors who have the "contributor" role.
You can add an attribute-based rule that grants all users the "read" permission for any repository that is public:
main.polar# ... resource Repository { permissions = ["read"]; roles = ["contributor", "admin"]; "read" if "contributor"; } has_permission(_: User, "read", repo: Repository) if repo.is_public; The has_permission rule above tells Oso to look up the is_public attribute on the Repository application type in order to determine whether or not someone should be granted "read" access. This rule will be evaluated alongside the "read" if "contributor" shorthand rule in the resource block so that a user can read a repository if they have the "contributor" role OR if the repository is public.
Grant roles with attributes Oso also supports granting users roles based on user or resource attributes. Oso uses has_role rules to look up a user’s roles in your application. By defining multiple has_role rules, you can customize how users are granted various roles.
For example, you could add a has_role rule to the policy above that grants the "admin" role to the repository creator:
main.polar# ... resource Repository { permissions = ["read"]; roles = ["contributor", "admin"]; "read" if "contributor"; } has_role(user: User, "admin", repo: Repository) if user = repo.created_by;
Build Authorization for Resource Hierarchies
Build Authorization for Resource Hierarchies in Node.js A resource hierarchy refers to a model with nested resources, where a user’s permissions and roles on a resource depend on the resource’s parent.
Common examples of resource hierarchies include:
File system permissions: access to a folder may grant access to documents within the folder Grouping resources by project: users have project-level roles and permissions that determine their access to resources within the project Organizations and multi-tenancy: top-level tenant/organization roles and permissions grant access to resources within the organization You can model resource hierarchies in Oso by defining relations between resources. You can write policies that use relations and query them to find out if a user has access to a single resource or to get a list of resources that a user has access to (using the data filtering feature).
This guide uses an example resource hierarchy from our GitClub sample application. In GitClub, Organization is the top-level resource, and Repository resources are nested within organizations. Users have roles at both the organization and repository level. A user’s organization role grants them a default role on every repository within that organization.
1. Register resource types and relations The first step to modeling a resource hierarchy is to register the application types that represent the resources you are protecting.
To make your implementation compatible with data filtering, you need to specify resource relations by creating Relation objects and passing them to register_class(). For more information on registering classes and relations for data filtering, see the data filtering guide.
app.js import { Relation, Oso } from "oso"; const oso = new Oso(); oso.setDataFilteringQueryDefaults({ combineQuery, buildQuery }); // Register the Organization type oso.registerClass(Organization, { execQuery: execFromRepo(Organization), types: { id: String, } }); // Register the Repository class, and its relation to the Organization type oso.registerClass(Repository, { execQuery: execFromRepo(Repository), types: { id: String, organization: new Relation("one", "Organization", "org_id", "id"), } }); 2. Declare parent relations After registering your resource types, you can define a resource block for each resource in your policy.
Inside each block, you should declare the permissions and roles that are available on that resource type. For child resource types, also declare relations to parent resources.
main.polarallow(actor, action, resource) if has_permission(actor, action, resource); resource Organization { permissions = ["read", "add_member"]; roles = ["member", "owner"]; } resource Repository { permissions = ["read", "push"]; roles = ["contributor", "maintainer", "admin"]; relations = { parent: Organization }; } Now that you’ve defined your resource relations in Polar, you can hook them up to the Relations you registered in Step 1 using has_relation rules:
main.polarhas_relation(parent_org: Organization, "parent", child_repo: Repository) if parent_org = child_repo.organization; # use the `organization` relation we registered in Step 1 Tip Using registered Relations to access related resources from your policy, rather than using an arbitrary application field or method, ensures that data filtering queries will work with your policy.
3. Write rules using parent relations You now have all the plumbing in place to write rules that use parent relations.
If you need to grant a role on a child resource based on a parent resource role, you can define a shorthand rule in the child resource block. For example, in GitClub the "owner" role on Organization resources grants a user the "admin" role on every Repository within the organization:
main.polarallow(actor, action, resource) if has_permission(actor, action, resource); resource Organization { permissions = ["read", "add_member"]; roles = ["member", "owner"]; } resource Repository { permissions = ["read", "push"]; roles = ["contributor", "maintainer", "admin"]; relations = { parent: Organization }; "admin" if "owner" on "parent"; } You can also use shorthand rules to grant permissions based on parent resource roles and permissions. For example, we could add that users with the Organization "member" role can "read" every repository in the organization:
main.polar# ... resource Repository { # ... "admin" if "owner" on "parent"; "read" if "member" on "parent"; } We could also modify that rule to say that users who have the Organization "read" permission have the "read" permission on every repository in the organization.
main.polar# ... resource Repository { # ... "admin" if "owner" on "parent"; "read" if "read" on "parent"; }
Use Context in Policies
Pass context beyond the actor, action, and resource into Oso policies.
Share Rules Across Resources
Oso policies make it possible to share rules across related resource types, and override them as needed.
Test Your Policy
Learn to test your Oso policies.
Connect with us on Slack
If you have any questions, or just want to talk something through, jump into Slack. An Oso engineer or one of the thousands of developers in the growing community will be happy to help.