What this is about
Picture a payslip payslip. Many people may read it: name, month, department. But only a few should see the field salary, and even fewer should change it.
For this you set a role on the simple field salary, e.g. payslip-salary-read. A simple field is a field with a value (text, number, date), not a relation to another model.
On a relation this works differently: there a field role opens an additional way to the other model. See Permissions on relations (field roles).
One record, two views
flowchart LR
A(["Person with payslip-read"]) -->|"response: +"| R1["employeeName: Anna<br/>salary: –"]
B(["Person with payslip-read<br/>and payslip-salary-read"]) -->|"response: +"| R2["employeeName: Anna<br/>salary: 5000"]
Both read the same record. The first person does not get to see salary, the second one does.
Which role for what
A role only applies to the operation it is set for. If you set no role for an operation, the role of the model is enough there.
| What the request does | Role on the field |
|---|---|
| read the field, filter or order by it | …-read |
| set a value on create | …-create |
| change an existing value (PUT or PATCH) | …-update |
clear a value (set it to null) | …-delete, or …-update if the field has no delete role |
The generator builds the names like for every field role: base role, field name, operation. See How role names are built.
Reading
| Person holds payslip-salary-read | How the response requests the field | Result |
|---|---|---|
| yes | – | field with its value |
| no | through + or * | field is missing (null), rest of the response as usual |
| no | by name, e.g. "salary" | 403 missing-permission|payslip-salary-read |
Why two answers? + means “all simple fields I may see”. If you name the field, you want exactly this value. An empty value could then not be told apart from “not set”, so CDMS refuses.
The field is missing everywhere the record comes out:
- on a read and in every row of a search
- in the response to create, PUT and PATCH (it is a read)
- when you read the model through a relation of another model
- in every version of the change history
- in every row of a search on an abstract model
Writing
When writing, CDMS checks the change, not the mere mention of the field.
| Value in the request | Role held | Result |
|---|---|---|
| same as the stored value | – | allowed, nothing changes |
| different value | …-update (on create …-create) | is stored |
| different value | no | 403 missing-permission|payslip-salary-update, nothing stored |
PATCH with null | …-delete | field is cleared |
PATCH with null | no | 403 missing-permission|payslip-salary-delete |
| PUT without a value | …-delete | field is cleared |
| PUT without a value | no | stored value stays, no error |
Values set by a default value or a hook are not checked. They come from the server, not from the person.
Searching
A filter on a value gives it away, step by step: “Is the salary greater than 4000? Greater than 5000?” An order gives it away through the position. That is why:
- A filter on a field you may not read results in 403 with the read role of the field. This also applies inside nested groups.
- An order by it results in 403 as well.
- A path such as
vault.codeis followed to its last field. Ifcodehas a read role, you need it for the filter. - On an abstract model the field of every subtype counts.
See also Broken filters.
Variants
When: POST /payslip/read/{id}, response: ["+"], only payslip-read
-
1CDMSchecks the read role of
payslipand reads the record -
2CDMS
payslip-salary-readis missing → leaves outsalary
Result: 200, salary is null.
When: POST /payslip/create with salary, without payslip-salary-create
-
1CDMSchecks the create role of
payslip -
2CDMS
salaryhas a value,payslip-salary-createis missing
Result: 403, nothing is created. Without salary in the body the create goes through.
When: read without payslip-salary-read, changed, sent back with PUT
-
1CDMS
salaryis missing in the body,payslip-salary-deleteis missing -
2CDMSkeeps the stored value and writes the other fields
Result: 200, the salary is unchanged.
When: POST /payslip/query with the filter salary AFTER 4500, without payslip-salary-read
-
1CDMSchecks every filter and every order against the read roles of the fields
Result: 403 missing-permission|payslip-salary-read, before anything is searched.
Pitfalls
Where to go next
- Roles on relations: Permissions on relations (field roles)
- The levels in context: The three levels at a glance
- Which fields a response contains: Field selection