13.4 Authorisation and policies
policy Orders {
allow get(id) if caller.isUser(db.find(Order, id)?.userId)
allow list, recent if caller.has(role: .Support)
allow * if caller.service("billing")
deny delete unless caller.has(role: .Admin) && caller is .User(mfa: true, ..)
}
@public rpc health() -> Ok
@allow(role: .Admin) rpc purge() -> Ok
@internal rpc create(…) -> Order- The subject of a policy: a service (rules per rpc), a sharded actor (per
msg), a topic (publish,subscribe), the dynamic config (set). - Evaluation: if at least one
denymatches — refusal; otherwise if at least oneallowmatches — permission; otherwise refusal. A refusal isAccessDenied(PERMISSION_DENIED). - A condition is an expression with access to the rpc parameters by name,
caller,ctx; the effectspureand readingdbare allowed. Exception: the conditions of dynamic-config policies (policy Limits { allow set if … }, §14.2) arepureonly, withoutdbandctx(an edition 1.0 correction, D-8056; DIV-028; D-5203). @allow(role: r)is shorthand forallow m if caller.has(role: r). The form@allow(lint: rule, reason: "…")is not an access policy but a suppression of an analyzer rule (III.4).- The condition of a topic
publishrule seescaller,element(the published value) and, forsharded by,key; the condition ofsubscribesees onlycaller. External principals are admitted if nodenycondition holds and anallowcondition does; a rule naming another operation is the errorunknown-policy-target(D-8064; D-7790). - The access model (
oxl access, the "Permissions" section of the inspector) covers rpcs, messages of sharded actors and topic operations: for each of them the rules and the roles that call it; an element without rules is marked open (D-8064; D-7790, D-7792).
Automatic permissions between services. The compiler builds a call graph: which code of which role calls which rpcs, messages and topics. For every pair "role → method" an implicit rule allow m if caller.service is role is generated. All other calls from services are forbidden at the level of the mTLS identity.
External principals. For User and Anonymous only the explicit applies: an allow rule, @public or @allow. Without them the call is rejected. @internal explicitly marks a method as inaccessible to external principals. An rpc that is not allowed to external principals, is not marked @internal and is not called from the program's code is the compile error missing-policy (the method is accessible to nobody).
Data permissions:
@table("orders") @owner(.userId, bypass: [.Support, .Admin])
data Order { … }
data Employee {
name: str
@visible(role: .HR) salary: Money?
}@owner(.field)— queries to the table from aUserwithout the roles frombypassautomatically get the conditionfield == caller.id. Limits of action: the condition is added tofind,query,count,update,delete; it is not applied toload(…)and to raw SQL (execute) — access through them MUST be restricted by a policy; writing a row whose owner differs fromcalleris rejected (an edition 1.0 correction, D-8056; DIV-029; D-5030, D-5069).@visible(role: r)— the field is not passed to principals without the roler; the field MUST be optional or have a default value.@auditon an rpc or a policy writes every decision to the system topicoxl.audit.oxl access diffshows the change of permissions after a code change;oxl access who-can Orders.deleteshows who can call the method and through which roles.
Related diagnostics
data-rights, missing-policy, unknown-policy-target
See also
Source: docs/spec/oxl-standard.en.md