# allow read via

> Names the functions whose OWN reads of the entity see every row — past its read rules and its field masks — to compute a figure everyone may see: `allow read via HarvestTotal;`. The function still runs as its caller, writes nothing itself, and answers a value (a number, text, a class of values, a list of those); the compiler refuses an answer that could carry a row. A helper it calls reads as the caller, and every other read anywhere is unchanged.

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

## Summary        {#summary}
A function runs as the person who called it, so it reads what they may read. That is right almost everywhere — and
wrong for a figure meant for everyone. If each gardener may read only their own harvests, a function that totals a
plot's harvests answers each caller a different number: the total of THEIR rows.

`allow read via PlotTotal;` names the function that computes the shared figure:

- **its own reads** of this entity see every row — no row rule, no field mask;
- **it answers a value, never a row** — the compiler checks the return type;
- **it writes nothing itself**, runs on the server, and is still called as its caller (its `[Authorize]` still holds);
- **nothing else changes** — a helper it calls, the page that shows the figure, every other read of the entity.

## Signature      {#signature}
```osy syntax
security {
  allow read when IsAuthenticated where Grower == user;   // each person reads their own rows
  allow read via PlotTotal, PlotTotals;                   // …and these two compute over all of them
}
```

It names functions, not strings — a misspelt name does not compile — and as many as the entity has figures.

## Description    {#description}

### What does the function see?   {#what-it-sees}
Every row of the entity the clause is written on, with every field, through any read its own code makes: a
collection (`plot.Harvests.Sum(…)`), a query (`Harvest.Where(…)`), a LINQ chain, a lambda, a `foreach`, a local
function declared inside it. That is all of "its own code".

| the read | sees |
|---|---|
| the function's own `plot.Harvests.Count()` | every harvest on the plot |
| the function's own `Harvest.Where(h => h.Plot == plot)` | every harvest on the plot |
| a lambda or a local function inside the function | every row — it is the function's own code |
| a field a `deny read` masks, read by the function's own code | the stored value |
| a function it calls (a helper) | only what the caller may read |
| the same read anywhere else — before the call, after it, on the page | only what the caller may read |
| another entity read by the function | judged by that entity's rules, as anywhere |

A privacy restriction the app or the platform places on a field is not a read rule, and stays.

### What may it answer?   {#values}
A value: a number, a string, a bool, a `DateTime`, a `Guid`, an enum; a `class` of yours whose fields are values; a
`List`, an array or a `Dictionary` of those, nested as deep as you like. A list of totals per kind is the usual shape.

An entity, an entity reference, a collection of rows, `object`, or a class with any of those inside is refused, and
the error names the path to the row — `` `Report.Detail.Rows[…]` is a `Harvest` row `` — so a row read past the rules
can never leave the function. A function that answers nothing (`void`) is refused too: its reads would have no purpose.

### Why may it not write?   {#no-writes}
Because it runs in a unit of work of its own, opened for its reads and thrown away when it returns — only its answer
comes back. A write in its own code is refused at compile time. A helper it calls may write, as the caller, under the
caller's rules: the helper is not elevated.

### A row it hands to a helper   {#handed-rows}
A helper runs as the caller, so a row the function passes to it is the caller's row or nothing: it is read again as
the caller, and one the caller may not read is refused exactly as any call with an unreadable row is
(`NotFoundException`). Compute inside the function, and hand helpers values.

### What it reads is what is stored   {#stored}
The function reads saved rows. If the caller's unit of work holds an unsaved row of the entity — a harvest just
created, an edit not yet committed — the call is refused and says so, rather than silently leaving the change out of
the figure. Commit first.

It also cannot pause: a read-via function may not wait on a person or a timer.

### Where does it run?   {#server}
On the server, always — a read the browser made would be the caller's own. A page calls it like any
server function; `live var total = PlotTotal(plot);` is asked of the server, and asked again when a row it read
changes.

### Who may write it?   {#owner}
The entity's owner: the package that declares it, naming its own top-level functions. A `partial` that adds to
another package's entity cannot name one, and neither can a function from another package.

