diff --git a/applications/views.py b/applications/views.py index d50d9e68b..375267a7c 100644 --- a/applications/views.py +++ b/applications/views.py @@ -9,9 +9,7 @@ from django.views.generic import CreateView, RedirectView, UpdateView, View from jobserver.authorization import ( - StaffAreaAdministrator, has_permission, - has_role, permissions, ) from jobserver.hash_utils import unhash_or_404 @@ -192,7 +190,9 @@ def page(request, pk_hash, key): # check the user can access this application validate_application_access(request.user, application) - if application.approved_at and not has_role(request.user, StaffAreaAdministrator): + if application.approved_at and not has_permission( + request.user, permissions.staff_area_access + ): messages.warning( request, "This application has been approved and can no longer be edited" ) diff --git a/jobserver/authorization/decorators.py b/jobserver/authorization/decorators.py index 4ea0194f3..d12d02044 100644 --- a/jobserver/authorization/decorators.py +++ b/jobserver/authorization/decorators.py @@ -3,7 +3,7 @@ from django.core.exceptions import PermissionDenied from .permissions import backend_manage -from .utils import has_permission, has_role +from .utils import has_permission def require_permission(permission): @@ -26,20 +26,4 @@ def wrapper_require_role(request, *args, **kwargs): return decorator_require_permission -def require_role(role): - """Decorator for views which require a given Role""" - - def decorator_require_role(f): # sigh - @wraps(f) - def wrapper_require_role(request, *args, **kwargs): - if not has_role(request.user, role): - raise PermissionDenied - - return f(request, *args, **kwargs) - - return wrapper_require_role - - return decorator_require_role - - require_manage_backends = require_permission(backend_manage) diff --git a/jobserver/authorization/permissions.py b/jobserver/authorization/permissions.py index 696268efa..53b1b20b6 100644 --- a/jobserver/authorization/permissions.py +++ b/jobserver/authorization/permissions.py @@ -10,6 +10,7 @@ repo_sign_off_with_outputs = "repo_sign_off_with_outputs" snapshot_create = "snapshot_create" snapshot_publish = "snapshot_publish" +staff_area_access = "staff_area_access" unreleased_outputs_view = "unreleased_outputs_view" user_manage = "user_manage" workspace_archive = "workspace_archive" diff --git a/jobserver/authorization/roles.py b/jobserver/authorization/roles.py index 78223fab9..2822223fd 100644 --- a/jobserver/authorization/roles.py +++ b/jobserver/authorization/roles.py @@ -11,6 +11,7 @@ repo_sign_off_with_outputs, snapshot_create, snapshot_publish, + staff_area_access, unreleased_outputs_view, user_manage, workspace_archive, @@ -33,6 +34,7 @@ class StaffAreaAdministrator: backend_manage, org_create, user_manage, + staff_area_access, ] diff --git a/jobserver/context_processors.py b/jobserver/context_processors.py index 62695c4db..6b7a1053b 100644 --- a/jobserver/context_processors.py +++ b/jobserver/context_processors.py @@ -6,7 +6,8 @@ from django.urls import reverse from furl import furl -from .authorization import StaffAreaAdministrator, has_role +from jobserver.authorization import has_permission, permissions + from .models import Backend, SiteAlert from .nav import NavItem, iter_nav @@ -31,7 +32,9 @@ def in_production(request): def can_view_staff_area(request): user = getattr(request, "user", None) or AnonymousUser() - return {"user_can_view_staff_area": has_role(user, StaffAreaAdministrator)} + return { + "user_can_view_staff_area": has_permission(user, permissions.staff_area_access) + } def disable_creating_jobs(request): diff --git a/jobserver/views/job_requests.py b/jobserver/views/job_requests.py index 8526180cf..5fcec7f39 100644 --- a/jobserver/views/job_requests.py +++ b/jobserver/views/job_requests.py @@ -20,9 +20,7 @@ from .. import honeycomb from ..authorization import ( - StaffAreaAdministrator, has_permission, - has_role, permissions, ) from ..backends import backends_to_choices @@ -321,7 +319,9 @@ def get(self, request, *args, **kwargs): can_cancel_jobs = job_request.created_by == request.user or has_permission( request.user, permissions.job_cancel, project=job_request.workspace.project ) - honeycomb_can_view_links = has_role(self.request.user, StaffAreaAdministrator) + honeycomb_can_view_links = has_permission( + self.request.user, permissions.staff_area_access + ) # build up is_missing_updates to define if we've not seen the backend # running this JobRequest for a while. diff --git a/jobserver/views/jobs.py b/jobserver/views/jobs.py index fa08c0da8..843b0c734 100644 --- a/jobserver/views/jobs.py +++ b/jobserver/views/jobs.py @@ -8,9 +8,7 @@ from .. import honeycomb from ..authorization import ( - StaffAreaAdministrator, has_permission, - has_role, permissions, ) from ..models import Job, JobRequest @@ -62,7 +60,9 @@ def get(self, request, *args, **kwargs): project=job.job_request.workspace.project, ) - honeycomb_can_view_links = has_role(self.request.user, StaffAreaAdministrator) + honeycomb_can_view_links = has_permission( + self.request.user, permissions.staff_area_access + ) # we need all HTML to be in HTML files, so we built this here and make # use of it in the template rather than looking it up with a templatetag diff --git a/jobserver/views/workspaces.py b/jobserver/views/workspaces.py index 0d839ca1d..f11576479 100644 --- a/jobserver/views/workspaces.py +++ b/jobserver/views/workspaces.py @@ -13,9 +13,7 @@ from django.views.generic import CreateView, FormView, ListView, View from ..authorization import ( - StaffAreaAdministrator, has_permission, - has_role, permissions, ) from ..forms import ( @@ -201,7 +199,9 @@ def get(self, request, *args, **kwargs): # Should we show the admin section in the UI? show_admin = can_archive_workspace or can_toggle_notifications - honeycomb_can_view_links = has_role(self.request.user, StaffAreaAdministrator) + honeycomb_can_view_links = has_permission( + self.request.user, permissions.staff_area_access + ) outputs = self.get_output_permissions(workspace) diff --git a/staff/views/applications.py b/staff/views/applications.py index ab763490b..796986bb9 100644 --- a/staff/views/applications.py +++ b/staff/views/applications.py @@ -13,15 +13,15 @@ from applications.models import Application from applications.wizard import Wizard from jobserver.actions import projects -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.hash_utils import unhash, unhash_or_404 from jobserver.models import Org, Project, User from ..forms import ApplicationApproveForm -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ApplicationApprove(FormView): form_class = ApplicationApproveForm model = Application @@ -97,7 +97,7 @@ def get_initial(self): return initial -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ApplicationDetail(View): def dispatch(self, request, *args, **kwargs): self.application = get_object_or_404( @@ -144,7 +144,7 @@ def post(self, request, *args, **kwargs): return redirect("staff:application-detail", self.application.pk_hash) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ApplicationEdit(UpdateView): fields = [ "status", @@ -176,7 +176,7 @@ def get_success_url(self): return self.object.get_staff_url() -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ApplicationList(ListView): response_class = TemplateResponse ordering = "-created_at" @@ -232,7 +232,7 @@ def get_queryset(self): return qs.distinct() -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ApplicationRemove(View): def post(self, request, *args, **kwargs): application = get_object_or_404( @@ -255,7 +255,7 @@ def post(self, request, *args, **kwargs): return redirect("staff:application-list") -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ApplicationRestore(View): def post(self, request, *args, **kwargs): application = get_object_or_404( diff --git a/staff/views/dashboards/copiloting.py b/staff/views/dashboards/copiloting.py index ada112772..a3a753c7e 100644 --- a/staff/views/dashboards/copiloting.py +++ b/staff/views/dashboards/copiloting.py @@ -9,8 +9,8 @@ from django.utils.decorators import method_decorator from django.views.generic import TemplateView -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.github import GitHubError, _get_github_api from jobserver.models import Project, ReleaseFile, Repo @@ -78,7 +78,7 @@ def build_repos_by_project(projects, get_github_api=_get_github_api): return {p.pk: [r for r in repos if r["pk"] in p.repo_ids] for p in projects} -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") @method_decorator(csp_exempt(), name="dispatch") class Copiloting(TemplateView): get_github_api = staticmethod(_get_github_api) diff --git a/staff/views/dashboards/index.py b/staff/views/dashboards/index.py index 161ca77a5..9138cd0df 100644 --- a/staff/views/dashboards/index.py +++ b/staff/views/dashboards/index.py @@ -1,10 +1,10 @@ from django.utils.decorators import method_decorator from django.views.generic import TemplateView -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class DashboardIndex(TemplateView): template_name = "staff/dashboards/index.html" diff --git a/staff/views/dashboards/projects.py b/staff/views/dashboards/projects.py index 559ee6e4e..a4ecb7252 100644 --- a/staff/views/dashboards/projects.py +++ b/staff/views/dashboards/projects.py @@ -6,12 +6,12 @@ from django.utils.decorators import method_decorator from django.views.generic import TemplateView -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.models import Org, Project, ReleaseFile -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") @method_decorator(csp_exempt(), name="dispatch") class ProjectsDashboard(TemplateView): template_name = "staff/dashboards/projects.html" diff --git a/staff/views/dashboards/repos.py b/staff/views/dashboards/repos.py index 81c141c88..afeb82309 100644 --- a/staff/views/dashboards/repos.py +++ b/staff/views/dashboards/repos.py @@ -10,8 +10,8 @@ from django.utils.decorators import method_decorator from django.views.generic import View -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.github import _get_github_api from jobserver.models import Project, Repo, Workspace @@ -19,7 +19,7 @@ logger = structlog.get_logger(__name__) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class PrivateReposDashboard(View): get_github_api = staticmethod(_get_github_api) @@ -147,7 +147,7 @@ def select(repo): ) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ReposWithMultipleProjects(View): @csp_exempt() def get(self, request, *args, **kwargs): diff --git a/staff/views/index.py b/staff/views/index.py index 5e79bdc6a..5feb8fb63 100644 --- a/staff/views/index.py +++ b/staff/views/index.py @@ -7,8 +7,8 @@ from django.views.generic import View from applications.models import Application -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.models import Backend, Org, Project, User, Workspace @@ -95,7 +95,7 @@ def get_results(q): return list(itertools.chain.from_iterable(queries)) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class Index(View): def get(self, request, *args, **kwargs): q = self.request.GET.get("q") diff --git a/staff/views/job_requests.py b/staff/views/job_requests.py index 625718808..1b47a82c7 100644 --- a/staff/views/job_requests.py +++ b/staff/views/job_requests.py @@ -3,32 +3,34 @@ from django.utils.decorators import method_decorator from django.views.generic import DetailView, ListView -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role -from jobserver.authorization.utils import has_role +from jobserver.authorization.decorators import ( + has_permission, + require_permission, +) +from jobserver.authorization.permissions import staff_area_access from jobserver.models import Backend, JobRequest, Org, Project, User, Workspace from jobserver.views.job_requests import JobRequestCancel as BaseJobRequestCancel from .qwargs_tools import qwargs -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class JobRequestCancel(BaseJobRequestCancel): def user_has_permission_to_cancel(self, request): - return has_role(request.user, StaffAreaAdministrator) + return has_permission(request.user, staff_area_access) def redirect(self): return redirect(self.job_request.get_staff_url()) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class JobRequestDetail(DetailView): context_object_name = "job_request" model = JobRequest template_name = "staff/job_request/detail.html" -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class JobRequestList(ListView): ordering = "-created_at" paginate_by = 25 diff --git a/staff/views/orgs.py b/staff/views/orgs.py index c1c91983c..43f563142 100644 --- a/staff/views/orgs.py +++ b/staff/views/orgs.py @@ -9,8 +9,9 @@ from django_htmx.http import HttpResponseClientRedirect from furl import furl -from jobserver.authorization import StaffAreaAdministrator, permissions -from jobserver.authorization.decorators import require_permission, require_role +from jobserver.authorization import permissions +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.models import Org, OrgMembership, User from ..forms import OrgAddGitHubOrgForm, OrgAddMemberForm @@ -18,7 +19,7 @@ from .qwargs_tools import qwargs -@require_role(StaffAreaAdministrator) +@require_permission(staff_area_access) def org_add_github_org(request, slug): org = get_object_or_404(Org, slug=slug) @@ -83,7 +84,7 @@ def get_template_names(self): return [template_name] -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class OrgDetail(FormView): form_class = OrgAddMemberForm template_name = "staff/org/detail.html" @@ -126,7 +127,7 @@ def get_initial(self): } -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class OrgEdit(UpdateView): fields = [ "name", @@ -158,7 +159,7 @@ def form_valid(self, form): return redirect(new.get_staff_url()) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class OrgList(ListView): queryset = Org.objects.order_by("name") paginate_by = 25 @@ -181,7 +182,7 @@ def get_queryset(self): return qs.distinct() -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class OrgRemoveGitHubOrg(View): def post(self, request, *args, **kwargs): org = get_object_or_404(Org, slug=self.kwargs["slug"]) @@ -199,7 +200,7 @@ def post(self, request, *args, **kwargs): return redirect(org.get_staff_url()) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class OrgRemoveMember(View): def post(self, request, *args, **kwargs): org = get_object_or_404(Org, slug=self.kwargs["slug"]) diff --git a/staff/views/projects.py b/staff/views/projects.py index bedd8b0fe..4f68b2415 100644 --- a/staff/views/projects.py +++ b/staff/views/projects.py @@ -17,8 +17,8 @@ from jobserver.actions import project_members as members from jobserver.actions import projects from jobserver.auditing.presenters.lookup import get_presenter -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.authorization.utils import roles_for from jobserver.models import AuditableEvent, Org, Project, ProjectMembership, User @@ -32,7 +32,7 @@ from .qwargs_tools import qwargs -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ProjectAddMember(FormView): form_class = ProjectAddMemberForm template_name = "staff/project/membership_create.html" @@ -72,7 +72,7 @@ def get_initial(self): } -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ProjectAuditLog(ListView): paginate_by = 25 template_name = "staff/project/audit_log.html" @@ -109,7 +109,7 @@ def get_queryset(self): return qs.distinct() -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ProjectDetail(DetailView): model = Project template_name = "staff/project/detail.html" @@ -129,7 +129,7 @@ def get_context_data(self, **kwargs): } -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ProjectEdit(UpdateView): form_class = ProjectEditForm model = Project @@ -142,7 +142,7 @@ def form_valid(self, form): return redirect(new.get_staff_url()) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ProjectLinkApplication(UpdateView): form_class = ProjectLinkApplicationForm model = Project @@ -175,7 +175,7 @@ def get_context_data(self, **kwargs): } -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ProjectList(ListView): queryset = Project.objects.order_by("-number", Lower("name")) paginate_by = 25 @@ -203,7 +203,7 @@ def get_queryset(self): return qs.distinct() -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ProjectMembershipEdit(UpdateView): context_object_name = "membership" form_class = ProjectMembershipForm @@ -244,7 +244,7 @@ def get_object(self): ) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ProjectMembershipRemove(View): def post(self, request, *args, **kwargs): membership = get_object_or_404( diff --git a/staff/views/redirects.py b/staff/views/redirects.py index 795b319f6..829e260e7 100644 --- a/staff/views/redirects.py +++ b/staff/views/redirects.py @@ -3,14 +3,14 @@ from django.utils.decorators import method_decorator from django.views.generic import DetailView, ListView, View -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from redirects.models import Redirect from .qwargs_tools import qwargs -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class RedirectDelete(View): def post(self, request, *args, **kwargs): obj = get_object_or_404(Redirect, pk=self.kwargs["pk"]) @@ -22,13 +22,13 @@ def post(self, request, *args, **kwargs): return redirect("staff:redirect-list") -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class RedirectDetail(DetailView): model = Redirect template_name = "staff/redirect/detail.html" -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class RedirectList(ListView): ordering = "-old_url" paginate_by = 25 diff --git a/staff/views/repos.py b/staff/views/repos.py index 6beb37064..5c3b50d5d 100644 --- a/staff/views/repos.py +++ b/staff/views/repos.py @@ -15,8 +15,9 @@ from django.utils.safestring import mark_safe from django.views.generic import ListView, View -from jobserver.authorization import StaffAreaAdministrator, has_permission, permissions -from jobserver.authorization.decorators import require_role +from jobserver.authorization import has_permission, permissions +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.github import _get_github_api from jobserver.issues import create_switch_repo_to_public_request from jobserver.models import Job, Org, Project, Repo, User @@ -46,7 +47,7 @@ def ran_at(job): return job.started_at or job.created_at -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class RepoDetail(View): get_github_api = staticmethod(_get_github_api) @@ -157,7 +158,7 @@ def get(self, request, *args, **kwargs): ) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class RepoList(ListView): model = Repo ordering = "name" @@ -189,7 +190,7 @@ def get_queryset(self): return qs.distinct() -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class RepoSignOff(View): get_github_api = staticmethod(_get_github_api) diff --git a/staff/views/researchers.py b/staff/views/researchers.py index 68fdec384..8b2d8ea44 100644 --- a/staff/views/researchers.py +++ b/staff/views/researchers.py @@ -3,14 +3,14 @@ from django.views.generic import UpdateView from applications.models import ResearcherRegistration -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.hash_utils import unhash_or_404 from ..forms import ResearcherRegistrationEditForm -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class ResearcherEdit(UpdateView): context_object_name = "researcher" form_class = ResearcherRegistrationEditForm diff --git a/staff/views/sentry.py b/staff/views/sentry.py index 945fcabbc..467642f04 100644 --- a/staff/views/sentry.py +++ b/staff/views/sentry.py @@ -1,21 +1,21 @@ import sentry_sdk from django.template.response import TemplateResponse -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access -@require_role(StaffAreaAdministrator) +@require_permission(staff_area_access) def error(request): 1 / 0 -@require_role(StaffAreaAdministrator) +@require_permission(staff_area_access) def index(request): return TemplateResponse(request, "staff/sentry/index.html") -@require_role(StaffAreaAdministrator) +@require_permission(staff_area_access) def message(request): sentry_sdk.capture_message("testing") return TemplateResponse(request, "staff/sentry/message.html") diff --git a/staff/views/site_alerts.py b/staff/views/site_alerts.py index bc2d9572b..3770bcff6 100644 --- a/staff/views/site_alerts.py +++ b/staff/views/site_alerts.py @@ -5,8 +5,8 @@ from django.utils.decorators import method_decorator from django.views.generic import CreateView, DeleteView, ListView, UpdateView -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.models import SiteAlert from staff.forms import SiteAlertForm @@ -29,7 +29,7 @@ def get_success_url(self): return reverse("staff:site-alerts:edit", kwargs={"pk": self.object.pk}) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class SiteAlertList(ListView): """List all of the SiteAlert instances.""" @@ -38,7 +38,7 @@ class SiteAlertList(ListView): template_name = "staff/site_alerts/list.html" -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class SiteAlertCreate( SuccessMessageMixin, SetUserMixin, GetSuccessURLMixin, CreateView ): @@ -50,7 +50,7 @@ class SiteAlertCreate( success_message = "Alert was created successfully" -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class SiteAlertUpdate( SuccessMessageMixin, SetUserMixin, GetSuccessURLMixin, UpdateView ): @@ -62,7 +62,7 @@ class SiteAlertUpdate( success_message = "Alert was updated successfully" -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class SiteAlertDelete(SuccessMessageMixin, DeleteView): """Delete an existing SiteAlert instance.""" diff --git a/staff/views/workspaces.py b/staff/views/workspaces.py index c68557d9b..ba07fc770 100644 --- a/staff/views/workspaces.py +++ b/staff/views/workspaces.py @@ -4,15 +4,15 @@ from django.utils.decorators import method_decorator from django.views.generic import DetailView, ListView, UpdateView -from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_role +from jobserver.authorization.decorators import require_permission +from jobserver.authorization.permissions import staff_area_access from jobserver.models import Org, Project, Workspace from ..forms import WorkspaceEditForm from .qwargs_tools import qwargs -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class WorkspaceDetail(DetailView): model = Workspace slug_field = "name" @@ -30,7 +30,7 @@ def get_context_data(self, **kwargs): } -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class WorkspaceEdit(UpdateView): form_class = WorkspaceEditForm model = Workspace @@ -58,7 +58,7 @@ def form_valid(self, form): return redirect(self.object.get_staff_url()) -@method_decorator(require_role(StaffAreaAdministrator), name="dispatch") +@method_decorator(require_permission(staff_area_access), name="dispatch") class WorkspaceList(ListView): model = Workspace ordering = "name" diff --git a/tests/unit/jobserver/api/test_jobs.py b/tests/unit/jobserver/api/test_jobs.py index a1f7bf01c..7c2befbdd 100644 --- a/tests/unit/jobserver/api/test_jobs.py +++ b/tests/unit/jobserver/api/test_jobs.py @@ -889,6 +889,7 @@ def test_userapidetail_success(api_rf, project_membership): "application_manage", "backend_manage", "org_create", + "staff_area_access", "user_manage", ] diff --git a/tests/unit/jobserver/authorization/test_decorators.py b/tests/unit/jobserver/authorization/test_decorators.py index d050f3ad8..710e6f71b 100644 --- a/tests/unit/jobserver/authorization/test_decorators.py +++ b/tests/unit/jobserver/authorization/test_decorators.py @@ -2,7 +2,11 @@ from django.core.exceptions import PermissionDenied from jobserver.authorization import StaffAreaAdministrator -from jobserver.authorization.decorators import require_manage_backends, require_role +from jobserver.authorization.decorators import ( + require_manage_backends, + require_permission, +) +from jobserver.authorization.permissions import staff_area_access from ....factories import UserFactory @@ -35,7 +39,7 @@ def test_require_role_success(rf): def dispatch(request): return request - returned_request = require_role(StaffAreaAdministrator)(dispatch)(request) + returned_request = require_permission(staff_area_access)(dispatch)(request) assert returned_request == request @@ -45,4 +49,4 @@ def test_require_role_without_role(rf): request.user = UserFactory(roles=[]) with pytest.raises(PermissionDenied): - require_role(StaffAreaAdministrator)(None)(request) + require_permission(staff_area_access)(None)(request) diff --git a/tests/unit/jobserver/test_nav.py b/tests/unit/jobserver/test_nav.py index 7b516903c..7a6d65609 100644 --- a/tests/unit/jobserver/test_nav.py +++ b/tests/unit/jobserver/test_nav.py @@ -1,6 +1,6 @@ import pytest -from jobserver.authorization import StaffAreaAdministrator, has_role +from jobserver.authorization import StaffAreaAdministrator, has_permission, permissions from jobserver.nav import NavItem, iter_nav from ...factories import UserFactory @@ -71,7 +71,9 @@ def test_iter_nav_optional_items(rf, roles, expected): NavItem( name="Only Shown for CoreDevs", url_name="staff:user-list", - predicate=lambda request: has_role(request.user, StaffAreaAdministrator), + predicate=lambda request: has_permission( + request.user, permissions.staff_area_access + ), ), ]