feat(tests): add more tests and make them pass
This commit is contained in:
+455
-75
@@ -1,91 +1,471 @@
|
||||
-- ============================================================================
|
||||
-- MORBAC PostgreSQL Extension - Utility Functions and Context Tests
|
||||
-- ============================================================================
|
||||
-- This test file covers utility functions and context evaluation including:
|
||||
-- - Obligations (pending_obligations)
|
||||
-- - Recommendations (pending_recommendations)
|
||||
-- - Context evaluation (eval_context)
|
||||
-- - User role queries (user_roles_in_org, user_has_role)
|
||||
-- - Policy compilation and idempotency
|
||||
-- - Adding new policy entries
|
||||
-- =============================================================================
|
||||
-- Utilities, Derived Roles, RLS, and Policy DSL Tests
|
||||
-- =============================================================================
|
||||
-- Tests miscellaneous utility functions and advanced features:
|
||||
--
|
||||
-- Prerequisites: Assumes 00_setup.sql has been run to set up base data
|
||||
-- ============================================================================
|
||||
-- 1. pending_obligations — returns obligation rules for a user
|
||||
-- 2. pending_recommendations — returns recommendation rules for a user
|
||||
-- 3. Obligations/recommendations do NOT affect is_allowed()
|
||||
-- 4. user_has_role — checks if user holds a named role
|
||||
-- 5. user_roles_in_org — lists all roles for user in org
|
||||
-- 6. eval_context — evaluates context predicates directly
|
||||
-- 7. Policy DSL idempotency — compile_policy() is safe to run multiple times
|
||||
-- 8. Policy DSL error handling — reports errors for missing org/role
|
||||
-- 9. Derived roles — computed via evaluator function
|
||||
-- 10. RLS helper: rls_check() with session variables
|
||||
--
|
||||
-- Prerequisites: 00_setup.sql -> 08_admin.sql
|
||||
-- =============================================================================
|
||||
|
||||
\echo ''
|
||||
\echo '=== Obligations and Recommendations ==='
|
||||
\echo '================================================================'
|
||||
\echo '09 — UTILITIES, DERIVED ROLES, RLS, POLICY DSL'
|
||||
\echo '================================================================'
|
||||
|
||||
\echo 'Obligations for Bob (employee at Acme):'
|
||||
SELECT * FROM morbac.pending_obligations(
|
||||
'bbbbbbbb-0000-0000-0000-000000000002'::uuid,
|
||||
'11111111-1111-1111-1111-111111111111'::uuid
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 1: pending_obligations
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 1. pending_obligations ---'
|
||||
|
||||
-- Dave (employee) has obligation: 'read reports' (set up in 00_setup via Policy DSL)
|
||||
-- The context is 'always' so it evaluates TRUE
|
||||
SELECT morbac.t('Dave (employee) has at least 1 pending obligation',
|
||||
(SELECT COUNT(*) FROM morbac.pending_obligations(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)) >= 1,
|
||||
TRUE);
|
||||
|
||||
-- Dave's obligation is for 'read reports'
|
||||
SELECT morbac.t('Dave has pending obligation: read reports',
|
||||
EXISTS(
|
||||
SELECT 1 FROM morbac.pending_obligations(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)
|
||||
WHERE activity = 'read' AND view = 'reports'
|
||||
), TRUE);
|
||||
|
||||
-- Karl (no role) has no obligations
|
||||
SELECT morbac.t_eq('Karl (no role) has 0 pending obligations',
|
||||
(SELECT COUNT(*) FROM morbac.pending_obligations(
|
||||
'30000000-0000-0000-0000-000000000011'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
))::bigint,
|
||||
0);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 2: pending_recommendations
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 2. pending_recommendations ---'
|
||||
|
||||
-- Dave (employee) has recommendation: 'read reports' during end_of_quarter (evaluates TRUE)
|
||||
SELECT morbac.t('Dave (employee) has at least 1 pending recommendation',
|
||||
(SELECT COUNT(*) FROM morbac.pending_recommendations(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)) >= 1,
|
||||
TRUE);
|
||||
|
||||
-- Eve (intern) has no recommendations
|
||||
SELECT morbac.t_eq('Eve (intern) has 0 pending recommendations',
|
||||
(SELECT COUNT(*) FROM morbac.pending_recommendations(
|
||||
'30000000-0000-0000-0000-000000000005'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
))::bigint,
|
||||
0);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 3: Obligations / Recommendations do NOT affect is_allowed
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 3. Obligations do not affect authorization ---'
|
||||
|
||||
-- Add a pure obligation for a view Dave has no permission rule for
|
||||
INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality)
|
||||
VALUES (
|
||||
'10000000-0000-0000-0000-000000000001',
|
||||
'20000000-0001-0000-0000-000000000004', -- employee
|
||||
'read', 'contracts',
|
||||
(SELECT id FROM morbac.contexts WHERE name = 'always'),
|
||||
'obligation'
|
||||
);
|
||||
|
||||
\echo ''
|
||||
\echo 'Recommendations for Charlie (staff at Beta):'
|
||||
SELECT * FROM morbac.pending_recommendations(
|
||||
'cccccccc-0000-0000-0000-000000000003'::uuid,
|
||||
'22222222-2222-2222-2222-222222222222'::uuid
|
||||
-- Obligation alone doesn't grant access
|
||||
SELECT morbac.t('Dave has obligation for read contracts but no permission [denied]',
|
||||
morbac.is_allowed_nocache(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid,
|
||||
'read', 'contracts'
|
||||
), FALSE);
|
||||
|
||||
-- Recommendations similarly do not grant access
|
||||
INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality)
|
||||
VALUES (
|
||||
'10000000-0000-0000-0000-000000000001',
|
||||
'20000000-0001-0000-0000-000000000004', -- employee
|
||||
'read', 'audit_logs',
|
||||
(SELECT id FROM morbac.contexts WHERE name = 'always'),
|
||||
'recommendation'
|
||||
);
|
||||
|
||||
\echo ''
|
||||
\echo '=== Utility Functions ==='
|
||||
-- Recommendation alone doesn't grant access
|
||||
SELECT morbac.t('Dave has recommendation for read audit_logs but no permission [denied]',
|
||||
morbac.is_allowed_nocache(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid,
|
||||
'read', 'audit_logs'
|
||||
), FALSE);
|
||||
|
||||
\echo 'Roles for Charlie at Acme Corp:'
|
||||
SELECT * FROM morbac.user_roles_in_org(
|
||||
'cccccccc-0000-0000-0000-000000000003'::uuid,
|
||||
'11111111-1111-1111-1111-111111111111'::uuid
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 4: user_has_role
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 4. user_has_role ---'
|
||||
|
||||
-- Alice has the CEO role at GlobalTech
|
||||
SELECT morbac.t('Alice has ceo role at GlobalTech',
|
||||
morbac.user_has_role(
|
||||
'30000000-0000-0000-0000-000000000001'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid,
|
||||
'ceo'
|
||||
), TRUE);
|
||||
|
||||
-- Alice does NOT have employee role (user_has_role checks direct assignment, not hierarchy)
|
||||
SELECT morbac.t('Alice does NOT have employee role directly at GlobalTech',
|
||||
morbac.user_has_role(
|
||||
'30000000-0000-0000-0000-000000000001'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid,
|
||||
'employee'
|
||||
), FALSE);
|
||||
|
||||
-- Dave has employee role
|
||||
SELECT morbac.t('Dave has employee role at GlobalTech',
|
||||
morbac.user_has_role(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid,
|
||||
'employee'
|
||||
), TRUE);
|
||||
|
||||
-- Dave does not have auditor role
|
||||
SELECT morbac.t('Dave does not have auditor role at GlobalTech',
|
||||
morbac.user_has_role(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid,
|
||||
'auditor'
|
||||
), FALSE);
|
||||
|
||||
-- Karl has no role
|
||||
SELECT morbac.t('Karl does not have employee role at GlobalTech [no role]',
|
||||
morbac.user_has_role(
|
||||
'30000000-0000-0000-0000-000000000011'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid,
|
||||
'employee'
|
||||
), FALSE);
|
||||
|
||||
-- Judy has engineer at Engineering but not at GlobalTech
|
||||
SELECT morbac.t('Judy does not have engineer role at GlobalTech [wrong org]',
|
||||
morbac.user_has_role(
|
||||
'30000000-0000-0000-0000-000000000010'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid,
|
||||
'engineer'
|
||||
), FALSE);
|
||||
|
||||
SELECT morbac.t('Judy has engineer role at Engineering [correct org]',
|
||||
morbac.user_has_role(
|
||||
'30000000-0000-0000-0000-000000000010'::uuid,
|
||||
'10000000-0000-0000-0000-000000000002'::uuid,
|
||||
'engineer'
|
||||
), TRUE);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 5: user_roles_in_org
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 5. user_roles_in_org ---'
|
||||
|
||||
-- Alice has exactly 1 role at GlobalTech (ceo)
|
||||
SELECT morbac.t_eq('Alice has exactly 1 role at GlobalTech (ceo)',
|
||||
(SELECT COUNT(*) FROM morbac.user_roles_in_org(
|
||||
'30000000-0000-0000-0000-000000000001'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
))::bigint,
|
||||
1);
|
||||
|
||||
SELECT morbac.t('Alice role at GlobalTech is ceo',
|
||||
EXISTS(
|
||||
SELECT 1 FROM morbac.user_roles_in_org(
|
||||
'30000000-0000-0000-0000-000000000001'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)
|
||||
WHERE role_name = 'ceo'
|
||||
), TRUE);
|
||||
|
||||
-- Judy has 1 role at Engineering (engineer)
|
||||
SELECT morbac.t_eq('Judy has 1 role at Engineering',
|
||||
(SELECT COUNT(*) FROM morbac.user_roles_in_org(
|
||||
'30000000-0000-0000-0000-000000000010'::uuid,
|
||||
'10000000-0000-0000-0000-000000000002'::uuid
|
||||
))::bigint,
|
||||
1);
|
||||
|
||||
-- Judy has 1 role at Sales (sales_rep)
|
||||
SELECT morbac.t_eq('Judy has 1 role at Sales',
|
||||
(SELECT COUNT(*) FROM morbac.user_roles_in_org(
|
||||
'30000000-0000-0000-0000-000000000010'::uuid,
|
||||
'10000000-0000-0000-0000-000000000003'::uuid
|
||||
))::bigint,
|
||||
1);
|
||||
|
||||
-- Karl has no roles at GlobalTech
|
||||
SELECT morbac.t_eq('Karl has 0 roles at GlobalTech',
|
||||
(SELECT COUNT(*) FROM morbac.user_roles_in_org(
|
||||
'30000000-0000-0000-0000-000000000011'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
))::bigint,
|
||||
0);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 6: eval_context
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 6. eval_context ---'
|
||||
|
||||
-- 'always' context always returns TRUE
|
||||
SELECT morbac.t('eval_context(always) = TRUE',
|
||||
morbac.eval_context((SELECT id FROM morbac.contexts WHERE name = 'always')),
|
||||
TRUE);
|
||||
|
||||
-- 'business_hours' context returns TRUE (our test implementation)
|
||||
SELECT morbac.t('eval_context(business_hours) = TRUE',
|
||||
morbac.eval_context((SELECT id FROM morbac.contexts WHERE name = 'business_hours')),
|
||||
TRUE);
|
||||
|
||||
-- 'after_hours' context returns FALSE (our test implementation)
|
||||
SELECT morbac.t('eval_context(after_hours) = FALSE',
|
||||
morbac.eval_context((SELECT id FROM morbac.contexts WHERE name = 'after_hours')),
|
||||
FALSE);
|
||||
|
||||
-- 'end_of_quarter' context returns TRUE (our test implementation)
|
||||
SELECT morbac.t('eval_context(end_of_quarter) = TRUE',
|
||||
morbac.eval_context((SELECT id FROM morbac.contexts WHERE name = 'end_of_quarter')),
|
||||
TRUE);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 7: Policy DSL — idempotency
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 7. Policy DSL idempotency ---'
|
||||
|
||||
-- All existing policies are already compiled (compiled=TRUE)
|
||||
-- Running compile_policy() again should compile 0 new entries
|
||||
SELECT morbac.t_eq('Re-running compile_policy() gives compiled_count=0 (all already compiled)',
|
||||
(SELECT compiled_count FROM morbac.compile_policy()),
|
||||
0);
|
||||
|
||||
SELECT morbac.t_eq('Re-running compile_policy() gives error_count=0',
|
||||
(SELECT error_count FROM morbac.compile_policy()),
|
||||
0);
|
||||
|
||||
-- Adding a new policy and verifying it compiles once but not twice
|
||||
INSERT INTO morbac.policy (org_name, role_name, activity, view, modality, context_name)
|
||||
VALUES ('GlobalTech HQ', 'employee', 'read', 'contracts', 'permission', 'always');
|
||||
|
||||
SELECT morbac.t_eq('compile_policy() compiles 1 new policy entry',
|
||||
(SELECT compiled_count FROM morbac.compile_policy()),
|
||||
1);
|
||||
|
||||
SELECT morbac.t_eq('compile_policy() is idempotent: compiled_count=0 on second run',
|
||||
(SELECT compiled_count FROM morbac.compile_policy()),
|
||||
0);
|
||||
|
||||
-- Verify the rule was actually created
|
||||
SELECT morbac.t_eq('Rule for employee read contracts was created',
|
||||
(SELECT COUNT(*) FROM morbac.rules r
|
||||
JOIN morbac.roles ro ON ro.id = r.role_id
|
||||
WHERE ro.name = 'employee'
|
||||
AND r.activity = 'read' AND r.view = 'contracts' AND r.modality = 'permission')::bigint,
|
||||
1);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 8: Policy DSL — error handling
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 8. Policy DSL error handling ---'
|
||||
|
||||
-- Insert a policy for a non-existent organization
|
||||
INSERT INTO morbac.policy (org_name, role_name, activity, view, modality, context_name)
|
||||
VALUES ('Nonexistent Corp', 'employee', 'read', 'documents', 'permission', 'always');
|
||||
|
||||
-- Should produce errors (org not found)
|
||||
SELECT morbac.t('compile_policy: nonexistent org produces errors',
|
||||
(SELECT error_count > 0 FROM morbac.compile_policy()),
|
||||
TRUE);
|
||||
|
||||
-- Insert a policy with a valid org but nonexistent role
|
||||
INSERT INTO morbac.policy (org_name, role_name, activity, view, modality, context_name)
|
||||
VALUES ('GlobalTech HQ', 'ghost_role', 'read', 'documents', 'permission', 'always');
|
||||
|
||||
-- Should produce errors (role not found)
|
||||
SELECT morbac.t('compile_policy: nonexistent role produces errors',
|
||||
(SELECT error_count > 0 FROM morbac.compile_policy()),
|
||||
TRUE);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 9: Derived roles
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo '--- 9. Derived roles ---'
|
||||
|
||||
-- Create a derived role: 'senior_employee' — dynamically granted to Dave (only)
|
||||
INSERT INTO morbac.roles (id, org_id, name, description)
|
||||
VALUES (
|
||||
'20000000-0001-0000-0000-000000000011',
|
||||
'10000000-0000-0000-0000-000000000001',
|
||||
'senior_employee',
|
||||
'Senior employee — granted dynamically based on tenure'
|
||||
);
|
||||
|
||||
\echo ''
|
||||
\echo 'Roles for Charlie at Beta Inc:'
|
||||
SELECT * FROM morbac.user_roles_in_org(
|
||||
'cccccccc-0000-0000-0000-000000000003'::uuid,
|
||||
'22222222-2222-2222-2222-222222222222'::uuid
|
||||
-- Evaluator function: returns TRUE only for Dave at GlobalTech
|
||||
CREATE OR REPLACE FUNCTION morbac.eval_senior_employee(p_user_id UUID, p_org_id UUID)
|
||||
RETURNS BOOLEAN LANGUAGE plpgsql STABLE AS $$
|
||||
BEGIN
|
||||
-- In a real system, this would query app.employees for tenure >= 2 years.
|
||||
-- For testing, we hardcode Dave's UUID.
|
||||
RETURN p_user_id = '30000000-0000-0000-0000-000000000004'::UUID
|
||||
AND p_org_id = '10000000-0000-0000-0000-000000000001'::UUID;
|
||||
END;
|
||||
$$;
|
||||
|
||||
INSERT INTO morbac.derived_roles (role_id, condition_evaluator, description)
|
||||
VALUES (
|
||||
'20000000-0001-0000-0000-000000000011',
|
||||
'morbac.eval_senior_employee'::regproc,
|
||||
'Senior employee role: granted to employees with 2+ years tenure'
|
||||
);
|
||||
|
||||
-- Grant senior_employee permission to read financial_data directly
|
||||
-- (separate from the view hierarchy path, to test derived role specifically)
|
||||
INSERT INTO morbac.rules (org_id, role_id, activity, view, context_id, modality)
|
||||
VALUES (
|
||||
'10000000-0000-0000-0000-000000000001',
|
||||
'20000000-0001-0000-0000-000000000011', -- senior_employee role
|
||||
'read', 'financial_data',
|
||||
(SELECT id FROM morbac.contexts WHERE name = 'always'),
|
||||
'permission'
|
||||
);
|
||||
|
||||
-- Dave should have senior_employee via derived role in get_comprehensive_roles
|
||||
SELECT morbac.t('Dave has senior_employee derived role in comprehensive roles',
|
||||
EXISTS(
|
||||
SELECT 1 FROM morbac.get_comprehensive_roles(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)
|
||||
WHERE role_id = '20000000-0001-0000-0000-000000000011'
|
||||
AND source = 'derived'
|
||||
), TRUE);
|
||||
|
||||
-- Eve (intern) does NOT satisfy the evaluator — no derived role
|
||||
SELECT morbac.t('Eve has no derived roles in comprehensive roles',
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM morbac.get_comprehensive_roles(
|
||||
'30000000-0000-0000-0000-000000000005'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)
|
||||
WHERE source = 'derived'
|
||||
), TRUE);
|
||||
|
||||
-- Karl (no role) does not get the derived role
|
||||
SELECT morbac.t('Karl has no derived roles in comprehensive roles',
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM morbac.get_comprehensive_roles(
|
||||
'30000000-0000-0000-0000-000000000011'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)
|
||||
WHERE source = 'derived'
|
||||
), TRUE);
|
||||
|
||||
-- Negative assignment overrides derived role
|
||||
INSERT INTO morbac.negative_role_assignments (user_id, role_id, org_id, reason)
|
||||
VALUES (
|
||||
'30000000-0000-0000-0000-000000000004', -- Dave
|
||||
'20000000-0001-0000-0000-000000000011', -- senior_employee
|
||||
'10000000-0000-0000-0000-000000000001',
|
||||
'Derived role explicitly blocked for testing'
|
||||
);
|
||||
|
||||
-- Derived role is blocked by negative assignment
|
||||
SELECT morbac.t('Dave: senior_employee derived role absent after negative assignment',
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM morbac.get_comprehensive_roles(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)
|
||||
WHERE role_id = '20000000-0001-0000-0000-000000000011'
|
||||
), TRUE);
|
||||
|
||||
-- Dave's direct employee role is still present (negative only blocks senior_employee)
|
||||
SELECT morbac.t('Dave: employee role still present after senior_employee negated',
|
||||
EXISTS(
|
||||
SELECT 1 FROM morbac.get_comprehensive_roles(
|
||||
'30000000-0000-0000-0000-000000000004'::uuid,
|
||||
'10000000-0000-0000-0000-000000000001'::uuid
|
||||
)
|
||||
WHERE role_id = '20000000-0001-0000-0000-000000000004'
|
||||
AND source = 'direct'
|
||||
), TRUE);
|
||||
|
||||
-- Clean up negative assignment
|
||||
DELETE FROM morbac.negative_role_assignments
|
||||
WHERE user_id = '30000000-0000-0000-0000-000000000004'
|
||||
AND role_id = '20000000-0001-0000-0000-000000000011';
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Section 10: RLS helper functions
|
||||
-- ---------------------------------------------------------------------------
|
||||
\echo ''
|
||||
\echo 'Does Alice have admin role at Acme Corp?'
|
||||
SELECT morbac.user_has_role(
|
||||
'aaaaaaaa-0000-0000-0000-000000000001'::uuid,
|
||||
'11111111-1111-1111-1111-111111111111'::uuid,
|
||||
'admin'
|
||||
) as has_admin_role;
|
||||
\echo '--- 10. RLS helper: current_user_id, current_org_id, rls_check ---'
|
||||
|
||||
-- Without session variables set, current_user_id and current_org_id return NULL
|
||||
SELECT morbac.t_null('current_user_id() returns NULL without session var',
|
||||
morbac.current_user_id()::text);
|
||||
|
||||
SELECT morbac.t_null('current_org_id() returns NULL without session var',
|
||||
morbac.current_org_id()::text);
|
||||
|
||||
-- rls_check without session context returns FALSE (no user/org)
|
||||
SELECT morbac.t('rls_check without session context returns FALSE',
|
||||
morbac.rls_check('read', 'documents'),
|
||||
FALSE);
|
||||
|
||||
-- Set session variables (Dave at GlobalTech)
|
||||
-- rls_check reads morbac.user_id and morbac.org_id session variables
|
||||
SET morbac.user_id = '30000000-0000-0000-0000-000000000004';
|
||||
SET morbac.org_id = '10000000-0000-0000-0000-000000000001';
|
||||
|
||||
-- rls_check uses morbac.user_id / morbac.org_id session vars
|
||||
SELECT morbac.t('rls_check(read, documents) as Dave at GlobalTech',
|
||||
morbac.rls_check('read', 'documents'),
|
||||
TRUE);
|
||||
|
||||
-- rls_check for activity Dave cannot do
|
||||
SELECT morbac.t('rls_check(approve, documents) as Dave at GlobalTech [no permission]',
|
||||
morbac.rls_check('approve', 'documents'),
|
||||
FALSE);
|
||||
|
||||
-- Switch to Karl (no role)
|
||||
SET morbac.user_id = '30000000-0000-0000-0000-000000000011';
|
||||
|
||||
SELECT morbac.t('rls_check(read, documents) as Karl (no role) [denied]',
|
||||
morbac.rls_check('read', 'documents'),
|
||||
FALSE);
|
||||
|
||||
-- Reset session to avoid polluting other queries
|
||||
RESET morbac.user_id;
|
||||
RESET morbac.org_id;
|
||||
|
||||
\echo ''
|
||||
\echo '=== Context Evaluation ==='
|
||||
|
||||
\echo 'Evaluating always context:'
|
||||
SELECT morbac.eval_context(
|
||||
(SELECT id FROM morbac.contexts WHERE name = 'always')
|
||||
) as result;
|
||||
|
||||
\echo ''
|
||||
\echo 'Evaluating business_hours context:'
|
||||
SELECT morbac.eval_context(
|
||||
(SELECT id FROM morbac.contexts WHERE name = 'business_hours')
|
||||
) as result;
|
||||
|
||||
\echo ''
|
||||
\echo 'Evaluating weekend context:'
|
||||
SELECT morbac.eval_context(
|
||||
(SELECT id FROM morbac.contexts WHERE name = 'weekend')
|
||||
) as result;
|
||||
|
||||
\echo ''
|
||||
\echo '=== Policy Re-compilation (Idempotency Test) ==='
|
||||
|
||||
\echo 'Compiling policy again (should show 0 new rules, idempotent):'
|
||||
SELECT * FROM morbac.compile_policy();
|
||||
|
||||
\echo ''
|
||||
\echo '=== Adding New Policy Entry ==='
|
||||
|
||||
INSERT INTO morbac.policy (org_name, role_name, activity, view, modality, context_name) VALUES
|
||||
('Acme Corp', 'admin', 'approve', 'documents', 'permission', 'always');
|
||||
|
||||
\echo 'Compiling new policy entry:'
|
||||
SELECT * FROM morbac.compile_policy();
|
||||
|
||||
\echo ''
|
||||
\echo '=== Utility Functions and Context Tests Completed ==='
|
||||
\echo '=== Utilities, Derived Roles, RLS, and Policy DSL Tests Completed ==='
|
||||
|
||||
Reference in New Issue
Block a user