48 lines
1.6 KiB
Python
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)
|