User-Resource Relationships

Many applications want to grant special permissions based on a relationship between a user and a resource, such as allowing a repository's creator to delete it. In Oso Cloud, these relationships are encoded as relations between the user type and the resource type.

It's not always obvious when to reach for a relation instead of a role. As a general rule of thumb, you should use relations when representing something that is a core concept of your application and roles when representing something that is assigned to a user exclusively to grant them access.

Implement the logic

In our example, an issue's creator is allowed to update and close the issue.

To contrast that relation with a role, repository maintainers are granted the "admin" role on all issues, which also grants them permission to close issues.


actor User { }
resource Repository {
roles = ["maintainer"];
}
resource Issue {
roles = ["reader", "admin"];
permissions = ["read", "comment", "update", "close"];
relations = { repository: Repository, creator: User };
# repository maintainers can administer issues
"admin" if "maintainer" on "repository";
"reader" if "admin";
"reader" if "creator";
"read" if "reader";
"comment" if "reader";
"update" if "creator";
"close" if "creator";
"close" if "admin";
}

Test it out

Our logic says that issue creators can close and update their own issues and repository maintainers can close any issue.


test "issue creator can update and close issues" {
setup {
has_relation(Issue{"537"}, "repository", Repository{"anvil"});
has_relation(Issue{"42"}, "repository", Repository{"anvil"});
has_relation(Issue{"537"}, "creator", User{"alice"});
}
assert allow(User{"alice"}, "close", Issue{"537"});
assert allow(User{"alice"}, "update", Issue{"537"});
assert_not allow(User{"alice"}, "close", Issue{"42"});
}
test "repository maintainers can close issues" {
setup {
has_relation(Issue{"537"}, "repository", Repository{"anvil"});
has_relation(Issue{"42"}, "repository", Repository{"anvil"});
has_relation(Issue{"537"}, "creator", User{"alice"});
has_role(User{"bob"}, "maintainer", Repository{"anvil"});
}
assert allow(User{"bob"}, "close", Issue{"537"});
assert_not allow(User{"bob"}, "update", Issue{"537"});
assert allow(User{"bob"}, "close", Issue{"42"});
}