Pivot scenario from region-based 2 users to source-based 4 users
Replaces the original APAC-vs-all 2-user demo (vpduser_a/b on KR_ANALYSTS/GLOBAL_ADMINS groups) with a 2x2 source-access matrix: vpduser_my -> MY_ONLY group -> MySQL view only vpduser_pg -> PG_ONLY group -> Postgres view only vpduser_both -> BOTH_SOURCES group -> both views vpduser_none -> (no group) -> nothing (default deny) Why: source-level segmentation is the more common production permission story than region-level filtering. Region filtering remains available as an opt-in variant via commented UPDATE in sql/adb/03_seed.sql. Key changes: - 03_seed.sql, 07_end_users.sql, 00_cleanup.sql, .env.example, run.sh updated for the new 4-user model. All 4 users get identical view GRANTs; the only differentiator is the permission table (proves the model is "data-driven, not GRANT-driven"). - 08-11 split into one file per user: my (+ 5 bypass attempts), pg, both, none (default-deny verification). - 12_tests_admin_audit.sql uses LEFT JOIN so vpduser_none shows up as NULL permissions, and filters by object_owner=USER to exclude cross-schema policies. - Removed inline "-- comment" after ";" lines in 03_seed.sql: SQL*Plus silently skipped the inserts (documented gotcha). - README.md + docs/01,02 updated for the 4-user matrix. docs/03 detailed guide keeps the region-filter example but now has a preface explaining it's a variant of the default 4-user model. - docs/04: db_type='mysql_community' note added (RDS MySQL). E2E verified: PG=0/MY=17, PG=12/MY=0, PG=12/MY=17, PG=0/MY=0 plus all 5 bypass attempts blocked. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,19 @@
|
||||
-- ============================================================
|
||||
-- 02_seed.sql
|
||||
-- Seed two end-users with different permissions to demonstrate VPD.
|
||||
-- 03_seed.sql
|
||||
-- Seed FOUR end-users to demonstrate a 2x2 source-access matrix:
|
||||
--
|
||||
-- VPDUSER_A -> group KR_ANALYSTS -> allowed_regions = 'APAC'
|
||||
-- VPDUSER_B -> group GLOBAL_ADMINS -> allowed_regions = '*' (all rows)
|
||||
-- user PG view MySQL view VPD predicate effect
|
||||
-- ------------- ----------- ------------ ---------------------------
|
||||
-- VPDUSER_MY blocked ALL rows PG='1=0' / MY=NULL(='*')
|
||||
-- VPDUSER_PG ALL rows blocked PG=NULL / MY='1=0'
|
||||
-- VPDUSER_BOTH ALL rows ALL rows PG=NULL / MY=NULL
|
||||
-- VPDUSER_NONE blocked blocked PG='1=0' / MY='1=0'
|
||||
--
|
||||
-- The `permission` table grants per (group, source/view).
|
||||
-- For this simplified demo every grant uses allowed_regions='*'
|
||||
-- (full visibility within the source). Row-level region filtering
|
||||
-- is still supported by the policy function — see commented examples
|
||||
-- at the bottom of this file for how to layer it in.
|
||||
--
|
||||
-- Run as ADMIN.
|
||||
-- ============================================================
|
||||
@@ -12,35 +22,66 @@ SET FEEDBACK ON
|
||||
|
||||
PROMPT === Seeding permission data ===
|
||||
|
||||
-- Tenant (kept generic — multi-tenant hook for future use).
|
||||
INSERT INTO app_customer (customer_id, customer_name) VALUES (1, 'Acme Corp');
|
||||
|
||||
INSERT INTO app_user (user_id, db_username, customer_id) VALUES (1, 'VPDUSER_A', 1);
|
||||
INSERT INTO app_user (user_id, db_username, customer_id) VALUES (2, 'VPDUSER_B', 1);
|
||||
-- Four end-users, each Oracle SESSION_USER value (uppercased).
|
||||
INSERT INTO app_user (user_id, db_username, customer_id) VALUES (1, 'VPDUSER_MY', 1);
|
||||
INSERT INTO app_user (user_id, db_username, customer_id) VALUES (2, 'VPDUSER_PG', 1);
|
||||
INSERT INTO app_user (user_id, db_username, customer_id) VALUES (3, 'VPDUSER_BOTH', 1);
|
||||
INSERT INTO app_user (user_id, db_username, customer_id) VALUES (4, 'VPDUSER_NONE', 1);
|
||||
|
||||
INSERT INTO app_group (group_id, customer_id, group_name) VALUES (10, 1, 'KR_ANALYSTS');
|
||||
INSERT INTO app_group (group_id, customer_id, group_name) VALUES (20, 1, 'GLOBAL_ADMINS');
|
||||
-- One group per access pattern (1:1 in this demo; in production one
|
||||
-- group typically aggregates many users).
|
||||
INSERT INTO app_group (group_id, customer_id, group_name) VALUES (10, 1, 'MY_ONLY');
|
||||
INSERT INTO app_group (group_id, customer_id, group_name) VALUES (20, 1, 'PG_ONLY');
|
||||
INSERT INTO app_group (group_id, customer_id, group_name) VALUES (30, 1, 'BOTH_SOURCES');
|
||||
-- (No group is needed for VPDUSER_NONE — absence of grants == fail-closed.)
|
||||
|
||||
-- A -> KR_ANALYSTS
|
||||
-- (VPDUSER_NONE deliberately has no user_group row -> fail-closed.)
|
||||
INSERT INTO user_group (user_id, group_id) VALUES (1, 10);
|
||||
-- B -> GLOBAL_ADMINS
|
||||
INSERT INTO user_group (user_id, group_id) VALUES (2, 20);
|
||||
INSERT INTO user_group (user_id, group_id) VALUES (3, 30);
|
||||
|
||||
-- Source registry.
|
||||
INSERT INTO db_source (source_id, source_name, source_type, dblink_name)
|
||||
VALUES (100, 'RDS_POSTGRES', 'DBLINK_PG', 'RDS_POSTGRES_LINK');
|
||||
INSERT INTO db_source (source_id, source_name, source_type, dblink_name)
|
||||
VALUES (200, 'RDS_MYSQL', 'DBLINK_MY', 'RDS_LINK');
|
||||
|
||||
-- Permissions: KR analysts see APAC only; Global admins see everything.
|
||||
-- Permissions: '*' means no row filter (full visibility on that view).
|
||||
-- Mapping (group -> view):
|
||||
-- MY_ONLY(10) -> V_CUSTOMERS_MY
|
||||
-- PG_ONLY(20) -> V_CUSTOMERS_PG
|
||||
-- BOTH_SOURCES(30) -> V_CUSTOMERS_PG + V_CUSTOMERS_MY
|
||||
-- (VPDUSER_NONE has no group, hence no permission row.)
|
||||
INSERT INTO permission (perm_id, group_id, source_id, object_name, allowed_regions)
|
||||
VALUES (1, 10, 100, 'V_CUSTOMERS_PG', 'APAC');
|
||||
VALUES (1, 10, 200, 'V_CUSTOMERS_MY', '*');
|
||||
INSERT INTO permission (perm_id, group_id, source_id, object_name, allowed_regions)
|
||||
VALUES (2, 10, 200, 'V_CUSTOMERS_MY', 'APAC');
|
||||
VALUES (2, 20, 100, 'V_CUSTOMERS_PG', '*');
|
||||
INSERT INTO permission (perm_id, group_id, source_id, object_name, allowed_regions)
|
||||
VALUES (3, 20, 100, 'V_CUSTOMERS_PG', '*');
|
||||
VALUES (3, 30, 100, 'V_CUSTOMERS_PG', '*');
|
||||
INSERT INTO permission (perm_id, group_id, source_id, object_name, allowed_regions)
|
||||
VALUES (4, 20, 200, 'V_CUSTOMERS_MY', '*');
|
||||
VALUES (4, 30, 200, 'V_CUSTOMERS_MY', '*');
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- HOW TO LAYER IN ROW-LEVEL REGION FILTERS (uncomment to try)
|
||||
-- ------------------------------------------------------------
|
||||
-- 'BOTH_SOURCES' showing only APAC from PG instead of '*':
|
||||
-- UPDATE permission SET allowed_regions = 'APAC'
|
||||
-- WHERE group_id = 30 AND object_name = 'V_CUSTOMERS_PG';
|
||||
-- COMMIT;
|
||||
--
|
||||
-- Multi-region (CSV) example for the same group on MySQL:
|
||||
-- UPDATE permission SET allowed_regions = 'APAC,EMEA'
|
||||
-- WHERE group_id = 30 AND object_name = 'V_CUSTOMERS_MY';
|
||||
-- COMMIT;
|
||||
--
|
||||
-- The policy function vpd_region_filter handles CSV → IN-list
|
||||
-- conversion automatically. See sql/adb/06_policy.sql.
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
PROMPT === Seed complete ===
|
||||
EXIT;
|
||||
|
||||
Reference in New Issue
Block a user