Skip to content

Commit 63cae42

Browse files
author
Bart van der Schoor
committed
[#912] Refactored case-views authorisation checks into a mixin
1 parent 4db3f1d commit 63cae42

File tree

3 files changed

+65
-69
lines changed

3 files changed

+65
-69
lines changed

src/open_inwoner/accounts/views/cases.py

+60-57
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dataclasses
22
from typing import List
33

4-
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
4+
from django.contrib.auth.mixins import AccessMixin
55
from django.core.cache import cache
66
from django.core.exceptions import PermissionDenied
77
from django.http import Http404, StreamingHttpResponse
@@ -14,6 +14,7 @@
1414

1515
from view_breadcrumbs import BaseBreadcrumbMixin
1616

17+
from open_inwoner.openzaak.api_models import Zaak
1718
from open_inwoner.openzaak.cases import (
1819
fetch_case_information_objects,
1920
fetch_case_information_objects_for_case_and_info,
@@ -37,24 +38,52 @@
3738
from open_inwoner.openzaak.utils import filter_info_object_visibility
3839

3940

40-
class CaseListView(
41-
BaseBreadcrumbMixin, LoginRequiredMixin, UserPassesTestMixin, TemplateView
42-
):
43-
template_name = "pages/cases/list.html"
41+
class CaseAccessMixin(AccessMixin):
42+
"""
43+
Shared authorisation check
4444
45-
@cached_property
46-
def crumbs(self):
47-
return [(_("Mijn aanvragen"), reverse("accounts:my_cases"))]
45+
Base checks:
46+
- user is authenticated
47+
- user has a BSN
48+
49+
When retrieving a case :
50+
- users BSN has a role for this case
51+
"""
52+
53+
case: Zaak = None
54+
55+
def dispatch(self, request, *args, **kwargs):
56+
if not request.user.is_authenticated:
57+
return self.handle_no_permission()
58+
59+
if not request.user.bsn:
60+
return self.handle_no_permission()
61+
62+
if "object_id" in kwargs:
63+
case_uuid = kwargs["object_id"]
64+
self.case = fetch_single_case(case_uuid)
65+
66+
if self.case:
67+
# check if we have a role in this case
68+
if not fetch_roles_for_case_and_bsn(self.case.url, request.user.bsn):
69+
return self.handle_no_permission()
4870

49-
def test_func(self):
50-
return self.request.user.bsn is not None
71+
return super().dispatch(request, *args, **kwargs)
5172

5273
def handle_no_permission(self):
5374
if self.request.user.is_authenticated:
5475
return redirect(reverse("root"))
5576

5677
return super().handle_no_permission()
5778

79+
80+
class CaseListView(BaseBreadcrumbMixin, CaseAccessMixin, TemplateView):
81+
template_name = "pages/cases/list.html"
82+
83+
@cached_property
84+
def crumbs(self):
85+
return [(_("Mijn aanvragen"), reverse("accounts:my_cases"))]
86+
5887
def get_context_data(self, **kwargs):
5988
context = super().get_context_data(**kwargs)
6089

@@ -124,9 +153,7 @@ class SimpleFile:
124153
url: str
125154

126155

127-
class CaseDetailView(
128-
BaseBreadcrumbMixin, LoginRequiredMixin, UserPassesTestMixin, TemplateView
129-
):
156+
class CaseDetailView(BaseBreadcrumbMixin, CaseAccessMixin, TemplateView):
130157
template_name = "pages/cases/status.html"
131158

132159
@cached_property
@@ -139,44 +166,30 @@ def crumbs(self):
139166
),
140167
]
141168

142-
def test_func(self):
143-
return self.request.user.bsn is not None
144-
145-
def handle_no_permission(self):
146-
if self.request.user.is_authenticated:
147-
return redirect(reverse("root"))
148-
149-
return super().handle_no_permission()
150-
151169
def get_context_data(self, **kwargs):
152170
context = super().get_context_data(**kwargs)
153171

154-
case_uuid = context["object_id"]
155-
case = fetch_single_case(case_uuid)
172+
if self.case:
173+
documents = self.get_case_document_files(self.case)
156174

157-
if case:
158-
# check if we have a role in this case
159-
if not fetch_roles_for_case_and_bsn(case.url, self.request.user.bsn):
160-
raise PermissionDenied()
161-
162-
documents = self.get_case_document_files(case)
163-
164-
statuses = fetch_status_history(case.url)
175+
statuses = fetch_status_history(self.case.url)
165176
statuses.sort(key=lambda status: status.datum_status_gezet)
166177

167-
case_type = fetch_single_case_type(case.zaaktype)
168-
status_types = fetch_status_types(case_type=case.zaaktype)
178+
case_type = fetch_single_case_type(self.case.zaaktype)
179+
status_types = fetch_status_types(case_type=self.case.zaaktype)
169180

