npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@crudy/pgmorbac

v1.0.0

Published

Multi-OrBAC for PostgreSQL: the pgmorbac extension SQL, packaged for Node.js projects and migration runners

Readme

A PostgreSQL extension implementing the Multi-OrBAC access control model - enabling organization-centric, context-aware, and hierarchical access control with advanced features like delegation, separation of duty, and cross-organizational policies.

Features

  • Multi-organization with organizational hierarchy
  • Unattributed (no-org) objects as a first-class rule target
  • Role-based access with full hierarchy support
  • Activity and view hierarchies with transitive permission inheritance
  • Prohibition precedence over permissions
  • Temporal delegation with time bounds
  • Temporal constraints on rules with validity periods
  • Separation of duty constraints
  • Derived roles computed from application logic
  • Cross-organizational policies
  • Context-aware rules
  • Audit logging for security-critical operations
  • Row-level security integration

Installation

Prerequisites

  • PostgreSQL 13 or higher
  • Development tools: make, bash

Quick Install

# Clone repository
git clone https://github.com/crudylabs/pgmorbac.git
cd pgmorbac

# Build and install extension
make build          # Build versioned file from src/
sudo make install   # Install to PostgreSQL

# Enable in your database
psql -d mydb -c "CREATE EXTENSION pgmorbac;"

Alternative: Using install script

# Build first
make build

# Run installer
sudo ./tools/install.sh

Manual Install

# Build versioned file from source
make build          # Concatenates src/ files into pgmorbac--1.0.0.sql

# Copy files to PostgreSQL extension directory
sudo cp pgmorbac.control $(pg_config --sharedir)/extension/
sudo cp pgmorbac--1.0.0.sql $(pg_config --sharedir)/extension/

# Enable in PostgreSQL
psql -d mydb -c "CREATE EXTENSION pgmorbac;"

From Node.js (npm)

@crudy/pgmorbac ships the same install and upgrade scripts for projects that apply SQL through a migration runner instead of CREATE EXTENSION:

npm install @crudy/pgmorbac
import { extensionSql, upgradeScripts, version, sqlDir } from '@crudy/pgmorbac';

await client.query(extensionSql());          // pgmorbac--<version>.sql
for (const u of upgradeScripts()) {          // pgmorbac--<from>--<to>.sql, in order
    await client.query(u.sql);
}

Quick Start

-- 1. Create organization
INSERT INTO morbac.orgs (name) VALUES ('Acme Corp');

-- 2. Create role
INSERT INTO morbac.roles (org_id, name)
SELECT id, 'employee' FROM morbac.orgs WHERE name = 'Acme Corp';

-- 3. Assign user to role
INSERT INTO morbac.user_roles (user_id, role_id, org_id)
SELECT
    '00000000-0000-0000-0000-000000000001'::uuid,
    r.id,
    o.id
FROM morbac.orgs o
JOIN morbac.roles r ON r.org_id = o.id
WHERE o.name = 'Acme Corp' AND r.name = 'employee';

-- 4. Define policy
INSERT INTO morbac.policy (org_name, role_name, activity, view, modality)
VALUES ('Acme Corp', 'employee', 'read', 'documents', 'permission');

-- 5. Compile policy
SELECT * FROM morbac.compile_policy();

-- 6. Check authorization
SELECT morbac.is_allowed(
    '00000000-0000-0000-0000-000000000001'::uuid,
    (SELECT id FROM morbac.orgs WHERE name = 'Acme Corp'),
    'read',
    'documents'
); -- Returns: true

-- 7. Initialize performance cache (recommended for production)
SELECT morbac.refresh_hierarchy_cache();

Usage

Authorization Check

-- Production (cached by default)
SELECT morbac.is_allowed(user_id, org_id, activity, view);

-- Debugging (bypasses cache)
SELECT morbac.is_allowed_nocache(user_id, org_id, activity, view);

-- Unattributed object (no org): pass NULL as the org
SELECT morbac.is_allowed(user_id, NULL, activity, view);

-- Capability probe for UI gating (any org, unattributed, or global)
SELECT morbac.has_permission(user_id, activity, view);

See PERFORMANCE.md for optimization details.

Row-Level Security (RLS)

-- Enable RLS on your table
ALTER TABLE app.documents ENABLE ROW LEVEL SECURITY;

-- Table without an org column
CREATE POLICY doc_access ON app.documents
    FOR SELECT
    USING (morbac.rls_check('read', 'documents'));

