fix(authentik): add minio policy scope mapping (homelab-admins->consoleAdmin else readonly) + set rock email — MinIO CLAIM_NAME=policy got no claim (no MinIO access); empty rock email broke Grafana OIDC (GitHub-style /emails 404)
This commit is contained in:
@@ -148,6 +148,28 @@ groups_mapping = get_or_create(
|
|||||||
)
|
)
|
||||||
GROUPS_MAPPING_PK = groups_mapping["pk"]
|
GROUPS_MAPPING_PK = groups_mapping["pk"]
|
||||||
|
|
||||||
|
# MinIO maps OIDC users to a MinIO policy via a "policy" claim
|
||||||
|
# (MINIO_IDENTITY_OPENID_CLAIM_NAME=policy). Emit consoleAdmin (full admin) for
|
||||||
|
# homelab-admins members, readonly for everyone else. Without this claim MinIO
|
||||||
|
# assigns no policy and OIDC users get no access.
|
||||||
|
_POLICY_EXPR = (
|
||||||
|
"return {\"policy\": \"consoleAdmin\" "
|
||||||
|
"if request.user.ak_groups.filter(name=\"homelab-admins\").exists() "
|
||||||
|
"else \"readonly\"}"
|
||||||
|
)
|
||||||
|
policy_mapping = get_or_create(
|
||||||
|
"/api/v3/propertymappings/provider/scope/",
|
||||||
|
"/api/v3/propertymappings/provider/scope/",
|
||||||
|
"scope_name=minio",
|
||||||
|
{
|
||||||
|
"name": "homelab: minio policy claim",
|
||||||
|
"scope_name": "minio",
|
||||||
|
"expression": _POLICY_EXPR,
|
||||||
|
},
|
||||||
|
patch_existing={"expression": _POLICY_EXPR},
|
||||||
|
)
|
||||||
|
POLICY_MAPPING_PK = policy_mapping["pk"]
|
||||||
|
|
||||||
# Fetch the standard openid/email/profile mapping pks (shipped by default).
|
# Fetch the standard openid/email/profile mapping pks (shipped by default).
|
||||||
status, res = api("GET", "/api/v3/propertymappings/provider/scope/")
|
status, res = api("GET", "/api/v3/propertymappings/provider/scope/")
|
||||||
by_scope = {m["scope_name"]: m["pk"] for m in res["results"]}
|
by_scope = {m["scope_name"]: m["pk"] for m in res["results"]}
|
||||||
@@ -182,6 +204,10 @@ if res.get("results"):
|
|||||||
status, rock = api("PATCH", f"/api/v3/core/users/{rock['pk']}/", {
|
status, rock = api("PATCH", f"/api/v3/core/users/{rock['pk']}/", {
|
||||||
"groups": [homelab_admins["pk"], grafana_admins["pk"]],
|
"groups": [homelab_admins["pk"], grafana_admins["pk"]],
|
||||||
"is_active": True,
|
"is_active": True,
|
||||||
|
# email is REQUIRED: Grafana's OIDC login reads the email claim from
|
||||||
|
# userinfo; an empty email makes Grafana fall back to a GitHub-style
|
||||||
|
# <userinfo>/emails call, which Authentik 404s -> login fails entirely.
|
||||||
|
"email": "[email protected]",
|
||||||
})
|
})
|
||||||
if status not in (200, 201):
|
if status not in (200, 201):
|
||||||
die(f"PATCH user rock -> {status} {rock}")
|
die(f"PATCH user rock -> {status} {rock}")
|
||||||
@@ -192,6 +218,8 @@ else:
|
|||||||
"username": "rock",
|
"username": "rock",
|
||||||
"name": "Rock",
|
"name": "Rock",
|
||||||
"is_active": True,
|
"is_active": True,
|
||||||
|
# Required for Grafana OIDC (see PATCH branch above).
|
||||||
|
"email": "[email protected]",
|
||||||
"groups": [homelab_admins["pk"], grafana_admins["pk"]],
|
"groups": [homelab_admins["pk"], grafana_admins["pk"]],
|
||||||
"path": "users",
|
"path": "users",
|
||||||
"type": "internal",
|
"type": "internal",
|
||||||
@@ -258,6 +286,9 @@ SERVICES = {
|
|||||||
app_pks_for_binding = []
|
app_pks_for_binding = []
|
||||||
|
|
||||||
for name, cfg in SERVICES.items():
|
for name, cfg in SERVICES.items():
|
||||||
|
# MinIO also needs the "policy" claim (via the minio scope mapping) so its
|
||||||
|
# MINIO_IDENTITY_OPENID_CLAIM_NAME=policy maps homelab-admins -> consoleAdmin.
|
||||||
|
provider_mappings = SCOPE_PKS + ([POLICY_MAPPING_PK] if name == "minio" else [])
|
||||||
ns, secret_name, key = cfg["client_secret_source"]
|
ns, secret_name, key = cfg["client_secret_source"]
|
||||||
client_secret = kubectl_get_secret_key(ns, secret_name, key)
|
client_secret = kubectl_get_secret_key(ns, secret_name, key)
|
||||||
if client_secret is None:
|
if client_secret is None:
|
||||||
@@ -284,7 +315,7 @@ for name, cfg in SERVICES.items():
|
|||||||
"authorization_flow": AUTHORIZATION_FLOW_PK,
|
"authorization_flow": AUTHORIZATION_FLOW_PK,
|
||||||
"invalidation_flow": INVALIDATION_FLOW_PK,
|
"invalidation_flow": INVALIDATION_FLOW_PK,
|
||||||
"signing_key": SIGNING_KEY_PK,
|
"signing_key": SIGNING_KEY_PK,
|
||||||
"property_mappings": SCOPE_PKS,
|
"property_mappings": provider_mappings,
|
||||||
"sub_mode": "hashed_user_id",
|
"sub_mode": "hashed_user_id",
|
||||||
"include_claims_in_id_token": True,
|
"include_claims_in_id_token": True,
|
||||||
# authentik 2026.x requires grant_types to be set explicitly; the
|
# authentik 2026.x requires grant_types to be set explicitly; the
|
||||||
@@ -301,7 +332,7 @@ for name, cfg in SERVICES.items():
|
|||||||
# never touch client_secret again once created (that's the source of
|
# never touch client_secret again once created (that's the source of
|
||||||
# truth in the k8s Secret, and re-sending it here is harmless anyway).
|
# truth in the k8s Secret, and re-sending it here is harmless anyway).
|
||||||
patch_existing={
|
patch_existing={
|
||||||
"property_mappings": SCOPE_PKS,
|
"property_mappings": provider_mappings,
|
||||||
"grant_types": ["authorization_code", "refresh_token"],
|
"grant_types": ["authorization_code", "refresh_token"],
|
||||||
"redirect_uris": [
|
"redirect_uris": [
|
||||||
{"matching_mode": "strict", "url": u} for u in cfg["redirect_uris"]
|
{"matching_mode": "strict", "url": u} for u in cfg["redirect_uris"]
|
||||||
|
|||||||
Reference in New Issue
Block a user