sync a58faaa refactor(authz)
This commit is contained in:
@@ -6,9 +6,10 @@
|
||||
# ./scripts/sicabe-authz render # authz.yml -> data.json
|
||||
# ./scripts/sicabe-authz apply # render + reload OPA
|
||||
# ./scripts/sicabe-authz fmt # rapikan format berkas ini
|
||||
# Policy Rego membaca data.authz.catalogs / data.authz.write_schemas — nama
|
||||
# catalog/schema TIDAK di-hardcode di Rego. Ganti nama catalog = ubah `catalogs:`
|
||||
# di sini -> render -> reload. Dokumentasi: docs/authorization-cli.md
|
||||
# Policy Rego membaca data.authz.catalogs + grant schema (data.roles.<r>.schema_grants)
|
||||
# — nama catalog/schema & cakupan GRANT TIDAK di-hardcode di Rego. Ganti nama
|
||||
# catalog = ubah `catalogs:` di sini -> render -> reload. Grant tingkat-schema:
|
||||
# `roles.<r>.schemas: [{schema, caps, columns}]`. Dokumentasi: docs/authorization-cli.md
|
||||
# =============================================================================
|
||||
version: 1
|
||||
catalogs: [iceberg]
|
||||
@@ -29,6 +30,16 @@ roles:
|
||||
- table: gold.orders_gold
|
||||
caps: [select, write, ddl]
|
||||
columns: '*'
|
||||
schemas:
|
||||
- schema: bronze
|
||||
caps: [select, write, ddl]
|
||||
columns: '*'
|
||||
- schema: silver
|
||||
caps: [select, write, ddl]
|
||||
columns: '*'
|
||||
- schema: gold
|
||||
caps: [select, write, ddl]
|
||||
columns: '*'
|
||||
data-catalog:
|
||||
grants:
|
||||
- table: bronze.orders
|
||||
|
||||
@@ -39,6 +39,26 @@
|
||||
"ddl": true,
|
||||
"columns": "*"
|
||||
}
|
||||
},
|
||||
"schema_grants": {
|
||||
"bronze": {
|
||||
"select": true,
|
||||
"write": true,
|
||||
"ddl": true,
|
||||
"columns": "*"
|
||||
},
|
||||
"silver": {
|
||||
"select": true,
|
||||
"write": true,
|
||||
"ddl": true,
|
||||
"columns": "*"
|
||||
},
|
||||
"gold": {
|
||||
"select": true,
|
||||
"write": true,
|
||||
"ddl": true,
|
||||
"columns": "*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"data-catalog": {
|
||||
@@ -90,6 +110,6 @@
|
||||
},
|
||||
"_generated": {
|
||||
"from": "authz.yml",
|
||||
"sha256": "8c7de053a5683e00c27f8d92157463c65ff6d45d5802e1e03b48bb9a2a933cb5"
|
||||
"sha256": "36620fa9962357759a498f40d22a3a729d8fa7a8fa69d9941dbd270806968aad"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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