# runas (actor, onBehalfOf: person) — an actor acting for a person

> `runas (Actor, onBehalfOf: person) { … }` runs a block as a declared actor FOR a person: inside it nothing is read or written that the actor or the person could not reach alone. A count is over the rows both may read, a column either may not read is hidden, a write needs both to be allowed. `scope: row` names the piece of work the block is done for, and every change the block makes is recorded with the actor, the person and that work.

<!-- id: security-on-behalf-of · area: security · stability: preview · html: https://osysharp.com/reference/security/on-behalf-of/ -->

## Summary        {#summary}
An actor that works FOR somebody must not reach further than either of them alone. A sorting machine holding a
dispatcher's role, started by a courier, would otherwise read every round in the depot for her.

`onBehalfOf:` says who the actor is working for, and the block is then held to both:

```osy syntax
runas (SortingMachine, onBehalfOf: courier) {
  var mine = Parcel.Count();           // the parcels BOTH may read: hers
}
```

## Signature      {#signature}
```osy syntax
runas (<Actor>, onBehalfOf: <person>) { … }                 // person: a row of your [Principal] type
runas (<Actor>, onBehalfOf: <person>, scope: <row>) { … }   // scope: the piece of work — a task, a ticket, an invoice
runas (<Actor>, scope: <row>) { … }                         // an actor acting for nobody, naming its work
```

