Last active
August 4, 2017 15:47
-
-
Save claustres/099d55c0cced7550eefe21c21af2eb58 to your computer and use it in GitHub Desktop.
Dynamic abilities
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Permissions = { | |
member: 0, | |
manager: 1, | |
owner: 2 | |
} | |
const Scopes = ['organisations', ...] | |
// Construct abilities for a given subject on a resource based on its permissions | |
function defineResourceRules (subject, resource, resourceService, can) { | |
const permissions = Permissions[resource.permissions] | |
// Members can read the target resource (eg organisation) and assets | |
if (permissions >= Permissions.member) { | |
can('read', resourceService, { _id: resource._id.toString() }) | |
can('read', 'assets') // No conditions needed as the assests servie is tied to the resource DB | |
} | |
// Managers can update the target resource (eg organisation) and manage authorisations/assets | |
if (permissions >= Permissions.manager) { | |
can('update', resourceService, { _id: resource._id.toString() }) | |
can('manage', 'authorisations', { resource: resource._id.toString() }) | |
can('manage', 'assets') // No conditions needed as the assests servie is tied to the resource DB | |
} | |
// Owners can remove the target resource (eg organisation) | |
if (permissions >= Permissions.owner) { | |
can('remove', resourceService, { _id: resource._id.toString() }) | |
} | |
} | |
// Compute abilities for a given subject (eg user) | |
function defineAbilitiesForSubject (subject) { | |
const { rules, can, cannot } = AbilityBuilder.extract() | |
// Anonymous rules = register | |
can('create', 'users') | |
if (subject) { | |
// Registered user basic rule : read/update profile | |
can(['update', 'remove'], 'users', { _id: subject._id.toString() }) | |
// Then compute abilities from user' scopes | |
Scopes.forEach(scope => { | |
// Create new resource in this scope (eg organisations) | |
can('create', scope) | |
// And manage it according to permissions | |
subject[scope].forEach(resource => defineResourceRules(subject, resource, can)) | |
}) | |
} | |
// CASL cannot infer the object type from the object itself so we need | |
// to tell it how he can find the object type, i.e. service name | |
return new Ability(rules, { subjectName: resource => { | |
if (!resource || typeof resource === 'string') { | |
return resource | |
} | |
return resource.type | |
}}) | |
} | |
// Check abilities of a given subject on a given resource | |
function hasAbilities (abilities, action, resource, serviceName) { | |
resource.type = serviceName | |
const result = abilities.can(action, resource) | |
// Not required anymore | |
delete resource.type | |
return result | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment