Files
training-software/accounts/services.py
Paperclip CTO 01661bf5a8
Some checks failed
CI / lint (push) Successful in 6s
CI / test (push) Failing after 7s
CI / build-container (push) Has been skipped
fix: restore CI by fixing lint, test collection, and notification syntax
2026-05-18 14:53:29 +02:00

48 lines
1.6 KiB
Python

from __future__ import annotations
from .models import AccountUser, GroupRoleMap, UserRoleBinding
def sync_roles_from_oidc_claims(user: AccountUser, claims: dict) -> None:
"""
Reconcile UserRoleBinding rows sourced from OIDC against the current
group claim set. Only "oidc"-sourced bindings are touched; manually
granted bindings are preserved.
"""
groups: list[str] = claims.get("groups", [])
desired: set[tuple] = set()
for mapping in GroupRoleMap.objects.filter(oidc_group__in=groups).select_related("role"):
desired.add((mapping.role_id, mapping.org_id))
existing = {
(b.role_id, b.org_id): b
for b in UserRoleBinding.objects.filter(user=user, source="oidc")
}
to_create = desired - set(existing)
to_delete = set(existing) - desired
for role_id, org_id in to_create:
UserRoleBinding.objects.create(user=user, role_id=role_id, org_id=org_id, source="oidc")
if to_delete:
for role_id, org_id in to_delete:
UserRoleBinding.objects.filter(
user=user, source="oidc", role_id=role_id, org_id=org_id
).delete()
def get_effective_capabilities(user: AccountUser, org_id=None) -> list[str]:
"""Return flat sorted capability list for a user, scoped to org_id when given."""
from .models import ROLE_CAPABILITIES
bindings = UserRoleBinding.objects.filter(user=user).select_related("role")
if org_id is not None:
bindings = bindings.filter(org_id__in=[org_id, None])
caps: set[str] = set()
for binding in bindings:
caps.update(ROLE_CAPABILITIES.get(binding.role.slug, []))
return sorted(caps)