Attribute Based Access Control (ABAC)
Attributes are a way to further describe your application. Every part of your application has characteristics that can be thought of as an attribute. You can use this to control your authorization logic in a very granular way. For example, a user in your app might have a status of active or inactive. Or, a resource might be public or private. A few more examples are given in the table below.
|Users||• Employment Status: full-time, part-time, contractor, ...|
• Reward Status: platinum, gold, silver, ...
• User Level: superadmin, user, guest
|Resources||• Privacy Status: private, public|
• Region: Africa, Americas, Asia, Europe, Oceania
• Confidentiality Status: top secret, secret, restricted,..., public
Attributes also have a few characteristics of their own! They might be binary (this or that, true or false, ect.), or they may come from a collection of terms. These factor contribute to how you use them in your authorization.
When modeling attributes that have only one or two states that you care about, you can use the rule name to describe the state that a user or resource currently has. The resulting rule statements usually take the form is_<ATTRIBUTE_STATE>.
|Example Use Case||Attribute||Attribute State||Rule Statement|
|All users have the read permission on public repos.||Privacy Status||public||Use this attribute to label a resource as public: is_public(repo).|
|Superadmins can edit resources across organizations.||User Level||superadmin||Use this attribute to label a user as a superadmin: is_superadmin(user).|
|Only rewards members users have access to certain resources.||Membership Type||rewards_member||Use this attribute to label a user as a rewards member: is_rewards_member(user).|
We'll use a concrete Github example and add two attributes:
- An attribute that marks a repo as public.
- An attribute that describes a user as the owner of a repo.
If the rule doesn't exist in longhand, write a new rule with the attribute rule statement. Make sure the new rule doesn't conflict with any shorthand rules defined in resource blocks.
In this example we'll be extending the logic for
read if collaborator. By extending the rule, rather than replacing it, we allow existing
has_role(actor, "collaborator, repo) facts to still be valid. The new
has_permission rule appears in the right
column code block below.
NOTE: What's not obvious in this example is that we are modifying a
has_permissionrule. You may recall that the shorthand statement
read if collaborator;, expands to:
has_permission(actor: Actor, "read", repo: Repository) if has_role(actor, "collaborator", repo);
Simple Resource Specific Pattern
Resource Specific Pattern with Attributes
Using this same pattern we can also assign default roles to users with certain attributes. For example, the owner of a repo should have admin permissions.
has_role(user: User, "admin", repo: Repository) if is_owner(user, repo);
NOTE: The ownership of a resource is dependent on two pieces of information: the specific resource in question, and the user labeled as the owner. The attribute rule statement must also contain this information to work as intended.
In the get started example above, we introduced a simple way to add attributes to rules. However, this approach is not very flexible.
Suppose the attribute has many possible states that you need to represent. Or perhaps the state of an attribute changes frequently. There are two variations to the attribute pattern that help model these scenarios:
- is_<ATTRIBUTE_STATE>(Type, Boolean)
- <ATTRIBUTE>(Type, <ATTRIBUTE_STATE>)
Borrowing from the first example, simply change the
has_permission rule to the following:
has_permission(_: Actor, "read", repo: Repository) if is_public(repo, true);
NOTE: This pattern is also referred to as a toggle pattern since it models attributes that toggle between one of two states.
This change doesn't do much from an authorization modeling perspective, however, it does have benefits for authorization
enforcement and logging (as you'll see in later sections). Before, the only way to indicate a repo was private was the
is_public. Now when a state changes within your application, you can simply update the state value. Use
is_public(repo, true) when a repos is public, and
is_public(repo, false) for when it's private.
In Github, public and private are labels repositories can have. For this last pattern we'll present, suppose there is a third label: open_collaboration. Anyone can collaborate on a repo with this label.
First, change the attribute rule statement from
has_label in the
has_permission(_: Actor, "read", repo: Repository) if has_label(repo, "public");
Next, create a new default collaborator role when a repo has the open_collaboration label. Reuse the
rule statement and change the attribute state.
has_role(_: Actor, "collaborator", repo: Repository) if has_label(repo, "open_collaboration");
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, connect with us on Slack. We're happy to help.