snapshot: preserve account security and profile UI cleanup
This commit is contained in:
@@ -2,6 +2,7 @@ from pathlib import Path
|
||||
from datetime import timedelta
|
||||
from tempfile import NamedTemporaryFile
|
||||
import json
|
||||
from io import BytesIO
|
||||
from functools import wraps
|
||||
|
||||
from celery import current_app
|
||||
@@ -33,7 +34,7 @@ from .backup_ops import (
|
||||
verify_backup_bundle,
|
||||
)
|
||||
from .branding import get_branding_email_copy, get_company_email_domain, get_default_notification_templates, get_portal_trial_config, is_trial_expired
|
||||
from .forms import AccountAvatarForm, AccountDetailsForm, AccountTOTPDisableForm, AccountTOTPEnableForm, OffboardingRequestForm, OnboardingRequestForm, PortalBrandingForm, PortalCompanyConfigForm, PortalTrialConfigForm, UserManagementCreateForm
|
||||
from .forms import AccountAvatarForm, AccountDetailsForm, AccountTOTPDisableForm, AccountTOTPEnableForm, AccountTOTPRegenerateRecoveryCodesForm, OffboardingRequestForm, OnboardingRequestForm, PortalBrandingForm, PortalCompanyConfigForm, PortalTrialConfigForm, UserManagementCreateForm
|
||||
from .form_builder import (
|
||||
DEFAULT_FIELD_ORDER,
|
||||
LOCKED_FIELD_RULES,
|
||||
@@ -46,7 +47,7 @@ from .models import AdminAuditLog, AsyncTaskLog, EmployeeProfile, FormFieldConfi
|
||||
from .emailing import send_system_email
|
||||
from .roles import ROLE_GROUP_NAMES, ROLE_LABELS, ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, assign_user_role, get_user_role_key, get_user_role_label, user_has_capability
|
||||
from .services import get_email_test_redirect, is_email_test_mode, is_nextcloud_enabled, upload_to_nextcloud
|
||||
from .totp import build_otpauth_uri, generate_totp_secret
|
||||
from .totp import build_otpauth_uri, generate_recovery_codes, generate_totp_secret
|
||||
from .tasks import (
|
||||
_generate_onboarding_intro_pdf,
|
||||
_generate_onboarding_intro_session_pdf,
|
||||
@@ -130,7 +131,9 @@ def healthz(request):
|
||||
@login_required
|
||||
def account_profile_page(request):
|
||||
session_secret_key = 'account_totp_pending_secret'
|
||||
session_codes_key = 'account_totp_recovery_codes'
|
||||
profile, created = UserProfile.objects.get_or_create(user=request.user)
|
||||
recovery_codes = request.session.pop(session_codes_key, [])
|
||||
pending_totp_secret = request.session.get(session_secret_key) or ''
|
||||
if profile.totp_enabled:
|
||||
pending_totp_secret = ''
|
||||
@@ -143,6 +146,7 @@ def account_profile_page(request):
|
||||
details_form = AccountDetailsForm(user=request.user, profile=profile)
|
||||
totp_enable_form = AccountTOTPEnableForm(user=request.user, secret=pending_totp_secret)
|
||||
totp_disable_form = AccountTOTPDisableForm(user=request.user, profile=profile)
|
||||
totp_regenerate_form = AccountTOTPRegenerateRecoveryCodesForm(user=request.user, profile=profile)
|
||||
account_edit_open = False
|
||||
totp_edit_open = False
|
||||
if request.method == 'POST':
|
||||
@@ -166,7 +170,9 @@ def account_profile_page(request):
|
||||
totp_edit_open = True
|
||||
totp_enable_form = AccountTOTPEnableForm(request.POST, user=request.user, secret=pending_totp_secret)
|
||||
if totp_enable_form.is_valid():
|
||||
profile.enable_totp(pending_totp_secret)
|
||||
recovery_codes = generate_recovery_codes()
|
||||
profile.enable_totp(pending_totp_secret, recovery_codes)
|
||||
request.session[session_codes_key] = recovery_codes
|
||||
request.session.pop(session_secret_key, None)
|
||||
messages.success(request, _('TOTP wurde aktiviert.'))
|
||||
return redirect('account_profile_page')
|
||||
@@ -180,10 +186,34 @@ def account_profile_page(request):
|
||||
messages.success(request, _('TOTP wurde deaktiviert.'))
|
||||
return redirect('account_profile_page')
|
||||
messages.error(request, _('TOTP konnte nicht deaktiviert werden.'))
|
||||
elif form_kind == 'totp_regenerate_codes':
|
||||
totp_edit_open = True
|
||||
totp_regenerate_form = AccountTOTPRegenerateRecoveryCodesForm(request.POST, user=request.user, profile=profile)
|
||||
if totp_regenerate_form.is_valid():
|
||||
recovery_codes = generate_recovery_codes()
|
||||
profile.set_recovery_codes(recovery_codes)
|
||||
profile.save(update_fields=['totp_recovery_codes', 'updated_at'])
|
||||
request.session[session_codes_key] = recovery_codes
|
||||
messages.success(request, _('Recovery-Codes wurden neu erzeugt.'))
|
||||
return redirect('account_profile_page')
|
||||
messages.error(request, _('Recovery-Codes konnten nicht neu erzeugt werden.'))
|
||||
|
||||
branding_context = get_branding_email_copy()
|
||||
totp_account_name = (request.user.email or request.user.username or '').strip()
|
||||
totp_issuer = (branding_context.get('portal_title') or branding_context.get('company_name') or 'Workdock').strip()
|
||||
totp_otpauth_uri = '' if profile.totp_enabled else build_otpauth_uri(pending_totp_secret, account_name=totp_account_name, issuer=totp_issuer)
|
||||
totp_qr_svg = ''
|
||||
if totp_otpauth_uri:
|
||||
try:
|
||||
import qrcode
|
||||
import qrcode.image.svg
|
||||
|
||||
qr_image = qrcode.make(totp_otpauth_uri, image_factory=qrcode.image.svg.SvgPathImage)
|
||||
stream = BytesIO()
|
||||
qr_image.save(stream)
|
||||
totp_qr_svg = stream.getvalue().decode('utf-8')
|
||||
except Exception:
|
||||
totp_qr_svg = ''
|
||||
return render(
|
||||
request,
|
||||
'workflows/account_profile.html',
|
||||
@@ -194,11 +224,14 @@ def account_profile_page(request):
|
||||
'details_form': details_form,
|
||||
'totp_enable_form': totp_enable_form,
|
||||
'totp_disable_form': totp_disable_form,
|
||||
'totp_regenerate_form': totp_regenerate_form,
|
||||
'account_edit_open': account_edit_open,
|
||||
'totp_edit_open': totp_edit_open,
|
||||
'role_label': get_user_role_label(request.user),
|
||||
'totp_pending_secret': pending_totp_secret,
|
||||
'totp_otpauth_uri': '' if profile.totp_enabled else build_otpauth_uri(pending_totp_secret, account_name=totp_account_name, issuer=totp_issuer),
|
||||
'totp_otpauth_uri': totp_otpauth_uri,
|
||||
'totp_qr_svg': totp_qr_svg,
|
||||
'totp_recovery_codes': recovery_codes,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user