Skip to main content

ABAC Design Patterns

Below you'd find common authorization design-patterns implemented in ABAC, and guides on how to implement them.

Ownership​

Ownership is one of the more common authorization patterns - in which a user can perform actions only on resources they own. There are three easy ways (in addition to writing policy directly as code) to implement ownership in Permit.io - all are equally valid and it's mostly a matter of taste.

  • Ownership via tenants​

    Ownership is defined by having the user and resource belong to the same tenant. While implemented as an ABAC policy behind the scenes - you don't need to actually do so yourself - and you can enjoy the simplified API which treats tenants as a first class citizen.

    Read here on creating and checking permissions based on tenancy.

  • Ownership via list on resource​

    The resource has an attribute listing all the user ids that own it. Use a dynamic resource (resource-set) to check against the list.

    It would look something like this:

    And the permission check (assuming loading the attribute as part of the check) would look something like this:

    const permitted = await permit.check(userKey, "read", {
    type: "file", // The resource name
    attributes: {
    owners: [
    "d08c85a349994aeb89a3f02c08bdb340", // user-1
    "48fb889360604253a5189580b48694cf", // user-2
    ],
    },
    });
  • Ownership via list on user profile​

    The user profile has an attribute listing all the resource ids that it owns. Use a user-set to check against the list.

Belonging to a group​

Groups are a classic ReBAC concept; but ReBAC is a subset of ABAC, and we can often use it to implement group affiliation. The group can be represented as a resource by itself, a tenant, or an attribute of users or resources.

  • Group by tenants​

    You can use tenants as groups - having several tenants together form the actual higher level tenant. Those can then be grouped by tenant attributes.

  • Group by attributes​

    You can assign users and resources to a group using an attribute like group_id or group_name - Implementation here is very similar to the ownership design pattern.

info

Once we add ReBAC and groups natively to Permit.io - those would be the default recommended way to implement groups.

Combining your own OPA Rego code with Permit.io generated ABAC code​

Permit.io allows you to extend its ABAC capabilities by writing custom Rego code. This enables you to create dynamic attributes that can be used in your ABAC policies.

How it works​

  1. Write custom Rego code that generates attributes
  2. Use these attributes in your ABAC policies just like static attributes
  3. The custom code runs alongside Permit.io's generated ABAC code

Example: Creating a Dynamic Intersection Attribute​

Let's say you want to check if there's an intersection between user and resource attributes. Instead of using static values, you can create a dynamic attribute using Rego.

Add this code to your gitops repository in the custom/ directory:

package permit.custom
import future.keywords.in

# Default value if no conditions are met
default custom_user_attributes["has_intersection"] = false

# Set to true if there's an intersection between user and resource attributes
custom_user_attributes["has_intersection"] := true {
# Convert attributes to sets for intersection testing
user_attr_set = {x | some x in data.users[input.user.key].attributes["attr_in_user"]}
res_attr_set = {x | some x in input.resource.attributes["attr_in_resource"]}
# Check if there's at least one common value
count(user_attr_set & res_attr_set) > 0
}
Important Requirements
  • The package name must be permit.custom
  • The rule name must be custom_user_attributes
  • These are the only requirements - you can write any Rego logic you need

Using the Custom Attribute​

Once defined, you can use this attribute in your ABAC policies just like any other attribute:

const permitted = await permit.check(userKey, "read", {
type: "document",
attributes: {
attr_in_resource: ["tag1", "tag2"],
},
});

Testing Your Custom Code​

  1. Using the OPA Playground

  2. Testing with Local PDP

    docker run -it -p 7767:7000 -p 8181:8181 -e PDP_API_KEY=permit_key_XXXXX permitio/pdp-v2:latest

    Then you can:

    • Send POST requests to http://localhost:8181/v1/data/permit with your input data
    • Get raw outputs including policy debug data
    • Update custom policies directly at http://localhost:8181/v1/policies/custom/example.rego
Development Workflow
  1. Start with the OPA Playground for quick iterations
  2. Move to local PDP for more realistic testing
  3. Once satisfied, commit to your gitops repository