This exercise builds Stage 4 and Stage 5 of the capstone pipeline: the Superset-style BI dashboard with row-level security and the governance masking layer. Building on the Gold mart from Lesson 33, you will create a virtual dataset, apply RLS filters scoped to two franchise roles, verify non-overlap and conservation properties, implement column masking for sensitive player contact data, and validate RBAC access control across four role tiers.
The exercise covers two steps. Step 1 builds the virtual dataset from the Gold mart, applies RLS for MI and GT franchise analysts, and verifies the three core RLS properties: single-team scoping, non-overlap, and conservation. Step 2 applies column masking to a player contact dataset for four roles and validates RBAC access control rules across staging, analytics, and mart-specific permission boundaries — completing the full governance layer for the capstone platform.
Step 1 — Virtual Dataset and RLS Validation
Build the Superset-style virtual dataset from the Gold mart joined with team information, apply RLS filters for MI analyst and GT analyst roles, and verify: each role sees only their team's bowler statistics, the MI and GT result sets share no overlapping bowler-season records, and the sum of MI rows, GT rows, and other-team rows equals the full Gold mart row count. Assert an admin role bypasses RLS and sees all records.
# capstone_dashboard_rls.py — Step 1: Virtual dataset and RLS validation
import duckdb
import pandas as pd
import numpy as np
np.random.seed(45)
# Extend gold_df with team association (reusing from Lesson 33)
gold_with_team = gold_df.copy()
team_map = {"Hardik Pandya": "MI", "Jasprit Bumrah": "MI", "Mohammed Shami": "PBKS"}
gold_with_team["current_team"] = gold_with_team["bowler_name"].map(team_map).fillna("GT")
# Add some GT bowlers for RLS testing
extra_gt = pd.DataFrame({
"bowler_name": ["Rashid Khan", "Mohammed Shami"],
"season": [2024, 2024],
"deliveries": [80, 90],
"runs_conceded": [95, 110],
"wickets": [5, 4],
"economy_rate": [7.1, 7.3],
"current_team": ["GT", "GT"],
})
gold_with_team = pd.concat([gold_with_team, extra_gt], ignore_index=True)
con.register("gold_with_team", gold_with_team)
def run_with_rls(con, user_team: str = None) -> pd.DataFrame:
base = "SELECT bowler_name, season, deliveries, economy_rate, current_team FROM gold_with_team"
if user_team:
return con.execute(f"SELECT * FROM ({base}) v WHERE current_team = '{user_team}'").df()
return con.execute(base).df()
mi_view = run_with_rls(con, "MI")
gt_view = run_with_rls(con, "GT")
admin_view = run_with_rls(con, None)
# Property 1: single-team scoping
assert (mi_view["current_team"] == "MI").all()
assert (gt_view["current_team"] == "GT").all()
print(f"MI analyst sees {len(mi_view)} rows (all MI) ✓")
print(f"GT analyst sees {len(gt_view)} rows (all GT) ✓")
# Property 2: non-overlap
mi_keys = set(zip(mi_view["bowler_name"], mi_view["season"]))
gt_keys = set(zip(gt_view["bowler_name"], gt_view["season"]))
assert mi_keys.isdisjoint(gt_keys)
print("No overlap between MI and GT result sets ✓")
# Property 3: conservation
other_view = admin_view[~admin_view["current_team"].isin(["MI","GT"])]
total_check = len(mi_view) + len(gt_view) + len(other_view)
assert total_check == len(admin_view)
print(f"Conservation: MI({len(mi_view)})+GT({len(gt_view)})+other({len(other_view)})={total_check} == total({len(admin_view)}) ✓")
print("Step 1 ✓: Virtual dataset and RLS validation complete")Step 2 — Column Masking and RBAC Validation
Apply column masking to a player contact dataset for four roles (ADMIN, ANALYST, FINANCE, PUBLIC), verify each role's masked output matches the expected visibility pattern, and run RBAC access control checks across the staging, analytics, and mart-level permission boundaries established in Module 5. Finally run the full capstone integration test, asserting all correctness invariants from Lessons 32 through 34 pass simultaneously.
# capstone_dashboard_rls.py — Step 2: Masking, RBAC, full integration test
import pandas as pd
# ── Column masking ─────────────────────────────────────────────────────────────
player_contacts = pd.DataFrame({
"player_name": ["Hardik Pandya", "Jasprit Bumrah"],
"email": ["[email protected]", "[email protected]"],
"contract_value":[15.5, 18.0],
})
def mask_email(v, role): return v if role in ("ADMIN",) else f"{v[0]}***@{v.split('@')[1]}"
def mask_contract(v, role): return v if role in ("ADMIN","FINANCE") else -1
for role in ["ADMIN","ANALYST","FINANCE","PUBLIC"]:
masked = player_contacts.copy()
masked["email"] = masked["email"].apply(lambda v: mask_email(v, role))
masked["contract"] = masked["contract_value"].apply(lambda v: mask_contract(v, role))
visible_email = role == "ADMIN"
visible_contract= role in ("ADMIN","FINANCE")
print(f" {role:<10}: email_visible={visible_email}, contract_visible={visible_contract}")
assert mask_email("[email protected]", "ADMIN") == "[email protected]"
assert mask_email("[email protected]", "ANALYST") == "h***@ipl.com"
assert mask_contract(15.5, "FINANCE") == 15.5
assert mask_contract(15.5, "PUBLIC") == -1
print("Column masking assertions passed ✓")
# ── RBAC validation ────────────────────────────────────────────────────────────
ROLE_PERMS = {
"DATA_ENGINEER_ROLE": {"schemas":["staging","analytics"], "ops":["SELECT","INSERT"]},
"ANALYST_ROLE": {"schemas":["analytics"], "ops":["SELECT"]},
"BI_SERVICE_ROLE": {"schemas":["analytics"], "ops":["SELECT"],
"tables":["mart_bowler_season_stats"]},
}
def check_access(role, schema, table, op):
p = ROLE_PERMS.get(role, {})
if schema not in p.get("schemas",[]): return False
if op not in p.get("ops",[]): return False
if "tables" in p and table not in p["tables"]: return False
return True
rbac_tests = [
("DATA_ENGINEER_ROLE","staging","raw_deliveries","INSERT", True),
("ANALYST_ROLE","analytics","fact_ipl_delivery","SELECT", True),
("ANALYST_ROLE","staging","raw_deliveries","SELECT", False),
("BI_SERVICE_ROLE","analytics","mart_bowler_season_stats","SELECT", True),
("BI_SERVICE_ROLE","analytics","fact_ipl_delivery","SELECT", False),
]
for role, schema, table, op, expected in rbac_tests:
assert check_access(role, schema, table, op) == expected
print("RBAC access control assertions passed ✓")
# ── Full Capstone Integration Test ────────────────────────────────────────────
print("\n" + "="*60)
print("Course 6 Capstone — Full Integration Test")
print("="*60)
final_assertions = [
("Stage 1: SCD Type 2 point-in-time correct", team_2023=="GT" and team_2024=="MI"),
("Stage 2: COPY INTO idempotent", r1["status"]=="LOADED" and r2["status"]=="SKIPPED"),
("Stage 2: Stream/Task MERGE idempotent", processed1==120 and processed2==0),
("Stage 2: Fact table row count correct", final_count==240),
("Stage 3: Schema tests passed", all_pass),
("Stage 3: Row conservation (fact=stg=int)", fact_count==len(stg_df)==len(int_df)),
("Stage 3: Ratio metric correctness verified", comparison["differs"].any()),
("Stage 4: RLS single-team scoping", (mi_view["current_team"]=="MI").all()),
("Stage 4: RLS non-overlap", mi_keys.isdisjoint(gt_keys)),
("Stage 4: RLS conservation", total_check==len(admin_view)),
("Stage 5: Column masking correct", mask_contract(15.5,"PUBLIC")==-1),
("Stage 5: RBAC access control correct", check_access("ANALYST_ROLE","staging","x","SELECT")==False),
]
all_ok = True
for name, ok in final_assertions:
print(f" {'✓' if ok else '✗'} {name}")
if not ok: all_ok = False
print()
if all_ok:
print(f"ALL {len(final_assertions)} ASSERTIONS PASSED — Capstone is submission-ready ✓")
else:
raise AssertionError("Some capstone assertions failed")
print("Step 2 ✓: Column masking, RBAC, and full integration test complete")