The policy module: the role table as data and, per protected schema, the clauses of its rule.
defmodule MyApp.Roles do
use Mediate.Rbac.Policy, version: "2026.09.1", author: "platform", approval: "ticket 41"
role :reader, [:read]
role :editor, [:read, :edit]
object MyApp.Folder do
grant :membership, MyApp.Membership
predicate :cleared, &MyApp.Predicates.cleared/2
end
endThe rule allows an operation on a row when any grant holds a role that
permits it, and when every applicable predicate holds. A grant's relationship schema declares its subject, object, and role
columns with Mediate.Schema.relationship/1. The grant reads them, and
takes on:, role:, or as: when the schema's declaration is not
enough. through: serves a relationship that names a row the protected
row points at, and not the protected row itself. It lists the hops from
the protected row outward. Each hop is a schema, the column of it the
inner set matches, and an optional where: capture that narrows the
hop's rows:
object MyApp.Page do
grant :membership, MyApp.Membership, on: :folder_id, through: [{MyApp.Folder, :id, where: &MyApp.Rules.open/0}]
endMediate.Rbac.Policy.Clause says what each option means. A predicate is
a capture of a named function, so the policy stays data a hash can name.
only: names the operations it applies to. version: defaults to the
content hash of the policy's modules, author: and approval: to
"unrecorded".
The role table is data at compile time. objects/1 builds the clauses
at each read of the policy, and checks each against the schemas it
names. Three declarations raise there:
- a schema without an object type
- a relationship the grant cannot read
- a predicate that is not a named capture
So a bad policy fails at boot, when Mediate.Rbac.publish/0 reads it,
and not on a request. A build at read time keeps the policy module free
of compile-time dependencies on the schemas and predicates it names. A
change to any of them recompiles nothing but itself.
Summary
Functions
Makes the module a policy. use_schema/0 has the options.
A grant clause: the relationship schema whose rows hold a role on the protected row.
The modules the rules live in: the policy and the module of every predicate and hop filter, sorted.
A protected schema and, in the block, the clauses of its rule.
The protected schemas and their clauses, built and checked against the schemas they name.
Every operation some role permits.
The use options: version, author, approval.
A predicate clause: a capture of a named function of the subject and the environment.
A row of the role table.
The role table.
The roles that permit an operation.
The role table as data: each role's name to its permissions.
The options use accepts.
Types
@type t() :: module()
A module that uses this one.
Functions
Makes the module a policy. use_schema/0 has the options.
A grant clause: the relationship schema whose rows hold a role on the protected row.
The modules the rules live in: the policy and the module of every predicate and hop filter, sorted.
A protected schema and, in the block, the clauses of its rule.
@spec objects(t()) :: [Mediate.Rbac.Policy.Object.t()]
The protected schemas and their clauses, built and checked against the schemas they name.
Every operation some role permits.
The use options: version, author, approval.
A predicate clause: a capture of a named function of the subject and the environment.
A row of the role table.
@spec roles(t()) :: [Mediate.Rbac.Policy.Role.t()]
The role table.
The roles that permit an operation.
The role table as data: each role's name to its permissions.
@spec use_schema() :: NimbleOptions.t()
The options use accepts.