Sign-in by customer: realm petrolyst, row rules, guest role (PD-21)

Keycloak import for realm petrolyst: two clients, petrolyst with MFA for
Superset and Traefik, petrolyst-app password only. It lists only
app.petrolyst.com and traefik.petrolyst.com, and no customer names, as this
repository is public. public.v_operator shows each customer's group. Dashboards
overview and site, role Petrolyst Guest, filtered by row. Superset refuses a
user with no mapped role. The realm travels with the release: each release's
compose files name their realm and secret file, and a rollback needs only its
target's secret. Not deployed.

Checks earlier on this content: tests/run.sh 109 of 109 and read-only, dashboards
import twice, column drift 0, compose config on old and new trees, the secret
check on a scratch folder, a rehearsal on a throwaway Keycloak 26.4. Never a real
deploy or rollback; shellcheck not installed today, clean when last run. Codex
inspected once; its fixes are not re-inspected, on the owner's word.

Bands: persistence, Superset. Rules: 1, 2, 3, 6, 14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nasim SadatandClaude Opus 5.5 committed 2026-10-30 18:31:14 +01:00
1 parent ab553526f6
commit 78a498f1ce
20 files changed
+279 -78

No files matched your search

@@ -17,7 +17,7 @@ live view and any drift is reported (exit status 2).
The database is never created or changed: the bundle's database uuid must
already exist under the name "Fuel", otherwise nothing is imported.
The dashboards (atoma-overview, atoma-site) get no role: admins see them in Superset, and
The dashboards (overview, site) get no role: admins see them in Superset, and
the Petrolyst app shows them with guest passes (PD-12). With --embedded-out FILE
--embed-domain URL, each of them is made embeddable from that origin (its embedding id stays
the same across imports), and FILE receives {slug: embedding id} for the Petrolyst server:
@@ -36,7 +36,7 @@ import zipfile
import yaml
DATABASE_NAME = "Fuel"
EMBEDDED_PREFIX = "atoma-"
EMBEDDED_SLUGS = {"overview", "site"}
STEPS = [("datasets", "SqlaTable"), ("charts", "Slice"), ("dashboards", "Dashboard")]
@@ -104,12 +104,17 @@ def main() -> int:
if path.startswith("dashboards/"):
dashboard = db.session.query(Dashboard).filter_by(
uuid=yaml.safe_load(text)["uuid"]).one()
if dashboard.slug.startswith(EMBEDDED_PREFIX) and args.embedded_out:
if dashboard.slug in EMBEDDED_SLUGS and args.embedded_out:
from superset.daos.dashboard import EmbeddedDashboardDAO
config = EmbeddedDashboardDAO.upsert(dashboard, [args.embed_domain])
db.session.flush()
embedded[dashboard.slug] = str(config.uuid)
# An empty or short list would stop the app's dashboards from opening.
if args.embedded_out and set(embedded) != EMBEDDED_SLUGS:
db.session.rollback()
print(f"embedded: expected {sorted(EMBEDDED_SLUGS)}, got {sorted(embedded)}", file=sys.stderr)
return 3
db.session.commit()
if args.embedded_out:
with open(args.embedded_out, "w") as out: