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.

ObjectExample Attributes
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.

Explicitly Defining an Attributes State

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 CaseAttributeAttribute StateRule Statement
All users have the read permission on public repos.Privacy StatuspublicUse this attribute to label a resource as public: is_public(repo).
Superadmins can edit resources across organizations.User LevelsuperadminUse this attribute to label a user as a superadmin: is_superadmin(user).
Only rewards members users have access to certain resources.Membership Typerewards_memberUse this attribute to label a user as a rewards member: is_rewards_member(user).

Get Started with Explicitly Defining an Attributes State

Action Items

Prerequisites

We'll use a concrete Github example and add two attributes:

  1. An attribute that marks a repo as public.
  2. An attribute that describes a user as the owner of a repo.

Step 1: Use the attribute rule statement to modify existing rule behavior

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_permission rule. 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


actor User {}
# Top level resource.
resource Organization {
...
}
# Repository resource block.
resource Repository {
permissions = [
"read",
"write",
"delete"
];
roles = [
"owner",
"collaborator"
];
# Rules assigning permissions to roles.
"read" if "collaborator";
"write" if "collaborator";
"collaborator" if "owner";
"delete" if "owner";
}
====>

Resource Specific Pattern with Attributes


actor User {}
# Top level resource.
resource Organization {
...
}
# Repository resource block.
resource Repository {
permissions = [
"read",
"write",
"delete"
];
roles = [
"admin",
"collaborator"
];
# Rules assigning permissions to roles.
"read" if "collaborator";
"write" if "collaborator";
"collaborator" if "admin";
"delete" if "admin";
}
# ATTRIBUTE PATTERN (Step 1):
# - Use the attribute rule statement to modify
# existing rule behavior.
has_permission(_: Actor, "read", repo: Repository) if
is_public(repo);

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.

Next Steps

Defining Attributes with Dynamic State

Get Started with Defining Attributes with Dynamic State

Action Items

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>)

Using Booleans to Signal the Attribute State (Toggle Patterns)

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 absence of 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.

Using Multiple Attribute States

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 is_public to has_label in the has_permission rule:


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 has_label rule statement and change the attribute state.


has_role(_: Actor, "collaborator", repo: Repository) if
has_label(repo, "open_collaboration");

Next Steps


Additional Resources

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.

Get started with Oso Cloud →