146 lines
5.8 KiB
Python
146 lines
5.8 KiB
Python
from django.contrib.auth import get_user_model
|
|
from django.shortcuts import render
|
|
from django.utils.encoding import force_bytes
|
|
from django.utils.http import urlsafe_base64_encode
|
|
from django.utils.translation import gettext as _
|
|
from django.urls import reverse
|
|
from django.contrib.auth.tokens import default_token_generator
|
|
|
|
from .branding import get_branding_email_copy
|
|
from .forms import UserManagementCreateForm
|
|
from .models import AdminAuditLog
|
|
from .emailing import send_system_email
|
|
from .roles import ROLE_GROUP_NAMES, ROLE_LABELS, ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, get_user_role_key
|
|
|
|
|
|
def user_management_rows(*, display_user_name_fn):
|
|
user_model = get_user_model()
|
|
role_order = {
|
|
ROLE_PLATFORM_OWNER: 0,
|
|
ROLE_SUPER_ADMIN: 0,
|
|
'admin': 1,
|
|
'it_staff': 2,
|
|
'staff': 3,
|
|
}
|
|
rows = []
|
|
for user in user_model.objects.all().order_by('-is_active', 'username'):
|
|
role_key = get_user_role_key(user)
|
|
rows.append(
|
|
{
|
|
'user': user,
|
|
'role_key': role_key,
|
|
'role_label': str(ROLE_LABELS[role_key]),
|
|
'role_sort': role_order.get(role_key, 99),
|
|
'display_name': display_user_name_fn(user),
|
|
}
|
|
)
|
|
rows.sort(key=lambda item: (not item['user'].is_active, item['role_sort'], item['user'].username.lower()))
|
|
return rows
|
|
|
|
|
|
def render_user_management(request, *, create_form=None, status_code: int = 200, audit_action_label_fn, display_user_name_fn):
|
|
recent_user_events = list(
|
|
AdminAuditLog.objects.select_related('actor')
|
|
.filter(action__in=['user_created', 'user_updated', 'user_password_reset_sent', 'user_deleted'])
|
|
.order_by('-created_at', '-id')[:12]
|
|
)
|
|
for row in recent_user_events:
|
|
row.action_label = audit_action_label_fn(row.action)
|
|
role_key = (row.details or {}).get('role')
|
|
row.role_label = str(ROLE_LABELS[role_key]) if role_key in ROLE_LABELS else role_key
|
|
include_product_owner = get_user_role_key(request.user) == ROLE_PLATFORM_OWNER
|
|
return render(
|
|
request,
|
|
'workflows/user_management.html',
|
|
{
|
|
'create_form': create_form or UserManagementCreateForm(include_product_owner=include_product_owner),
|
|
'rows': user_management_rows(display_user_name_fn=display_user_name_fn),
|
|
'role_choices': [
|
|
(key, str(ROLE_LABELS[key]))
|
|
for key in ROLE_GROUP_NAMES
|
|
if include_product_owner or key != ROLE_PLATFORM_OWNER
|
|
],
|
|
'include_product_owner': include_product_owner,
|
|
'recent_user_events': recent_user_events,
|
|
},
|
|
status=status_code,
|
|
)
|
|
|
|
|
|
def platform_owner_user_count() -> int:
|
|
user_model = get_user_model()
|
|
return sum(1 for user in user_model.objects.all() if get_user_role_key(user) == ROLE_PLATFORM_OWNER and user.is_active)
|
|
|
|
|
|
def super_admin_user_count() -> int:
|
|
user_model = get_user_model()
|
|
return sum(1 for user in user_model.objects.all() if get_user_role_key(user) == ROLE_SUPER_ADMIN and user.is_active)
|
|
|
|
|
|
def would_remove_last_super_admin(user, new_role_key: str | None = None, new_is_active: bool | None = None, deleting: bool = False) -> bool:
|
|
if get_user_role_key(user) != ROLE_SUPER_ADMIN or not user.is_active:
|
|
return False
|
|
if super_admin_user_count() > 1:
|
|
return False
|
|
if deleting:
|
|
return True
|
|
if new_role_key is not None and new_role_key != ROLE_SUPER_ADMIN:
|
|
return True
|
|
if new_is_active is not None and not new_is_active:
|
|
return True
|
|
return False
|
|
|
|
|
|
def would_remove_last_platform_owner(user, new_role_key: str | None = None, new_is_active: bool | None = None, deleting: bool = False) -> bool:
|
|
if get_user_role_key(user) != ROLE_PLATFORM_OWNER or not user.is_active:
|
|
return False
|
|
if platform_owner_user_count() > 1:
|
|
return False
|
|
if deleting:
|
|
return True
|
|
if new_role_key is not None and new_role_key != ROLE_PLATFORM_OWNER:
|
|
return True
|
|
if new_is_active is not None and not new_is_active:
|
|
return True
|
|
return False
|
|
|
|
|
|
def send_user_access_email(request, target_user, *, invitation: bool, display_user_name_fn) -> None:
|
|
email = (target_user.email or '').strip()
|
|
if not email:
|
|
raise ValueError(_('Für diesen Benutzer ist keine E-Mail-Adresse hinterlegt.'))
|
|
|
|
uid = urlsafe_base64_encode(force_bytes(target_user.pk))
|
|
token = default_token_generator.make_token(target_user)
|
|
reset_path = reverse('password_reset_confirm', kwargs={'uidb64': uid, 'token': token})
|
|
reset_url = request.build_absolute_uri(reset_path)
|
|
branding_copy = get_branding_email_copy()
|
|
|
|
if invitation:
|
|
subject = _('Zugangseinladung für %(username)s') % {'username': target_user.username}
|
|
body = _(
|
|
'Hallo %(name)s,\n\n'
|
|
'für Sie wurde ein Benutzerkonto im %(portal_title)s angelegt.\n'
|
|
'Bitte öffnen Sie den folgenden Link, um Ihr Passwort zu setzen:\n'
|
|
'%(url)s\n\n'
|
|
'Wenn Sie diese Einladung nicht erwartet haben, melden Sie sich bitte bei Ihrem Administrator.'
|
|
) % {
|
|
'name': display_user_name_fn(target_user),
|
|
'portal_title': branding_copy['portal_title'],
|
|
'url': reset_url,
|
|
}
|
|
else:
|
|
subject = _('Passwort zurücksetzen für %(username)s') % {'username': target_user.username}
|
|
body = _(
|
|
'Hallo %(name)s,\n\n'
|
|
'für Ihr Konto wurde ein Link zum Zurücksetzen des Passworts erstellt.\n'
|
|
'Bitte öffnen Sie den folgenden Link:\n'
|
|
'%(url)s\n\n'
|
|
'Wenn Sie diese Anfrage nicht erwartet haben, können Sie diese E-Mail ignorieren.'
|
|
) % {
|
|
'name': display_user_name_fn(target_user),
|
|
'url': reset_url,
|
|
}
|
|
|
|
send_system_email(subject=subject, body=body, to=[email])
|