Align provisioned identities with OIDC subjects
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

This commit is contained in:
tegwick 2026-08-14 18:53:37 +02:00
parent cbbd66697f
commit 2a2d8ba055
3 changed files with 16 additions and 5 deletions

View file

@ -68,9 +68,10 @@ mutation CreateUser($id: String!, $email: String!, $display: String!) {
except Exception:
pass
raise
return Result("netkingdom-lldap", username, "password_setup_required", resumed)
return Result("netkingdom-lldap", _oidc_subject(username), "password_setup_required", resumed)
def suspend(self, subject: str) -> Result:
subject = _directory_username(subject)
token = self._login()
_, groups = self._directory(token)
group_id = self._ensure_group(token, groups, "netkingdom-suspended")
@ -78,6 +79,7 @@ mutation CreateUser($id: String!, $email: String!, $display: String!) {
return Result("netkingdom-lldap", subject, "suspended", False)
def reactivate(self, subject: str) -> Result:
subject = _directory_username(subject)
token = self._login()
_, groups = self._directory(token)
group = next((item for item in groups if item.get("displayName") == "netkingdom-suspended"), None)
@ -89,6 +91,7 @@ mutation Remove($userId: String!, $groupId: Int!) {
return Result("netkingdom-lldap", subject, "active", False)
def deprovision(self, subject: str) -> Result:
subject = _directory_username(subject)
token = self._login()
if self._user(token, subject) is None:
return Result("netkingdom-lldap", subject, "deprovisioned", True)
@ -249,7 +252,7 @@ def dispatch(
if path == "/v1/identities/reconcile":
return provisioner.reconcile(payload)
_required(payload, "external_subject", "idempotency_key", "correlation_id")
subject = str(payload["external_subject"])
subject = _directory_username(str(payload["external_subject"]))
if path == "/v1/identities/suspend":
return provisioner.suspend(subject)
if path == "/v1/identities/reactivate":
@ -305,3 +308,11 @@ def _desired(payload: dict[str, Any]) -> tuple[str, str, set[str], str]:
if desired_status == "suspended":
groups.add("netkingdom-suspended")
return subject, email, groups, desired_status
def _directory_username(subject: str) -> str:
return subject[4:].split(",", 1)[0] if subject.startswith("uid=") else subject
def _oidc_subject(username: str) -> str:
return f"uid={username},ou=people,dc=netkingdom,dc=local"

View file

@ -6,7 +6,7 @@ import os
from urllib.parse import parse_qs, urlsplit
import secrets
from provisioner import LLDAPProvisioner, dispatch
from provisioner import LLDAPProvisioner, _directory_username, dispatch
from password_setup import LLDAPPasswordSetter, PasswordSetupGrants
@ -46,7 +46,7 @@ class Handler(BaseHTTPRequestHandler):
response = asdict(result)
if path == "/v1/identities/provision" and result.status == "password_setup_required":
response["password_setup_url"] = self.password_setups.issue(
result.external_subject
_directory_username(result.external_subject)
)
self._send(200, response)

View file

@ -45,7 +45,7 @@ spec:
seccompProfile: {type: RuntimeDefault}
containers:
- name: portal
image: forgejo.coulomb.social/coulomb/user-engine@sha256:6e1909fab6546ba5ee7c23fbdcd5b8b8a6550a8e509bff36ea37391c9d684083
image: forgejo.coulomb.social/coulomb/user-engine@sha256:e3b5f65bafc1c0260dfdf2567a52766e67506ceb878a51759a2e9a307c4b5eb8
imagePullPolicy: IfNotPresent
ports: [{name: http, containerPort: 8080}]
env: