Osy#betaRun it on Osyrin: osyrin.com

Reference / Security

Security.Authorize — does THIS person satisfy a policy

bool Security.Authorize(Principal person, PolicyName) · bool Security.Authorize(Principal person, PolicyName(args))

`Security.Authorize(person, Policy)` answers whether a given person satisfies a declared policy — evaluated exactly as it is for a request of theirs, never as the caller. One yes or no about one person; it grants nothing. What a kit asks to find "the leads" or "the reviewers" without knowing how the app decides who they are.

stable1 example compiled by CIsecuritypolicyserverkits

Summary#

A policy is a question about the caller. Sometimes you need its answer about somebody else — who on a list is a lead, whom to tell. Ask it of them:

if (Security.Authorize(member, LeadsInbox)) { leads.Add(member); }

Signature#

bool Security.Authorize(Principal person, PolicyName)            // a policy that takes no argument, named bare
bool Security.Authorize(Principal person, PolicyName(argument))  // a parameterised one, with its argument
// person: the principal row, or its id

Description#

Whose answer#

The person's. The policy is evaluated with THEM as user — their account and their roles, the way a request of theirs is armed — so a workflow body (which reads everything) and an admin's request get the same answer as anybody else would about that person. An id that is nobody answers false, never what an anonymous visitor would be granted. It is the same evaluation [Authorize(PolicyName)] and if (PolicyName) use for the caller, so a policy means one thing in all three places.

It grants nothing#

It answers one yes or no about one person, and nothing else. Nothing runs as them, nothing is written as them, nothing they could read is handed back, and the caller's own access is untouched: asking whether somebody is a lead does not make the caller one — the next line's if (IsLead) and every row rule still answer for the caller.

Only a policy you can see#

The second argument NAMES a declared policy; it is not a condition to evaluate. It must be one this code can see — the app's own, or (in a kit) one the kit declares, or a package it uses declares. A kit therefore asks only about a rule the app was handed and filled in — policy LeadsInbox; in the kit, policy LeadsInbox => IsLead; in the app — and never about the app's other rules. Anything else is a compile error that lists every policy there is to name:

Security.Authorize(m, IsLeed)          // there is no `policy IsLeed` this code can see — the policies this code can name: …
Security.Authorize(m, m.Grade == 1)    // the second argument names a policy; it is not a condition to evaluate
Security.Authorize(m, IsAuthenticated) // `IsAuthenticated` is the platform's own state of a caller, not a declared policy

In a kit#

This is how a kit reaches "the people the app says" without knowing the app's roles. The WorkQueues kit declares who leads it as a policy the app supplies, and finds a queue's leads by asking it of each member:

// in the kit
policy LeadsInbox;
public List<Principal> InboxLeads(WorkItem item) {
  List<Principal> leads = [];
  foreach (var m in QueueMember.Where(m => m.Queue == item.Queue && m.Active).Include(m => m.Member).ToList()) {
    if (Security.Authorize(m.Member, LeadsInbox)) { leads.Add(m.Member); }
  }
  return leads;
}
// in the app — the only thing it writes about its leads
policy LeadsInbox => IsLead;

What it costs#

One evaluation per person asked: their principal and roles (kept between requests, as a request's are) and whatever the policy itself reads. Ask it of the people a decision is about — a queue's members — not of every account.

Examples#

enum Grade { Lead, Nurse }

[Principal] entity Carer {
  [MaxLength(100)] string Name = "";
  security { allow read, create when IsAnonymous || IsAuthenticated; }
}

entity Grading {
  [Required] Carer Who;
  [Required] Grade Grade;
  security { allow read, create when IsAnonymous || IsAuthenticated; }
}

entity Ward {
  [Required, MaxLength(40)] string Name;
  Carer? Head;
  security { allow read, create when IsAnonymous || IsAuthenticated; }
}

policy LeadsWards => Grading.Any(g => g.Who == user && g.Grade == Grade.Lead);
policy Heads(Ward w) => w.Head == user;

/// The leads on the rota — asked of each carer, by the app's own rule, whoever is asking.
List<string> LeadsOnTheRota() {
  List<string> leads = [];
  foreach (var c in Carer.OrderBy(c => c.Name).ToList()) {
    if (Security.Authorize(c, LeadsWards)) { leads.Add(c.Name); }
  }
  return leads;
}

/// Whether `carer` heads `ward` — a parameterised policy, with its argument.
bool HeadsIt(Carer carer, Ward ward) { return Security.Authorize(carer, Heads(ward)); }

See also#

Related

policy

Names an authorization rule once so every place that needs it can say the name. A policy is a boolean about the caller…

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…

security { }

The rules that decide who may read and write an entity's rows. A where clause filters by the row (the owner sees their…

Inbox.Notifications — the people the inbox names, told in their bell

`Osysharp.WorkQueues.Notifications` joins `Osysharp.WorkQueues` to `Osysharp.Notifications`: the person an item is…