Obligations & Recommendations
Compliance duties attached to permissions.
Situation. Valmont's compliance officer wants two things recorded in the policy itself, not in a wiki: physicians must countersign lab results they read, and they should attach a motive when they write a prescription.
Assumes the shared Valmont setup.
Obligations and recommendations never block
Both modalities are informational. is_allowed ignores them entirely; they
exist so your application can read the duty out of the same policy store that
grants the permission.
-- duty: countersign lab results
INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality)
SELECT o.id, r.id, 'read', 'lab-results',
(SELECT id FROM morbac.contexts WHERE name = 'always'), 'obligation'
FROM morbac.orgs o JOIN morbac.roles r ON r.org_id = o.id AND r.name = 'physician'
WHERE o.name = 'Clinique Valmont Nord'
ON CONFLICT DO NOTHING;
-- advice: motivate the prescription
INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality)
SELECT o.id, r.id, 'prescribe', 'prescriptions',
(SELECT id FROM morbac.contexts WHERE name = 'always'), 'recommendation'
FROM morbac.orgs o JOIN morbac.roles r ON r.org_id = o.id AND r.name = 'physician'
WHERE o.name = 'Clinique Valmont Nord'
ON CONFLICT DO NOTHING;Reading them back
SELECT * FROM morbac.pending_obligations(
'11111111-1111-4111-8111-111111111111',
(SELECT id FROM morbac.orgs WHERE name = 'Clinique Valmont Nord'));
-- rule_id | role_name | activity | view | context_name | created_at
-- | physician | read | lab-results | always | ...
SELECT * FROM morbac.pending_recommendations(
'11111111-1111-4111-8111-111111111111',
(SELECT id FROM morbac.orgs WHERE name = 'Clinique Valmont Nord'));A UI typically renders obligations as a required checkbox and recommendations as a hint. The permission decision is separate and unchanged:
SELECT morbac.is_allowed('11111111-1111-4111-8111-111111111111',
(SELECT id FROM morbac.orgs WHERE name = 'Clinique Valmont Nord'),
'read', 'lab-results'); -- depends only on permission rulesVoiding semantics
A duty that contradicts a ban is meaningless, so both listing functions filter themselves:
- an obligation is voided by an applicable prohibition whose priority is greater than or equal to it;
- a recommendation is voided by an applicable prohibition, and also by an applicable obligation, since a duty supersedes advice.
Suspend the physicians from lab results and the obligation disappears from the list rather than nagging about an action they cannot take:
INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality, priority)
SELECT o.id, r.id, 'read', 'lab-results',
(SELECT id FROM morbac.contexts WHERE name = 'always'), 'prohibition', 10
FROM morbac.orgs o JOIN morbac.roles r ON r.org_id = o.id AND r.name = 'physician'
WHERE o.name = 'Clinique Valmont Nord'
ON CONFLICT DO NOTHING;
SELECT count(*) FROM morbac.pending_obligations(
'11111111-1111-4111-8111-111111111111',
(SELECT id FROM morbac.orgs WHERE name = 'Clinique Valmont Nord')); -- 0When to use which
| Need | Modality |
|---|---|
| block the action | prohibition |
| allow the action | permission |
| allow, and record a mandatory follow-up | permission + obligation |
| allow, and record advice | permission + recommendation |
Enforcement of an obligation stays in your application: pgmorbac tells you it exists, it does not stop the transaction.