-- Table with an org column: pass it. A NULL org_id means the record
-- is unattributed (awaiting attribution).
CREATE POLICY doc_access ON app.documents
    FOR SELECT
    USING (morbac.rls_check('read', 'documents', org_id));

Unattributed (no-org) Records

A record whose org_id is NULL is unattributed. Give a role access to that pool without granting anything org-wide:

INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality, scope)
SELECT o.id, r.id, 'read', 'documents', c.id, 'permission', 'unattributed'
FROM morbac.orgs o
JOIN morbac.roles r ON r.org_id = o.id AND r.name = 'triage'
JOIN morbac.contexts c ON c.name = 'always'
WHERE o.name = 'Acme Corp';

Choose which records a query returns:

SET morbac.org_ids = '[null]';              -- unattributed only (attribution queue)
SET morbac.org_ids = '["<org-uuid>", null]';-- that org plus unattributed
SET morbac.org_id  = '<org-uuid>';          -- that org only
-- nothing set                              -- everything authorized, unattributed included

See DOCUMENTATION.md for the full org target vocabulary (a specific organization, unattributed, or all).

Advanced Features

-- Temporal rules: Rule active only during business hours or specific period
INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality, valid_from, valid_until)
VALUES (org_id, role_id, 'write', 'sensitive_data', ctx_id, 'permission',
        '2024-01-01 00:00:00', '2024-12-31 23:59:59');

-- Delegation: Alice delegates role to Bob for 1 week
INSERT INTO morbac.delegations (delegator_id, delegatee_id, role_id, org_id, valid_until)
VALUES (alice_id, bob_id, role_id, org_id, NOW() + INTERVAL '7 days');

-- Separation of Duty: Invoice creator and approver roles are mutually exclusive
INSERT INTO morbac.sod_conflicts (role_a_id, role_b_id, org_id)
VALUES (creator_role_id, approver_role_id, org_id);

-- Cross-Org: Global auditor can access subsidiary
INSERT INTO morbac.cross_org_rules (source_org_id, target_org_id, role_id, activity, view, context_id, modality)
VALUES (global_org_id, subsidiary_org_id, auditor_role_id, 'read', 'reports',
        (SELECT id FROM morbac.contexts WHERE name = 'always'), 'permission');

-- Audit logging: Track changes to user roles
SELECT morbac.enable_audit('user_roles');

-- Query audit log
SELECT * FROM morbac.audit_log
WHERE table_name = 'user_roles'
ORDER BY timestamp DESC
LIMIT 10;

Development & Testing

# Development workflow
# 1. Edit source files in src/ directory
# 2. Build and test
make build         # Build versioned pgmorbac--X.Y.Z.sql from src/
make test          # Build and run test suite

# Manual testing
make build
createdb morbac_test
psql -d morbac_test -f pgmorbac.sql
# ... test manually ...
dropdb morbac_test

See DEVELOPMENT.md for source code organization.

Documentation

Architecture

Authorization decision flow:

  1. Collect all roles for user (direct, delegated, derived, hierarchy)
  2. Filter out negative role assignments
  3. Find the highest-priority applicable prohibition and highest-priority applicable permission
  4. If a permission with strictly higher priority than the prohibition exists, allow
  5. If a prohibition exists (and no higher-priority permission), deny
  6. If no prohibition, allow if any permission was found; otherwise deny

Prohibitions win over permissions at equal or unset priority.

Research

Based on the CNRS research paper: "Extending the OrBAC model to handle multi-organization environments"

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

See CONTRIBUTING.md for guidelines.

git clone https://github.com/crudylabs/pgmorbac.git
cd pgmorbac

# Edit source files in src/
vim src/authorization.sql

# Build and test your changes
make check         # Run test suite

# When ready to release
# 1. Update version in pgmorbac.control
# 2. Build versioned file
make build         # Creates pgmorbac.sql from src/
# 3. Tag in git
git tag v1.0.1
git push --tags

Support

Comparison with Traditional RBAC

| Feature | Traditional RBAC | Traditional OrBAC | pgmorbac (Multi-OrBAC) | |---------|-----------------|-------------------|-------------------------| | Multi-tenancy | No | Single organization | Multi-organization | | Prohibitions | No | Yes | Yes | | Context-aware | No | Yes | Yes | | Hierarchies | Basic roles only | Roles, activities, views | Organizations, roles, activities, views | | Delegation | No | Yes | Yes (temporal) | | Separation of Duty | No | Possible | Database-enforced | | Cross-organization | No | No | Yes |