### The warning: a function that can be asked about one row   {#one-row}
The compiler makes sure the answer holds no row. It cannot know WHICH rows the answer is about. `bool HasPicked(Plot
plot, Gardener who)` that filters `h.Grower == who` answers, for whoever calls it, a question about one person's
private rows. The lint `security-read-via-asks-about-one-row` warns when the function narrows the rows by its own
PARAMETER on a field the entity's read rules protect (one its `where` narrows on, or one a `deny read` masks).
Narrowing by the plot, a constant or `user` does not warn. When answering about one row is exactly the point, say so
on the function: `[SuppressWarning("security-read-via-asks-about-one-row")]`.

### What `osy explain` says   {#explain}
Beside who may read the entity's rows, a line per function: `` `PlotTotal` may read every Harvest to compute its
answer (it returns a number, never a row) — its own reads only, never a function it calls. `` `osy model --json`
lists the functions under the entity's `security.readVia`.

## Examples       {#examples}

### A plot's total, the same for every gardener   {#example-total}
Each gardener reads only their own harvests; the plot's total and the totals per crop count everybody's — and the
weights a `deny read` hides from everyone are summed inside the function.

```osy title="private harvests, a shared total" run app=security-read-via
[Principal] entity Gardener {
  [Unique, MaxLength(80)] string Name;
  security { allow read when IsAuthenticated; }
}

enum Crop { Beans, Squash, Herbs }

entity Plot {
  [Required, MaxLength(80)] string Label;
  [ForeignKey(Plot)] Harvest[] Harvests;
  security { allow read when IsAuthenticated; }
}

entity Harvest {
  [Required] Plot Plot;
  Gardener Grower;
  Crop Crop;
  int Grams;
  security {
    allow read, create when IsAuthenticated where Grower == user;
    deny read Grams;
    allow read via PlotTotal, PlotTotals;
  }
}

class CropTotal {
  public Crop Crop;
  public int Grams;
}

/// The plot's whole harvest, in grams.
int PlotTotal(Plot plot) => plot.Harvests.Sum(h => h.Grams);

/// One total per crop — a List of a class of values.
List<CropTotal> PlotTotals(Plot plot) {
  var totals = new List<CropTotal>();
  foreach (var crop in new[] { Crop.Beans, Crop.Squash, Crop.Herbs })
    totals.Add(new CropTotal { Crop = crop, Grams = plot.Harvests.Where(h => h.Crop == crop).Sum(h => h.Grams) });
  return totals;
}

[TestFixture]
void Season() {
  var ida = new Gardener { Name = "Ida" };
  var oto = new Gardener { Name = "Oto" };
  var plot = new Plot { Label = "North bed" };
  new Harvest { Plot = plot, Grower = ida, Crop = Crop.Beans, Grams = 400 };
  new Harvest { Plot = plot, Grower = oto, Crop = Crop.Beans, Grams = 250 };
  new Harvest { Plot = plot, Grower = oto, Crop = Crop.Squash, Grams = 900 };
}

principal Ida => Gardener.Single(g => g.Name == "Ida");

[Test(Season)]
[runas(Ida)]
void The_total_counts_every_harvest_and_a_read_still_sees_only_her_own() {
  var plot = Plot.Single();
  Assert.Equal(1550, PlotTotal(plot));
  Assert.Equal(650, PlotTotals(plot).Single(t => t.Crop == Crop.Beans).Grams);
  Assert.Equal(0, PlotTotals(plot).Single(t => t.Crop == Crop.Herbs).Grams);
  Assert.Equal(1, Harvest.Count());                 // her own row — the function changed no other read
}
```

### Refused: an answer that could carry a row   {#example-refused}
```osy title="the error names Shelf.Rows[…]" syntax
class Shelf { public string Label; public List<Harvest> Rows; }

Shelf Leaky(Plot plot) => new Shelf { Label = plot.Label, Rows = plot.Harvests.ToList() };
// error: `Leaky` reads every Harvest, so what it returns must be a value that can hold no row —
//        and `Shelf.Rows[…]` is a `Harvest` row. …
```

## See also       {#see-also}
- [security { }](https://osysharp.com/reference/security/entity-security/) — the `security { }` block this clause belongs to
- [deny … except via](https://osysharp.com/reference/security/except-via/) — the other way a function is named in it: the only functions that may make a write
- [What may go past the caller's own rights — and what never does](https://osysharp.com/reference/security/past-the-callers-rights/) — every declared route past the caller's own rules, this one among them
- [Security.Authorize — does THIS person satisfy a policy](https://osysharp.com/reference/security/authorize/) — who may call the function at all
