Permissions and rules
Every model says which roles may do what with its records. Evlet checks this on every read and every write, from the browser, the API and every other caller.
#Roles and rules
Each user has a role. The common roles are admin, editor and viewer. A model lists what each role may do in its permissions:
from evlet import Model, Rule
class Customer(Model):
permissions = {
'admin': Rule(('read', 'create', 'update', 'delete')),
'editor': Rule(('read', 'create', 'update')),
'viewer': Rule(('read',)),
}The rules:
- Default deny. A model with
permissionsgives nothing to a role it does not name. - Operations.
read,create,updateanddelete. Writing needs read access too. - Every caller. The list page, the record page, the API, actions, attachments and Link values all check the same rules.
A child table follows its document. Someone who may read the invoice may read its lines. Someone who may update the invoice may add, change and remove lines.
#Limit fields
A rule can name the fields a role may read and write. Leave a list out to mean every field. An empty tuple means no fields.
permissions = {
'admin': Rule(('read', 'create', 'update', 'delete')),
'editor': Rule(('read', 'update'), write_fields=('email', 'phone')),
'viewer': Rule(('read',), read_fields=('customer_name', 'active')),
}With these rules:
- An editor may change a customer's phone number. Changing the customer's name fails with a 403:
One or more fields cannot be written. - A viewer sees only
id,customer_nameandactive. The other fields are left out of every answer, not just hidden on screen.
#Limit rows with a scope
A scope is a function that takes the user and returns a filter. The role then sees and changes only the rows that match:
class Deal(Model):
permissions = {
'admin': Rule(('read', 'create', 'update', 'delete')),
'editor': Rule(('read', 'create', 'update'), scope=lambda user: {'stage': 'open'}),
}Editors see open deals only. The scope is applied in the database query, before paging and counting, so totals and page numbers match what the role may see. A save that would move a record out of the scope is refused.
#Code you write is trusted
Permissions guard people and API clients. Your own app code is trusted:
contextcalls such ascontext.create(...)check the permissions of the user you pass in.- The session inside a move or an action,
self.context.session, writes without permission checks. It is your code, running inside a step that was already checked. - Hiding a field or a button in the browser grants nothing and removes nothing. Only the rules decide.