sync a58faaa refactor(authz)
This commit is contained in:
@@ -38,9 +38,7 @@ identity_user := input.context.identity.user
|
||||
# SPESIFIK (data.authz.*) — JANGAN meneruskan `data` utuh ke builtin, sebab itu
|
||||
# membuat compiler menganggapnya bergantung pada `allow` (rego_recursion_error).
|
||||
default data_catalogs := []
|
||||
default write_schemas := []
|
||||
data_catalogs := data.authz.catalogs
|
||||
write_schemas := data.authz.write_schemas
|
||||
|
||||
# Catalog yang boleh di-listing = catalog terdaftar + 'system' (bawaan Trino).
|
||||
allowed_catalogs contains c if { some c in data_catalogs }
|
||||
@@ -157,6 +155,15 @@ base_table_name(name) := name if {
|
||||
|
||||
table_id(schema, table) := sprintf("%s_%s", [schema, base_table_name(table)])
|
||||
|
||||
# ── Grant tingkat-SCHEMA: berlaku untuk SEMUA tabel di schema tersebut ───────
|
||||
# Bentuk data (dari authz.yml `roles.<role>.schemas`):
|
||||
# data.roles.<role>.schema_grants.<schema> = { select/write/ddl, columns }
|
||||
# Digabung (union) dengan grant per-tabel. Tabel baru (termasuk sementara dbt/Sling
|
||||
# milik engineer) otomatis ikut ter-cover bila grant-nya di level schema.
|
||||
schema_grant(role, schema) := sg if {
|
||||
sg := data.roles[role].schema_grants[schema]
|
||||
}
|
||||
|
||||
allow if {
|
||||
cap := op_caps[op]
|
||||
res := input.action.resource
|
||||
@@ -257,30 +264,89 @@ allow if {
|
||||
any_cap(grant)
|
||||
}
|
||||
|
||||
# ── Grant tingkat-SCHEMA (union dgn grant per-tabel di atas) ─────────────────
|
||||
# Operasi per-tabel (non-SELECT) yang dicakup cap.
|
||||
allow if {
|
||||
cap := op_caps[op]
|
||||
res := input.action.resource
|
||||
some r in effective_roles
|
||||
sg := schema_grant(r, res.table.schemaName)
|
||||
sg[cap] == true
|
||||
}
|
||||
|
||||
# SELECT per-kolom pada tabel di schema yang di-grant.
|
||||
allow if {
|
||||
op == "SELECTFROMCOLUMNS"
|
||||
res := input.action.resource
|
||||
res.table.catalogName in data_catalogs
|
||||
cols := res.table.columns
|
||||
count(cols) > 0
|
||||
some r in effective_roles
|
||||
sg := schema_grant(r, res.table.schemaName)
|
||||
sg.select == true
|
||||
cols_all_ok(sg.columns, cols)
|
||||
}
|
||||
|
||||
# SELECT tanpa kolom eksplisit (count(*)) pada schema yang di-grant.
|
||||
allow if {
|
||||
op == "SELECTFROMCOLUMNS"
|
||||
res := input.action.resource
|
||||
res.table.catalogName in data_catalogs
|
||||
count(res.table.columns) == 0
|
||||
some r in effective_roles
|
||||
sg := schema_grant(r, res.table.schemaName)
|
||||
sg.select == true
|
||||
}
|
||||
|
||||
# Listing tabel: terlihat bila schema punya grant apapun.
|
||||
allow if {
|
||||
op == "FILTERTABLES"
|
||||
res := input.action.resource
|
||||
res.table.catalogName in data_catalogs
|
||||
some r in effective_roles
|
||||
sg := schema_grant(r, res.table.schemaName)
|
||||
any_cap(sg)
|
||||
}
|
||||
|
||||
# FilterColumns (visibilitas kolom) utk schema yang di-grant.
|
||||
allow if {
|
||||
op == "FILTERCOLUMNS"
|
||||
res := input.action.resource
|
||||
some r in effective_roles
|
||||
sg := schema_grant(r, res.table.schemaName)
|
||||
sg.select == true
|
||||
sg.columns == "*"
|
||||
}
|
||||
allow if {
|
||||
op == "FILTERCOLUMNS"
|
||||
res := input.action.resource
|
||||
cols := res.table.columns
|
||||
count(cols) > 0
|
||||
some r in effective_roles
|
||||
sg := schema_grant(r, res.table.schemaName)
|
||||
sg.select == true
|
||||
cols_all_ok(sg.columns, cols)
|
||||
}
|
||||
|
||||
allow if {
|
||||
op == "SetSystemSessionProperty"
|
||||
}
|
||||
|
||||
# ── DEV/EVAL: data-engineer bebas DDL + tulis di schema bronze ───────────────
|
||||
# Diminta eksplisit (2026-09-22) untuk mempermudah ingestion (Sling) dan
|
||||
# transform (dbt) di layer bronze.
|
||||
# ── Blok luas: role dengan schema-grant 'write' = OPERASI APA PUN di schema ──
|
||||
# Dulu HARDCODE (role data-engineer + schema bronze/silver/gold via write_schemas).
|
||||
# Kini DATA-DRIVEN dari authz.yml `roles.<role>.schemas` — eksplisit & terport.
|
||||
#
|
||||
# ⚠️ Cakupan blok ini SENGAJA LEBIH LUAS dari least-privilege per-tabel:
|
||||
# 1. Berlaku untuk OPERASI APA PUN yang menargetkan tabel di schema bronze —
|
||||
# termasuk operasi level-kolom yang dikirim Trino untuk UPDATE/MERGE, yang
|
||||
# tidak punya entri di op_caps sehingga sebelumnya jatuh ke deny-by-default.
|
||||
# Tanpa ini, `MERGE` (upsert) selalu gagal: "Access Denied: Cannot update
|
||||
# columns [...]".
|
||||
# 2. Berlaku untuk tabel apa pun, termasuk tabel sementara `<nama>_tmp` milik
|
||||
# Sling. Tabel itu tidak akan cocok dengan grant biasa karena policy hanya
|
||||
# men-strip sufiks milik dbt (`__dbt_tmp`, `__dbt_backup`).
|
||||
#
|
||||
# Efek: tabel baru di bronze tidak lagi perlu baris grant di data.json.
|
||||
# Mempersempit kembali: hapus blok ini, atau tambahkan syarat daftar nama tabel
|
||||
# eksplisit. Layer lain (silver/gold) TIDAK terpengaruh — tetap least-privilege.
|
||||
# Cakupan sengaja LEBIH LUAS dari least-privilege per-tabel, karena:
|
||||
# 1. Menangkap OPERASI APA PUN di schema itu — termasuk operasi level-kolom yang
|
||||
# dikirim Trino untuk UPDATE/MERGE dan TIDAK punya entri di op_caps. Tanpa ini
|
||||
# `MERGE` (upsert) gagal: "Access Denied: Cannot update columns [...]".
|
||||
# 2. Berlaku untuk tabel apa pun di schema itu, termasuk tabel sementara
|
||||
# `<nama>_tmp` (Sling) dan tabel baru → tak perlu grant per-tabel.
|
||||
# Hanya bila schema-grant punya 'write' — role 'select'-only (mis. data-analyst)
|
||||
# TIDAK terpengaruh (tetap least-privilege).
|
||||
allow if {
|
||||
res := input.action.resource
|
||||
res.table.schemaName in write_schemas
|
||||
some r in effective_roles
|
||||
r == "data-engineer"
|
||||
sg := schema_grant(r, res.table.schemaName)
|
||||
sg.write == true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user