TypeDB Blog
Identity and access management with TypeDB: Part I, polymorphism
The rise of remote work culture, machine identities and cloud infrastructure has led to new business requirements which traditional identity and access management (IAM) platforms are struggling to meet. This is, in no small part, because they’re built on relational databases.
While standards have been defined to facilitate the adoption of IAM platforms, designing an IAM platform that can support a broad variety of applications, resources and permissions and enforce sophisticated business policies, both now and in the future, is a daunting task.
In this blog series, we’re going to show how TypeDB can meet these requirements with a powerful yet lean data model and intuitive query design. The primary focus will be on a role-based access control (RBAC) architecture, but our approach can be easily adapted to other frameworks.
First, let’s start with modeling polymorphic permissions. In the next blog, we’ll explain how to automate permission inheritance, and in the third one, we’ll see how policy compliance can be enforced at the schema level. If you want to see it in action first, feel free to run the interactive demo.
The first hurdle is defining a general data model for permissions, also known as entitlements. IAM platforms might need to model any of the following situations in order to determine if an attempt to access a system resource should be permitted or not:
- Users having permission to access their department’s files on the company server, for instance the Engineering Department.
- The Finance Department having permission to approve purchase orders via the company’s online finance portal.
- Web admins having permission to push changes to the company website via a version control system.
- The Support Team having permission to view data records relating to customers that have submitted support tickets they’ve been assigned.
- Users having access to the sales email address have permission to view and make changes to customer accounts via the company’s on-premises account management system.
- A contractor having permission to view certain files, for instance those in the Cloud repository, for the duration of their contract.
All of these situations can be broadly summarized under the statement:
“
<subject>
having permission to perform<action>
on<object>
”
This is a form of polymorphism, where different types of data can fulfill the same roles. While the statement is very simple conceptually, modeling polymorphic subjects, objects, and actions is difficult using a relational database, and impossible without sacrificing certain constraints. As a result, the physical data model ends up looking very different than the logical data model, which makes it difficult to build and maintain.
On the other hand, the TypeDB implementation is simple and elegant because the physical data model is identical to the logical one. We simply define our subject
, object
, and action
types, and any needed subtypes:
define
subject sub entity, abstract;
object sub entity, abstract;
action sub entity, owns name;
contractor sub subject, owns email;
business-unit sub subject, owns name;
user-role sub subject, owns name;
user-account sub subject, owns email;
directory sub object, owns path;
application sub object, owns name;
repository sub object, owns name;
database sub object, owns name;
id sub attribute, abstract, value string;
path sub id;
name sub id;
email sub id;
For the moment, each subject and object has a single attribute so we can identify it, but we can add as many as necessary later on. Next, we define permission
as a ternary (i.e. ‘3-way’) relation between a subject
, object
, and action
, along with its permitted role players:
define
permission sub relation,
relates subject,
relates object,
relates action;
subject plays permission:subject;
object plays permission:object;
action plays permission:action;
Here’s the schema diagram rendered by the graph visualizer in TypeDB Studio:
Thanks to TypeDB’s subtyping, we don’t have to define all the different subtypes of subject
and object
as playing roles in permission. Finally, we load all of our subjects, objects, and actions into the database, then match the ones we’re interested in and insert the appropriate permissions between them:
match
$s isa business-unit, has name "Engineering";
$o isa directory, has path "root/engineering";
$a isa action, has name "manage directory";
insert
($s, $o, $a) isa permission;
match
$s isa business-unit, has name "Finance";
$o isa application, has name "Sage";
$a isa action, has name "approve order";
insert
($s, $o, $a) isa permission;
match
$s isa user-role, has name "web admin";
$o isa repository, has name "web";
$a isa action, has name "push commit";
insert
($s, $o, $a) isa permission;
match
$s isa business-unit, has name "Customer Support";
$o isa database, has name "customers";
$a isa action, has name "select record";
insert
($s, $o, $a) isa permission;
match
$s isa user-account, has email "sales@vaticle.com";
$o isa application, has name "Salesforce";
$a isa action, has name "modify account";
insert
($s, $o, $a) isa permission;
match
$s isa contractor, has email "james.holden@rocinante-consulting.com";
$o isa repository, has name "cloud";
$a isa action, has name "view file";
insert
($s, $o, $a) isa permission;
Each of these queries inserts one of the example permissions described earlier, and we’ve used the same query format for each one. In addition to subtyping, we’ve taken advantage of type inference too: when we insert a permission, we don’t have to state which entity plays which role, as the schema provides all the context necessary for the database to figure it out.
It’s now possible to query all of the data we inserted with a single polymorphic query:
match
$s isa subject, has id $s-id;
$o isa object, has id $o-id;
$a isa action, has name $n;
$p ($s, $o, $a) isa permission;
Here’s what we see when we run this query in TypeDB Studio:
This is a pretty good first pass, but there’s plenty of room for improvement. For instance, it’s currently possible to execute the following query:
match
$s isa business-unit, has name "Engineering";
$o isa application, has name "Salesforce";
$a isa action, has name "push commit";
insert
($s, $o, $a) isa permission;
This doesn’t make sense as it’s impossible to push a commit to the Salesforce application. In order to prevent stuff like this, we’ll extend the schema to introduce a new relation type called access
:
define
access sub relation,
relates object,
relates action;
object plays access:object;
action plays access:action;
permission sub relation,
relates subject,
relates access;
subject plays permission:subject;
access plays permission:access;
Now, instead of inserting a permission as a ternary relation between a subject, object, and action, we now insert an access relation between the object and action, followed by a permission relation between the subject and access. This is possible because TypeDB is formally backed by the theory of dependent types, the logic of the 21st century, which allows one relation to play a role in another: in this case the access relation in the permission relation. With this change, whenever we insert a new object, we insert accesses between it and every valid action that can be performed on it. Then, we can only insert permissions where an access already exists, preventing nonsensical permissions from being added to the database:
match
$s isa business-unit, has name "Engineering";
$o isa directory, has path "root/engineering";
$a isa action, has name "manage directory";
$ac ($o, $a) isa access;
insert
($s, $ac) isa permission;
match
$s isa business-unit, has name "Finance";
$o isa application, has name "Sage";
$a isa action, has name "approve order";
$ac ($o, $a) isa access;
insert
($s, $ac) isa permission;
match
$s isa user-role, has name "web admin";
$o isa repository, has name "web";
$a isa action, has name "push commit";
$ac ($o, $a) isa access;
insert
($s, $ac) isa permission;
match
$s isa business-unit, has name "Customer Support";
$o isa database, has name "customers";
$a isa action, has name "select record";
$ac ($o, $a) isa access;
insert
($s, $ac) isa permission;
match
$s isa user-account, has email "sales@vaticle.com";
$o isa application, has name "Salesforce";
$a isa action, has name "modify account";
$ac ($o, $a) isa access;
insert
($s, $ac) isa permission;
match
$s isa contractor, has email "james.holden@rocinante-consulting.com";
$o isa repository, has name "cloud";
$a isa action, has name "view file";
$ac ($o, $a) isa access;
insert
($s, $ac) isa permission;
To query our new data we can use:
match
$s isa subject, has id $s-id;
$o isa object, has id $o-id;
$a isa action, has name $n;
$ac ($o, $a) isa access;
$p ($s, $ac) isa permission;
Because of TypeQL’s declarative and composable nature, we can always query exactly what we want. If we’re just interested in finding permissions that web admins have, we can use the query:
match
$s isa user-role, has name "web admin";
$o isa object, has id $o-id;
$a isa action, has name $n;
$ac ($o, $a) isa access;
$p ($s, $ac) isa permission;
If we want to know who can push commits on our repos, we can use:
match
$s isa subject, has id $s-id;
$o isa repository;
$a isa action, has name "push commit";
$ac ($o, $a) isa access;
$p ($s, $ac) isa permission;
If we want to know who can push commits to our web repo specifically, we can use:
match
$s isa subject, has id $s-id;
$o isa repository, has name "web";
$a isa action, has name "push commit";
$ac ($o, $a) isa access;
$p ($s, $ac) isa permission;
And if we want to find out if our contractor James Holden can push commits to web then we just use:
match
$s isa contractor, has email "james.holden@rocinante-consulting.com";
$o isa repository, has name "web";
$a isa action, has name "push commit";
$ac ($o, $a) isa access;
$p ($s, $ac) isa permission;
In this blog post, we’ve seen how we can build a flexible type hierarchy for modeling permissions in an IAM platform. In the next posts in this blog series, we’ll explore how to automate permission inheritance and enforce policy compliance at the schema level. In the meantime, you can try it out yourself by running our interactive demo, or learn more out about building IAM platforms on TypeDB by reading our white paper.