Roles and ACL definition
Let's say we are defining two roles: a public role, which can see all visible comments, and a moderator role, who can edit and hide comments of given article in assigned category.
A simplified model definition for this example will look like this:
export class Category {
// ....
}
export class Article {
category = def.manyHasOne(Category)
// ....
}
export class Comment {
article = def.manyHasOne(Article)
content = def.stringColumn()
hiddenAt = def.dateTimeColumn()
}
We are using high level ACL definition API. For some cases, you might prefer a low level definition API
Public role definition
First, we create a public role using createRole function.
There is only single mandatory argument - a role identifier. In second argument, we can define various role options, as described here.
import { AclDefinition as acl } from '@contember/schema-definition'
export const publicRole = acl.createRole('public')
Second, we assign an access rule to a Comment
entity using allow function.
In a first argument of the function we pass previously defined role. Second argument is an object with the access definition itself.
In when
we define a predicate. In read
there is an array of accessible fields. You can also use true
instead of an array to make all fields accessible:
@acl.allow(publicRole, {
read: ['content'],
when: { hiddenAt: { isNull: true } },
})
export class Comment {
// ...
}
That's all. Now, if you access the API with public
role, you can see not hidden Comment
rows, and you can access its content
field.
Moderator role definition
Now, we define a second mentioned role - a moderator
. Again, we define a role:
export const moderatorRole = acl.createRole('moderator')
Now it gets a bit more tricky, as we want to allow to only moderate comments in given category.
Let's define an entity variable, where a category ID (or a list of categories) will be stored for given user.
export const categoryIdVariable = acl.createEntityVariable('categoryId', 'Category', moderatorRole)
You can manage this variable on memberships using Tenant API using its name - categoryId
.
Now we attach another ACL definition to our Comment
entity:
@acl.allow(moderatorRole, {
update: ['hiddenAt', 'content'],
when: { article: { category: { id: categoryIdVariable } } },
})
// other ACL definitions
export class Comment {
// ...
}
As you can see, you can traverse through relations. Our definition says, that moderator
can update fields hiddenAt
and content
of any Comment
of an Article
in a Category
defined in categoryId
variable.
Don't forget to create a migration to apply changes:
npm run contember migrations:diff my-blog setup-acl
Full example:
import { SchemaDefinition as def, Acldefinition as acl } from '@contember/schema-definition'
export const publicRole = acl.createRole('public')
export const moderatorRole = acl.createRole('moderator')
export const categoryIdVariable = acl.createEntityVariable('categoryId', 'Category', moderatorRole)
export class Category {
// ....
}
export class Article {
category = def.manyHasOne(Category)
// ....
}
@acl.allow(moderatorRole, {
when: { article: { category: { id: categoryIdVariable } } },
update: ['hiddenAt', 'content'],
})
@acl.allow(publicRole, {
when: { hiddenAt: { isNull: true } },
read: ['content'],
})
export class Comment {
article = def.manyHasOne(Article)
content = def.stringColumn()
hiddenAt = def.dateTimeColumn()
}