Introduction
What pgmorbac is and how Multi-OrBAC models access control.
pgmorbac implements Multi-OrBAC (Organization-Based Access Control across
multiple organizations) entirely inside PostgreSQL. One schema, morbac,
holds the model; one function, morbac.is_allowed(user_id, org_id, activity, view), answers every authorization question.
Why OrBAC instead of plain RBAC
Role-based access control answers "what can this role do". It has no notion of where: a role is global, so a hospital chain with two sites either duplicates every role per site or gives every physician access to both. OrBAC adds the organization as a first-class dimension: a rule is always Rule(organization, role, activity, view, context, modality).
Throughout this documentation we follow one running example: the Groupe Hospitalier Valmont, a French hospital network.
Dr. Camille Rousseau is a physician at Clinique Valmont Nord. That sentence is exactly
what pgmorbac stores: a row in morbac.user_roles binding her user id to the
physician role in the Clinique Valmont Nord organization. She has no standing in
Clinique Valmont Sud, and nothing needs to be written to make that true: absence of
a rule means denial.
The six ingredients
| Concept | Table | Valmont example |
|---|---|---|
| Organization | morbac.orgs | Clinique Valmont Nord, child of Groupe Hospitalier Valmont |
| Role | morbac.roles | physician (scoped to Clinique Valmont Nord) |
| Activity | morbac.activities | read, prescribe |
| View | morbac.views | prescriptions, patients |
| Context | morbac.contexts | always, or a custom on_shift() predicate |
| Rule | morbac.rules | physicians may prescribe on prescriptions in the North subtree |
Activities and views are abstract: prescriptions is a category, not a table.
Your application decides what concrete resources map to each view, and
row-level security policies enforce the decision per row.
One call decides
SELECT morbac.is_allowed(
'11111111-1111-4111-8111-111111111111', -- Dr. Camille Rousseau
(SELECT id FROM morbac.orgs WHERE name = 'Clinique Valmont Nord'),
'prescribe',
'prescriptions'
);The engine resolves her effective roles (direct, inherited, delegated, derived), collects applicable prohibitions and permissions across four rule stores (org rules, cross-org rules, user rules, global rules), evaluates contexts and validity windows, and applies priority resolution where ties go to prohibition. The result is cached with a configurable TTL and invalidated automatically on any relevant change.