feat(iam): provision the LLM inference roles and assert project roles
The api-gw gates /v1/* on a capability named in the access token. ZITADEL had no such role, so every inference call was 403 once the token came from ZITADEL instead of Authentik. - add llm:inference (Authentik spelling, keeps consumer manifests unchanged) and llm-inference (dash alias) to the project roles - enable projectRoleAssertion on the project, which is what puts urn:zitadel:iam:org:project:roles into the access token; without it the role list never reaches the resource server
This commit is contained in:
@@ -80,6 +80,10 @@ ROLES = {
|
|||||||
"sqs-writers": "SQS/Temporal queue read/write access",
|
"sqs-writers": "SQS/Temporal queue read/write access",
|
||||||
"s3-users": "S3 read access",
|
"s3-users": "S3 read access",
|
||||||
"s3-writers": "S3 read/write access",
|
"s3-writers": "S3 read/write access",
|
||||||
|
# LLM gate for the api-gw. The colon form matches the Authentik-era
|
||||||
|
# permission string, so consumer manifests did not change.
|
||||||
|
"llm:inference": "LLM inference access (api-gw)",
|
||||||
|
"llm-inference": "LLM inference access (dash alias)",
|
||||||
}
|
}
|
||||||
|
|
||||||
_WEB = {"app_type_id": "OIDC_APP_TYPE_WEB", "auth_method_id": "OIDC_AUTH_METHOD_TYPE_BASIC"}
|
_WEB = {"app_type_id": "OIDC_APP_TYPE_WEB", "auth_method_id": "OIDC_AUTH_METHOD_TYPE_BASIC"}
|
||||||
@@ -266,13 +270,26 @@ def _api(method: str, path: str, token: str, body: dict | None = None,
|
|||||||
|
|
||||||
def _ensure_project(token: str, name: str) -> str:
|
def _ensure_project(token: str, name: str) -> str:
|
||||||
code, d = _api("POST", "/management/v1/projects/_search", token, {})
|
code, d = _api("POST", "/management/v1/projects/_search", token, {})
|
||||||
|
project_id = ""
|
||||||
for row in d.get("result") or []:
|
for row in d.get("result") or []:
|
||||||
if row.get("name") == name:
|
if row.get("name") == name:
|
||||||
return row["id"]
|
project_id = row["id"]
|
||||||
code, d = _api("POST", "/management/v1/projects", token, {"name": name})
|
break
|
||||||
if code not in (200, 201) or not d.get("id"):
|
if not project_id:
|
||||||
die(f"create project {name} failed: {code} {d}")
|
code, d = _api("POST", "/management/v1/projects", token, {"name": name})
|
||||||
return d["id"]
|
if code not in (200, 201) or not d.get("id"):
|
||||||
|
die(f"create project {name} failed: {code} {d}")
|
||||||
|
project_id = d["id"]
|
||||||
|
|
||||||
|
# projectRoleAssertion puts the project roles into the ACCESS token as
|
||||||
|
# `urn:zitadel:iam:org:project:roles`. Without it the api-gw sees no
|
||||||
|
# llm:inference role and answers 403 for every inference call, even when
|
||||||
|
# the caller is correctly authorised in ZITADEL.
|
||||||
|
code, d = _api("PUT", f"/management/v1/projects/{project_id}",
|
||||||
|
token, {"name": name, "projectRoleAssertion": True})
|
||||||
|
if code not in (200, 201):
|
||||||
|
die(f"enable projectRoleAssertion on {name} failed: {code} {d}")
|
||||||
|
return project_id
|
||||||
|
|
||||||
|
|
||||||
def _ensure_roles(token: str, project_id: str, roles: dict[str, str]) -> None:
|
def _ensure_roles(token: str, project_id: str, roles: dict[str, str]) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user