Skip to content

Commit

Permalink
✨ [#1155] -- expose session expiry in response header
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-maertens committed Jan 6, 2022
1 parent 5a7c3d4 commit dd1daa3
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 7 deletions.
9 changes: 7 additions & 2 deletions src/openforms/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# 2.2.x. It's available from Django 3.1 onwards.
SAMESITE_VALUE = "None"

SESSION_EXPIRES_IN_HEADER = "X-Session-Expires-In"


class SameSiteNoneCookieMiddlware:
def __init__(self, get_response):
Expand Down Expand Up @@ -41,5 +43,8 @@ def __call__(self, request):
else config.form_session_timeout
)
# https://docs.djangoproject.com/en/2.2/topics/http/sessions/#django.contrib.sessions.backends.base.SessionBase.set_expiry
request.session.set_expiry(int(timedelta(minutes=timeout).total_seconds()))
return self.get_response(request)
expires_in = int(timedelta(minutes=timeout).total_seconds())
request.session.set_expiry(expires_in)
response = self.get_response(request)
response[SESSION_EXPIRES_IN_HEADER] = expires_in
return response
44 changes: 39 additions & 5 deletions src/openforms/tests/test_session_expiry.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@

from django.conf import settings
from django.test import override_settings
from django.urls import path
from django.utils import timezone
from django.utils.translation import gettext as _

from freezegun import freeze_time
from rest_framework import status
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase
from rest_framework.views import APIView

from openforms.config.models import GlobalConfiguration
from openforms.forms.tests.factories import FormFactory, FormStepFactory
Expand All @@ -28,6 +31,19 @@
SESSION_CACHES["session"] = {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}


class SessionTestView(APIView):
authentication_classes = ()
permission_classes = (permissions.AllowAny,)

def get(self, request):
return Response({"ok": True})


urlpatterns = [
path("tests/session", SessionTestView.as_view()),
]


@override_settings(
CORS_ALLOW_ALL_ORIGINS=True, CACHES=SESSION_CACHES, SESSION_CACHE_ALIAS="session"
)
Expand All @@ -40,16 +56,22 @@ class FormUserSessionExpiryTests(APITestCase):
def setUpTestData(cls):
super().setUpTestData()

config = GlobalConfiguration.get_solo()
config.form_session_timeout = 5 # minimum value
config.save()

cls.form = FormFactory.create()
cls.steps = FormStepFactory.create_batch(2, form=cls.form)
cls.form_url = reverse(
"api:form-detail", kwargs={"uuid_or_slug": cls.form.uuid}
)

def setUp(self):
super().setUp()

patcher = patch("openforms.middleware.GlobalConfiguration.get_solo")
self.mock_global_config = patcher.start()
self.addCleanup(patcher.stop)
self.mock_global_config.return_value = GlobalConfiguration(
form_session_timeout=5
) # minimum value

def test_activity_within_expiry_does_not_expire_session(self):
"""
Assert that any activity within the expiry period postpones the expiry.
Expand Down Expand Up @@ -155,6 +177,18 @@ def test_activity_over_expiry_returns_a_403_error(self, _mock):
},
)

@override_settings(ROOT_URLCONF=__name__)
def test_session_expiry_header_included(self):
"""
Assert that the response contains a header indicating when the session expires.
"""
response = self.client.get("/tests/session")

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(
response["X-Session-Expires-In"], "300"
) # 5 minutes * 60s = 300s


@disable_2fa
@override_settings(
Expand Down

0 comments on commit dd1daa3

Please sign in to comment.