Summary#
Before you TELL somebody about a record — a notification, a mention, a mail with its title in it — ask whether THEY may read it. Their own rules answer, not yours:
if (Security.MayRead(colleague, ticket)) { Notify(colleague, TicketNotice.Mentioned, ticket.Id); }Signature#
bool Security.MayRead(Principal person, <entity> row) // person: the row, or its id · row: the row itselfDescription#
Whose rules. The person's — a fresh read armed with their account and their roles, the one a request of theirs gets:
the entity's allow read, every deny read a kit wrote, a restricted queue they are not in, a row restricted for a
privacy purpose. The caller's own access is irrelevant: a workflow body (which reads everything) and an admin's request
(which reads more) both get the person's answer. SECURITY-INVARIANT may-read-answers-as-the-person-never-as-the-caller.
The row, not its id. Which rules apply depends on what kind of row it is, so pass the row you read —
Security.MayRead(p, Ticket.Single(t => t.Id == id)).
Somebody who is nobody. An id that is no account answers false — never what an anonymous visitor could read.
As stored. It reads the row as the database holds it: a row this unit of work made and has not committed yet is not readable by anybody else, so it answers false. Ask after the commit — the notifications kit delivers after it for exactly this reason.
It reads; it never acts as them. Nothing runs as the person, nothing is written as them, and nothing they could
read is handed back — only the answer. There is still no way to become another user outside a [Test]
(acting for another principal).
Examples#
[Principal] entity Member {
[MaxLength(100)] string Name = "";
security { allow read, create when IsAnonymous || IsAuthenticated; }
}
entity Memo {
[Required, MaxLength(100)] string Title;
[Required] Member Owner;
bool Private;
security {
allow read, create when IsAnonymous;
allow read when IsAuthenticated where !Private || Owner == user;
}
}
/// Who may be told about `memo`: everybody who may read it, under their own rules.
List<string> WhoMayBeTold(Memo memo) {
return Member.ToList().Where(m => Security.MayRead(m, memo)).Select(m => m.Name).ToList();
}See also#
- The Notifications kit — telling a person that something needs them — every notification topic answers
MayReadwith it - The ReplyComposer kit — where a person writes a reply, on any record — a note cannot name somebody who may not read its record
- security { } — the rules it evaluates
- restricted rows and purpose reads (Restriction, [PurposeRead]) — a restricted row is out of every read, this one included
- Security.Authorize — does THIS person satisfy a policy — the same idea for a policy: does THIS person satisfy it