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
    Concepts

    Decision Cache

    The authorization cache, its TTL, and invalidation triggers.

    is_allowed can be called for every row of a large scan, so its result is cached. morbac.auth_cache stores one row per (user_id, org_id, activity, view) with the boolean decision and an expiry computed from the cache_ttl_seconds config key (default 300 seconds).

    SELECT morbac.get_config('cache_ttl_seconds');   -- 300
    SELECT morbac.set_config('cache_ttl_seconds', '60');

    morbac.is_allowed(...) consults the cache first and falls back to morbac.is_allowed_nocache(...), which always computes from scratch. Use the nocache variant when debugging a surprising decision.

    Invalidation

    You never invalidate manually in normal operation. Triggers on every table that can change a decision (user_roles, rules, user_rules, global_rules, cross_org_rules, delegations, negative assignments, role hierarchy, system principals, contexts, ...) call morbac.invalidate_cache(user_id, org_id) with the narrowest scope they can prove:

    • a role assignment change invalidates that user in that org;
    • a rule change invalidates the whole org;
    • hierarchy changes refresh the materialized closures and flush everything.

    Manual controls exist for completeness:

    SELECT morbac.invalidate_cache(:user_id, :org_id);  -- narrow flush
    SELECT morbac.invalidate_cache();                   -- full flush (both args NULL)
    SELECT morbac.cleanup_auth_cache();                 -- purge expired rows

    What the TTL really bounds

    Trigger-based invalidation covers everything inside the database. What it cannot see is the outside world changing under a context evaluator: if business-hours flips at 19:00, cached true decisions may survive up to TTL seconds past the flip. Size cache_ttl_seconds to the staleness your most dynamic context can tolerate; keep contexts coarse-grained and the default TTL is fine.

    cleanup_auth_cache() is housekeeping, not correctness: expired rows are ignored by reads either way. Schedule it if the table grows large.

    PreviousRow-Level SecurityNext is_allowed
    pgmorbac - Multi-OrBAC permission engine for PostgreSQL
    DocumentationDownloadGitea