The seam. Its purpose is log completeness: no read or write of a protected schema goes unrecorded, because a call that carries no decision and no exemption raises before any SQL. It is not a reference monitor. It sees repo calls and nothing else, and no control asks it for more.
use Mediate.Repo after use Ecto.Repo overrides every function of the
Ecto surface the repo defines, and puts each in one of four buckets:
query:
all,one,get,get_by,reload,aggregate,exists?,stream,preload, andall_by, with their bang forms. A read of a protected schema under a decision publishes one access event after it returns.update_allanddelete_allsit here too, and refuse on an audited schema.write:
insert,update,delete,insert_or_update, andinsert_all, with their bang forms. A single-row write to an audited schema publishes one change event inside its transaction.raw:
queryandquery_many, with their bang forms. A raw call passes under an exemption and refuses a decision.plumbing: everything else passes.
defmodule MyApp.Repo do
use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres use Mediate.Repoend
The mediate: option
Every query, write, and raw call takes it, in one of three forms:
- a
%Mediate.Decision{}fromMediate.authorize/4orMediate.scope/4 {:exempt, reason}with a non-empty reason, a declared exemption the seam records with the caller{:exempt, :library}, which the seam accepts from aMediate.*caller alone
The root source of a query decides it. A protected root passes when the
decision names its object type, when the parent's decision carries it
through carries/1, or when the call is exempt. A joined or subquery
source passes the same way, and a source with no schema or no object type
passes without a check. A preload is a query of its own with the
association's schema as root.
A nested association write that Ecto makes on the caller's behalf reuses
the parent's mediation, because Ecto forwards only timeout, log,
telemetry_event, prefix, and allow_stale to it. prepare_query/3
is the extension point: the seam judges every query there, and an
adapter wraps every mediated call through around_query/3.
What refuses
%Mediate.Error{reason: :unmediated}for a call on a protected schema with no decision and no exemption.%Mediate.Error{reason: :invalid}with:bulk_writein the detail forupdate_all,delete_all, andinsert_allon an audited schema, with:upsertfor a write withon_conflict:on a fact schema, and with:mediatefor a raw call given a decision.%Mediate.Error{}with the denial's own reason, one ofMediate.Answer.reasons/0, for a deny decision handed to a call.
Options
role::app, the application's repo, or:owner, the library's own channel. Defaults to:app.
An owner-role repo runs migrations and the library's own writes. Every call on it carries the library exemption, and it records nothing.
Summary
Types
@type entry() :: {atom(), non_neg_integer(), :query | :write | :raw | :plumbing}
One function of the surface the repo compiled against: its name, its arity, and its bucket.