forked from CGM_Public/pretix_original
Allow to re-auth by using the U2F token
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
{% extends "pretixcontrol/auth/base.html" %}
|
{% extends "pretixcontrol/auth/base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
|
{% load compress %}
|
||||||
|
{% load staticfiles %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form class="form-signin" action="" method="post">
|
<form class="form-signin" id="u2f-form" action="" method="post">
|
||||||
<form action="" method="post">
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h3>{% trans "Welcome back!" %}</h3>
|
<h3>{% trans "Welcome back!" %}</h3>
|
||||||
<p>
|
<p>
|
||||||
@@ -17,6 +18,15 @@
|
|||||||
<input class="form-control" id="id_password" name="password" placeholder="{% trans "Password" %}"
|
<input class="form-control" id="id_password" name="password" placeholder="{% trans "Password" %}"
|
||||||
title="" type="password" required="" autofocus>
|
title="" type="password" required="" autofocus>
|
||||||
</div>
|
</div>
|
||||||
|
{% if jsondata %}
|
||||||
|
<div class="sr-only alert alert-danger" id="u2f-error">
|
||||||
|
{% trans "U2F failed. Check that the correct authentication device is correctly plugged in." %}
|
||||||
|
</div>
|
||||||
|
<p><small>
|
||||||
|
<span class="fa fa-usb"></span>
|
||||||
|
{% trans "Alternatively, you can use your U2F device." %}
|
||||||
|
</small></p>
|
||||||
|
{% endif %}
|
||||||
<div class="form-group text-right">
|
<div class="form-group text-right">
|
||||||
<button type="submit" class="btn btn-primary btn-block">
|
<button type="submit" class="btn btn-primary btn-block">
|
||||||
{% trans "Continue" %}
|
{% trans "Continue" %}
|
||||||
@@ -26,5 +36,15 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if jsondata %}
|
||||||
|
<script type="text/json" id="u2f-login">
|
||||||
|
{{ jsondata|safe }}
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% compress js %}
|
||||||
|
<script type="text/javascript" src="{% static "jquery/js/jquery-2.1.1.min.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/u2f-api.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/u2f.js" %}"></script>
|
||||||
|
{% endcompress %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from django_otp.plugins.otp_static.models import StaticDevice
|
|||||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||||
from u2flib_server import u2f
|
from u2flib_server import u2f
|
||||||
from u2flib_server.jsapi import DeviceRegistration
|
from u2flib_server.jsapi import DeviceRegistration
|
||||||
|
from u2flib_server.utils import rand_bytes
|
||||||
|
|
||||||
from pretix.base.forms.user import User2FADeviceAddForm, UserSettingsForm
|
from pretix.base.forms.user import User2FADeviceAddForm, UserSettingsForm
|
||||||
from pretix.base.models import Event, NotificationSetting, U2FDevice, User
|
from pretix.base.models import Event, NotificationSetting, U2FDevice, User
|
||||||
@@ -48,9 +49,27 @@ class RecentAuthenticationRequiredMixin:
|
|||||||
class ReauthView(TemplateView):
|
class ReauthView(TemplateView):
|
||||||
template_name = 'pretixcontrol/user/reauth.html'
|
template_name = 'pretixcontrol/user/reauth.html'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def app_id(self):
|
||||||
|
return get_u2f_appid(self.request)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
password = request.POST.get("password", "")
|
password = request.POST.get("password", "")
|
||||||
if request.user.check_password(password):
|
valid = False
|
||||||
|
|
||||||
|
if '_u2f_challenge' in self.request.session and password.startswith('{'):
|
||||||
|
devices = [DeviceRegistration.wrap(device.json_data)
|
||||||
|
for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)]
|
||||||
|
challenge = self.request.session.pop('_u2f_challenge')
|
||||||
|
try:
|
||||||
|
u2f.verify_authenticate(devices, challenge, password, [self.app_id])
|
||||||
|
valid = True
|
||||||
|
except Exception:
|
||||||
|
logger.exception('U2F login failed')
|
||||||
|
|
||||||
|
valid = valid or request.user.check_password(password)
|
||||||
|
|
||||||
|
if valid:
|
||||||
t = int(time.time())
|
t = int(time.time())
|
||||||
request.session['pretix_auth_login_time'] = t
|
request.session['pretix_auth_login_time'] = t
|
||||||
request.session['pretix_auth_last_used'] = t
|
request.session['pretix_auth_last_used'] = t
|
||||||
@@ -61,6 +80,22 @@ class ReauthView(TemplateView):
|
|||||||
messages.error(request, _('The password you entered was invalid, please try again.'))
|
messages.error(request, _('The password you entered was invalid, please try again.'))
|
||||||
return self.get(request, *args, **kwargs)
|
return self.get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
ctx = super().get_context_data()
|
||||||
|
|
||||||
|
devices = [DeviceRegistration.wrap(device.json_data)
|
||||||
|
for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)]
|
||||||
|
if devices:
|
||||||
|
challenge = u2f.start_authenticate(devices, challenge=rand_bytes(32))
|
||||||
|
self.request.session['_u2f_challenge'] = challenge.json
|
||||||
|
ctx['jsondata'] = challenge.json
|
||||||
|
else:
|
||||||
|
if '_u2f_challenge' in self.request.session:
|
||||||
|
del self.request.session['_u2f_challenge']
|
||||||
|
ctx['jsondata'] = None
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class UserSettings(UpdateView):
|
class UserSettings(UpdateView):
|
||||||
model = User
|
model = User
|
||||||
|
|||||||
@@ -104,6 +104,9 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sr-only.alert::before {
|
||||||
|
background: none !important;
|
||||||
|
}
|
||||||
.alert-success::before {
|
.alert-success::before {
|
||||||
background: $state-success-border;
|
background: $state-success-border;
|
||||||
content: "\f00c";
|
content: "\f00c";
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ $(function () {
|
|||||||
if (data.errorCode && data.errorCode != 5) {
|
if (data.errorCode && data.errorCode != 5) {
|
||||||
$("#u2f-error").removeClass("sr-only");
|
$("#u2f-error").removeClass("sr-only");
|
||||||
} else {
|
} else {
|
||||||
$('#u2f-response').val(JSON.stringify(data));
|
$('#u2f-response, #id_password').val(JSON.stringify(data));
|
||||||
$('#u2f-form').submit();
|
$('#u2f-form').submit();
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|||||||
Reference in New Issue
Block a user