Authorization Modeling

The first step in using Oso to power your app's authorization is laying out who is allowed to do what. We call this your authorization model, which you express as rules.

In Oso, rules center around resources. Resources are the things that you're protecting in your app; they typically correspond to database tables, API endpoints, and classes in your app.

The Rules Workbench is a visual rules editor. You should start there. Then, if you need to add anything custom, you can add custom rules by writing custom code in Polar, our declarative configuration language for authorization.

Get started with rules

The following tutorial walks through a common process for figuring out what components your authorization model needs.

To get started, identify a good example resource.

For example, in a project management application, users often interact with tasks. So we'll start by modelling who can access tasks.

Tasks belong to projects, and projects belong to organizations. These are all kinds of resources!

We'll represent each resource with a resource block


actor User { }
resource Organization {
# ...
}
resource Project {
# ...
}
resource Task {
# ...
}

As we said, tasks belong to projects, and projects belong to organizations.

These are relationships between our resources, and a super common part of most authorization logic. We want to be able to describe what projects people can interact with based on their organization, and what tasks they can interact with based on their project.

In a relational database model, these would typically be represented as foreign keys.

In Oso Cloud, we represent them with relations. Adding it to our resource block is declaring it as a thing that we want to use in our rules.


actor User { }
resource Organization {
# ...
}
resource Project {
relations = {
organization: Organization,
};
# ...
}
resource Task {
relations = {
project: Project,
};
# ...
}

This forms the beginning of our authorization logic.

Rules in Oso Cloud express what actions users can perform on those resources. The logic will always apply to a specific instance of a resource, but the logic is written abstractly.

For example, lets say that project admins can read all tasks that belong to that project.

The generic logic says: "A user can read a task if they are an admin on the project that the task belongs to." Note that we didn't have to specify which project or task we were talking about, that will happen when we add data.


actor User { }
resource Organization {
# ...
}
resource Project {
roles = ["admin"];
relations = {
organization: Organization,
};
# ...
}
resource Task {
permissions = ["read"];
relations = {
project: Project,
};
"read" if "admin" on "project";
# ...
}

A quick way to try out this logic with some example data is by writing a policy test.

Policy tests have a setup block that contains example data for a test case.

We've made alice an admin of a project, and then two tasks that belong to different projects.

Given this data, we can assert that alice can read the first task, but not the second.


resource Organization {
# ...
}
resource Project {
roles = ["admin"];
relations = {
organization: Organization,
};
# ...
}
resource Task {
permissions = ["read"];
relations = {
project: Project,
};
"read" if "admin" on "project";
# ...
}
test "project admins can read tasks" {
setup {
has_role(User{"alice"}, "admin", Project{"project-1"});
has_relation(Task{"task-1"}, "project", Project{"project-1"});
has_relation(Task{"task-2"}, "project", Project{"project-2"});
}
assert allow(User{"alice"}, "read", Task{"task-1"});
assert_not allow(User{"alice"}, "read", Task{"task-2"});
}

To get started, identify a good example resource.

For example, in a project management application, users often interact with tasks. So we'll start by modelling who can access tasks.

Tasks belong to projects, and projects belong to organizations. These are all kinds of resources!

We'll represent each resource with a resource block

As we said, tasks belong to projects, and projects belong to organizations.

These are relationships between our resources, and a super common part of most authorization logic. We want to be able to describe what projects people can interact with based on their organization, and what tasks they can interact with based on their project.

In a relational database model, these would typically be represented as foreign keys.

In Oso Cloud, we represent them with relations. Adding it to our resource block is declaring it as a thing that we want to use in our rules.

This forms the beginning of our authorization logic.

Rules in Oso Cloud express what actions users can perform on those resources. The logic will always apply to a specific instance of a resource, but the logic is written abstractly.

For example, lets say that project admins can read all tasks that belong to that project.

The generic logic says: "A user can read a task if they are an admin on the project that the task belongs to." Note that we didn't have to specify which project or task we were talking about, that will happen when we add data.

A quick way to try out this logic with some example data is by writing a policy test.

Policy tests have a setup block that contains example data for a test case.

We've made alice an admin of a project, and then two tasks that belong to different projects.

Given this data, we can assert that alice can read the first task, but not the second.


actor User { }
resource Organization {
# ...
}
resource Project {
# ...
}
resource Task {
# ...
}

More details

To learn more about modeling in Polar, checkout these guides: