issue-views: test for project membership

This commit is contained in:
Klaas van Schelven
2024-03-25 23:38:17 +01:00
parent 6100767548
commit 3ffa0f9671
4 changed files with 67 additions and 20 deletions

View File

@@ -1,3 +1,40 @@
from functools import wraps
from django.shortcuts import get_object_or_404
from django.core.exceptions import PermissionDenied
from projects.models import Project
from issues.models import Issue
def login_exempt(view):
view.login_exempt = True
return view
def project_membership_required(function):
@wraps(function)
def wrapper(request, *args, **kwargs):
project_id = kwargs.pop("project_id")
project = get_object_or_404(Project, pk=project_id)
kwargs["project"] = project
if project.users.filter(pk=request.user.pk).exists():
return function(request, *args, **kwargs)
raise PermissionDenied("You don't have permission to access this project")
return wrapper
def issue_membership_required(function):
@wraps(function)
def wrapper(request, *args, **kwargs):
issue_pk = kwargs.pop("issue_pk")
issue = get_object_or_404(Issue, pk=issue_pk)
kwargs["issue"] = issue
if issue.project.users.filter(pk=request.user.pk).exists():
return function(request, *args, **kwargs)
raise PermissionDenied("You don't have permission to access this project")
return wrapper

View File

@@ -4,7 +4,7 @@ from django.test import TestCase as DjangoTestCase
from django.contrib.auth.models import User
from datetime import datetime, timezone
from projects.models import Project
from projects.models import Project, ProjectMembership
from releases.models import create_release_if_needed
from bugsink.registry import reset_pc_registry, get_pc_registry
from bugsink.period_counter import PeriodCounter, TL_DAY
@@ -424,6 +424,7 @@ class ViewTests(DjangoTestCase):
def setUp(self):
self.user = User.objects.create_user(username='test', password='test')
self.project = Project.objects.create()
ProjectMembership.objects.create(project=self.project, user=self.user)
self.issue = Issue.objects.create(project=self.project, **denormalized_issue_fields())
self.event = create_event(self.project, self.issue)
self.client.force_login(self.user)

View File

@@ -6,8 +6,7 @@ from django.db.models import Q
from django.http import HttpResponseRedirect
from events.models import Event
from projects.models import Project
from bugsink.decorators import project_membership_required, issue_membership_required
from .utils import get_issue_grouper_for_data
from .models import Issue, IssueQuerysetStateManager, IssueStateManager
@@ -101,7 +100,8 @@ def _apply_action(manager, issue_or_qs, action):
manager.unmute(issue_or_qs)
def issue_list(request, project_id, state_filter="open"):
@project_membership_required
def issue_list(request, project, state_filter="open"):
if request.method == "POST":
issue_ids = request.POST.getlist('issue_ids[]')
issue_qs = Issue.objects.filter(pk__in=issue_ids)
@@ -124,11 +124,9 @@ def issue_list(request, project_id, state_filter="open"):
}
issue_list = d_state_filter[state_filter](
Issue.objects.filter(project_id=project_id)
Issue.objects.filter(project=project)
).order_by("-last_seen")
project = get_object_or_404(Project, pk=project_id)
return render(request, "issues/issue_list.html", {
"project": project,
"issue_list": issue_list,
@@ -145,11 +143,11 @@ def issue_list(request, project_id, state_filter="open"):
})
def issue_last_event(request, issue_pk):
issue = get_object_or_404(Issue, pk=issue_pk)
@issue_membership_required
def issue_last_event(request, issue):
last_event = issue.event_set.order_by("timestamp").last()
return redirect(issue_event_stacktrace, issue_pk=issue_pk, event_pk=last_event.pk)
return redirect(issue_event_stacktrace, issue_pk=issue.pk, event_pk=last_event.pk)
def _handle_post(request, issue):
@@ -166,8 +164,8 @@ def _handle_post(request, issue):
return HttpResponseRedirect(request.path_info)
def issue_event_stacktrace(request, issue_pk, event_pk):
issue = get_object_or_404(Issue, pk=issue_pk)
@issue_membership_required
def issue_event_stacktrace(request, issue, event_pk):
if request.method == "POST":
return _handle_post(request, issue)
@@ -201,8 +199,8 @@ def issue_event_stacktrace(request, issue_pk, event_pk):
})
def issue_event_details(request, issue_pk, event_pk):
issue = get_object_or_404(Issue, pk=issue_pk)
@issue_membership_required
def issue_event_details(request, issue, event_pk):
if request.method == "POST":
return _handle_post(request, issue)
@@ -216,8 +214,8 @@ def issue_event_details(request, issue_pk, event_pk):
})
def issue_history(request, issue_pk):
issue = get_object_or_404(Issue, pk=issue_pk)
@issue_membership_required
def issue_history(request, issue):
if request.method == "POST":
return _handle_post(request, issue)
@@ -229,8 +227,8 @@ def issue_history(request, issue_pk):
})
def issue_grouping(request, issue_pk):
issue = get_object_or_404(Issue, pk=issue_pk)
@issue_membership_required
def issue_grouping(request, issue):
if request.method == "POST":
return _handle_post(request, issue)
@@ -242,8 +240,8 @@ def issue_grouping(request, issue_pk):
})
def issue_event_list(request, issue_pk):
issue = get_object_or_404(Issue, pk=issue_pk)
@issue_membership_required
def issue_event_list(request, issue):
if request.method == "POST":
return _handle_post(request, issue)

11
templates/403.html Normal file
View File

@@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block title %}403 Forbidden{% endblock %}
{% block content %}
<div class="m-4">
<h1 class="text-4xl mt-4 font-bold">Permission denied</h1>
<div class="pt-2">{{ exception }}</div>
</div>
{% endblock %}