Gracl
All Hands, May 2016
An attempt at graph-based permissions
- Motivation
-
tyranid-gracl
- Jargon
- Algorithm
Motivation
Engineering
Product
A
B
C
Want to let Product access everyone in Engineering, except for user B
Motivation
Engineering
Product
A
B
C
old permissions model, basic user ACL
Motivation
Engineering
Product
A
B
C
New model - expressing same permissions more concisely
Motivation
-
Inheritance based on database model structure
-
Allow / deny at different levels of specificity
What we basically want
tyranid-gracl
- repo located here
- in progress documentation
- plugin for Tyranid
- written in TypeScript
tyranid-gracl
: Jargon
- Subject / Resource
- Subject / Resource Hierarchy
- Permissions Hierarchy
- CRUD / Abstract Permissions
Subject:
"The thing / person doing the accessing"
Accessing
Sandy can view twitter, Sandy is the subject
Jargon
Resource:
"The thing / person being accessed"
Accessing
Everyone in twitter can view Sandy's tweets, Sandy is now the resource
Jargon
Subject Hierarchy
Sandy has access to twitter (resource) through the subject hierarchy, because her organization, and transitively her team, was given access to twitter.
Organization (subject)
Teams (subjects)
Sandy
This allows access to cascade to lower level subjects
Users (subjects)
Twitter (resource)
Jargon
Resource Hierarchy
Sandy has access to Bill's photos (resources) through the resource hierarchy, because she can access Bill.
Bill (resource)
This means that access to an "upper level" resource implies access to "lower level" resources.
Bill's Photos (resources)
Sandy (subject)
Jargon
Permissions Hierarchy
own
edit
view
implies
implies
own
own
Bill owns his photos, so he can implicitly edit and view them
Jargon
CRUD / Abstract
own
edit
view
delete
view_network_map
view_communications_metadata
Permissions that are relevant to Tyranid collections (without needing to specify the collection) -- used internally by Tyranid for automated authentication
Permissions that aren't related to specific collections, but refer to concepts
Jargon
CRUD / Abstract
[
// generic crud permissions for filtering (not abstract)
{ name: 'view', abstract: false, parents: [ 'edit', 'delete' ] },
{ name: 'edit', abstract: false, parent: 'own' },
{ name: 'delete', abstract: false, parent: 'own' },
{ name: 'own', abstract: false },
// view-org -> view-group -> view-user
{ name: 'view-user', collection: true, parents: [ 'view-group' ] },
{ name: 'view-group', collection: true, parents: [ 'view-organization' ] },
// network permissions
{ name: 'view_network_map_people', abstract: true },
{ name: 'view_network_map_plans', abstract: true },
{ name: 'view_network_ona_team', abstract: true, parent: 'view_network_ona_individual' },
{ name: 'view_network_ona_individual', abstract: true },
]
Jargon
Permission Document
/**
* (Permissions model can be accessed via Tyr.secure.permissionsModel)
Example Document
*/
{
"_id" : ObjectId("572e75b37b81c2399c1033bc"),
"resourceId" : "o00560c1d577a9c381622ea7b7b",
"subjectId" : "u005567f2ab387fa974fc6f3a70",
"subjectType" : "user",
"resourceType" : "organization",
"access" : {
"edit-user" : true,
"edit-organization" : false,
"edit-group" : true,
"edit-dataAdapter" : true,
"edit_permissions" : true
}
}
Jargon
Subject hierarchy
Resource hiearchy
Users
Teams
Organizations
Organizations
Teams
Users
Items
Algorithm
view?
What if we want to check if a user can view a given component?
const canView = await item.$isAllowed('view-triangleLayerItem', user);
Algorithm
Algorithm
view?
Subject hierarchy
Resource hiearchy
Check 1 / ?
First check, does a permission document exist directly relating the user to the component?
Algorithm
view?
Subject hierarchy
Resource hiearchy
Check 2 / ?
Next, recurse up the subject Hierarchy, checking if the users teams have a relevant permission, returning if deny or allow is found.
Algorithm
view?
Subject hierarchy
Resource hiearchy
Check 3 / ?
Recurse again to the users organization if no deny or allow has been found
Algorithm
view?
Subject hierarchy
Resource hiearchy
Check 4 / ?
If no permissions were found
anywhere in the subject hierarchy for the component, advance one step up the resource hierarchy and recurse up the subject hierarchy again if necessary,
Algorithm
view?
Subject hierarchy
Resource hiearchy
Check 5 / 5
Finally! we found a permission, which gave the users team access to the components owner via an explicit allow.
allow
Algorithm
Note, if we didn't find any permissions set anywhere in the hierarchy, we would start the whole process again with a higher permission in the permissions hierarchy -- "edit" for example.
Thus, for p permissions, R resources, and S subjects in the chain we get worst case time complexity = O(pRS)
Algorithm
Subject hierarchy
Resource hiearchy
deny
Note, the order of recursion allows lower level denies to take precidence
In this case, even though the users team is allowed, the user does not have access, as their org is denied.
Automated Security within Tyranid
const filteredItems = await Tyr
.byName
.triangleLayerItem
.findAll({
query: {
groupId: {
$in: desiredGroups
}
},
auth: req.user
});
What if we want to find all components a user can view?
findAll( )
Automated Security within Tyranid
findAll( )
query(col, perm, auth) {
permissions = <subjectId in auth SH and resourceType in col RH>
resources = Map<resourceType, permissions>
finalQuery = {}
for ([resourceType, permissions] of resources) {
path = findPathBetween(col, resourceType)
docs = Tyr.byName[resourceType].findAll(map(permissions, resourceId))
for (intermediate_col of path) {
// walk documents through link path
docs = intermediate_col.findAll(docs.link_to_intermediate_col_field);
}
if (permissions.allow) addInclude(finalQuery, map(docs, 'id'));
if (permissions.deny) addExclude(finalQuery, map(docs, 'id'));
}
return finalQuery
}
Conclusions
- Inheritance is powerful, but can be dangerous
- Try to use inheritance when possible instead of adding more permissions
- TypeScript is amazing
Graph based Permissions
By Ben Southgate
Graph based Permissions
- 1,624