pgmorbacMulti-OrBAC for PostgreSQL
    DocumentationDownloadSource
    Use cases
    • How do I...?
    Getting Started
    • Introduction
    • Quick start
    • How pgmorbac compares
    • Upgrade
    Concepts
    • Organizations
    • Roles
    • Activities
    • Views
    • Contexts
    • Rules & Modalities
    • Global Rules
    • System Principals
    • Constraints
    • Row-Level Security
    • Decision Cache
    Resolution
    • is_allowed
    Recipes
    • Org Hierarchies
    • Multi-Tenant Isolation
    • Wildcard Admin
    • Delegation
    • Service Accounts
    • Deny Rules
    • Separation of Duty
    • Derived Roles
    • Cross-Org Access
    • Obligations & Recommendations
    • Temporal Access
    Integration
    • Fastify
    • PostgREST & RLS Context
    • SQL Only
    Reference
    • Function Reference
    Getting Started

    Quick start

    From empty database to an enforced permission in five minutes.

    From an empty database to an enforced permission in about five minutes. Every step is plain SQL; there is nothing to compile and no service to run.

    1. Install the schema

    unzip pgmorbac-0.1.0.zip && cd pgmorbac-0.1.0
    make install                      # uses pg_config / PGXS
    CREATE EXTENSION pgmorbac;

    No filesystem access to the server (managed PostgreSQL)? Run the script directly instead; it produces the identical schema:

    psql -v ON_ERROR_STOP=1 -d mydb -f pgmorbac--0.1.0.sql

    Running a Node.js backend? The @crudy/pgmorbac npm package ships the same SQL, so a migration runner can apply it at boot instead of shelling out to psql:

    npm install @crudy/pgmorbac
    SELECT morbac.get_config('schema_version');   -- 0.1.0

    2. Describe your organizations

    INSERT INTO morbac.orgs (name) VALUES ('Groupe Hospitalier Valmont');
    INSERT INTO morbac.orgs (name, parent_id)
    SELECT 'Clinique Valmont Nord', id FROM morbac.orgs
    WHERE name = 'Groupe Hospitalier Valmont';

    3. Declare what exists and who acts

    Activities are actions, views are categories of resources, roles are scoped to an organization:

    INSERT INTO morbac.views (name) VALUES ('patients') ON CONFLICT DO NOTHING;
     
    INSERT INTO morbac.roles (org_id, name)
    SELECT id, 'nurse' FROM morbac.orgs WHERE name = 'Clinique Valmont Nord';
     
    INSERT INTO morbac.user_roles (user_id, role_id, org_id)
    SELECT '22222222-2222-4222-8222-222222222222', 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 = 'nurse';

    read, create, update and delete already exist; add your own domain verbs whenever an action deserves one.

    4. Write the rule

    INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality, scope)
    SELECT o.id, r.id, 'read', 'patients',
           (SELECT id FROM morbac.contexts WHERE name = 'always'),
           'permission', 'subtree'
    FROM morbac.orgs o
    JOIN morbac.roles r ON r.org_id = o.id AND r.name = 'nurse'
    WHERE o.name = 'Clinique Valmont Nord';

    5. Ask

    SELECT morbac.is_allowed(
        '22222222-2222-4222-8222-222222222222',
        (SELECT id FROM morbac.orgs WHERE name = 'Clinique Valmont Nord'),
        'read', 'patients');                                  -- true
     
    SELECT morbac.is_allowed(
        '22222222-2222-4222-8222-222222222222',
        (SELECT id FROM morbac.orgs WHERE name = 'Groupe Hospitalier Valmont'),
        'read', 'patients');                                  -- false

    That second answer is the point: nothing is granted implicitly, and a subtree scope points downward, never up.

    6. Let the database enforce it

    Answering questions is useful; enforcing them per row is the reason to keep authorization in the database:

    ALTER TABLE app.patients ENABLE ROW LEVEL SECURITY;
     
    CREATE POLICY patients_select ON app.patients FOR SELECT
    USING (morbac.rls_check('read', 'patients', org_id));
     
    GRANT USAGE ON SCHEMA morbac TO my_app_role;
    GRANT EXECUTE ON FUNCTION morbac.rls_check(TEXT, TEXT, UUID, UUID) TO my_app_role;

    Then set the acting user for each request, transaction-locally:

    SELECT set_config('morbac.user_id', $1, true),
           set_config('morbac.org_id',  $2, true);

    Now SELECT * FROM app.patients returns only the rows that user may see, from any client.

    Where to go next

    • How do I...? - the situation catalogue, searchable and filterable. Start here if you have a concrete problem.
    • How pgmorbac compares - RBAC, ABAC, ReBAC, PBAC, and where this fits.
    • Concepts - the model, one page per idea.
    • Upgrading - version stamps and upgrade scripts.

    What just got created

    The morbac schema only: organizations, roles and assignments, activities and views with their hierarchies, contexts, four rule stores, constraint tables, the decision cache and the is_allowed family. Nothing outside it is touched.

    PreviousIntroductionNext How pgmorbac compares
    pgmorbac - Multi-OrBAC permission engine for PostgreSQL
    DocumentationDownloadGitea