170181
status_types_mapping = {st.url: st for st in status_types}
171182
for status in statuses:
172183
status_type = status_types_mapping[status.statustype]
173184
status.statustype = status_type
174185

175186
context["case"] = {
176-
"identification": case.identificatie,
177-
"start_date": case.startdatum,
178-
"end_date": (case.einddatum if hasattr(case, "einddatum") else None),
179-
"description": case.omschrijving,
187+
"identification": self.case.identificatie,
188+
"start_date": self.case.startdatum,
189+
"end_date": (
190+
self.case.einddatum if hasattr(self.case, "einddatum") else None
191+
),
192+
"description": self.case.omschrijving,
180193
"type_description": (
181194
case_type.omschrijving if case_type else _("No data available")
182195
),
@@ -248,34 +261,19 @@ def get_anchors(self, statuses, documents):
248261
return anchors
249262

250263

251-
class CaseDocumentDownloadView(LoginRequiredMixin, UserPassesTestMixin, View):
252-
def test_func(self):
253-
return self.request.user.bsn is not None
254-
255-
def handle_no_permission(self):
256-
if self.request.user.is_authenticated:
257-
return redirect(reverse("root"))
258-
259-
return super().handle_no_permission()
260-
261-
def get(self, *args, **kwargs):
262-
case_uuid = kwargs["object_id"]
263-
case = fetch_single_case(case_uuid)
264-
if not case:
264+
class CaseDocumentDownloadView(CaseAccessMixin, View):
265+
def get(self, request, *args, **kwargs):
266+
if not self.case:
265267
raise Http404
266268

267-
# check if we have a role this case
268-
if not fetch_roles_for_case_and_bsn(case.url, self.request.user.bsn):
269-
raise PermissionDenied()
270-
271269
info_object_uuid = kwargs["info_id"]
272270
info_object = fetch_single_information_object(uuid=info_object_uuid)
273271
if not info_object:
274272
raise Http404
275273

276274
# check if this info_object belongs to this case
277275
if not fetch_case_information_objects_for_case_and_info(
278-
case.url, info_object.url
276+
self.case.url, info_object.url
279277
):
280278
raise PermissionDenied()
281279

@@ -286,6 +284,7 @@ def get(self, *args, **kwargs):
286284
):
287285
raise PermissionDenied()
288286

287+
# retrieve and stream content
289288
content_stream = download_document(info_object.inhoud)
290289
if not content_stream:
291290
raise Http404
@@ -297,3 +296,7 @@ def get(self, *args, **kwargs):
297296
}
298297
response = StreamingHttpResponse(content_stream, headers=headers)
299298
return response
299+
300+
def handle_no_permission(self):
301+
# plain error and no redirect
302+
raise PermissionDenied()

src/open_inwoner/openzaak/tests/test_documents.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -197,26 +197,19 @@ def test_document_content_with_bad_confidentiality_is_http_403(self, m):
197197
)
198198
self.app.get(url, user=self.user, status=403)
199199

200-
def test_user_is_redirected_to_root_when_not_logged_in_via_digid(self, m):
200+
def test_response_is_http_403_when_not_logged_in_via_digid(self, m):
201201
self._setUpMocks(m)
202202
user = UserFactory(
203203
first_name="",
204204
last_name="",
205205
login_type=LoginTypeChoices.default,
206206
)
207-
response = self.app.get(self.informatie_object_file.url, user=user)
208-
209-
self.assertRedirects(response, reverse("root"))
207+
self.app.get(self.informatie_object_file.url, user=user, status=403)
210208

211209
def test_anonymous_user_has_no_access_to_download_page(self, m):
212210
self._setUpMocks(m)
213211
user = AnonymousUser()
214-
response = self.app.get(self.informatie_object_file.url, user=user)
215-
216-
self.assertRedirects(
217-
response,
218-
f"{reverse('login')}?next={self.informatie_object_file.url}",
219-
)
212+
self.app.get(self.informatie_object_file.url, user=user, status=403)
220213

221214
def test_no_data_is_retrieved_when_case_object_http_404(self, m):
222215
self._setUpOASMocks(m)

src/open_inwoner/openzaak/tests/test_statuses.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -357,14 +357,14 @@ def test_no_access_when_no_roles_are_found_for_user_bsn(self, m):
357357
# no roles found
358358
json=paginated_response([]),
359359
)
360-
self.app.get(
360+
response = self.app.get(
361361
reverse(
362362
"accounts:case_status",
363363
kwargs={"object_id": "d8bbdeb7-770f-4ca9-b1ea-77b4730bf67d"},
364364
),
365365
user=self.user,
366-
status=403,
367366
)
367+
self.assertRedirects(response, reverse("root"))
368368

369369
def test_no_data_is_retrieved_when_http_404(self, m):
370370
mock_service_oas_get(m, ZAKEN_ROOT, "zrc")

0 commit comments

Comments
 (0)