Osy#betaRun it on Osyrin: osyrin.com

Reference / Security

What may go past the caller's own rights — and what never does

A request always runs as the person who made it; there is no system mode, sudo or definer's rights a function can switch into. What does write past one person's own rules is declared and narrow: a function a deny names with `except via`, a block run as a declared actor (its declared roles, never an account of the code's choosing), an event handler and a workflow body (the app's authority, held by every kit's denies), the sign-in flow's own role, a grant link to one row, and — in tests only — a fixture. One route READS past them: a function an entity names in `allow read via`, which sees every row to compute a value and returns no row.

stable1 example compiled by CIsecuritykitsauthorization

Summary#

Every request runs as its caller, and no function can become an account of its choosing. There is no [System] an app may put on a function, no "run as system", no definer's rights. When something must happen that the person could not write themselves — a member spending from a balance only the kit may change, a desk acting for a member, a robot settling what a clerk may not — one of the declared, narrow routes below does it.

Description#

Which routes are there?#

routewhat it goes pasthow narrow
a function a deny … except via F namesthat deny, for F's OWN writesonly the member and operation named; F runs on the server and needs [Authorize(…)]; the write still needs a grant — deny … except via
a function an allow read via F namesthe entity's READ rules and field masks, for F's OWN readsF answers a value the compiler checks holds no row, writes nothing itself, runs on the server; a helper it calls reads as the caller — allow read via
a runas (Actor) { … } blockthe caller's rules, for the block — it answers to the ACTOR'sa class marked [Actor], with exactly its declared roles; onBehalfOf: holds it to a person's rights too — [Actor] — a principal that code may run as, runas (actor, onBehalfOf: person) — an actor acting for a person
an [On] handlerthe person's rules — it runs with the app's authority, after the commitevery kit's deny still holds it (but its own kit's) — [[function-on#authority]]
a workflow bodythe app's security { } blocksa kit's deny still binds it, except that kit's own code — What a workflow body may write
an [AuthMethod]nothing extra: it runs as the sign-in flow's role, under that role's ordinary grantsauth bootstrap (login, before anyone is signed in)
a grant linksigning in, for ONE row of ONE entityexpiring, revocable, admitted by the entity's own rule — grants (a link that opens one row)
a [TestFixture]every security { } blocktests only — Fixtures: built once, cloned for every test, and built on each other

How does one person act for another?#

As themselves, through a function. A desk spending from a member's balance calls the function the balance's deny names; whether the desk may is that function's [Authorize(…)], and which member is an ordinary parameter, checked by the function. A kiosk or an integration is a user of its own (acting for another principal). To ask whether ANOTHER person could read a row, use Security.MayRead.

What does not exist?#

  • an attribute that lifts a function out of its caller's rules — [System] is the platform's own, not an author's;
  • a way for a request to become somebody else — another person's account is never run as, outside a test's runas; the one thing code runs as is an actor the app declared;
  • an allow that opens a direct write past an except via deny — the named function is the way through.
entity Visit {
  [MaxLength(80)] string Room;
  security { allow read, create when IsAnonymous || IsAuthenticated; }
}

entity Ledger {
  [MaxLength(200)] string Line;
  security { allow read when IsAnonymous || IsAuthenticated; }   // nobody may write one
}

[Event("Visited")]
public sealed class Visited { public Visit Visit; }

void Enter(string room) { Raise(new Visited { Visit = new Visit { Room = room } }); }

[On]
void Record(Visited e) { new Ledger { Line = "visited " + e.Visit.Room }; }

[Test]
void The_person_visits_and_the_app_records_it() {
  Enter("sauna");
  UnitOfWork.Commit();
  Background.Settle();
  Assert.Equal("visited sauna", Ledger.Single().Line);
}

See also#

Related

deny … except via

Names the ONLY functions whose own code may change something a deny withholds: `deny update UnitsLeft except via…

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…

[Actor] — a principal that code may run as

A class marked `[Actor]` declares a principal that code may run as: a robot, a nightly job, an integration, a digital…

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…

[On] — hear an event, after the commit that raised it

`[On]` on a function makes it a HANDLER of the event its one parameter is. Every handler of an event — in the app and…

What a workflow body may write

A workflow body runs on a system data context, so its writes are NOT gated by the app's `security {}` blocks. The…

auth bootstrap (login, before anyone is signed in)

Under deny-all, login faces a paradox: it must read a user row *before* anyone is authenticated. `app.AuthBootstrap`…

grants (a link that opens one row)

A `grant` is a link the platform signs that lets whoever holds it reach ONE row of ONE entity, for a while, without…

acting for another principal

A shared terminal, a kiosk, a scanner or a back-office integration is a **user of your app**, not a mode of it. It…

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…

Fixtures: built once, cloned for every test, and built on each other

A [TestFixture] is built once per run, and every test that names it runs on its own copy of what it left — rows, files…

Writing a kit — its parts, and every seam an app connects

The page to read before writing a kit. What `osy kits new` makes, and each seam in a few lines with the one page that…