Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions partner_programs/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,29 @@ def has_permission(self, request, view):
return program.experts.filter(user=request.user).exists()


class IsAdminManagerOrExpertOfProgram(BasePermission):
"""Allow administrators, managers, and experts of one program."""

def has_permission(self, request, view):
user = request.user
if not user or not user.is_authenticated:
return False

program_id = view.kwargs.get("pk") or view.kwargs.get("program_id")
if not program_id:
return False

try:
program = PartnerProgram.objects.get(pk=program_id)
except PartnerProgram.DoesNotExist:
return False

return (
can_manage_program(user, program)
or program.experts.filter(user=user).exists()
)


class IsAdminOrManagerOfProgram(BasePermission):
"""
Доступ разрешён только админам и менеджерам конкретной программы.
Expand Down
92 changes: 92 additions & 0 deletions partner_programs/tests/test_program_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
create_project,
create_user,
)
from users.models import CustomUser


class PartnerProgramProjectFilterAPITests(TestCase):
Expand All @@ -21,6 +22,11 @@ def setUp(self):
self.program = create_partner_program()
self.program.managers.add(self.manager)

def _create_expert(self, *, prefix, program):
expert = create_user(prefix=prefix, user_type=CustomUser.EXPERT)
expert.expert.programs.add(program)
return expert

def test_manager_can_get_filterable_program_fields(self):
filterable = create_program_field(
self.program,
Expand Down Expand Up @@ -53,6 +59,92 @@ def test_non_manager_cannot_get_filterable_program_fields(self):

self.assertEqual(response.status_code, 403)

def test_anonymous_cannot_get_filterable_program_fields(self):
response = self.client.get(f"/programs/{self.program.id}/filters/")

self.assertEqual(response.status_code, 401)

def test_program_expert_can_get_filterable_program_fields(self):
expert = self._create_expert(
prefix="program-filter-expert",
program=self.program,
)
self.client.force_authenticate(expert)

response = self.client.get(f"/programs/{self.program.id}/filters/")

self.assertEqual(response.status_code, 200)

def test_other_program_expert_cannot_get_filterable_program_fields(self):
other_program = create_partner_program()
expert = self._create_expert(
prefix="other-program-filter-expert",
program=other_program,
)
self.client.force_authenticate(expert)

response = self.client.get(f"/programs/{self.program.id}/filters/")

self.assertEqual(response.status_code, 403)

def test_staff_can_get_filterable_program_fields(self):
staff = create_user(prefix="program-filter-staff", is_staff=True)
self.client.force_authenticate(staff)

response = self.client.get(f"/programs/{self.program.id}/filters/")

self.assertEqual(response.status_code, 200)

def test_superuser_can_get_filterable_program_fields(self):
superuser = create_user(
prefix="program-filter-superuser",
is_staff=True,
is_superuser=True,
)
self.client.force_authenticate(superuser)

response = self.client.get(f"/programs/{self.program.id}/filters/")

self.assertEqual(response.status_code, 200)

def test_expert_receives_same_filter_schema_as_manager(self):
create_program_field(
self.program,
name="track",
label="Track",
field_type="select",
options=["ai", "edu"],
show_filter=True,
)
expert = self._create_expert(
prefix="program-filter-schema-expert",
program=self.program,
)

self.client.force_authenticate(self.manager)
manager_response = self.client.get(f"/programs/{self.program.id}/filters/")
self.client.force_authenticate(expert)
expert_response = self.client.get(f"/programs/{self.program.id}/filters/")

self.assertEqual(manager_response.status_code, 200)
self.assertEqual(expert_response.status_code, 200)
self.assertEqual(expert_response.data, manager_response.data)

def test_filter_schema_endpoint_remains_read_only(self):
expert = self._create_expert(
prefix="program-filter-read-only-expert",
program=self.program,
)
self.client.force_authenticate(expert)

response = self.client.post(
f"/programs/{self.program.id}/filters/",
{},
format="json",
)

self.assertEqual(response.status_code, 405)

def test_manager_can_filter_program_projects_by_field_value(self):
field = create_program_field(
self.program,
Expand Down
3 changes: 2 additions & 1 deletion partner_programs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
)
from partner_programs.pagination import PartnerProgramPagination
from partner_programs.permissions import (
IsAdminManagerOrExpertOfProgram,
IsAdminOrManagerOfProgram,
IsProjectLeader,
can_manage_program,
Expand Down Expand Up @@ -418,7 +419,7 @@ def post(self, request, pk, *args, **kwargs):


class ProgramFiltersAPIView(APIView):
permission_classes = [IsAuthenticated, IsAdminOrManagerOfProgram]
permission_classes = [IsAuthenticated, IsAdminManagerOrExpertOfProgram]

def get(self, request, pk):
program = get_object_or_404(PartnerProgram, pk=pk)
Expand Down
Loading