Add optional timeouts for backend sessions

This commit is contained in:
Raphael Michel
2017-09-04 19:29:19 +02:00
parent 88f5af3e77
commit 2f15d410fe
10 changed files with 204 additions and 53 deletions

View File

@@ -1,8 +1,9 @@
from urllib.parse import urljoin, urlparse
import time
from urllib.parse import quote, urljoin, urlparse
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.urlresolvers import get_script_prefix, resolve
from django.contrib.auth import REDIRECT_FIELD_NAME, logout
from django.core.urlresolvers import get_script_prefix, resolve, reverse
from django.http import Http404
from django.shortcuts import redirect, resolve_url
from django.utils.deprecation import MiddlewareMixin
@@ -28,6 +29,24 @@ class PermissionMiddleware(MiddlewareMixin):
"auth.invite",
)
def _login_redirect(self, request):
# Taken from django/contrib/auth/decorators.py
path = request.build_absolute_uri()
# urlparse chokes on lazy objects in Python 3, force to str
resolved_login_url = force_str(
resolve_url(settings.LOGIN_URL_CONTROL))
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(
path, resolved_login_url, REDIRECT_FIELD_NAME)
def process_request(self, request):
url = resolve(request.path_info)
url_name = url.url_name
@@ -42,22 +61,18 @@ class PermissionMiddleware(MiddlewareMixin):
if url_name in self.EXCEPTIONS:
return
if not request.user.is_authenticated:
# Taken from django/contrib/auth/decorators.py
path = request.build_absolute_uri()
# urlparse chokes on lazy objects in Python 3, force to str
resolved_login_url = force_str(
resolve_url(settings.LOGIN_URL_CONTROL))
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return self._login_redirect(request)
return redirect_to_login(
path, resolved_login_url, REDIRECT_FIELD_NAME)
if not settings.PRETIX_LONG_SESSIONS or not request.session.get('pretix_auth_long_session', False):
last_used = request.session.get('pretix_auth_last_used', 0) or time.time()
if time.time() - request.session.get('pretix_auth_login_time', 0) > settings.PRETIX_SESSION_TIMEOUT_ABSOLUTE:
logout(request)
request.session['pretix_auth_login_time'] = 0
return self._login_redirect(request)
if time.time() - last_used > settings.PRETIX_SESSION_TIMEOUT_RELATIVE and url_name != 'user.reauth':
return redirect(reverse('control:user.reauth') + '?next=' + quote(request.get_full_path()))
request.session['pretix_auth_last_used'] = int(time.time())
if 'event' in url.kwargs and 'organizer' in url.kwargs:
request.event = Event.objects.filter(

View File

@@ -8,24 +8,23 @@
{% csrf_token %}
{% bootstrap_field form.email %}
{% bootstrap_field form.password %}
{% if form.keep_logged_in %}
{% bootstrap_field form.keep_logged_in %}
{% endif %}
<div class="form-group buttons">
<button type="submit" class="btn btn-primary btn-block">
{% trans "Log in" %}
</button>
{% if can_reset %}
<a href="{% url "control:auth.forgot" %}" class="btn btn-link">
<a href="{% url "control:auth.forgot" %}" class="btn btn-link btn-block">
{% trans "Lost password?" %}
</a>
{% endif %}
<button type="submit" class="btn btn-primary">
{% trans "Log in" %}
</button>
</div>
{% if can_register %}
<div class="form-group buttons">
<a href="{% url "control:auth.register" %}" class="btn btn-link">
{% if can_register %}
<a href="{% url "control:auth.register" %}" class="btn btn-link btn-block">
{% trans "Register" %}
</a>
</div>
{% endif %}
{% endif %}
</div>
</form>
{% endblock %}

View File

@@ -10,6 +10,9 @@
{% bootstrap_field form.email %}
{% bootstrap_field form.password %}
{% bootstrap_field form.password_repeat %}
{% if form.keep_logged_in %}
{% bootstrap_field form.keep_logged_in %}
{% endif %}
<div class="form-group buttons">
<a href="{% url "control:auth.login" %}" class="btn btn-link">
&laquo; {% trans "Login" %}

View File

@@ -1,26 +1,30 @@
{% extends "pretixcontrol/base.html" %}
{% extends "pretixcontrol/auth/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block content %}
<div class="row">
<div class="col-xs-12 col-md-4 col-md-offset-4">
<form action="" method="post">
{% csrf_token %}
<h2><span class="fa fa-shield"></span> {% trans "Confirm password" %}</h2>
<p>
{% trans "Please confirm your password to continue with this operation. We'll remember your password for an hour or until you log out." %}
</p>
<div class="form-group">
<input class="form-control" id="id_password" name="password" placeholder="{% trans "Password" %}"
title="" type="password" required="">
</div>
<div class="form-group text-right">
<button type="submit" class="btn btn-primary">
{% trans "Continue" %}
</button>
</div>
</form>
<form class="form-signin" action="" method="post">
<form action="" method="post">
{% csrf_token %}
<h3>{% trans "Welcome back!" %}</h3>
<p>
{% trans "We just want to make sure it's really you. Please re-enter your password to continue." %}
</p>
<div class="form-group">
<input class="form-control"
value="{{ request.user.get_full_name }}" disabled>
</div>
</div>
<div class="form-group">
<input class="form-control" id="id_password" name="password" placeholder="{% trans "Password" %}"
title="" type="password" required="" autofocus>
</div>
<div class="form-group text-right">
<button type="submit" class="btn btn-primary btn-block">
{% trans "Continue" %}
</button>
<a href={% url "control:auth.logout" %}"" class="btn btn-link btn-block">
{% trans "Log in as someone else" %}
</a>
</div>
</form>
{% endblock %}

View File

@@ -42,6 +42,9 @@ def login(request):
if request.method == 'POST':
form = LoginForm(data=request.POST)
if form.is_valid() and form.user_cache:
request.session['pretix_auth_long_session'] = (
settings.PRETIX_LONG_SESSIONS and form.cleaned_data.get('keep_logged_in', False)
)
if form.user_cache.require_2fa:
request.session['pretix_auth_2fa_user'] = form.user_cache.pk
request.session['pretix_auth_2fa_time'] = str(int(time.time()))
@@ -93,6 +96,9 @@ def register(request):
user.log_action('pretix.control.auth.user.created', user=user)
auth_login(request, user)
request.session['pretix_auth_login_time'] = int(time.time())
request.session['pretix_auth_long_session'] = (
settings.PRETIX_LONG_SESSIONS and form.cleaned_data.get('keep_logged_in', False)
)
return redirect('control:index')
else:
form = RegistrationForm()
@@ -144,6 +150,9 @@ def invite(request, token):
user.log_action('pretix.control.auth.user.created', user=user)
auth_login(request, user)
request.session['pretix_auth_login_time'] = int(time.time())
request.session['pretix_auth_long_session'] = (
settings.PRETIX_LONG_SESSIONS and form.cleaned_data.get('keep_logged_in', False)
)
with transaction.atomic():
inv.team.members.add(request.user)

View File

@@ -42,7 +42,9 @@ class ReauthView(TemplateView):
def post(self, request, *args, **kwargs):
password = request.POST.get("password", "")
if request.user.check_password(password):
request.session['pretix_auth_login_time'] = int(time.time())
t = int(time.time())
request.session['pretix_auth_login_time'] = t
request.session['pretix_auth_last_used'] = t
if "next" in request.GET and is_safe_url(request.GET.get("next")):
return redirect(request.GET.get("next"))
return redirect(reverse('control:index'))