diff --git a/src/pretix/api/auth/permission.py b/src/pretix/api/auth/permission.py index 9a921695cd..f5887d2534 100644 --- a/src/pretix/api/auth/permission.py +++ b/src/pretix/api/auth/permission.py @@ -1,3 +1,7 @@ +import time + +from django.conf import settings +from django.contrib.auth import logout from rest_framework.exceptions import PermissionDenied from rest_framework.permissions import SAFE_METHODS, BasePermission @@ -19,6 +23,18 @@ class EventPermission(BasePermission): else: required_permission = None + if request.user.is_authenticated: + # If this logic is updated, make sure to also update the logic in pretix/control/middleware.py + 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', time.time()) + if time.time() - request.session.get('pretix_auth_login_time', time.time()) > settings.PRETIX_SESSION_TIMEOUT_ABSOLUTE: + logout(request) + request.session['pretix_auth_login_time'] = 0 + return False + if time.time() - last_used > settings.PRETIX_SESSION_TIMEOUT_RELATIVE: + return False + request.session['pretix_auth_last_used'] = int(time.time()) + perm_holder = (request.auth if isinstance(request.auth, TeamAPIToken) else request.user) if 'event' in request.resolver_match.kwargs and 'organizer' in request.resolver_match.kwargs: diff --git a/src/tests/api/conftest.py b/src/tests/api/conftest.py index 30035c2a83..dfbf679a02 100644 --- a/src/tests/api/conftest.py +++ b/src/tests/api/conftest.py @@ -72,6 +72,17 @@ def user(): return User.objects.create_user('dummy@dummy.dummy', 'dummy') +@pytest.fixture +def user_client(client, team, user): + team.can_view_orders = True + team.can_view_vouchers = True + team.all_events = True + team.save() + team.members.add(user) + client.force_authenticate(user=user) + return client + + @pytest.fixture def token_client(client, team): team.can_view_orders = True diff --git a/src/tests/api/test_permissions.py b/src/tests/api/test_permissions.py index f06875be47..becfeb4032 100644 --- a/src/tests/api/test_permissions.py +++ b/src/tests/api/test_permissions.py @@ -1,4 +1,7 @@ +import time + import pytest +from django.test import override_settings from pretix.base.models import Organizer @@ -134,3 +137,99 @@ def test_token_event_permission_not_allowed(token_client, team, organizer, event assert resp.status_code == 403 else: assert resp.status_code in (404, 403) + + +@pytest.mark.django_db +def test_log_out_after_absolute_timeout(user_client, team, organizer, event): + session = user_client.session + session['pretix_auth_long_session'] = False + session['pretix_auth_login_time'] = int(time.time()) - 3600 * 12 - 60 + session.save() + + response = user_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug)) + assert response.status_code == 403 + + +@pytest.mark.django_db +def test_dont_logout_before_absolute_timeout(user_client, team, organizer, event): + session = user_client.session + session['pretix_auth_long_session'] = True + session['pretix_auth_login_time'] = int(time.time()) - 3600 * 12 + 60 + session.save() + + response = user_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug)) + assert response.status_code == 200 + + +@pytest.mark.django_db +@override_settings(PRETIX_LONG_SESSIONS=False) +def test_ignore_long_session_if_disabled_in_config(user_client, team, organizer, event): + session = user_client.session + session['pretix_auth_long_session'] = True + session['pretix_auth_login_time'] = int(time.time()) - 3600 * 12 - 60 + session.save() + + response = user_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug)) + assert response.status_code == 403 + + +@pytest.mark.django_db +def test_dont_logout_in_long_session(user_client, team, organizer, event): + session = user_client.session + session['pretix_auth_long_session'] = True + session['pretix_auth_login_time'] = int(time.time()) - 3600 * 12 - 60 + session.save() + + response = user_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug)) + assert response.status_code == 200 + + +@pytest.mark.django_db +def test_log_out_after_relative_timeout(user_client, team, organizer, event): + session = user_client.session + session['pretix_auth_long_session'] = False + session['pretix_auth_login_time'] = int(time.time()) - 3600 * 6 + session['pretix_auth_last_used'] = int(time.time()) - 3600 * 3 - 60 + session.save() + + response = user_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug)) + assert response.status_code == 403 + + +@pytest.mark.django_db +def test_dont_logout_before_relative_timeout(user_client, team, organizer, event): + session = user_client.session + session['pretix_auth_long_session'] = True + session['pretix_auth_login_time'] = int(time.time()) - 3600 * 6 + session['pretix_auth_last_used'] = int(time.time()) - 3600 * 3 + 60 + session.save() + + response = user_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug)) + assert response.status_code == 200 + + +@pytest.mark.django_db +def test_dont_logout_by_relative_in_long_session(user_client, team, organizer, event): + session = user_client.session + session['pretix_auth_long_session'] = True + session['pretix_auth_login_time'] = int(time.time()) - 3600 * 5 + session['pretix_auth_last_used'] = int(time.time()) - 3600 * 3 - 60 + session.save() + + response = user_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug)) + assert response.status_code == 200 + + +@pytest.mark.django_db +def test_update_session_activity(user_client, team, organizer, event): + t1 = int(time.time()) - 5 + session = user_client.session + session['pretix_auth_long_session'] = False + session['pretix_auth_login_time'] = int(time.time()) - 3600 * 5 + session['pretix_auth_last_used'] = t1 + session.save() + + response = user_client.get('/api/v1/organizers/{}/events/'.format(organizer.slug)) + assert response.status_code == 200 + + assert user_client.session['pretix_auth_last_used'] > t1