Corp/backend/api/oidc/validators.py
2026-09-07 19:43:48 +03:00

88 lines
3.0 KiB
Python

from __future__ import annotations
from urllib.parse import urljoin
from django.conf import settings
from oauth2_provider.oauth2_validators import OAuth2Validator
def _preferred_username(user) -> str:
username = (getattr(user, "username", "") or "").strip()
return username or f"employee-{user.pk}"
def _is_active_employee(user) -> bool:
if not user or not getattr(user, "is_authenticated", False):
return False
if not getattr(user, "is_active", False):
return False
if not getattr(user, "email_verified", False):
return False
return bool(getattr(user, "is_actually_active", True))
def _employee_groups(user) -> list[str]:
groups = {"portal-users"}
groups.update(user.groups.values_list("name", flat=True))
if getattr(user, "is_staff", False):
groups.add("portal-staff")
if getattr(user, "is_superuser", False):
groups.add("portal-admins")
return sorted(groups)
def _picture_url(user) -> str | None:
avatar = getattr(user, "avatar", None)
if not avatar:
return None
try:
return urljoin(f"{settings.SITE_URL}/", avatar.url.lstrip("/"))
except (ValueError, AttributeError):
return None
class CorporateOAuth2Validator(OAuth2Validator):
"""Expose only verified corporate identity claims to OIDC clients."""
oidc_claim_scope = {
**OAuth2Validator.oidc_claim_scope,
"groups": "groups",
}
def validate_code(self, client_id, code, client, request, *args, **kwargs):
valid = super().validate_code(client_id, code, client, request, *args, **kwargs)
return bool(valid and _is_active_employee(getattr(request, "user", None)))
def validate_bearer_token(self, token, scopes, request):
valid = super().validate_bearer_token(token, scopes, request)
return bool(valid and _is_active_employee(getattr(request, "user", None)))
def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs):
valid = super().validate_refresh_token(
refresh_token, client, request, *args, **kwargs
)
return bool(valid and _is_active_employee(getattr(request, "user", None)))
def get_additional_claims(self, request):
user = request.user
full_name = user.get_full_name().strip()
updated_at = getattr(user, "updated_at", None)
claims = {
"name": full_name or str(user),
"given_name": user.first_name,
"family_name": user.last_name,
"preferred_username": _preferred_username(user),
"nickname": _preferred_username(user),
"email": user.email,
"email_verified": bool(user.email_verified),
"locale": "ru-RU",
"zoneinfo": settings.TIME_ZONE,
"groups": _employee_groups(user),
}
picture = _picture_url(user)
if picture:
claims["picture"] = picture
if updated_at:
claims["updated_at"] = int(updated_at.timestamp())
return claims