.can
The .can method is the bread and butter of lonamic. By default, can
returns a promise. This encourages developers to use the same coding style for all roles, and allows the developer to ignore whether some role is implemented with concurrency or not.
Like everything in lonamic, can
can be customized by passing in a .canHandler function into the acl constructor. (To see an example of this, look at the source of core.js. Internally, lonamic passes the can
method to lonamic in the exact same way you would if you made a custom function.) Although customization is possible, lonamic should work for the majority of use cases out of the box.
First, let's define a simple acl to show the functionality of can
:
const Lonamic = require('lonamic').lonamic
const roles = {
'user': {
can: ['posts:read']
},
'author': {
can: [{
name: 'posts:edit',
when ({ params }, next) {
setTimeout(() => {
// fake concurrency
next(null, params.post.author.id === params.author.id)
})
}
}],
inherits: ['user']
}
}
const lonamic = Lonamic(roles)
const posts = [
{ author: { id: '1' } },
{ author: { id: '2' } }
]
const authors = [
{ id: '1' },
{ id: '2' }
]
The above acl defines two roles, user
and author
. Users can read posts, and authors can edit posts if they are the ones who wrote it, and inherit all user permissions. We also define some posts and authors to verify that lonamic is correctly discerning when an author can and cannot edit a post.
Simple Can
For simple can methods, you can use the signature lonamic.can(roleId, action)
, where actions are some permission defined in the acl:
async function simpleCan() {
const [a, b, c, d] = await Promise.all([
lonamic.can('user', 'posts:read'), // true
lonamic.can('user', 'posts:edit'), // false
lonamic.can('user', 'undefined'), // false
lonamic.can('author', 'posts:read') // true (via inheritance)
])
console.log(a, b, c, d)
}
simpleCan()
Complex (when) Can
For complex can
methods, you should use the signature lonamic.can(roleId, action, params)
:
async function complexCan() {
const [postA, postB] = posts
const [authorA, authorB] = authors
const [a, b, c, d] = await Promise.all([
// true
lonamic.can('author', 'posts:edit', { post: postA, author: authorA }),
// true
lonamic.can('author', 'posts:edit', { post: postB, author: authorB }),
// false
lonamic.can('author', 'posts:edit', { post: postA, author: authorB }),
// false
lonamic.can('author', 'posts:edit', { post: postB, author: authorA })
])
console.log(a, b, c, d)
}
complexCan()
or, if you prefer the callback signature (lonamic.can(roleId, action, params, cb)
):
lonamic.can('author', 'posts:edit', {
post: posts[0],
author: authors[0]
}, function (err, can) {
console.log(can) // true
})
Full Example
const Lonamic = require('lonamic').lonamic
const roles = {
'user': {
can: ['posts:read']
},
'author': {
can: [{
name: 'posts:edit',
when({ params }, next) {
setTimeout(() => {
// fake concurrency
next(null, params.post.author.id === params.author.id)
}, 100)
}
}],
inherits: ['user']
}
}
const lonamic = Lonamic(roles)
const posts = [
{ author: { id: '1' } },
{ author: { id: '2' } }
]
const authors = [
{ id: '1' },
{ id: '2' }
]
async function simpleCan() {
const [a, b, c, d] = await Promise.all([
lonamic.can('user', 'posts:read'), // true
lonamic.can('user', 'posts:edit'), // false
lonamic.can('user', 'undefined'), // false
lonamic.can('author', 'posts:read') // true (via inheritance)
])
console.log(a, b, c, d)
}
simpleCan()
async function complexCan() {
const [postA, postB] = posts
const [authorA, authorB] = authors
const [a, b, c, d] = await Promise.all([
// true
lonamic.can('author', 'posts:edit', { post: postA, author: authorA }),
// true
lonamic.can('author', 'posts:edit', { post: postB, author: authorB }),
// false
lonamic.can('author', 'posts:edit', { post: postA, author: authorB }),
// false
lonamic.can('author', 'posts:edit', { post: postB, author: authorA })
])
console.log(a, b, c, d)
}
complexCan()
lonamic.can('author', 'posts:edit', {
post: posts[0],
author: authors[0]
}, function (err, can) {
console.log(can) // true
})