feat: add OIDC sign-in, roles, sessions and client address rule
Authorization-code flow with PKCE through an idp adapter (with a fake), ID token validation, identity and roles resolved from the directory with a short cache, refusal of unknown, disabled and locked accounts with one generic page, sessions that end when the account becomes unavailable, sign-out that ends the server-side session, role decorators and the trusted-proxy client address rule. Adds the OIDC_* and PUBLIC_BASE_URL configuration. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N3t4WL7bd9bdHHNjEsxzqC
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
"""Access control for views: signed in, Publisher, Admin (FR-1, FR-2, FR-25)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import wraps
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
def login_required(view):
|
||||
@wraps(view)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
if getattr(request, "portal_user", None) is None:
|
||||
target = reverse("login")
|
||||
if request.method in ("GET", "HEAD"):
|
||||
target += "?" + urlencode({"next": request.get_full_path()})
|
||||
return redirect(target)
|
||||
return view(request, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def _role_required(attribute: str):
|
||||
def decorator(view):
|
||||
@wraps(view)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
if not getattr(request.portal_user, attribute):
|
||||
return render(request, "accounts/forbidden.html", status=403)
|
||||
return view(request, *args, **kwargs)
|
||||
|
||||
return login_required(wrapper)
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
publisher_required = _role_required("is_publisher")
|
||||
admin_required = _role_required("is_admin")
|
||||
Reference in New Issue
Block a user