Skip to content

Instantly share code, notes, and snippets.

@claustres
Last active August 4, 2017 15:47
Show Gist options
  • Save claustres/099d55c0cced7550eefe21c21af2eb58 to your computer and use it in GitHub Desktop.
Save claustres/099d55c0cced7550eefe21c21af2eb58 to your computer and use it in GitHub Desktop.
Dynamic abilities
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