Files
pgmorbac/tests/00_setup.sql
T

349 lines
18 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =============================================================================
-- Test Setup — GlobalTech Inc. Company Scenario
-- =============================================================================
-- This file establishes the full company structure used across all test files:
--
-- GlobalTech HQ (root)
-- ├── Engineering Dept (child)
-- └── Sales Dept (child)
--
-- Role hierarchy in GlobalTech (senior -> junior, i.e. senior inherits junior perms):
-- ceo -> director -> manager -> employee -> intern
-- + specialized roles: auditor, accountant, hr_manager, contractor, compliance_officer
--
-- Role hierarchy in Engineering:
-- tech_lead -> engineer
--
-- Users:
-- Alice — CEO at GlobalTech
-- Bob — Director at GlobalTech
-- Carol — Manager at GlobalTech
-- Dave — Employee at GlobalTech
-- Eve — Intern at GlobalTech
-- Frank — Contractor at GlobalTech
-- Grace — HR Manager at GlobalTech
-- Heidi — Auditor at GlobalTech
-- Ivan — Accountant at GlobalTech
-- Judy — Engineer at Engineering + Sales Rep at Sales (multi-org)
-- Karl — No role (unauthorized user)
-- Leo — Employee at GlobalTech (used for delegation target)
-- =============================================================================
-- Clean slate
DROP EXTENSION IF EXISTS pgmorbac CASCADE;
DROP SCHEMA IF EXISTS morbac CASCADE;
CREATE EXTENSION pgmorbac;
\echo '=== Setup: Verify schema created ==='
SELECT tablename FROM pg_tables WHERE schemaname = 'morbac' ORDER BY tablename;
-- ---------------------------------------------------------------------------
-- ORGANIZATIONS
-- ---------------------------------------------------------------------------
\echo ''
\echo '=== Setup: Organizations ==='
INSERT INTO morbac.orgs (id, name) VALUES
('10000000-0000-0000-0000-000000000001', 'GlobalTech HQ'),
('10000000-0000-0000-0000-000000000002', 'Engineering Dept'),
('10000000-0000-0000-0000-000000000003', 'Sales Dept');
-- Engineering and Sales are children of GlobalTech
UPDATE morbac.orgs SET parent_id = '10000000-0000-0000-0000-000000000001'
WHERE id IN (
'10000000-0000-0000-0000-000000000002',
'10000000-0000-0000-0000-000000000003'
);
SELECT o.name, COALESCE(p.name, '—') AS parent
FROM morbac.orgs o LEFT JOIN morbac.orgs p ON o.parent_id = p.id
ORDER BY o.parent_id NULLS FIRST, o.name;
-- ---------------------------------------------------------------------------
-- ROLES
-- ---------------------------------------------------------------------------
\echo ''
\echo '=== Setup: Roles ==='
-- GlobalTech HQ roles
INSERT INTO morbac.roles (id, org_id, name, description) VALUES
('20000000-0001-0000-0000-000000000001', '10000000-0000-0000-0000-000000000001', 'ceo', 'Chief Executive Officer'),
('20000000-0001-0000-0000-000000000002', '10000000-0000-0000-0000-000000000001', 'director', 'Senior Director'),
('20000000-0001-0000-0000-000000000003', '10000000-0000-0000-0000-000000000001', 'manager', 'Department Manager'),
('20000000-0001-0000-0000-000000000004', '10000000-0000-0000-0000-000000000001', 'employee', 'Regular Employee'),
('20000000-0001-0000-0000-000000000005', '10000000-0000-0000-0000-000000000001', 'intern', 'Intern'),
('20000000-0001-0000-0000-000000000006', '10000000-0000-0000-0000-000000000001', 'auditor', 'Internal Auditor'),
('20000000-0001-0000-0000-000000000007', '10000000-0000-0000-0000-000000000001', 'accountant', 'Accountant'),
('20000000-0001-0000-0000-000000000008', '10000000-0000-0000-0000-000000000001', 'hr_manager', 'Human Resources Manager'),
('20000000-0001-0000-0000-000000000009', '10000000-0000-0000-0000-000000000001', 'contractor', 'External Contractor'),
('20000000-0001-0000-0000-000000000010', '10000000-0000-0000-0000-000000000001', 'compliance_officer', 'Compliance Officer');
-- Engineering Dept roles
INSERT INTO morbac.roles (id, org_id, name, description) VALUES
('20000000-0002-0000-0000-000000000001', '10000000-0000-0000-0000-000000000002', 'tech_lead', 'Technical Lead'),
('20000000-0002-0000-0000-000000000002', '10000000-0000-0000-0000-000000000002', 'engineer', 'Software Engineer');
-- Sales Dept roles
INSERT INTO morbac.roles (id, org_id, name, description) VALUES
('20000000-0003-0000-0000-000000000001', '10000000-0000-0000-0000-000000000003', 'sales_manager', 'Sales Manager'),
('20000000-0003-0000-0000-000000000002', '10000000-0000-0000-0000-000000000003', 'sales_rep', 'Sales Representative');
SELECT r.name AS role, o.name AS organization
FROM morbac.roles r JOIN morbac.orgs o ON r.org_id = o.id
ORDER BY o.name, r.name;
-- ---------------------------------------------------------------------------
-- ROLE HIERARCHY (GlobalTech)
-- Senior role inherits all permissions granted to junior role
-- ---------------------------------------------------------------------------
\echo ''
\echo '=== Setup: Role Hierarchy (GlobalTech HQ) ==='
INSERT INTO morbac.role_hierarchy (senior_role_id, junior_role_id) VALUES
-- CEO inherits from Director
('20000000-0001-0000-0000-000000000001', '20000000-0001-0000-0000-000000000002'),
-- Director inherits from Manager
('20000000-0001-0000-0000-000000000002', '20000000-0001-0000-0000-000000000003'),
-- Manager inherits from Employee
('20000000-0001-0000-0000-000000000003', '20000000-0001-0000-0000-000000000004'),
-- Employee inherits from Intern
('20000000-0001-0000-0000-000000000004', '20000000-0001-0000-0000-000000000005');
-- Engineering: tech_lead inherits from engineer
INSERT INTO morbac.role_hierarchy (senior_role_id, junior_role_id) VALUES
('20000000-0002-0000-0000-000000000001', '20000000-0002-0000-0000-000000000002');
SELECT
sr.name AS senior_role,
jr.name AS junior_role
FROM morbac.role_hierarchy rh
JOIN morbac.roles sr ON sr.id = rh.senior_role_id
JOIN morbac.roles jr ON jr.id = rh.junior_role_id
ORDER BY sr.name;
-- ---------------------------------------------------------------------------
-- USER — ROLE ASSIGNMENTS
-- ---------------------------------------------------------------------------
\echo ''
\echo '=== Setup: User-Role Assignments ==='
-- GlobalTech HQ users
INSERT INTO morbac.user_roles (user_id, role_id, org_id) VALUES
-- Alice: CEO
('30000000-0000-0000-0000-000000000001', '20000000-0001-0000-0000-000000000001', '10000000-0000-0000-0000-000000000001'),
-- Bob: Director
('30000000-0000-0000-0000-000000000002', '20000000-0001-0000-0000-000000000002', '10000000-0000-0000-0000-000000000001'),
-- Carol: Manager
('30000000-0000-0000-0000-000000000003', '20000000-0001-0000-0000-000000000003', '10000000-0000-0000-0000-000000000001'),
-- Dave: Employee
('30000000-0000-0000-0000-000000000004', '20000000-0001-0000-0000-000000000004', '10000000-0000-0000-0000-000000000001'),
-- Eve: Intern
('30000000-0000-0000-0000-000000000005', '20000000-0001-0000-0000-000000000005', '10000000-0000-0000-0000-000000000001'),
-- Frank: Contractor
('30000000-0000-0000-0000-000000000006', '20000000-0001-0000-0000-000000000009', '10000000-0000-0000-0000-000000000001'),
-- Grace: HR Manager
('30000000-0000-0000-0000-000000000007', '20000000-0001-0000-0000-000000000008', '10000000-0000-0000-0000-000000000001'),
-- Heidi: Auditor
('30000000-0000-0000-0000-000000000008', '20000000-0001-0000-0000-000000000006', '10000000-0000-0000-0000-000000000001'),
-- Ivan: Accountant
('30000000-0000-0000-0000-000000000009', '20000000-0001-0000-0000-000000000007', '10000000-0000-0000-0000-000000000001'),
-- Leo: Employee (delegation target)
('30000000-0000-0000-0000-000000000012', '20000000-0001-0000-0000-000000000004', '10000000-0000-0000-0000-000000000001');
-- Judy: Engineer at Engineering + Sales Rep at Sales (multi-org user)
INSERT INTO morbac.user_roles (user_id, role_id, org_id) VALUES
('30000000-0000-0000-0000-000000000010', '20000000-0002-0000-0000-000000000002', '10000000-0000-0000-0000-000000000002'),
('30000000-0000-0000-0000-000000000010', '20000000-0003-0000-0000-000000000002', '10000000-0000-0000-0000-000000000003');
-- Karl: no role (unauthorized user — intentionally not assigned any role)
SELECT ur.user_id, r.name AS role, o.name AS organization
FROM morbac.user_roles ur
JOIN morbac.roles r ON ur.role_id = r.id
JOIN morbac.orgs o ON ur.org_id = o.id
ORDER BY ur.user_id;
-- ---------------------------------------------------------------------------
-- ACTIVITIES AND VIEWS
-- ---------------------------------------------------------------------------
\echo ''
\echo '=== Setup: Activities and Views ==='
INSERT INTO morbac.activities (name, description) VALUES
('read', 'Read / view data'),
('write', 'Create or modify data'),
('delete', 'Delete data'),
('approve', 'Approve requests or documents'),
('export', 'Export data to external format'),
('audit', 'Audit trail review'),
('manage', 'Full administrative control');
INSERT INTO morbac.views (name, description) VALUES
('documents', 'General company documents'),
('reports', 'Business reports'),
('financial_data','Financial records'),
('hr_data', 'Human resources data'),
('contracts', 'Legal contracts'),
('audit_logs', 'System audit logs'),
('public_data', 'Publicly accessible data');
-- ---------------------------------------------------------------------------
-- CONTEXTS
-- ---------------------------------------------------------------------------
\echo ''
\echo '=== Setup: Contexts ==='
-- business_hours: always TRUE for tests (simulates daytime access)
CREATE OR REPLACE FUNCTION morbac.ctx_business_hours()
RETURNS BOOLEAN LANGUAGE plpgsql STABLE AS $$
BEGIN RETURN TRUE; END;
$$;
-- after_hours: always FALSE for tests (simulates outside-hours access)
CREATE OR REPLACE FUNCTION morbac.ctx_after_hours()
RETURNS BOOLEAN LANGUAGE plpgsql STABLE AS $$
BEGIN RETURN FALSE; END;
$$;
-- end_of_quarter: always TRUE for tests (simulates quarterly reporting period)
CREATE OR REPLACE FUNCTION morbac.ctx_end_of_quarter()
RETURNS BOOLEAN LANGUAGE plpgsql STABLE AS $$
BEGIN RETURN TRUE; END;
$$;
INSERT INTO morbac.contexts (name, description, evaluator) VALUES
('business_hours', 'During business hours (MonFri 917)', 'morbac.ctx_business_hours'::regproc),
('after_hours', 'Outside business hours', 'morbac.ctx_after_hours'::regproc),
('end_of_quarter', 'End-of-quarter reporting window', 'morbac.ctx_end_of_quarter'::regproc);
SELECT name, description FROM morbac.contexts ORDER BY name;
-- ---------------------------------------------------------------------------
-- POLICIES (via Policy DSL — resolved by compile_policy)
-- ---------------------------------------------------------------------------
\echo ''
\echo '=== Setup: Policies ==='
-- GlobalTech HQ policies
INSERT INTO morbac.policy (org_name, role_name, activity, view, modality, context_name) VALUES
-- Intern: read public data only
('GlobalTech HQ', 'intern', 'read', 'public_data', 'permission', 'always'),
-- Employee: read documents and reports; write documents only during business hours
('GlobalTech HQ', 'employee', 'read', 'documents', 'permission', 'always'),
('GlobalTech HQ', 'employee', 'write', 'documents', 'permission', 'business_hours'),
('GlobalTech HQ', 'employee', 'read', 'reports', 'permission', 'always'),
('GlobalTech HQ', 'employee', 'read', 'public_data', 'permission', 'always'),
-- Obligation: employees must review reports weekly
('GlobalTech HQ', 'employee', 'read', 'reports', 'obligation', 'always'),
-- Manager: approve and delete documents; read financial data
('GlobalTech HQ', 'manager', 'approve','documents', 'permission', 'always'),
('GlobalTech HQ', 'manager', 'delete', 'documents', 'permission', 'always'),
('GlobalTech HQ', 'manager', 'read', 'financial_data', 'permission', 'always'),
-- HR Manager: full access to HR data
('GlobalTech HQ', 'hr_manager', 'read', 'hr_data', 'permission', 'always'),
('GlobalTech HQ', 'hr_manager', 'write', 'hr_data', 'permission', 'always'),
('GlobalTech HQ', 'hr_manager', 'delete', 'hr_data', 'permission', 'always'),
-- Auditor: read audit logs, financial data, and documents
('GlobalTech HQ', 'auditor', 'read', 'audit_logs', 'permission', 'always'),
('GlobalTech HQ', 'auditor', 'read', 'financial_data', 'permission', 'always'),
('GlobalTech HQ', 'auditor', 'read', 'documents', 'permission', 'always'),
-- Accountant: read and write financial data
('GlobalTech HQ', 'accountant', 'read', 'financial_data', 'permission', 'always'),
('GlobalTech HQ', 'accountant', 'write', 'financial_data', 'permission', 'always'),
-- Contractor: read documents; PROHIBITED from financial and HR data
('GlobalTech HQ', 'contractor', 'read', 'documents', 'permission', 'always'),
('GlobalTech HQ', 'contractor', 'read', 'financial_data', 'prohibition', 'always'),
('GlobalTech HQ', 'contractor', 'read', 'hr_data', 'prohibition', 'always'),
-- Compliance Officer: read audit logs and financial data
('GlobalTech HQ', 'compliance_officer', 'read', 'audit_logs', 'permission', 'always'),
('GlobalTech HQ', 'compliance_officer', 'read', 'financial_data', 'permission', 'always'),
-- Recommendation: staff should review reports end of quarter
-- Note: voided by the obligation above (conflict resolution: obligation > recommendation)
('GlobalTech HQ', 'employee', 'read', 'reports', 'recommendation','end_of_quarter'),
-- Recommendation: staff should stay informed on public data (no conflicting obligation)
('GlobalTech HQ', 'employee', 'read', 'public_data', 'recommendation','always');
-- Engineering Dept policies
INSERT INTO morbac.policy (org_name, role_name, activity, view, modality, context_name) VALUES
('Engineering Dept', 'engineer', 'read', 'documents', 'permission', 'always'),
('Engineering Dept', 'engineer', 'write', 'documents', 'permission', 'always'),
('Engineering Dept', 'tech_lead', 'approve', 'documents', 'permission', 'always');
-- Sales Dept policies
INSERT INTO morbac.policy (org_name, role_name, activity, view, modality, context_name) VALUES
('Sales Dept', 'sales_rep', 'read', 'documents', 'permission', 'always'),
('Sales Dept', 'sales_rep', 'write', 'contracts', 'permission', 'always'),
('Sales Dept', 'sales_manager', 'read', 'reports', 'permission', 'always'),
('Sales Dept', 'sales_manager', 'approve', 'contracts', 'permission', 'always');
-- Compile all policies into rules
\echo ''
\echo '=== Setup: Compiling Policies ==='
SELECT * FROM morbac.compile_policy();
-- ---------------------------------------------------------------------------
-- Test infrastructure
-- ---------------------------------------------------------------------------
\echo ''
\echo '=== Setup: Test Infrastructure ==='
-- Disable auth cache so state changes between calls are always reflected
SELECT morbac.set_config('cache_ttl_seconds', '0');
-- morbac.t(label, actual, expected) — boolean assertion
CREATE OR REPLACE FUNCTION morbac.t(label TEXT, actual BOOLEAN, expect BOOLEAN)
RETURNS TEXT LANGUAGE sql STABLE AS $$
SELECT CASE WHEN actual IS NOT DISTINCT FROM expect
THEN 'CHECK PASS: ' || label
ELSE 'CHECK FAIL: ' || label
|| ' (expected ' || expect::text || ', got ' || COALESCE(actual::text, 'NULL') || ')'
END;
$$;
-- morbac.t_null(label, actual) — assert value is NULL
CREATE OR REPLACE FUNCTION morbac.t_null(label TEXT, actual TEXT)
RETURNS TEXT LANGUAGE sql STABLE AS $$
SELECT CASE WHEN actual IS NULL
THEN 'CHECK PASS: ' || label
ELSE 'CHECK FAIL: ' || label || ' (expected NULL, got: ' || actual || ')'
END;
$$;
-- morbac.t_not_null(label, actual) — assert value is NOT NULL
CREATE OR REPLACE FUNCTION morbac.t_not_null(label TEXT, actual TEXT)
RETURNS TEXT LANGUAGE sql STABLE AS $$
SELECT CASE WHEN actual IS NOT NULL
THEN 'CHECK PASS: ' || label
ELSE 'CHECK FAIL: ' || label || ' (expected a value, got NULL)'
END;
$$;
-- morbac.t_eq(label, actual, expected) — assert two numeric values are equal
CREATE OR REPLACE FUNCTION morbac.t_eq(label TEXT, actual NUMERIC, expect NUMERIC)
RETURNS TEXT LANGUAGE sql STABLE AS $$
SELECT CASE WHEN actual = expect
THEN 'CHECK PASS: ' || label
ELSE 'CHECK FAIL: ' || label
|| ' (expected ' || expect || ', got ' || actual || ')'
END;
$$;
\echo ''
\echo '=== Setup Complete ==='
\echo ' Organizations : GlobalTech HQ, Engineering Dept, Sales Dept'
\echo ' Roles : 14 across 3 orgs'
\echo ' Users : Alice, Bob, Carol, Dave, Eve, Frank, Grace, Heidi, Ivan, Judy, Karl, Leo'
\echo ' Activities : read, write, delete, approve, export, audit, manage'
\echo ' Views : documents, reports, financial_data, hr_data, contracts, audit_logs, public_data'
\echo ' Contexts : always (true), business_hours (true), after_hours (false), end_of_quarter (true)'