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) 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'); 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) 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 -- Cross-Org: Global auditor can access subsidiary
INSERT INTO morbac.cross_org_rules (source_org_id, target_org_id, role_id, activity, view, modality) 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
- [DOCUMENTATION.md](docs/DOCUMENTATION.md) - Architecture, API reference, integration guides - [DOCUMENTATION.md](docs/DOCUMENTATION.md): Architecture, API reference, integration guides
- [PERFORMANCE.md](docs/PERFORMANCE.md) - Performance optimization and caching guide - [PERFORMANCE.md](docs/PERFORMANCE.md): Performance optimization and caching guide
- [ADMIN_GUIDE.md](docs/ADMIN_GUIDE.md) - Organization administrator setup and delegation - [ADMIN_GUIDE.md](docs/ADMIN_GUIDE.md): Organization administrator setup and delegation
- [DEVELOPMENT.md](docs/DEVELOPMENT.md) - Development workflow and version management - [DEVELOPMENT.md](docs/DEVELOPMENT.md): Development workflow and version management
- [SECURITY.md](docs/SECURITY.md) - Security policy - [SECURITY.md](SECURITY.md): Security policy
- [CONTRIBUTING.md](docs/CONTRIBUTING.md) - Contribution guidelines - [CONTRIBUTING.md](CONTRIBUTING.md): Contribution guidelines
- [src/README.md](src/README.md) - Source code organization - [CHANGELOG.md](CHANGELOG.md): Version history
- [tests/README.md](tests/README.md) - Test structure and setup
- [CHANGELOG.md](CHANGELOG.md) - Version history
## Architecture ## Architecture
@@ -201,8 +199,8 @@ Authorization decision flow:
1. Collect all roles for user (direct, delegated, derived, hierarchy) 1. Collect all roles for user (direct, delegated, derived, hierarchy)
2. Filter out negative role assignments 2. Filter out negative role assignments
3. Check for prohibitions - if found, deny access 3. Check for prohibitions: if found, deny access
4. Check for permissions - if found, allow access 4. Check for permissions: if found, allow access
5. Default deny 5. Default deny
Prohibitions always override permissions. Prohibitions always override permissions.
@@ -216,7 +214,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
## Contributing ## Contributing
See [CONTRIBUTING.md](docs/CONTRIBUTING.md) for guidelines. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
```bash ```bash
git clone https://github.com/yourusername/pg_morbac.git git clone https://github.com/yourusername/pg_morbac.git
@@ -242,16 +240,16 @@ git push --tags
- Read the [documentation](docs/DOCUMENTATION.md) - Read the [documentation](docs/DOCUMENTATION.md)
- Report bugs via [GitHub Issues](https://github.com/yourusername/pg_morbac/issues) - Report bugs via [GitHub Issues](https://github.com/yourusername/pg_morbac/issues)
- Ask questions in [Discussions](https://github.com/yourusername/pg_morbac/discussions) - 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 ## Comparison with Traditional RBAC
| Feature | Traditional RBAC | pg_morbac | | Feature | Traditional RBAC | Traditional OrBAC | pg_morbac (Multi-OrBAC) |
|---------|-----------------|-----------| |---------|-----------------|-------------------|-------------------------|
| Multi-tenancy | No | Yes | | Multi-tenancy | No | Single organization | Multi-organization |
| Prohibitions | No | Yes | | Prohibitions | No | Yes | Yes |
| Context-aware | No | Yes | | Context-aware | No | Yes | Yes |
| Hierarchies | Basic roles only | Organizations, roles, activities, views | | Hierarchies | Basic roles only | Roles, activities, views | Organizations, roles, activities, views |
| Delegation | No | Yes | | Delegation | No | Yes | Yes (temporal) |
| Separation of Duty | No | Yes | | Separation of Duty | No | Possible | Database-enforced |
| Cross-organization | No | Yes | | 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 ## 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 **Key columns:**
CREATE TABLE morbac.admin_rules ( - `org_id`, `role_id`: Role receiving capabilities
id UUID PRIMARY KEY, - `admin_activity`: Action type (e.g., 'manage', 'assign_role')
org_id UUID REFERENCES morbac.orgs(id), - `admin_target`: Target type (e.g., 'policies', 'roles', specific role name, '*' for wildcard)
role_id UUID REFERENCES morbac.roles(id), - `modality`: Permission or prohibition
admin_activity TEXT, -- 'manage', 'assign_role', etc. - `context_id`: Optional conditional evaluation
admin_target TEXT, -- 'policies', 'roles', role name, etc.
modality morbac.modality, -- 'permission' or 'prohibition'
context_id UUID REFERENCES morbac.contexts(id)
);
```
## Common Admin Patterns ## Common Admin Patterns
@@ -375,9 +370,9 @@ SELECT morbac.admin_assign_role(
Multi-OrBAC provides complete delegation of administrative capabilities: Multi-OrBAC provides complete delegation of administrative capabilities:
- **No superadmins needed** for day-to-day operations - **No superadmins needed**: for day-to-day operations
- **Organization-scoped** - admins only affect their own org - **Organization-scoped**: admins only affect their own org
- **Fine-grained** - control exactly what each admin role can do - **Fine-grained**: control exactly what each admin role can do
- **Safe** - automatic constraint validation (SoD, cardinality) - **Safe**: automatic constraint validation (SoD, cardinality)
- **Auditable** - all actions go through tracked functions - **Auditable**: all actions go through tracked functions
- **Context-aware** - limit when admin actions can occur - **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` 2. **Create release**: Run `make release`
- Reads version from `.control` file - 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 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 Targets
- `make test` - Test with development file (temporary database) - `make test`: Test with development file (temporary database)
- `make test` - Full test with persistent database - `make test`: Full test with persistent database
- `make release` - Generate versioned file from development file - `make release`: Generate versioned file from development file
- `make install` - Create release and install in PostgreSQL - `make install`: Create release and install in PostgreSQL
- `make cleanup` - Drop test database - `make cleanup`: Drop test database
- `make help` - Show all targets - `make help`: Show all targets
## Benefits ## 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: pg_morbac is built on these principles:
1. **Pure PostgreSQL** - No external dependencies (except pgcrypto) 1. **Pure PostgreSQL**: No external dependencies (except pgcrypto)
2. **Schema Isolation** - All objects in `morbac` schema 2. **Schema Isolation**: All objects in `morbac` schema
3. **Default Deny** - No permission = access denied 3. **Default Deny**: No permission = access denied
4. **Prohibition Precedence** - Prohibitions checked first, always override permissions 4. **Prohibition Precedence**: Prohibitions checked first, always override permissions
5. **Organization-Centric** - All policies scoped to organizations 5. **Organization-Centric**: All policies scoped to organizations
6. **Multi-Tenant Native** - Users and resources can span organizations 6. **Multi-Tenant Native**: Users and resources can span organizations
### Authorization Flow ### Authorization Flow
@@ -92,160 +92,108 @@ erDiagram
### Core Tables ### 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 ### Advanced Feature Tables
#### morbac.delegations **morbac.delegations**
Temporal role delegation. Temporal role delegation with time bounds.
```sql **Key columns:**
CREATE TABLE morbac.delegations ( - `delegator_user_id`: User granting the role
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - `delegate_user_id`: User receiving the role
delegator_user_id UUID NOT NULL, - `role_id`, `org_id`: Role being delegated
delegate_user_id UUID NOT NULL, - `valid_from`, `valid_until`: Time window (NULL = indefinite)
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)
);
```
**Columns:** **Behavior:** Automatically included in `get_comprehensive_roles()` when active.
- `valid_from` - Delegation start time
- `valid_until` - Optional end time (NULL = indefinite)
**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 **Behavior:** Overrides direct assignments, delegations, and derived roles.
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)
);
```
**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 **Behavior:** Prevents users from holding both roles simultaneously. Validated via `check_sod_violation()` before role assignment.
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)
);
```
**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 **Behavior:** Validated via `check_cardinality_violation()` before role assignment.
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)
);
```
**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 **Behavior:** Function is called at runtime to determine role membership. Included in `get_comprehensive_roles()`.
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()
);
```
**Columns:** **morbac.cross_org_rules**
- `evaluator` - Function returning `TABLE(user_id UUID, org_id UUID)`
#### 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 **Behavior:** Allows roles in source organization to access resources in target organization.
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()
);
```
**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 **Behavior:** Enables organization-scoped administrators without database superuser privileges.
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)
);
``` ```
## Core Concepts ## Core Concepts
@@ -254,10 +202,10 @@ CREATE TABLE morbac.admin_rules (
Multi-OrBAC implements four modalities: Multi-OrBAC implements four modalities:
1. **Permission** - Allows action (if no prohibition) 1. **Permission**: Allows action (if no prohibition)
2. **Prohibition** - Denies action (always wins) 2. **Prohibition**: Denies action (always wins)
3. **Obligation** - Must be done (informational only) 3. **Obligation**: Must be done (informational only)
4. **Recommendation** - Should be done (informational only) 4. **Recommendation**: Should be done (informational only)
Only permissions and prohibitions affect `is_allowed()` decisions. 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 ### 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 ```sql
INSERT INTO morbac.sod_conflicts (role1_id, role2_id, org_id, description) -- Create roles
VALUES (preparer_role_id, approver_role_id, org_id, 'Cannot prepare and approve same transaction'); 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 ### Cardinality Constraints
Min/max users per role: Enforce minimum and maximum users per role:
```sql ```sql
-- Require 1-3 administrators
INSERT INTO morbac.role_cardinality (role_id, org_id, min_users, max_users) INSERT INTO morbac.role_cardinality (role_id, org_id, min_users, max_users)
VALUES (admin_role_id, org_id, 1, 3); VALUES (admin_role_id, org_id, 1, 3);
-- Validate before assignment
SELECT morbac.check_cardinality_violation(admin_role_id, org_id); 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: Dynamically computed roles via custom functions:
@@ -591,55 +540,55 @@ WHERE table_name = 'rules'
### Authorization Functions ### 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 ```sql
SELECT morbac.is_allowed(user_uuid, org_uuid, 'read', 'documents'); 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 ### 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 ### 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 ### 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 ### Context Functions
**`eval_context(context_id)`** - Evaluate a context predicate. **`eval_context(context_id)`**: Evaluate a context predicate.
### Policy DSL Functions ### 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 ### 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 ```sql
CREATE POLICY my_policy ON app.table CREATE POLICY my_policy ON app.table
@@ -648,11 +597,11 @@ FOR SELECT USING (morbac.rls_check('read', 'documents'));
### Informational Functions ### 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 ```sql
morbac.user_has_role( morbac.user_has_role(
@@ -666,9 +615,7 @@ morbac.user_has_role(
### PostgREST Integration ### PostgREST Integration
#### 1. Setup Headers Configure PostgREST to pass user/org context via headers:
Configure PostgREST to pass user/org context:
```nginx ```nginx
# Nginx config # Nginx config
@@ -676,18 +623,17 @@ proxy_set_header X-User-Id $user_id;
proxy_set_header X-Org-Id $org_id; proxy_set_header X-Org-Id $org_id;
``` ```
#### 2. Enable RLS Enable RLS and grant permissions:
```sql ```sql
ALTER TABLE app.documents ENABLE ROW LEVEL SECURITY; ALTER TABLE app.documents ENABLE ROW LEVEL SECURITY;
-- Grant usage to PostgREST role
GRANT USAGE ON SCHEMA morbac TO postgrest_role; GRANT USAGE ON SCHEMA morbac TO postgrest_role;
GRANT SELECT ON ALL TABLES IN 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; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA morbac TO postgrest_role;
``` ```
#### 3. Create RLS Policies Create RLS policies:
```sql ```sql
CREATE POLICY document_read ON app.documents CREATE POLICY document_read ON app.documents
@@ -699,37 +645,13 @@ FOR INSERT
WITH CHECK (morbac.rls_check('write', 'documents')); WITH CHECK (morbac.rls_check('write', 'documents'));
``` ```
#### 4. Set Context (alternative to headers) Alternative method using session variables:
```sql ```sql
-- In application connection
SET morbac.user_id = '123e4567-e89b-12d3-a456-426614174000'; SET morbac.user_id = '123e4567-e89b-12d3-a456-426614174000';
SET morbac.org_id = '987fcdeb-51a2-43d7-9c6e-5a8b7c9d0e1f'; 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 ### Application Integration
Python example: Python example:
@@ -754,19 +676,17 @@ const res = await client.query('SELECT * FROM app.documents');
### Multi-Organization Resources ### Multi-Organization Resources
Resources can belong to multiple organizations: Resources can belong to multiple organizations using a junction table:
```sql ```sql
CREATE TABLE app.document_orgs ( -- Application defines document-org relationships
document_id UUID, -- morbac.rls_check() enforces access rules
org_id UUID,
PRIMARY KEY (document_id, org_id)
);
CREATE POLICY doc_access ON app.documents FOR SELECT USING ( 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 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') AND morbac.rls_check('read', 'documents')
); );
``` ```
@@ -792,12 +712,6 @@ Context optimization:
- Keep context logic simple - 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)`. 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 ### Benchmarking
@@ -837,7 +751,7 @@ Not protected against:
### Security Best Practices ### Security Best Practices
#### 1. Context Function Security **Context Function Security**
```sql ```sql
-- BAD: Leaks information -- BAD: Leaks information
@@ -853,7 +767,7 @@ RETURNS BOOLEAN STABLE AS $$
$$; $$;
``` ```
#### 2. User ID Validation **User ID Validation**
```sql ```sql
-- Validate user exists before authorization -- Validate user exists before authorization
@@ -865,7 +779,7 @@ SELECT morbac.is_allowed(
); );
``` ```
#### 3. Org Context Validation **Org Context Validation**
```sql ```sql
-- Verify user belongs to org -- 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 ### Comparison: Multi-OrBAC vs Traditional RBAC
| Aspect | Traditional RBAC | Multi-OrBAC (pg_morbac) | | Aspect | Traditional RBAC | Multi-OrBAC (pg_morbac) |
@@ -905,7 +807,7 @@ CREATE TABLE app.delegation_audit (
## Additional Resources ## Additional Resources
- [Multi-OrBAC Research Paper](https://webhost.laas.fr/TSF/deswarte/Publications/06427.pdf) - [Multi-OrBAC Research Paper](https://webhost.laas.fr/TSF/deswarte/Publications/06427.pdf)
- [test_morbac.sql](test_morbac.sql) - Comprehensive examples - [test_morbac.sql](test_morbac.sql): Comprehensive examples
- [CHANGELOG.md](CHANGELOG.md) - Version history - [CHANGELOG.md](CHANGELOG.md): Version history
- [CONTRIBUTING.md](CONTRIBUTING.md) - Contribution guidelines - [CONTRIBUTING.md](CONTRIBUTING.md): Contribution guidelines
- [SECURITY.md](SECURITY.md) - Security policy - [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: pg_morbac includes several layers of optimization:
1. **Materialized Views** - Precomputed hierarchy transitive closures 1. **Materialized Views**: Precomputed hierarchy transitive closures
2. **Authorization Cache** - Configurable TTL cache for authorization decisions (default: 5 minutes) 2. **Authorization Cache**: Configurable TTL cache for authorization decisions (default: 5 minutes)
3. **Composite Indexes** - Optimized indexes for common query patterns 3. **Composite Indexes**: Optimized indexes for common query patterns
4. **Partial Indexes** - Indexes only on active (temporally valid) rules 4. **Partial Indexes**: Indexes only on active (temporally valid) rules
5. **Configurable Settings** - All magic numbers centralized in morbac.config table 5. **Configurable Settings**: All magic numbers centralized in morbac.config table
## Initial Setup ## Initial Setup
@@ -42,9 +42,9 @@ SELECT morbac.refresh_hierarchy_cache();
``` ```
**Available configuration keys:** **Available configuration keys:**
- `cache_ttl_seconds` - Authorization cache TTL in seconds (default: 300) - `cache_ttl_seconds`: Authorization cache TTL in seconds (default: 300)
- `hierarchy_max_depth` - Maximum hierarchy depth to prevent infinite loops (default: 10) - `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) - `enable_audit_by_default`: Whether to enable audit logging on installation (default: false)
## Using Cached Authorization ## Using Cached Authorization