This page explains the design approach of Permify’s ABAC support as well as demonstrates how to create and use attribute based permissions in Permify.
Attribute-Based Access Control (ABAC) is like a security guard that decides who gets to access what based on specific characteristics or “attributes”.
These attributes can be associated with users, resources, or the environment, and their values can influence the outcome of an access request.
Let’s make an analogy, it’s the best way to understand complex ideas.
Think about an amusement park, and there are 3 different rides. In order to access each ride, you need to have different qualities. For example:
Similar to this ABAC checks certain qualities that you have defined on users, resources, or the environment.
It’s obvious but the simple answer is “use cases”… Sometimes, using ReBAC and RBAC isn’t the best fit for the job. It’s like using winter tires on a hot desert road, or summer tires in a snowstorm - they’re just not the right tools for the conditions.
As you can see ABAC has a more contextual approach. You can define access rights regarding context around subjects and objects in an application.
To support ABAC in Permify, we’ve added two main components into our DSL: attributes and rules.
Attributes are used to define properties for entities in specific data types. For instance, an attribute could be an IP range associated with an organization, defined as a string array:
Here are the all attribute types that you use when defining an attribute
.
Rules are structures that allow you to write specific conditions for the model. You can think rules as simple functions of every software language have. They accept parameters and are based on condition to return a true/false result.
In the following example schema, a rule could be used to check if a given IP address falls within a specified IP range:
We design our schema language based on Common Expression Language (CEL). So the syntax looks nearly identical to equivalent expressions in C++, Go, Java, and TypeScript.
Please let us know via our Discord channel if you have questions regarding syntax, definitions or any operator you identify not working as expected.
Let’s examine some of common usage of ABAC with small schema examples.
For attributes that represent a binary choice or state, such as a yes/no question, the Boolean
data type is an excellent choice.
String can be used as attribute data type in a variety of scenarios where text-based information is needed to make access control decisions. Here are a few examples:
Integer can be used as attribute data type in several scenarios where numerical information is needed to make access control decisions. Here are a few examples:
Double can be used as attribute data type in several scenarios where precise numerical information is needed to make access control decisions. Here are a few examples:
In this example, is_public
is defined as a boolean attribute. If an attribute is a boolean, it can be directly used without the need for a rule. This is only applicable for boolean types.
In this context, if the is_public
attribute of the repository is set to true, everyone can view it. If it’s not public (i.e., is_public
is false), only the owner, in this case user:1
, can view it.
The permissions in this model are defined as such:
permission view = is_public or owner
This means that the ‘view’ permission is granted if either the repository is public (is_public
is true) or if the current user is the owner of the repository.
relationships:
attributes:
Check Evolution Sub Queries Post View → post:1#is_public → true → post:1#admin@user:1 → true
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_post:1$is_public
→ truecheck*{snapshot}*{schema*version}*{context}\_post:1#admin@user:1
→ trueIn this example, to be able to view the repository it must not be a weekend, and the user must be a member of the organization.
The permissions in this model state that to ‘view’ the repository, the user must fulfill two conditions: the current day (according to the context data day_of_week
) must not be a weekend (determined by the is_weekday
rule), and the user must be a member of the organization that owns the repository.
Relationships:
Check Evolution Sub Queries Organization View → organization:1$is_weekday(valid_weekdays) → true → organization:1#member@user:1 → true
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_organization:1$is_weekday(valid_weekdays)
→ truecheck*{snapshot}*{schema*version}*{context}\_post:1#member@user:1
→ trueThis model represents a banking system with two entities: user
and account
.
user
: Represents a customer of the bank.account
: Represents a bank account that has an owner
(which is a user
), and a balance
(amount of money in the account).The check_balance rule: This rule verifies if the withdrawal amount is less than or equal to the account’s balance and doesn’t exceed 5000 (the maximum amount allowed for a withdrawal). It accepts two parameters, the withdrawal amount (amount) and the account’s current balance (balance). The owner check: This condition checks if the person requesting the withdrawal is the owner of the account.
Both of these conditions need to be true for the withdraw
permission to be granted. In other words, a user can withdraw money from an account only if they are the owner of that account, and the amount they want to withdraw is within the account balance and doesn’t exceed 5000.
Relationships
Attributes
Check Evolution Sub Queries For Account Withdraw → account:1$check_balance(balance) → true → account:1#owner@user:1 → true
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_account:1$check_balance(balance)
→ truecheck*{snapshot}*{schema*version}*{context}\_account:1#owner@user:1
→ trueIn this model:
employee
: Represents an individual worker. It has no specific attributes or relations in this case.organization
: Represents an entire organization, which has a founding_year
attribute. The view
permission is granted if the check_founding_year
rule (which checks if the organization was founded after 2000) returns true.department
: Represents a department within the organization. It has a budget
attribute and a relation to its parent organization
. The view
permission is granted if the department’s budget is more than 10,000 (checked by the check_budget
rule) and if the organization.view
permission is true.Note: In this model, permissions can refer to higher-level permissions (like organization.view
). However, you cannot use the attribute of a relation in this way. For example, you cannot directly reference organization.founding_year
in a permission expression. Permissions can depend on permissions in a related entity, but not directly on the related entity’s attributes.
Relationships
Attributes
Check Evolution Sub Queries For Department View
→ department:1$check_budget(budget) → true
→ department:1#organization@user:1 → true → organization:2$check_founding_year(founding_year) → false
→ organization:1$check_founding_year(founding_year) → true
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_department:1$check_budget(budget)
→ truecheck*{snapshot}*{schema*version}*{context}\_organization:2$check_founding_year(founding_year)
→ falsecheck*{snapshot}*{schema*version}*{context}\_organization:1$check_founding_year(founding_year)
→ trueModel
In this case, the part written as ‘context’ refers to the context within the request. Any type of data can be added from within the request and can be called within the model.
For instance,
Relationships
Attributes
Check request
Check Evolution Sub Queries Organization View → organization:1$check_ip_range(context.ip_address,ip_range) → true → organization:1#admin@user:1 → true
Cache Mechanism The cache mechanism works by hashing the snapshot of the database, schema version, and sub-queries as keys and adding their results, so it will operate in the same way in calls as in relationships. For example,
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_organization:1#admin@user:1
→ truecheck*{snapshot}*{schema*version}*{context}\_organization:1$check_ip_range(ip_range)
→ trueInstall Permify
Validation Yaml Structure
Note: The ‘data’ field within the ‘context’ can be assigned a desired value as a key-value pair. Later, this value can be retrieved within the model using ‘request.key’.
Example in validation file:
This YAML snippet specifies a validation context with no tuples or attributes, and a data field indicating the day of the week is Saturday.
Example in model
In the model, a delete
permission rule is set. It calls the function is_weekday
with the value of valid_weekdays
from the related entity. If is_weekday(["monday", "tuesday", "wednesday", "thursday", "friday"])
is true, the delete permission is granted.
Create Validation File
Run validation command
Our team is happy to help you get started with Permify. If you’d like to learn more about using Permify in your app or have any questions about this example, schedule a consultation call with one of our account executivess. Alternatively you can join our discord community to discuss.
This page explains the design approach of Permify’s ABAC support as well as demonstrates how to create and use attribute based permissions in Permify.
Attribute-Based Access Control (ABAC) is like a security guard that decides who gets to access what based on specific characteristics or “attributes”.
These attributes can be associated with users, resources, or the environment, and their values can influence the outcome of an access request.
Let’s make an analogy, it’s the best way to understand complex ideas.
Think about an amusement park, and there are 3 different rides. In order to access each ride, you need to have different qualities. For example:
Similar to this ABAC checks certain qualities that you have defined on users, resources, or the environment.
It’s obvious but the simple answer is “use cases”… Sometimes, using ReBAC and RBAC isn’t the best fit for the job. It’s like using winter tires on a hot desert road, or summer tires in a snowstorm - they’re just not the right tools for the conditions.
As you can see ABAC has a more contextual approach. You can define access rights regarding context around subjects and objects in an application.
To support ABAC in Permify, we’ve added two main components into our DSL: attributes and rules.
Attributes are used to define properties for entities in specific data types. For instance, an attribute could be an IP range associated with an organization, defined as a string array:
Here are the all attribute types that you use when defining an attribute
.
Rules are structures that allow you to write specific conditions for the model. You can think rules as simple functions of every software language have. They accept parameters and are based on condition to return a true/false result.
In the following example schema, a rule could be used to check if a given IP address falls within a specified IP range:
We design our schema language based on Common Expression Language (CEL). So the syntax looks nearly identical to equivalent expressions in C++, Go, Java, and TypeScript.
Please let us know via our Discord channel if you have questions regarding syntax, definitions or any operator you identify not working as expected.
Let’s examine some of common usage of ABAC with small schema examples.
For attributes that represent a binary choice or state, such as a yes/no question, the Boolean
data type is an excellent choice.
String can be used as attribute data type in a variety of scenarios where text-based information is needed to make access control decisions. Here are a few examples:
Integer can be used as attribute data type in several scenarios where numerical information is needed to make access control decisions. Here are a few examples:
Double can be used as attribute data type in several scenarios where precise numerical information is needed to make access control decisions. Here are a few examples:
In this example, is_public
is defined as a boolean attribute. If an attribute is a boolean, it can be directly used without the need for a rule. This is only applicable for boolean types.
In this context, if the is_public
attribute of the repository is set to true, everyone can view it. If it’s not public (i.e., is_public
is false), only the owner, in this case user:1
, can view it.
The permissions in this model are defined as such:
permission view = is_public or owner
This means that the ‘view’ permission is granted if either the repository is public (is_public
is true) or if the current user is the owner of the repository.
relationships:
attributes:
Check Evolution Sub Queries Post View → post:1#is_public → true → post:1#admin@user:1 → true
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_post:1$is_public
→ truecheck*{snapshot}*{schema*version}*{context}\_post:1#admin@user:1
→ trueIn this example, to be able to view the repository it must not be a weekend, and the user must be a member of the organization.
The permissions in this model state that to ‘view’ the repository, the user must fulfill two conditions: the current day (according to the context data day_of_week
) must not be a weekend (determined by the is_weekday
rule), and the user must be a member of the organization that owns the repository.
Relationships:
Check Evolution Sub Queries Organization View → organization:1$is_weekday(valid_weekdays) → true → organization:1#member@user:1 → true
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_organization:1$is_weekday(valid_weekdays)
→ truecheck*{snapshot}*{schema*version}*{context}\_post:1#member@user:1
→ trueThis model represents a banking system with two entities: user
and account
.
user
: Represents a customer of the bank.account
: Represents a bank account that has an owner
(which is a user
), and a balance
(amount of money in the account).The check_balance rule: This rule verifies if the withdrawal amount is less than or equal to the account’s balance and doesn’t exceed 5000 (the maximum amount allowed for a withdrawal). It accepts two parameters, the withdrawal amount (amount) and the account’s current balance (balance). The owner check: This condition checks if the person requesting the withdrawal is the owner of the account.
Both of these conditions need to be true for the withdraw
permission to be granted. In other words, a user can withdraw money from an account only if they are the owner of that account, and the amount they want to withdraw is within the account balance and doesn’t exceed 5000.
Relationships
Attributes
Check Evolution Sub Queries For Account Withdraw → account:1$check_balance(balance) → true → account:1#owner@user:1 → true
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_account:1$check_balance(balance)
→ truecheck*{snapshot}*{schema*version}*{context}\_account:1#owner@user:1
→ trueIn this model:
employee
: Represents an individual worker. It has no specific attributes or relations in this case.organization
: Represents an entire organization, which has a founding_year
attribute. The view
permission is granted if the check_founding_year
rule (which checks if the organization was founded after 2000) returns true.department
: Represents a department within the organization. It has a budget
attribute and a relation to its parent organization
. The view
permission is granted if the department’s budget is more than 10,000 (checked by the check_budget
rule) and if the organization.view
permission is true.Note: In this model, permissions can refer to higher-level permissions (like organization.view
). However, you cannot use the attribute of a relation in this way. For example, you cannot directly reference organization.founding_year
in a permission expression. Permissions can depend on permissions in a related entity, but not directly on the related entity’s attributes.
Relationships
Attributes
Check Evolution Sub Queries For Department View
→ department:1$check_budget(budget) → true
→ department:1#organization@user:1 → true → organization:2$check_founding_year(founding_year) → false
→ organization:1$check_founding_year(founding_year) → true
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_department:1$check_budget(budget)
→ truecheck*{snapshot}*{schema*version}*{context}\_organization:2$check_founding_year(founding_year)
→ falsecheck*{snapshot}*{schema*version}*{context}\_organization:1$check_founding_year(founding_year)
→ trueModel
In this case, the part written as ‘context’ refers to the context within the request. Any type of data can be added from within the request and can be called within the model.
For instance,
Relationships
Attributes
Check request
Check Evolution Sub Queries Organization View → organization:1$check_ip_range(context.ip_address,ip_range) → true → organization:1#admin@user:1 → true
Cache Mechanism The cache mechanism works by hashing the snapshot of the database, schema version, and sub-queries as keys and adding their results, so it will operate in the same way in calls as in relationships. For example,
Request keys before hash
check*{snapshot}*{schema*version}*{context}\_organization:1#admin@user:1
→ truecheck*{snapshot}*{schema*version}*{context}\_organization:1$check_ip_range(ip_range)
→ trueInstall Permify
Validation Yaml Structure
Note: The ‘data’ field within the ‘context’ can be assigned a desired value as a key-value pair. Later, this value can be retrieved within the model using ‘request.key’.
Example in validation file:
This YAML snippet specifies a validation context with no tuples or attributes, and a data field indicating the day of the week is Saturday.
Example in model
In the model, a delete
permission rule is set. It calls the function is_weekday
with the value of valid_weekdays
from the related entity. If is_weekday(["monday", "tuesday", "wednesday", "thursday", "friday"])
is true, the delete permission is granted.
Create Validation File
Run validation command
Our team is happy to help you get started with Permify. If you’d like to learn more about using Permify in your app or have any questions about this example, schedule a consultation call with one of our account executivess. Alternatively you can join our discord community to discuss.