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
    Resolution

    is_allowed

    The full decision algorithm, step by step.

    morbac.is_allowed(user_id, org_id, activity, view) is the single entry point. This page specifies exactly what it does, in order. The uncached twin is_allowed_nocache implements the algorithm; is_allowed adds the cache in front.

    Inputs are expanded first

    • Roles: get_comprehensive_roles(user, org) = direct + delegated + inherited + derived, minus negative assignments.
    • Activities: get_effective_activities(a) = the activity plus every senior activity that implies it.
    • Views: get_effective_views(v) = the view plus its hierarchy expansion.
    • Org scope: each org rule matches when org_in_scope(asked_org, rule_org, rule_scope) holds, so subtree rules apply without copies.

    The steps

    Steps 1-3.5 collect the highest-priority applicable prohibition; steps 4-6.5 collect the highest-priority applicable permission. "Applicable" means: matches the expanded roles/activities/views, its validity window covers now (is_rule_valid), and its context evaluates true. Within each store, candidates are visited in descending priority and the first whose context passes wins that store.

    StepStoreNotes
    1rules (prohibition)org scope resolved via org_in_scope
    2cross_org_rules (prohibition)role held in source_org_id, resource in target_org_id
    3user_rules (prohibition)direct user bans in this org
    3.5global_rules (prohibition)NULL columns are wildcards
    4rules (permission)
    5cross_org_rules (permission)
    6user_rules (permission)
    6.5global_rules (permission)the wildcard-admin path

    Step 7: priority resolution

    no prohibition found        -> allow iff any permission found
    prohibition at priority P   -> allow iff a permission has priority > P

    Equal priority denies: ties go to prohibition, and no rules at all means deny. The engine fails closed in both directions.

    System principals

    For users registered in morbac.system_principals, steps 1-3.5 are skipped entirely: no prohibition of any kind can stop them. They still need a permission from steps 4-6.5.

    NULL org

    Calling is_allowed(user, NULL, activity, view) evaluates only the global stores (org, cross-org and user rules all require an org). This is how global resources with no organization are authorized.

    Debugging a decision

    -- bypass the cache
    SELECT morbac.is_allowed_nocache(:user, :org, 'read', 'prescriptions');
     
    -- what roles did the engine see?
    SELECT r.name, cr.source FROM morbac.get_comprehensive_roles(:user, :org) cr
    JOIN morbac.roles r ON r.id = cr.role_id;
     
    -- which rules could match?
    SELECT * FROM morbac.rules
    WHERE activity IN (SELECT activity FROM morbac.get_effective_activities('read'))
      AND view IN (SELECT view FROM morbac.get_effective_views('prescriptions'));

    Every function used above, and the rest of the morbac API, is listed in the function reference.

    PreviousDecision CacheNext Org Hierarchies
    pgmorbac - Multi-OrBAC permission engine for PostgreSQL
    DocumentationDownloadGitea