Skip to content

Commit

Permalink
Merge pull request #3134 from open-formulieren/feature/1530-logout-bu…
Browse files Browse the repository at this point in the history
…tton

[#1530] Part 9 - Logout button
sergei-maertens authored Jun 8, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 1f88d0f + b08bd7f commit 7a5d26b
Showing 7 changed files with 149 additions and 5 deletions.
14 changes: 12 additions & 2 deletions src/openforms/authentication/signals.py
Original file line number Diff line number Diff line change
@@ -27,13 +27,20 @@
authentication_success = Signal()
co_sign_authentication_success = Signal()
"""
Signal a succesful co-sign authentication.
Signal a successful co-sign authentication.
Provides:
:arg request: the HttpRequest instance
:arg plugin: authentication plugin identifier
:arg submission: The :class:`Submission` instance being co-signed.
"""
authentication_logout = Signal()
"""
Signals that a user that had logged in with an authentication plugin logged out.
Provides:
:arg request: the HttpRequest instance
"""


#
@@ -116,7 +123,10 @@ def set_auth_attribute_on_session(
store_auth_details(instance, form_auth)


@receiver(submission_complete, dispatch_uid="auth.clean_submission_auth")
@receiver(
[submission_complete, authentication_logout],
dispatch_uid="auth.clean_submission_auth",
)
def clean_submission_auth(sender, request: Request, **kwargs):
if FORM_AUTH_SESSION_KEY in request.session:
del request.session[FORM_AUTH_SESSION_KEY]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% extends 'ui/views/abstract/detail.html' %}
{% load i18n %}

{% block extra_css %}
{% include "forms/sdk_css_snippet.html" %}
{{ block.super }} {# Called after SDK CSS for custom design overrides #}
{% endblock %}

{% block card %}
<div class="card">
<header class="card__header">
<h1 class="title">
{% trans "You successfully logged out." %}
</h1>
</header>
</div>
{% endblock %}
35 changes: 34 additions & 1 deletion src/openforms/authentication/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from unittest.mock import patch

from django.test import override_settings
from django.test import TestCase, override_settings
from django.test.client import RequestFactory
from django.utils.translation import gettext as _

@@ -18,6 +18,7 @@

from ..constants import (
CO_SIGN_PARAMETER,
FORM_AUTH_SESSION_KEY,
REGISTRATOR_SUBJECT_SESSION_KEY,
AuthAttribute,
)
@@ -567,3 +568,35 @@ def test_view_sets_registrator_subject_session_data(self):
self.assertIn(REGISTRATOR_SUBJECT_SESSION_KEY, self.app.session)
data = self.app.session[REGISTRATOR_SUBJECT_SESSION_KEY]
self.assertEqual(data, {"skipped_subject_info": True})


class LogoutTestViewTests(TestCase):
def test_no_auth_info_in_session(self):
logout_url = reverse("authentication:logout")

response = self.client.post(logout_url)

self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_logout_calls_plugin_logout(self):
session = self.client.session
session[FORM_AUTH_SESSION_KEY] = {
"plugin": "digid",
"attribute": "bsn",
"value": "123456782",
}
session.save()

with patch(
"openforms.authentication.contrib.digid.plugin.DigidAuthentication.logout"
) as m_logout:
response = self.client.post(reverse("authentication:logout"), data={})

m_logout.assert_called_once()

self.assertRedirects(
response,
reverse("authentication:logout-confirmation"),
fetch_redirect_response=False,
)
self.assertNotIn(FORM_AUTH_SESSION_KEY, self.client.session)
8 changes: 8 additions & 0 deletions src/openforms/authentication/urls.py
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@
from .views import (
AuthenticationReturnView,
AuthenticationStartView,
AuthPluginLogoutConfirmationView,
AuthPluginLogoutView,
RegistratorSubjectInfoView,
)

@@ -24,4 +26,10 @@
AuthenticationReturnView.as_view(),
name="return",
),
path(
"logout/confirmation/",
AuthPluginLogoutConfirmationView.as_view(),
name="logout-confirmation",
),
path("logout/", AuthPluginLogoutView.as_view(), name="logout"),
]
32 changes: 30 additions & 2 deletions src/openforms/authentication/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import logging
from typing import Optional

from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin
from django.core.exceptions import PermissionDenied
from django.http import (
HttpResponseBadRequest,
HttpResponseNotAllowed,
HttpResponseRedirect,
)
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic.base import TemplateView, View
from django.views.generic.edit import FormView

from drf_spectacular.types import OpenApiTypes
@@ -37,7 +39,11 @@
from .exceptions import InvalidCoSignData
from .forms import RegistratorSubjectInfoForm
from .registry import register
from .signals import authentication_success, co_sign_authentication_success
from .signals import (
authentication_logout,
authentication_success,
co_sign_authentication_success,
)

logger = logging.getLogger(__name__)

@@ -425,3 +431,25 @@ def form_valid(self, form):

assert self.cleaned_next_url
return HttpResponseRedirect(self.cleaned_next_url)


class AuthPluginLogoutView(UserPassesTestMixin, View):
http_method_names = ["post"]
raise_exception = True

def test_func(self):
return FORM_AUTH_SESSION_KEY in self.request.session

def post(self, request, *args, **kwargs):
auth_info = request.session[FORM_AUTH_SESSION_KEY]

plugin = register[auth_info["plugin"]]
plugin.logout(request=request)

authentication_logout.send(sender=self.__class__, request=request)

return HttpResponseRedirect(reverse("authentication:logout-confirmation"))


class AuthPluginLogoutConfirmationView(TemplateView):
template_name = "of_authentication/logout_confirmation.html"
Original file line number Diff line number Diff line change
@@ -28,6 +28,13 @@ <h1 class="title">
</button>
</div>
</form>
<form method="post" action="{% url "authentication:logout" %}">{% csrf_token %}
<div class="openforms-toolbar openforms-toolbar--reverse openforms-toolbar--bottom">
<button class="openforms-button openforms-button--danger" type="submit">
<span class="openforms-button__label">{% trans "Log out" %}</span>
</button>
</div>
</form>
</p>
{% endblock %}
</div>
41 changes: 41 additions & 0 deletions src/openforms/submissions/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -238,3 +238,44 @@ def test_submission_already_cosigned_raises_error(self):
"Could not find a submission corresponding to this code that requires co-signing",
error_node.text.strip(),
)

@override_settings(LANGUAGE_CODE="en")
def test_logout_button(self):
SubmissionFactory.from_components(
components_list=[
{
"key": "cosign",
"type": "cosign",
"label": "Cosign component",
"validate": {"required": True},
"authPlugin": "digid",
},
],
submitted_data={"cosign": "[email protected]"},
completed=True,
cosign_complete=False,
form__slug="form-to-cosign",
form_url="http://url-to-form.nl/startpagina",
public_registration_reference="OF-IMAREFERENCE",
)
session = self.app.session
session[FORM_AUTH_SESSION_KEY] = {
"plugin": "digid",
"attribute": "bsn",
"value": "123456782",
}
session.save()

response = self.app.get(
reverse(
"submissions:find-submission-for-cosign",
kwargs={"form_slug": "form-to-cosign"},
)
)

form = response.forms[1]
logout_response = form.submit().follow()

title_node = logout_response.html.find("h1")

self.assertEqual(title_node.text.strip(), "You successfully logged out.")

0 comments on commit 7a5d26b

Please sign in to comment.