diff --git a/src/pretix/control/templates/pretixcontrol/users/form.html b/src/pretix/control/templates/pretixcontrol/users/form.html index 1f846bf8bf..56db1faff2 100644 --- a/src/pretix/control/templates/pretixcontrol/users/form.html +++ b/src/pretix/control/templates/pretixcontrol/users/form.html @@ -1,6 +1,7 @@ {% extends "pretixcontrol/base.html" %} {% load i18n %} {% load bootstrap3 %} +{% load icon %} {% block title %}{% trans "User" %}{% endblock %} {% block content %}

{% trans "User" %} {{ user.email }}

@@ -59,8 +60,72 @@ {% bootstrap_field form.is_verified layout='control' %} {% endif %} {% bootstrap_field form.last_login layout='control' %} - {% bootstrap_field form.require_2fa layout='control' %} {% bootstrap_field form.needs_password_change layout='control' %} + {% bootstrap_field form.require_2fa layout='control' %} +
+
+
+
+

+ {% trans "Available two-factor authentication methods" %} +

+
+ + {% for d in devices %} + + + + + + {% endfor %} +
+ {% if d.devicetype == 'totp' %} + TOTP + {% elif d.devicetype == 'u2f' %} + U2F + {% elif d.devicetype == 'webauthn' %} + WebAuthn + {% elif d.devicetype == 'emergency' %} + {% trans "Emergency tokens" %} + {% endif %} + {% if d.confirmed %} + {% icon "check" %} + {% else %} + {% icon "warning" %} + {% endif %} + + {{ d.name }} + + {% if d.throttling_failure_timestamp %} + {% blocktrans trimmed with date=d.throttling_failure_timestamp|date:"SHORT_DATETIME_FORMAT" count cnt=d.throttling_failure_count %} + 1 failed attempt since {{ date }} + {% plural %} + {{ cnt }} failed attempts since {{ date }} + {% endblocktrans %} +
+ {% endif %} + {% if d.devicetype == 'totp' %} + + step = {{ d.step }}, + t0 = {{ d.t0 }}, + digits = {{ d.digits }}, + tolerance = {{ d.tolerance }}, + drift = {{ d.drift }}, + last_t = {{ d.last_t }} + + {% elif d.devicetype == 'u2f' %} + + sign_count = {{ d.sign_count }} + + {% elif d.devicetype == 'emergency' %} + + token_count = {{ d.token_set.count }} + + {% endif %} +
+
+
+
{% trans "Team memberships" %} diff --git a/src/pretix/control/views/users.py b/src/pretix/control/views/users.py index 971be19c5b..dc570a4c48 100644 --- a/src/pretix/control/views/users.py +++ b/src/pretix/control/views/users.py @@ -41,15 +41,16 @@ from django.utils.translation import gettext_lazy as _ from django.views import View from django.views.generic import ListView, TemplateView from django_otp.plugins.otp_static.models import StaticDevice +from django_otp.plugins.otp_totp.models import TOTPDevice from hijack import signals from pretix.base.auth import get_auth_backends -from pretix.base.models import User +from pretix.base.models import User, U2FDevice, WebAuthnDevice from pretix.control.forms.filter import UserFilterForm from pretix.control.forms.users import UserEditForm from pretix.control.permissions import AdministratorPermissionRequiredMixin from pretix.control.views import CreateView, UpdateView -from pretix.control.views.user import RecentAuthenticationRequiredMixin +from pretix.control.views.user import RecentAuthenticationRequiredMixin, REAL_DEVICE_TYPES def get_used_backend(request): @@ -107,6 +108,21 @@ class UserEditView(AdministratorPermissionRequiredMixin, RecentAuthenticationReq ctx['backend'] = ( b[self.object.auth_backend].verbose_name if self.object.auth_backend in b else self.object.auth_backend ) + + ctx['devices'] = [] + for dt in [*REAL_DEVICE_TYPES, StaticDevice]: + objs = list(dt.objects.filter(user=self.request.user, confirmed=True)) + for obj in objs: + if dt == TOTPDevice: + obj.devicetype = 'totp' + elif dt == U2FDevice: + obj.devicetype = 'u2f' + elif dt == WebAuthnDevice: + obj.devicetype = 'webauthn' + elif dt == StaticDevice: + obj.devicetype = 'emergency' + ctx['devices'] += objs + return ctx def get_success_url(self):