# Security.MayRead — may THIS person read this row, under their own rules

> `Security.MayRead(person, row)` answers whether a given person may read a given row under THEIR OWN security — the app's read rules, every kit's floor, restricted rows — exactly as a request of theirs would be answered. Never the caller's: a workflow body reads everything, and a notifier asking through it would tell anybody about anything. What a notifier asks before telling somebody about a record, and a mention before naming somebody on one.

<!-- id: security-may-read · area: security · stability: preview · html: https://osysharp.com/reference/security/may-read/ -->

## Summary        {#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:

```osy syntax
if (Security.MayRead(colleague, ticket)) { Notify(colleague, TicketNotice.Mentioned, ticket.Id); }
```

## Signature      {#signature}
```osy syntax
bool Security.MayRead(Principal person, <entity> row)   // person: the row, or its id · row: the row itself
```

## Description    {#description}
**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](https://osysharp.com/reference/security/acting-for-another-principal/)).

## Examples       {#examples}
```osy title="the private memo is its owner's; the open one is everybody's" test app=security-may-read
[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       {#see-also}
- [The Notifications kit — telling a person that something needs them](https://osysharp.com/reference/ui/notifications-kit/) — every notification topic answers `MayRead` with it
- [The ReplyComposer kit — where a person writes a reply, on any record](https://osysharp.com/reference/ui/composer-kit/) — a note cannot name somebody who may not read its record
- [security { }](https://osysharp.com/reference/security/entity-security/) — the rules it evaluates
- [restricted rows and purpose reads (Restriction, [PurposeRead])](https://osysharp.com/reference/security/restriction/) — a restricted row is out of every read, this one included
- [Security.Authorize — does THIS person satisfy a policy](https://osysharp.com/reference/security/authorize/) — the same idea for a policy: does THIS person satisfy it
