Deny Rules
Prohibitions, priorities, and suspending access without deleting grants.
Situation. Olivier Fontaine, a billing-clerk at Clinique Valmont Nord, is
under internal investigation. His access to invoices must stop today, but
nothing about his account should be deleted: the investigation may clear him,
and HR wants the grant history intact.
Assumes the shared Valmont setup.
Do not delete the grant, prohibit the action
INSERT INTO morbac.user_roles (user_id, role_id, org_id)
SELECT '66666666-6666-4666-8666-666666666666', r.id, r.org_id
FROM morbac.roles r JOIN morbac.orgs o ON o.id = r.org_id
WHERE o.name = 'Clinique Valmont Nord' AND r.name = 'billing-clerk'
ON CONFLICT DO NOTHING;
INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality)
SELECT o.id, r.id, 'read', 'invoices',
(SELECT id FROM morbac.contexts WHERE name = 'always'), 'permission'
FROM morbac.orgs o JOIN morbac.roles r ON r.org_id = o.id AND r.name = 'billing-clerk'
WHERE o.name = 'Clinique Valmont Nord'
ON CONFLICT DO NOTHING;
SELECT morbac.is_allowed('66666666-6666-4666-8666-666666666666',
(SELECT id FROM morbac.orgs WHERE name = 'Clinique Valmont Nord'),
'read', 'invoices'); -- trueSuspend him with a user-level prohibition:
INSERT INTO morbac.user_rules (user_id, org_id, activity, view, context_id, modality, priority)
SELECT '66666666-6666-4666-8666-666666666666', o.id, 'read', 'invoices',
(SELECT id FROM morbac.contexts WHERE name = 'always'), 'prohibition', 100
FROM morbac.orgs o WHERE o.name = 'Clinique Valmont Nord'
ON CONFLICT DO NOTHING;
SELECT morbac.is_allowed('66666666-6666-4666-8666-666666666666',
(SELECT id FROM morbac.orgs WHERE name = 'Clinique Valmont Nord'),
'read', 'invoices'); -- falseHis colleague Nathalie Boucher, same role, is unaffected: the prohibition is
bound to a user, not to billing-clerk.
Why priority matters
Priority defaults to 0 (NULL). Ties go to prohibition, so a bare prohibition already beats a bare permission. Setting 100 here makes the intent explicit and survives someone later adding a permission at priority 1.
Choosing the right level of ban
| Scope of the ban | Store | Example |
|---|---|---|
| this user, this org | user_rules | the suspension above |
| this user, every org | global_rules with user_id set | company-wide lockout |
| everyone in a role, this org | rules | freeze billing during an audit |
| everyone, everywhere | global_rules with user_id NULL | emergency stop on a view |
An emergency stop on a whole view, all orgs:
INSERT INTO morbac.global_rules (user_id, activity, view, context_id, modality, priority)
VALUES (NULL, NULL, 'invoices',
(SELECT id FROM morbac.contexts WHERE name = 'always'), 'prohibition', 900)
ON CONFLICT DO NOTHING;Remember it does not stop system principals, by design.
A wildcard stop like this outranks every permission in every organization, so treat it as an incident tool and remove it as soon as the incident is over:
DELETE FROM morbac.global_rules
WHERE user_id IS NULL AND view = 'invoices' AND modality = 'prohibition';Lifting the ban
DELETE FROM morbac.user_rules
WHERE user_id = '66666666-6666-4666-8666-666666666666'
AND modality = 'prohibition' AND view = 'invoices';Access returns instantly; the original role grant was never touched. Enable auditing to keep the paper trail:
SELECT morbac.enable_audit('user_rules');