Skip to content

Commit 952443d

Browse files
authored
Permission improvements (#4831)
1 parent 96e0153 commit 952443d

5 files changed

Lines changed: 59 additions & 25 deletions

File tree

hypha/apply/activity/views.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ class AttachmentView(PrivateMediaView):
134134
def dispatch(self, *args, **kwargs):
135135
file_pk = kwargs.get("file_pk")
136136
self.instance = get_object_or_404(ActivityAttachment, uuid=file_pk)
137-
137+
activity = self.instance.activity
138+
if activity.visibility not in Activity.visibility_for(self.request.user):
139+
raise PermissionDenied
138140
return super().dispatch(*args, **kwargs)
139141

140142
def get_media(self, *args, **kwargs):

hypha/apply/funds/views/partials.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from django.conf import settings
55
from django.contrib.auth import get_user_model
6-
from django.contrib.auth.decorators import login_required
6+
from django.contrib.auth.decorators import login_required, user_passes_test
77
from django.db.models import Q
88
from django.http import Http404, HttpRequest, HttpResponse
99
from django.shortcuts import get_object_or_404, render
@@ -31,6 +31,7 @@
3131
from hypha.apply.review.options import REVIEWER
3232
from hypha.apply.todo.options import DOWNLOAD_SUBMISSIONS_EXPORT
3333
from hypha.apply.todo.views import remove_tasks_of_related_obj_for_specific_code
34+
from hypha.apply.users.decorators import is_apply_staff
3435
from hypha.apply.users.roles import REVIEWER_GROUP_NAME
3536

3637
from .. import services
@@ -244,6 +245,9 @@ def partial_reviews_card(request: HttpRequest, pk: str) -> HttpResponse:
244245
HttpResponse
245246
"""
246247
submission = get_object_or_404(ApplicationSubmission, pk=pk)
248+
has_permission(
249+
"submission_view", request.user, object=submission, raise_exception=True
250+
)
247251

248252
assigned_reviewers = submission.assigned.review_order()
249253

@@ -298,6 +302,7 @@ def partial_meta_terms_card(request, pk):
298302

299303

300304
@login_required
305+
@user_passes_test(is_apply_staff)
301306
@require_http_methods(["GET", "POST"])
302307
def sub_menu_update_status(request: HttpRequest) -> HttpResponse:
303308
submission_ids = request.GET.getlist("selectedSubmissionIds")
@@ -330,14 +335,15 @@ def sub_menu_update_status(request: HttpRequest) -> HttpResponse:
330335

331336

332337
@login_required
338+
@user_passes_test(is_apply_staff)
333339
@require_http_methods(["GET", "POST"])
334340
def sub_menu_bulk_update_lead(request: HttpRequest) -> HttpResponse:
335341
if request.method == "POST":
336342
submission_ids = request.POST.getlist("selectedSubmissionIds")
337343
lead = request.POST.get("lead")
338344

339345
submissions = ApplicationSubmission.objects.filter(id__in=submission_ids)
340-
lead = User.objects.get(id=lead)
346+
lead = get_object_or_404(User.objects.staff(), id=lead)
341347

342348
services.bulk_update_lead(
343349
submissions=submissions, user=request.user, request=request, lead=lead
@@ -368,6 +374,7 @@ def sub_menu_bulk_update_lead(request: HttpRequest) -> HttpResponse:
368374

369375

370376
@login_required
377+
@user_passes_test(is_apply_staff)
371378
@require_http_methods(["GET", "POST"])
372379
def sub_menu_bulk_update_reviewers(request: HttpRequest) -> HttpResponse:
373380
if request.method == "POST":

hypha/apply/projects/permissions.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,28 @@ def can_view_contract_category_documents(user, project, **kwargs):
418418
return False, _("Forbidden Error")
419419

420420

421+
def can_skip_pafapproval_process(user, project, **kwargs):
422+
if not user.is_authenticated:
423+
return False, _("Login Required")
424+
425+
if project.status != DRAFT:
426+
return False, _("Project is not in Draft state")
427+
428+
if not (user.is_apply_staff or user.is_apply_staff_admin):
429+
return False, _("Only Staff can skip the PAF approval process")
430+
431+
if no_pafreviewer_role():
432+
return True, _(
433+
"Staff can skip PAF approval when no reviewer roles are configured"
434+
)
435+
436+
return False, _("PAF reviewer roles are configured, cannot skip approval process")
437+
438+
421439
def can_edit_paf(user, project):
440+
if not user.is_authenticated:
441+
return False, _("Login Required")
442+
422443
if no_pafreviewer_role() and project.status != COMPLETE:
423444
return True, _(
424445
"Project form is editable for active projects if no reviewer roles"
@@ -482,5 +503,6 @@ def add_invoice(role, user, project) -> bool:
482503
"submit_contract_documents": can_submit_contract_documents,
483504
"project_access": can_access_project,
484505
"paf_edit": can_edit_paf,
506+
"skip_pafapproval_process": can_skip_pafapproval_process,
485507
"view_contract_documents": can_view_contract_category_documents,
486508
}

hypha/apply/projects/templatetags/project_tags.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ def project_show_reports_section(project):
2828

2929
@register.simple_tag
3030
def user_can_skip_pafapproval_process(project, user):
31-
if project.status == DRAFT and (user.is_apply_staff or user.is_apply_staff_admin):
32-
return no_pafreviewer_role()
33-
return False
31+
permission, _ = has_permission(
32+
"skip_pafapproval_process", user, object=project, raise_exception=False
33+
)
34+
return permission
3435

3536

3637
@register.simple_tag

hypha/apply/projects/views/project.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -675,11 +675,10 @@ class UploadContractView(ProjectBySubmissionIdMixin, View):
675675

676676
def dispatch(self, request, *args, **kwargs):
677677
self.project = self.get_object()
678-
permission, _ = has_permission(
679-
"contract_upload", request.user, object=self.project
678+
has_permission(
679+
"contract_upload", request.user, object=self.project, raise_exception=True
680680
)
681-
if permission:
682-
return super().dispatch(request, *args, **kwargs)
681+
return super().dispatch(request, *args, **kwargs)
683682

684683
def get(self, *args, **kwargs):
685684
form = self.get_form()
@@ -818,13 +817,19 @@ def post(self, *args, **kwargs):
818817
)
819818

820819

820+
@method_decorator(login_required, name="dispatch")
821821
class SkipPAFApprovalProcessView(UpdateView):
822822
model = Project
823823
form_class = SkipPAFApprovalProcessForm
824824

825825
def dispatch(self, request, *args, **kwargs):
826826
self.object = get_object_or_404(Project, id=kwargs.get("pk"))
827-
# permissions
827+
has_permission(
828+
"skip_pafapproval_process",
829+
request.user,
830+
object=self.object,
831+
raise_exception=True,
832+
)
828833
return super().dispatch(request, *args, **kwargs)
829834

830835
def form_valid(self, form):
@@ -884,7 +889,7 @@ def dispatch(self, request, *args, **kwargs):
884889
).exists():
885890
raise PermissionDenied
886891
contract = self.project.contracts.order_by("-created_at").first()
887-
permission, _ = has_permission(
892+
has_permission(
888893
"submit_contract_documents",
889894
request.user,
890895
object=self.project,
@@ -1024,7 +1029,7 @@ class ChangePAFStatusView(View):
10241029

10251030
def dispatch(self, request, *args, **kwargs):
10261031
self.object = get_object_or_404(Project, submission__pk=self.kwargs["pk"])
1027-
permission, _ = has_permission(
1032+
has_permission(
10281033
"paf_status_update",
10291034
self.request.user,
10301035
object=self.object,
@@ -1274,7 +1279,7 @@ class ChangeProjectstatusView(View):
12741279

12751280
def dispatch(self, request, *args, **kwargs):
12761281
self.project = get_object_or_404(Project, submission__id=self.kwargs["pk"])
1277-
permission, _ = has_permission(
1282+
has_permission(
12781283
"project_status_update", request.user, self.project, raise_exception=True
12791284
)
12801285
return super().dispatch(request, *args, **kwargs)
@@ -1339,7 +1344,7 @@ class UpdateAssignApproversView(View):
13391344

13401345
def dispatch(self, request, *args, **kwargs):
13411346
self.project = get_object_or_404(Project, submission__id=self.kwargs["pk"])
1342-
permission, _ = has_permission(
1347+
has_permission(
13431348
"update_paf_assigned_approvers",
13441349
request.user,
13451350
self.project,
@@ -1447,7 +1452,7 @@ class UpdatePAFApproversView(View):
14471452

14481453
def dispatch(self, request, *args, **kwargs):
14491454
self.project = get_object_or_404(Project, submission__id=self.kwargs["pk"])
1450-
permission, _ = has_permission(
1455+
has_permission(
14511456
"paf_approvers_update",
14521457
request.user,
14531458
self.project,
@@ -1659,7 +1664,7 @@ class AdminProjectDetailView(
16591664

16601665
def dispatch(self, *args, **kwargs):
16611666
project = self.get_object()
1662-
permission, _ = has_permission(
1667+
has_permission(
16631668
"project_access", self.request.user, object=project, raise_exception=True
16641669
)
16651670
return super().dispatch(*args, **kwargs)
@@ -1688,7 +1693,7 @@ class ApplicantProjectDetailView(
16881693

16891694
def dispatch(self, request, *args, **kwargs):
16901695
project = self.get_object()
1691-
permission, _ = has_permission(
1696+
has_permission(
16921697
"project_access", request.user, object=project, raise_exception=True
16931698
)
16941699
return super().dispatch(request, *args, **kwargs)
@@ -1770,7 +1775,7 @@ class CategoryTemplatePrivateMediaView(ProjectBySubmissionIdMixin, PrivateMediaV
17701775
def dispatch(self, *args, **kwargs):
17711776
self.project = self.get_object()
17721777
self.category_type = kwargs["type"]
1773-
permission, _ = has_permission(
1778+
has_permission(
17741779
"project_access",
17751780
self.request.user,
17761781
object=self.project,
@@ -1834,7 +1839,7 @@ class ContractDocumentPrivateMediaView(ProjectBySubmissionIdMixin, PrivateMediaV
18341839
def dispatch(self, *args, **kwargs):
18351840
self.project = self.get_object()
18361841
self.document = ContractPacketFile.objects.get(pk=kwargs["file_pk"])
1837-
permission, _ = has_permission(
1842+
has_permission(
18381843
"view_contract_documents",
18391844
self.request.user,
18401845
object=self.project,
@@ -2033,6 +2038,7 @@ def get_supporting_documents(self, project):
20332038
return documents_dict
20342039

20352040

2041+
@method_decorator(staff_or_finance_or_contracting_required, name="dispatch")
20362042
class ProjectFormsEditView(BaseStreamForm, ProjectBySubmissionIdMixin, UpdateView):
20372043
model = Project
20382044

@@ -2042,11 +2048,7 @@ def buttons(self):
20422048
def dispatch(self, request, *args, **kwargs):
20432049
self.object = self.get_object()
20442050

2045-
permission, msg = has_permission(
2046-
"paf_edit", self.request.user, self.object, raise_exception=True
2047-
)
2048-
if not permission:
2049-
messages.info(self.request, msg)
2051+
has_permission("paf_edit", self.request.user, self.object, raise_exception=True)
20502052
return super().dispatch(request, *args, **kwargs)
20512053

20522054
def get_context_data(self, **kwargs):

0 commit comments

Comments
 (0)