Modeling Authorization
In Permify, you can define that a user has certain permissions because of their relation to other entities. An example of this would be granting a manager the same permissions as their subordinates, or giving a user access to a resource because they belong to a certain group.
This is facilitated by our relationship-based access control, which allows the definition of complex permission structures based on the relationships between users, roles, and resources.
Permify Schema
Permify has its own language that you can model your authorization logic with it. The language allows to define arbitrary relations between users and objects, such as owner, editor, commenter or roles like admin, manager, member and also dynamic attributes such as boolean variables, IP range, time period, etc.
You can define your entities, relations between them and access control decisions with using Permify Schema. It includes set-algebraic operators such as intersection and union for specifying potentially complex access control policies in terms of those user-object relations.
Here’s a simple breakdown of our schema.
Permify Schema can be created on our playground as well as in any IDE or text editor. We also have a VS Code extension to ease modeling Permify Schema with code snippets and syntax highlights. Note that on VS code the file with extension is “.perm”.
Developing a Schema
This guide will show how to develop a Permify Schema from scratch with a simple example, yet it will show almost every aspect of our modeling language.
We’ll follow a simplified version of the GitHub access control system, where teams and organizations have control over the viewing, editing, or deleting access rights of repositories.
Before start I want to share the full implementation of simple Github access control example with using Permify Schema.
You can start developing Permify Schema on VSCode. You can install the extension by searching for Perm in the extensions marketplace.
Defining Entities
The very first step to build Permify Schema is creating your Entities. Entity is an object that defines your resources that held role in your permission system.
Think of entities as tables in your database. We are strongly advice to name entities same as your database table name that its corresponds. In that way you can easily model and reason your authorization as well as eliminating the error possibility.
You can create entities using entity
keyword. Let’s create some entities according to our example GitHub authorization logic.”
Entities has 2 different attributes. These are;
- relations
- actions or permissions
Defining Relations
Relations represent relationships between entities. It’s probably the most critical part of the schema because Permify mostly based on relations between resources and their permissions.
Keyword relation need to used to create a entity relation with name and type attributes.
Relation Attributes:
- name: relation name.
- type: relation type, basically the entity it’s related to (e.g. user, organization, document, etc.)
An example relation takes form of,
Lets turn back to our example and define our relations inside our entities:
User Entity
→ The user entity is a mandatory entity in Permify. It generally will be empty but it will used a lot in other entities as a relation type to referencing users.
Roles and User Types
You can define user types and roles within the entity. If you specifically want to define a global role, such as admin
, we advise defining it at the entity with the most global hierarchy, such as an organization. Then, spread it to the rest of the entities to include it within permissions.
For the sake of simplicity, let’s define only 2 user types in an organization, these are administrators and direct members of the organization.
Parent-Child Relationship
→ Let’s say teams can belong organizations and can have a member inside of it as follows,
The parent relation is indicating the organization the team belongs to. This way we can achieve parent-child relationship within these entities.
Ownership
In Github workflow, organizations and users can have multiple repositories, so each repository is related with an organization and with users. We can define repository relations as as follows.
The owner relation indicates the creator of the repository, that way we can achieve ownership in Permify.
Multiple Relation Types
As you can see we have new syntax above,
When we look at the maintainer relation, it indicates that the maintainer can be an user
as well as this user can be a team member
.
You can use # to reach entities relation. When we look at the @team#member
it specifies that if the user has a relation with the team, this relation can only be the member
. We called that feature locking, because it basically locks the relation type according to the prefixed entity.
Actual purpose of feature locking is to giving ability to specify the sets of users that can be assigned.
For example:
When you define it like this, you can only add users directly as tuples (you can find out what relation tuples is in next section):
- organization:1#viewer@user:U1
- organization:1#viewer@user:U2
However, if you define it as:
You will then be able to specify not only individual users but also members of an organization:
- organization:1#viewer@user:U1
- organization:1#viewer@user:U2
- organization:1#viewer@organization:O1#member
With organization:1#viewer@organization:O1#member
all members of the organization O1 will have the right to perform the relevant action.
In other words, all members in O1 now end up having the relevant viewer
relation.
You can think of these definitions as a precaution taken against creating undesired user set relationships.
Defining multiple relation types totally optional. The goal behind it to improve validation and reasonability. And for complex models, it allows you to model your entities in a more structured way.
Defining Permissions
Actions describe what relations, or relation’s relation can do. Think of actions as permissions of the entity it belongs. So actions defines who can perform a specific action on a resource in which circumstances.
The basic form of authorization check in Permify is Can the user U perform action X on a resource Y ?.
Intersection and Exclusion
The Permify Schema supports and
, or
and not
operators to achieve permission intersection and exclusion. The keywords action or permission can be used with those operators to form rules for your authorization logic.
Intersection
Lets get back to our github example and create a read action on repository entity to represent usage of and
&, or
operators,
→ If we examine the read
action rules; user that is organization admin
and following users can read the repository: owner
of the repository, or maintainer
, or member
of the organization which repository belongs to.
The same read
can also be defined using the permission keyword, as follows:
Using action
and permission
will yield the same result for defining permissions in your authorization logic. See why we have 2 keywords for defining an permission from the Nested Hierarchies section.
It’s important to understand the nature of the and
operation: it creates an intersection between relations but is not necessarily tied to specific entities.
For example, in the following model, users can see a repository if they are a member or admin of any organization.
Let’s say user:1
is a member of organization:1
and an admin of organization:2
. If repository:1
belongs to organization:1
(via the parent relationship), then user:1
is granted access to edit repository:1
.
Exclusion
After this point, we’ll move beyond the GitHub example and explore more advanced abilities of Permify DSL.
Before delving into details, let’s examine the not
operator and conclude Intersection and Exclusion section.
Here is the post entity from our sample Instagram Authorization Structure example,
As you can see from the comment and like actions, a user tagged with the restricted
attribute — details of defining attributes can be found in the Attribute Based Permissions (ABAC) section — won’t be able to like or comment on the specific post.
This is a simple example to demonstrate how you can exclude users, resources, or any subject from permissions using the not
operator.
Permission Union
Permify allows you to set permissions that are effectively the union of multiple permission sets.
You can define permissions as relations to union all rules that permissions have. Here is an simple demonstration how to achieve permission union in our DSL, you can use actions (or permissions) when defining another action (or permission) like relations,
The delete
action inherits the rules from the edit
action. By doing that, we’ll be able to state that only organization administrators and any relation capable of performing the edit action (member or manager) can also perform the delete action.
Permission union is super beneficial in scenarios where a user needs to have varied access across different departments or roles.
Let’s examine our modeling guides for common permission use cases.
Attribute Based Permissions (ABAC)
To support Attribute Based Access Control (ABAC) in Permify, we’ve added two main components into our schema language: attribute
and rule
.
Defining Attributes
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
.
Defining Rules
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:
Using Attributes Across Entities
This feature is currently in beta and available upon request. Let us know, and we’ll give you early access!
You can use attributes across entities with rules. Specifically, rules can be written inside entities to create entity-specific conditioning.
In the example below, we defined a check_confidentiality rule inside the organization entity. This rule is responsible for comparing the specific organization’s authority level with the given authority level.
If the organization’s authority is higher than the given authority, it will return true.
As shown in the example, we used this rule in the edit
permission of the repository to check whether the organization:X has edit access or not, depending on the authority level it possess.
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.
Modeling Guides
Let’s examine our modeling guides for common permission use cases.
Role-Based Access Control (RBAC)
Relationship Based Access Control (ReBAC)
Attribute Based Access Control (ABAC)
More Comprehensive Examples
You can check out more comprehensive schema examples from the Real World Examples section.
Here is what each example focuses on,
- Google Docs: how users can gain direct access to a document through organizational roles or through inherited/nested permissions.
- Facebook Groups: how users can perform various actions based on the roles and permissions within the groups they belong.
- Notion: how one global entity (workspace) can manage access rights in the child entities that belong to it.
- Instagram: how public/private attributes play role in granting access to specific users.
- Mercury: how attributes and rules interact within the hierarchical relationships.