`<Actor>` is a declared actor — a class marked `[Actor]`, or `Actor.From(row)` ([[Actor] — a principal that code may run as](https://osysharp.com/reference/security/declared-actor/)). The two
named arguments may come in either order.

## Description    {#description}

### What may the block read?   {#reads}
Only what **both** may read. The actor's read rules and the person's are both applied to every query, in the
database: a list holds the rows both may read, and so does every count, sum and page of it. A column either of them
may not read is hidden ([[security-entity-security#field-mask]]).

| the question | the actor alone | for the person |
|---|---|---|
| `Parcel.Count()` | every parcel its roles admit | only those that are also the person's to read |
| a column the person may not read | its value | hidden |
| a row only the PERSON may read | not readable | still not readable — the actor gains nothing from the person |

A report compiled by `Explore` is read the same way: it counts and groups only what both may read. Two reads cannot
carry the person's rule beside the actor's yet, and are **refused** inside the block rather than answered as the
actor alone: a semantic search (`Memory.Search`), and a read of an entity that lives in an external data source
([data held somewhere else (app.DataSources)](https://osysharp.com/reference/config/data-sources/)).

### What may the block write?   {#writes}
Only what **both** may write. A create, an update of each column and a delete are refused unless the actor's rules
and the person's both allow them — row by row, and for a statement over many rows
([Update](https://osysharp.com/reference/query/update/)) only the rows both may change are changed. A refusal is the same denial either of them would meet alone.

An action run by name inside the block ([[Action] — what an app offers to be done, by name](https://osysharp.com/reference/function/action/)) is held to the same two.

### What does "nobody" mean?   {#nobody}
`onBehalfOf:` with no person — a `null`, or an account that no longer exists — is **nobody's** rights: what a
signed-out visitor may do. It never means "no limit". To run as the actor alone, leave `onBehalfOf:` out.

### Can code inside the block act for somebody else?   {#nested}
No. A block entered for a person keeps that person for everything it calls: a function inside it that runs as an
actor without naming anybody is still held to them, and one that names a different person is refused.

### What does `scope:` record?   {#scope}
Which piece of work the block is done for. Every change an actor makes is written to the app's change trail
([audit read access (app.Audit)](https://osysharp.com/reference/config/audit/)) whatever else the app audits, and each entry says:

| | |
|---|---|
| who acted | the actor's account, and its declared name |
| for whom | the person named by `onBehalfOf:` — empty when it acted for nobody |
| for what | the row named by `scope:`, with its type |

A block that names no `scope:` still gets an identity of its own, so every change of one block can be found together.
That is what lets "why does this row say that?" be answered from the row: its last change leads to the actor, the
person and the piece of work.

## Examples       {#examples}
```osy title="the machine reads and writes for a courier only what she could herself" run app=security-on-behalf-of
using Osysharp.Observability;

[Role] enum DepotRole { Dispatcher, Courier }

[Principal] entity Account {
  [Unique, MaxLength(200)] string Email;
  security { allow read when IsAuthenticated; }
}

[RoleGrant] entity RoleGrant {
  [Required] Account Holder;
  [Required] DepotRole Level;
  security { allow read when IsAuthenticated; }
}

policy IsDispatcher => RoleGrant.Any(g => g.Holder == user && g.Level == DepotRole.Dispatcher);
policy IsCourier    => RoleGrant.Any(g => g.Holder == user && g.Level == DepotRole.Courier);

[Actor(Roles = [DepotRole.Dispatcher])]
public class SortingMachine {
  public static Account Principal => new Account { Email = "sorting-machine@depot.example" };
}

entity Parcel {
  [Required, MaxLength(40)] string Code;
  [Required] Account Courier;
  [MaxLength(40)] string Bay = "";
  [MaxLength(200)] string Note = "";
  security {
    allow read, update when IsDispatcher;                        // a dispatcher sees and moves every parcel
    allow read when IsCourier where Courier == user;             // a courier sees her own
    allow update Note when IsCourier where Courier == user;      // …and may leave a note on it — not move it
  }
}

int MachineCounts()               { runas (SortingMachine) { return Parcel.Count(); } }
int CountFor(Account courier)     { runas (SortingMachine, onBehalfOf: courier) { return Parcel.Count(); } }
int CountForNobody()              { runas (SortingMachine, onBehalfOf: null) { return Parcel.Count(); } }

void NoteFor(Account courier, string code, string note) {
  runas (SortingMachine, onBehalfOf: courier) {
    var parcel = Parcel.Single(p => p.Code == code);
    parcel.Note = note;
  }
}

void MoveFor(Account courier, string code, string bay) {
  runas (SortingMachine, onBehalfOf: courier) {
    var parcel = Parcel.Single(p => p.Code == code);
    parcel.Bay = bay;
  }
}

[TestFixture]
void Depot() {
  var dana = new Account { Email = "dana@depot.example" };
  var cleo = new Account { Email = "cleo@depot.example" };
  var omar = new Account { Email = "omar@depot.example" };
  new RoleGrant { Holder = dana, Level = DepotRole.Dispatcher };
  new RoleGrant { Holder = cleo, Level = DepotRole.Courier };
  new RoleGrant { Holder = omar, Level = DepotRole.Courier };
  new Parcel { Code = "C-1", Courier = cleo };
  new Parcel { Code = "C-2", Courier = cleo };
  new Parcel { Code = "O-1", Courier = omar };
}

principal Dana => Account.Single(a => a.Email == "dana@depot.example");

[Test(Depot)]
[runas(Dana)]
void for_a_courier_the_machine_reaches_only_what_she_could() {
  var cleo = Account.Single(a => a.Email == "cleo@depot.example");

  Assert.Equal(3, MachineCounts());        // the machine alone: every parcel
  Assert.Equal(2, CountFor(cleo));         // for Cleo: her two
  Assert.Equal(0, CountForNobody());       // for nobody: nothing

  NoteFor(cleo, "C-1", "leave at the side door");                       // both may write the note
  Assert.Denied(() => MoveFor(cleo, "C-1", "B4"));                     // the machine may move it; Cleo may not

  var parcel = Parcel.Single(p => p.Code == "C-1");
  Assert.Equal("leave at the side door", parcel.Note);
  Assert.Equal("", parcel.Bay);
}
```

## See also       {#see-also}
- [[Actor] — a principal that code may run as](https://osysharp.com/reference/security/declared-actor/) — declaring an actor, and running as one
- [security { }](https://osysharp.com/reference/security/entity-security/) — the rules both the actor and the person are held to
- [Security.MayRead — may THIS person read this row, under their own rules](https://osysharp.com/reference/security/may-read/) — asking whether another person may read one row
- [[Action] — what an app offers to be done, by name](https://osysharp.com/reference/function/action/) — actions run by name, held to the same two inside a block
- [audit read access (app.Audit)](https://osysharp.com/reference/config/audit/) — the change trail an actor's changes are written to
- [runas](https://osysharp.com/reference/testing/runas/) — `runas` in a test
