docs: cleanup documentation files

This commit is contained in:
2026-02-20 11:01:32 +01:00
parent 2d5f4ca669
commit b68bded4bb
5 changed files with 183 additions and 288 deletions
+22 -24
View File
@@ -146,9 +146,9 @@ VALUES (org_id, role_id, 'write', 'sensitive_data', ctx_id, 'permission',
INSERT INTO morbac.delegations (delegator_user_id, delegate_user_id, role_id, org_id, valid_until)
VALUES (alice_id, bob_id, role_id, org_id, NOW() + INTERVAL '7 days');
-- Separation of Duty: Preparer and approver are mutually exclusive
-- Separation of Duty: Invoice creator and approver roles are mutually exclusive
INSERT INTO morbac.sod_conflicts (role1_id, role2_id, org_id)
VALUES (preparer_role_id, approver_role_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, modality)
@@ -185,15 +185,13 @@ See [src/README.md](src/README.md) for source code organization.
## Documentation
- [DOCUMENTATION.md](docs/DOCUMENTATION.md) - Architecture, API reference, integration guides
- [PERFORMANCE.md](docs/PERFORMANCE.md) - Performance optimization and caching guide
- [ADMIN_GUIDE.md](docs/ADMIN_GUIDE.md) - Organization administrator setup and delegation
- [DEVELOPMENT.md](docs/DEVELOPMENT.md) - Development workflow and version management
- [SECURITY.md](docs/SECURITY.md) - Security policy
- [CONTRIBUTING.md](docs/CONTRIBUTING.md) - Contribution guidelines
- [src/README.md](src/README.md) - Source code organization
- [tests/README.md](tests/README.md) - Test structure and setup
- [CHANGELOG.md](CHANGELOG.md) - Version history
- [DOCUMENTATION.md](docs/DOCUMENTATION.md): Architecture, API reference, integration guides
- [PERFORMANCE.md](docs/PERFORMANCE.md): Performance optimization and caching guide
- [ADMIN_GUIDE.md](docs/ADMIN_GUIDE.md): Organization administrator setup and delegation
- [DEVELOPMENT.md](docs/DEVELOPMENT.md): Development workflow and version management
- [SECURITY.md](SECURITY.md): Security policy
- [CONTRIBUTING.md](CONTRIBUTING.md): Contribution guidelines
- [CHANGELOG.md](CHANGELOG.md): Version history
## Architecture
@@ -201,8 +199,8 @@ Authorization decision flow:
1. Collect all roles for user (direct, delegated, derived, hierarchy)
2. Filter out negative role assignments
3. Check for prohibitions - if found, deny access
4. Check for permissions - if found, allow access
3. Check for prohibitions: if found, deny access
4. Check for permissions: if found, allow access
5. Default deny
Prohibitions always override permissions.
@@ -216,7 +214,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
## Contributing
See [CONTRIBUTING.md](docs/CONTRIBUTING.md) for guidelines.
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
```bash
git clone https://github.com/yourusername/pg_morbac.git
@@ -242,16 +240,16 @@ git push --tags
- Read the [documentation](docs/DOCUMENTATION.md)
- Report bugs via [GitHub Issues](https://github.com/yourusername/pg_morbac/issues)
- Ask questions in [Discussions](https://github.com/yourusername/pg_morbac/discussions)
- Security issues: see [SECURITY.md](docs/SECURITY.md)
- Security issues: see [SECURITY.md](SECURITY.md)
## Comparison with Traditional RBAC
| Feature | Traditional RBAC | pg_morbac |
|---------|-----------------|-----------|
| Multi-tenancy | No | Yes |
| Prohibitions | No | Yes |
| Context-aware | No | Yes |
| Hierarchies | Basic roles only | Organizations, roles, activities, views |
| Delegation | No | Yes |
| Separation of Duty | No | Yes |
| Cross-organization | No | Yes |
| Feature | Traditional RBAC | Traditional OrBAC | pg_morbac (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 |
+13 -18
View File
@@ -13,19 +13,14 @@ Multi-OrBAC allows you to delegate administrative capabilities to specific roles
## Administration Rules
The `morbac.admin_rules` table defines what administrative actions specific roles can perform:
The `morbac.admin_rules` table defines administrative capabilities for specific roles:
```sql
CREATE TABLE morbac.admin_rules (
id UUID PRIMARY KEY,
org_id UUID REFERENCES morbac.orgs(id),
role_id UUID REFERENCES morbac.roles(id),
admin_activity TEXT, -- 'manage', 'assign_role', etc.
admin_target TEXT, -- 'policies', 'roles', role name, etc.
modality morbac.modality, -- 'permission' or 'prohibition'
context_id UUID REFERENCES morbac.contexts(id)
);
```
**Key columns:**
- `org_id`, `role_id`: Role receiving capabilities
- `admin_activity`: Action type (e.g., 'manage', 'assign_role')
- `admin_target`: Target type (e.g., 'policies', 'roles', specific role name, '*' for wildcard)
- `modality`: Permission or prohibition
- `context_id`: Optional conditional evaluation
## Common Admin Patterns
@@ -375,9 +370,9 @@ SELECT morbac.admin_assign_role(
Multi-OrBAC provides complete delegation of administrative capabilities:
- **No superadmins needed** for day-to-day operations
- **Organization-scoped** - admins only affect their own org
- **Fine-grained** - control exactly what each admin role can do
- **Safe** - automatic constraint validation (SoD, cardinality)
- **Auditable** - all actions go through tracked functions
- **Context-aware** - limit when admin actions can occur
- **No superadmins needed**: for day-to-day operations
- **Organization-scoped**: admins only affect their own org
- **Fine-grained**: control exactly what each admin role can do
- **Safe**: automatic constraint validation (SoD, cardinality)
- **Auditable**: all actions go through tracked functions
- **Context-aware**: limit when admin actions can occur
+7 -7
View File
@@ -25,7 +25,7 @@ When ready to release a new version:
2. **Create release**: Run `make release`
- Reads version from `.control` file
- Copies `pg_morbac.sql` `pg_morbac--X.Y.Z.sql`
- Copies `pg_morbac.sql` to `pg_morbac--X.Y.Z.sql`
3. **Test**: Run `make test` with the versioned file
@@ -49,12 +49,12 @@ These contain only the ALTER/ADD statements needed for the upgrade.
## Make Targets
- `make test` - Test with development file (temporary database)
- `make test` - Full test with persistent database
- `make release` - Generate versioned file from development file
- `make install` - Create release and install in PostgreSQL
- `make cleanup` - Drop test database
- `make help` - Show all targets
- `make test`: Test with development file (temporary database)
- `make test`: Full test with persistent database
- `make release`: Generate versioned file from development file
- `make install`: Create release and install in PostgreSQL
- `make cleanup`: Drop test database
- `make help`: Show all targets
## Benefits
+132 -230
View File
@@ -19,12 +19,12 @@ Complete technical documentation for the Multi-OrBAC PostgreSQL extension.
pg_morbac is built on these principles:
1. **Pure PostgreSQL** - No external dependencies (except pgcrypto)
2. **Schema Isolation** - All objects in `morbac` schema
3. **Default Deny** - No permission = access denied
4. **Prohibition Precedence** - Prohibitions checked first, always override permissions
5. **Organization-Centric** - All policies scoped to organizations
6. **Multi-Tenant Native** - Users and resources can span organizations
1. **Pure PostgreSQL**: No external dependencies (except pgcrypto)
2. **Schema Isolation**: All objects in `morbac` schema
3. **Default Deny**: No permission = access denied
4. **Prohibition Precedence**: Prohibitions checked first, always override permissions
5. **Organization-Centric**: All policies scoped to organizations
6. **Multi-Tenant Native**: Users and resources can span organizations
### Authorization Flow
@@ -92,160 +92,108 @@ erDiagram
### Core Tables
**morbac.orgs** - Organizations with hierarchy support (`parent_id` self-reference). Unique name required. Supports metadata as JSONB.
**morbac.orgs**: Organizations with hierarchy support (`parent_id` self-reference). Unique name required. Supports metadata as JSONB.
**morbac.roles** - Roles scoped to organizations. Unique `(org_id, name)` constraint ensures role names are unique within each org.
**morbac.roles**: Roles scoped to organizations. Unique `(org_id, name)` constraint ensures role names are unique within each org.
**morbac.role_hierarchy** - Role inheritance via `(senior_role_id, junior_role_id)`. Senior roles inherit all junior permissions. Supports transitive closure.
**morbac.role_hierarchy**: Role inheritance via `(senior_role_id, junior_role_id)`. Senior roles inherit all junior permissions. Supports transitive closure.
**morbac.user_roles** - Direct user-to-role assignments. Primary key on `(user_id, role_id, org_id)`. Note: `user_id` is external (application manages users).
**morbac.user_roles**: Direct user-to-role assignments. Primary key on `(user_id, role_id, org_id)`. Note: `user_id` is external (application manages users).
**morbac.activities** - Abstract actions (global scope). Text primary key. Examples: `read`, `write`, `delete`, `approve`.
**morbac.activities**: Abstract actions (global scope). Text primary key. Examples: `read`, `write`, `delete`, `approve`.
**morbac.activity_hierarchy** - Activity inheritance via `(parent_activity, child_activity)`. Permission to parent grants child activities.
**morbac.activity_hierarchy**: Activity inheritance via `(parent_activity, child_activity)`. Permission to parent grants child activities.
**morbac.views** - Abstract object categories (global scope). Text primary key. Examples: `documents`, `reports`, `financial_data`.
**morbac.views**: Abstract object categories (global scope). Text primary key. Examples: `documents`, `reports`, `financial_data`.
**morbac.view_hierarchy** - View inheritance via `(parent_view, child_view)`. Access to parent grants child views.
**morbac.view_hierarchy**: View inheritance via `(parent_view, child_view)`. Access to parent grants child views.
**morbac.contexts** - Contextual conditions as callable predicates. Column `evaluator` (REGPROC) references a function returning BOOLEAN (preferably STABLE). Built-in context `always` returns true.
**morbac.contexts**: Contextual conditions as callable predicates. Column `evaluator` (REGPROC) references a function returning BOOLEAN (preferably STABLE). Built-in context `always` returns true.
**morbac.rules** - Core policy rules linking org, role, activity, view, context, and modality. Indexed on `(org_id, role_id, activity, view, modality)` for fast lookups.
**morbac.rules**: Core policy rules linking org, role, activity, view, context, and modality. Indexed on `(org_id, role_id, activity, view, modality)` for fast lookups.
**morbac.policy** - Policy DSL using names instead of UUIDs. Insert here, then call `compile_policy()` to generate rules.
**morbac.policy**: Policy DSL using names instead of UUIDs. Insert here, then call `compile_policy()` to generate rules.
### Advanced Feature Tables
#### morbac.delegations
**morbac.delegations**
Temporal role delegation.
Temporal role delegation with time bounds.
```sql
CREATE TABLE morbac.delegations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
delegator_user_id UUID NOT NULL,
delegate_user_id UUID NOT NULL,
role_id UUID NOT NULL REFERENCES morbac.roles(id) ON DELETE CASCADE,
org_id UUID NOT NULL REFERENCES morbac.orgs(id) ON DELETE CASCADE,
valid_from TIMESTAMPTZ NOT NULL DEFAULT now(),
valid_until TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CHECK (valid_until IS NULL OR valid_until > valid_from)
);
```
**Key columns:**
- `delegator_user_id`: User granting the role
- `delegate_user_id`: User receiving the role
- `role_id`, `org_id`: Role being delegated
- `valid_from`, `valid_until`: Time window (NULL = indefinite)
**Columns:**
- `valid_from` - Delegation start time
- `valid_until` - Optional end time (NULL = indefinite)
**Behavior:** Automatically included in `get_comprehensive_roles()` when active.
**Automatic handling:** Included in `get_comprehensive_roles()` when active.
**morbac.negative_role_assignments**
#### morbac.negative_role_assignments
Explicit role prohibitions with highest precedence.
Explicit role prohibitions.
**Key columns:**
- `user_id`, `role_id`, `org_id`: Assignment to prohibit
- `reason`: Explanation for prohibition
```sql
CREATE TABLE morbac.negative_role_assignments (
user_id UUID NOT NULL,
role_id UUID NOT NULL REFERENCES morbac.roles(id) ON DELETE CASCADE,
org_id UUID NOT NULL REFERENCES morbac.orgs(id) ON DELETE CASCADE,
reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (user_id, role_id, org_id)
);
```
**Behavior:** Overrides direct assignments, delegations, and derived roles.
**Precedence:** Overrides direct assignments, delegations, and derived roles.
**morbac.sod_conflicts**
#### morbac.sod_conflicts
Separation of Duty constraints enforce mutually exclusive roles.
Separation of Duty constraints.
**Key columns:**
- `role1_id`, `role2_id`: Conflicting role pair
- `org_id`: Organization scope
- `description`: Explanation of conflict
```sql
CREATE TABLE morbac.sod_conflicts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
role1_id UUID NOT NULL REFERENCES morbac.roles(id) ON DELETE CASCADE,
role2_id UUID NOT NULL REFERENCES morbac.roles(id) ON DELETE CASCADE,
org_id UUID NOT NULL REFERENCES morbac.orgs(id) ON DELETE CASCADE,
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(role1_id, role2_id, org_id),
CHECK (role1_id < role2_id)
);
```
**Behavior:** Prevents users from holding both roles simultaneously. Validated via `check_sod_violation()` before role assignment.
**Usage:** Validated via `check_sod_violation()` before role assignment.
**morbac.role_cardinality**
#### morbac.role_cardinality
Constrains the number of users per role.
Min/max users per role.
**Key columns:**
- `role_id`, `org_id`: Role to constrain
- `min_users`: Minimum required users (default 0)
- `max_users`: Maximum allowed users (NULL = unlimited)
```sql
CREATE TABLE morbac.role_cardinality (
role_id UUID PRIMARY KEY REFERENCES morbac.roles(id) ON DELETE CASCADE,
org_id UUID NOT NULL REFERENCES morbac.orgs(id) ON DELETE CASCADE,
min_users INTEGER NOT NULL DEFAULT 0,
max_users INTEGER,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CHECK (min_users >= 0),
CHECK (max_users IS NULL OR max_users >= min_users)
);
```
**Behavior:** Validated via `check_cardinality_violation()` before role assignment.
**Usage:** Validated via `check_cardinality_violation()`.
**morbac.derived_roles**
#### morbac.derived_roles
Dynamically computed roles via custom functions.
Dynamically computed roles.
**Key columns:**
- `role_id`, `org_id`: Role to compute
- `evaluator`: Function returning `TABLE(user_id UUID, org_id UUID)`
- `description`: Explanation of computation logic
```sql
CREATE TABLE morbac.derived_roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
role_id UUID NOT NULL REFERENCES morbac.roles(id) ON DELETE CASCADE,
org_id UUID NOT NULL REFERENCES morbac.orgs(id) ON DELETE CASCADE,
evaluator REGPROC NOT NULL,
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```
**Behavior:** Function is called at runtime to determine role membership. Included in `get_comprehensive_roles()`.
**Columns:**
- `evaluator` - Function returning `TABLE(user_id UUID, org_id UUID)`
**morbac.cross_org_rules**
#### morbac.cross_org_rules
Inter-organizational access policies.
Inter-organizational rules.
**Key columns:**
- `source_org_id`: Organization where role is held
- `target_org_id`: Organization where access is granted
- `role_id`, `activity`, `view`: Policy specification
- `modality`: Permission or prohibition
```sql
CREATE TABLE morbac.cross_org_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source_org_id UUID NOT NULL REFERENCES morbac.orgs(id) ON DELETE CASCADE,
target_org_id UUID NOT NULL REFERENCES morbac.orgs(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES morbac.roles(id) ON DELETE CASCADE,
activity TEXT NOT NULL REFERENCES morbac.activities(name) ON DELETE CASCADE,
view TEXT NOT NULL REFERENCES morbac.views(name) ON DELETE CASCADE,
modality morbac.modality NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```
**Behavior:** Allows roles in source organization to access resources in target organization.
**Usage:** Allows roles in `source_org` to access resources in `target_org`.
**morbac.admin_rules**
#### morbac.admin_rules
Administration meta-policies for delegated management.
Administration meta-policies.
**Key columns:**
- `org_id`, `role_id`: Role receiving admin capabilities
- `can_manage_policies`: Can create/modify policies
- `can_manage_roles`: Can create/modify roles
- `can_manage_users`: Can assign/revoke user roles
```sql
CREATE TABLE morbac.admin_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
org_id UUID NOT NULL REFERENCES morbac.orgs(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES morbac.roles(id) ON DELETE CASCADE,
can_manage_policies BOOLEAN NOT NULL DEFAULT false,
can_manage_roles BOOLEAN NOT NULL DEFAULT false,
can_manage_users BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(org_id, role_id)
);
**Behavior:** Enables organization-scoped administrators without database superuser privileges.
```
## Core Concepts
@@ -254,10 +202,10 @@ CREATE TABLE morbac.admin_rules (
Multi-OrBAC implements four modalities:
1. **Permission** - Allows action (if no prohibition)
2. **Prohibition** - Denies action (always wins)
3. **Obligation** - Must be done (informational only)
4. **Recommendation** - Should be done (informational only)
1. **Permission**: Allows action (if no prohibition)
2. **Prohibition**: Denies action (always wins)
3. **Obligation**: Must be done (informational only)
4. **Recommendation**: Should be done (informational only)
Only permissions and prohibitions affect `is_allowed()` decisions.
@@ -373,41 +321,42 @@ VALUES (user_uuid, admin_role_id, org_id, 'Security audit requirement');
### Separation of Duty
Mutually exclusive role constraints:
Prevents users from holding conflicting roles that could enable fraud or abuse.
**Example: Financial Controls**
In an accounting system, the same person should not both create and approve invoices:
```sql
INSERT INTO morbac.sod_conflicts (role1_id, role2_id, org_id, description)
VALUES (preparer_role_id, approver_role_id, org_id, 'Cannot prepare and approve same transaction');
-- Create roles
INSERT INTO morbac.roles (org_id, name) VALUES
(org_id, 'invoice_creator'),
(org_id, 'invoice_approver');
SELECT morbac.check_sod_violation(user_uuid, org_id);
-- Define conflict
INSERT INTO morbac.sod_conflicts (role1_id, role2_id, org_id, description)
VALUES (creator_role_id, approver_role_id, org_id, 'Financial controls: prevent self-approval');
-- Check before assignment
SELECT morbac.check_sod_violation(alice_uuid, org_id);
-- Returns: empty array if valid, or ['invoice_creator', 'invoice_approver'] if conflict exists
```
If Alice already has the `invoice_creator` role, attempting to assign `invoice_approver` will violate the constraint.
### Cardinality Constraints
Min/max users per role:
Enforce minimum and maximum users per role:
```sql
-- Require 1-3 administrators
INSERT INTO morbac.role_cardinality (role_id, org_id, min_users, max_users)
VALUES (admin_role_id, org_id, 1, 3);
-- Validate before assignment
SELECT morbac.check_cardinality_violation(admin_role_id, org_id);
-- Returns: error message if constraint violated, NULL if valid
```
Check before INSERT INTO user_roles
```sql
-- Require 1-3 admins
INSERT INTO morbac.role_cardinality (role_id, org_id, min_users, max_users)
VALUES (admin_role_id, org_id, 1, 3);
-- Validate
SELECT morbac.check_cardinality_violation(admin_role_id, org_id);
-- Returns: TEXT (error message) or NULL (valid)
```
### Derived Roles
```sql
-- Define evaluator
### Derived Roles
Dynamically computed roles via custom functions:
@@ -591,55 +540,55 @@ WHERE table_name = 'rules'
### Authorization Functions
**`is_allowed(user_id, org_id, activity, view)`** - Main authorization decision. Returns BOOLEAN. Checks prohibitions first, then permissions, defaults to deny.
**`is_allowed(user_id, org_id, activity, view)`**: Main authorization decision. Returns BOOLEAN. Checks prohibitions first, then permissions, defaults to deny.
```sql
SELECT morbac.is_allowed(user_uuid, org_uuid, 'read', 'documents');
```
**`get_comprehensive_roles(user_id, org_id)`** - Returns all roles for user (direct, delegated, derived, hierarchy, minus negative assignments).
**`get_comprehensive_roles(user_id, org_id)`**: Returns all roles for user (direct, delegated, derived, hierarchy, minus negative assignments).
**`get_effective_roles(user_id, org_id)`** - Alias for `get_comprehensive_roles()`.
**`get_effective_roles(user_id, org_id)`**: Alias for `get_comprehensive_roles()`.
### Hierarchy Functions
**`get_org_ancestors(org_id)`** - Returns all parent organizations with depth.
**`get_org_ancestors(org_id)`**: Returns all parent organizations with depth.
**`get_org_descendants(org_id)`** - Returns all child organizations with depth.
**`get_org_descendants(org_id)`**: Returns all child organizations with depth.
**`get_inherited_roles(role_id)`** - Returns all junior roles (transitive).
**`get_inherited_roles(role_id)`**: Returns all junior roles (transitive).
**`get_effective_activities(activity)`** - Returns activity plus all child activities.
**`get_effective_activities(activity)`**: Returns activity plus all child activities.
**`get_effective_views(view)`** - Returns view plus all child views.
**`get_effective_views(view)`**: Returns view plus all child views.
### Validation Functions
**`check_sod_violation(user_id, org_id)`** - Returns TEXT[] of conflicting role pairs or empty array.
**`check_sod_violation(user_id, org_id)`**: Returns empty array if valid, or array of conflicting role name pairs if violations exist.
**`check_cardinality_violation(role_id, org_id)`** - Returns error message or NULL if valid.
**`check_cardinality_violation(role_id, org_id)`**: Returns error message if constraint violated, NULL if valid.
### Administration Functions
**`is_admin_allowed(user_id, org_id, capability)`** - Check administrative permissions. Capabilities: `manage_policies`, `manage_roles`, `manage_users`.
**`is_admin_allowed(user_id, org_id, capability)`**: Check administrative permissions. Capabilities: `manage_policies`, `manage_roles`, `manage_users`.
**`eval_derived_role(user_id, org_id, derived_role_id)`** - Evaluate if user has derived role.
**`eval_derived_role(user_id, org_id, derived_role_id)`**: Evaluate if user has derived role.
### Context Functions
**`eval_context(context_id)`** - Evaluate a context predicate.
**`eval_context(context_id)`**: Evaluate a context predicate.
### Policy DSL Functions
**`compile_policy()`** - Compile policy DSL into rules. Returns TABLE with success status and messages. Idempotent.
**`compile_policy()`**: Compile policy DSL into rules. Returns TABLE with success status and messages. Idempotent.
### RLS Helper Functions
**`current_user_id()`** - Get user ID from `request.header.x-user-id` (PostgREST) or `current_setting('morbac.user_id')`.
**`current_user_id()`**: Get user ID from `request.header.x-user-id` (PostgREST) or `current_setting('morbac.user_id')`.
**`current_org_id()`** - Get org ID from `request.header.x-org-id` (PostgREST) or `current_setting('morbac.org_id')`.
**`current_org_id()`**: Get org ID from `request.header.x-org-id` (PostgREST) or `current_setting('morbac.org_id')`.
**`rls_check(activity, view)`** - Authorization check for RLS policies using current user/org context.
**`rls_check(activity, view)`**: Authorization check for RLS policies using current user/org context.
```sql
CREATE POLICY my_policy ON app.table
@@ -648,11 +597,11 @@ FOR SELECT USING (morbac.rls_check('read', 'documents'));
### Informational Functions
**`pending_obligations(user_id, org_id)`** - Returns obligations for user (informational only).
**`pending_obligations(user_id, org_id)`**: Returns obligations for user (informational only).
**`pending_recommendations(user_id, org_id)`** - Returns recommendations for user (informational only).
**`pending_recommendations(user_id, org_id)`**: Returns recommendations for user (informational only).
**`user_has_role(user_id, org_id, role_name)`** - Check if user has specific role by name.
**`user_has_role(user_id, org_id, role_name)`**: Check if user has specific role by name.
```sql
morbac.user_has_role(
@@ -666,9 +615,7 @@ morbac.user_has_role(
### PostgREST Integration
#### 1. Setup Headers
Configure PostgREST to pass user/org context:
Configure PostgREST to pass user/org context via headers:
```nginx
# Nginx config
@@ -676,18 +623,17 @@ proxy_set_header X-User-Id $user_id;
proxy_set_header X-Org-Id $org_id;
```
#### 2. Enable RLS
Enable RLS and grant permissions:
```sql
ALTER TABLE app.documents ENABLE ROW LEVEL SECURITY;
-- Grant usage to PostgREST role
GRANT USAGE ON SCHEMA morbac TO postgrest_role;
GRANT SELECT ON ALL TABLES IN SCHEMA morbac TO postgrest_role;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA morbac TO postgrest_role;
```
#### 3. Create RLS Policies
Create RLS policies:
```sql
CREATE POLICY document_read ON app.documents
@@ -699,37 +645,13 @@ FOR INSERT
WITH CHECK (morbac.rls_check('write', 'documents'));
```
#### 4. Set Context (alternative to headers)
Alternative method using session variables:
```sql
-- In application connection
SET morbac.user_id = '123e4567-e89b-12d3-a456-426614174000';
SET morbac.org_id = '987fcdeb-51a2-43d7-9c6e-5a8b7c9d0e1f';
```
## Integration Guides
### PostgREST Integration
Configure PostgREST to pass headers, enable RLS, and create policies:
```sql
-- Enable RLS
ALTER TABLE app.documents ENABLE ROW LEVEL SECURITY;
GRANT USAGE ON SCHEMA morbac TO postgrest_role;
GRANT SELECT ON ALL TABLES IN SCHEMA morbac TO postgrest_role;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA morbac TO postgrest_role;
-- Create RLS policies
CREATE POLICY document_read ON app.documents
FOR SELECT USING (morbac.rls_check('read', 'documents'));
-- Alternative: Set context directly
SET morbac.user_id = '123e4567...';
SET morbac.org_id = '987fcdeb...';
```
### Application Integration
Python example:
@@ -754,19 +676,17 @@ const res = await client.query('SELECT * FROM app.documents');
### Multi-Organization Resources
Resources can belong to multiple organizations:
Resources can belong to multiple organizations using a junction table:
```sql
CREATE TABLE app.document_orgs (
document_id UUID,
org_id UUID,
PRIMARY KEY (document_id, org_id)
);
-- Application defines document-org relationships
-- morbac.rls_check() enforces access rules
CREATE POLICY doc_access ON app.documents FOR SELECT USING (
EXISTS (SELECT 1 FROM app.document_orgs
EXISTS (
SELECT 1 FROM app.document_orgs
WHERE document_id = app.documents.id
AND org_id = morbac.current_org_id())
AND org_id = morbac.current_org_id()
)
AND morbac.rls_check('read', 'documents')
);
```
@@ -792,12 +712,6 @@ Context optimization:
- Keep context logic simple
All foreign keys are indexed. Critical composite indexes exist on `(user_id, org_id)` and `(org_id, role_id, activity, view, modality)`.
CREATE INDEX idx_rules_lookup ON morbac.rules(org_id, role_id, activity, view, modality);
-- Hierarchy traversal
CREATE INDEX idx_role_hierarchy_senior ON morbac.role_hierarchy(senior_role_id);
CREATE INDEX idx_role_hierarchy_junior ON morbac.role_hierarchy(junior_role_id);
```
### Benchmarking
@@ -837,7 +751,7 @@ Not protected against:
### Security Best Practices
#### 1. Context Function Security
**Context Function Security**
```sql
-- BAD: Leaks information
@@ -853,7 +767,7 @@ RETURNS BOOLEAN STABLE AS $$
$$;
```
#### 2. User ID Validation
**User ID Validation**
```sql
-- Validate user exists before authorization
@@ -865,7 +779,7 @@ SELECT morbac.is_allowed(
);
```
#### 3. Org Context Validation
**Org Context Validation**
```sql
-- Verify user belongs to org
@@ -875,18 +789,6 @@ SELECT EXISTS(
);
```
#### 4. Delegation Auditing
```sql
-- Log delegations
CREATE TABLE app.delegation_audit (
delegation_id UUID REFERENCES morbac.delegations(id),
action TEXT,
by_user UUID,
at_time TIMESTAMPTZ DEFAULT now()
);
```
### Comparison: Multi-OrBAC vs Traditional RBAC
| Aspect | Traditional RBAC | Multi-OrBAC (pg_morbac) |
@@ -905,7 +807,7 @@ CREATE TABLE app.delegation_audit (
## Additional Resources
- [Multi-OrBAC Research Paper](https://webhost.laas.fr/TSF/deswarte/Publications/06427.pdf)
- [test_morbac.sql](test_morbac.sql) - Comprehensive examples
- [CHANGELOG.md](CHANGELOG.md) - Version history
- [CONTRIBUTING.md](CONTRIBUTING.md) - Contribution guidelines
- [SECURITY.md](SECURITY.md) - Security policy
- [test_morbac.sql](test_morbac.sql): Comprehensive examples
- [CHANGELOG.md](CHANGELOG.md): Version history
- [CONTRIBUTING.md](CONTRIBUTING.md): Contribution guidelines
- [SECURITY.md](SECURITY.md): Security policy
+8 -8
View File
@@ -6,11 +6,11 @@ This guide explains the performance optimizations built into pg_morbac and how t
pg_morbac includes several layers of optimization:
1. **Materialized Views** - Precomputed hierarchy transitive closures
2. **Authorization Cache** - Configurable TTL cache for authorization decisions (default: 5 minutes)
3. **Composite Indexes** - Optimized indexes for common query patterns
4. **Partial Indexes** - Indexes only on active (temporally valid) rules
5. **Configurable Settings** - All magic numbers centralized in morbac.config table
1. **Materialized Views**: Precomputed hierarchy transitive closures
2. **Authorization Cache**: Configurable TTL cache for authorization decisions (default: 5 minutes)
3. **Composite Indexes**: Optimized indexes for common query patterns
4. **Partial Indexes**: Indexes only on active (temporally valid) rules
5. **Configurable Settings**: All magic numbers centralized in morbac.config table
## Initial Setup
@@ -42,9 +42,9 @@ SELECT morbac.refresh_hierarchy_cache();
```
**Available configuration keys:**
- `cache_ttl_seconds` - Authorization cache TTL in seconds (default: 300)
- `hierarchy_max_depth` - Maximum hierarchy depth to prevent infinite loops (default: 10)
- `enable_audit_by_default` - Whether to enable audit logging on installation (default: false)
- `cache_ttl_seconds`: Authorization cache TTL in seconds (default: 300)
- `hierarchy_max_depth`: Maximum hierarchy depth to prevent infinite loops (default: 10)
- `enable_audit_by_default`: Whether to enable audit logging on installation (default: false)
## Using Cached Authorization