diff --git a/backend/api/v1/tasks/serializers.py b/backend/api/v1/tasks/serializers.py
index a279ead2..86162d16 100644
--- a/backend/api/v1/tasks/serializers.py
+++ b/backend/api/v1/tasks/serializers.py
@@ -144,6 +144,12 @@ class TaskAutomationRunSerializer(serializers.ModelSerializer):
class TaskAutomationSerializer(serializers.ModelSerializer):
+ """Validate shared automation rules exposed to authenticated task users.
+
+ ``can_manage`` intentionally mirrors the temporary broad API permission. It
+ should use the dedicated automation permission once that role is introduced.
+ """
+
created_by = EmployeeBriefSerializer(read_only=True)
boards = serializers.PrimaryKeyRelatedField(
queryset=TaskBoard.objects.filter(is_archived=False),
@@ -188,7 +194,7 @@ class TaskAutomationSerializer(serializers.ModelSerializer):
def get_can_manage(self, obj):
user = getattr(self.context.get("request"), "user", None)
- return bool(user and (user.is_staff or user.is_superuser))
+ return bool(user and user.is_authenticated)
def validate_name(self, value):
name = value.strip()
diff --git a/backend/api/v1/tasks/views.py b/backend/api/v1/tasks/views.py
index dd7f783a..2be28bfd 100644
--- a/backend/api/v1/tasks/views.py
+++ b/backend/api/v1/tasks/views.py
@@ -32,7 +32,7 @@ from procurement.models import ProcurementRequest
from requests_app.models import Request as EmployeeRequest
from rest_framework import filters, parsers, status, viewsets
from rest_framework.decorators import action
-from rest_framework.permissions import IsAdminUser, IsAuthenticated
+from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from schedule.models import Event
@@ -186,17 +186,22 @@ def send_task_checklist_update(task, event, item_id):
class TaskAutomationViewSet(viewsets.ModelViewSet):
+ """Manage shared task automation rules.
+
+ Automation management is temporarily available to every authenticated user.
+ Rules are system-wide and may target multiple boards, so this broad access must
+ be replaced with a dedicated permission before permission roles are finalized.
+ """
+
+ # TODO(tasks-automation-permissions): Replace broad authenticated access with
+ # a dedicated permission/role for managing shared automation rules.
+ permission_classes = [IsAuthenticated]
serializer_class = TaskAutomationSerializer
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ["name", "description"]
ordering_fields = ["position", "name", "created_at", "last_run_at"]
ordering = ["kind", "position", "name", "id"]
- def get_permissions(self):
- if self.action in {"catalog", "buttons", "run"}:
- return [IsAuthenticated()]
- return [IsAdminUser()]
-
def get_queryset(self):
queryset = (
TaskAutomation.objects.select_related("created_by")
diff --git a/backend/tests/api/v1/tasks/test_task_automations.py b/backend/tests/api/v1/tasks/test_task_automations.py
index 04175162..d19325c1 100644
--- a/backend/tests/api/v1/tasks/test_task_automations.py
+++ b/backend/tests/api/v1/tasks/test_task_automations.py
@@ -213,7 +213,7 @@ def test_scheduled_automation_runs_relative_to_task_due_date(task_setup):
assert run.context["scheduled_for"] == local_run_at.isoformat()
-def test_regular_user_can_run_eligible_button_but_cannot_manage_rules(
+def test_regular_user_can_manage_rules_and_run_eligible_button(
api_client,
user_factory,
task_setup,
@@ -232,6 +232,19 @@ def test_regular_user_can_run_eligible_button_but_cannot_manage_rules(
api_client.force_authenticate(employee)
list_response = api_client.get(reverse("api:v1:tasks:task-automation-list"))
+ create_response = api_client.post(
+ reverse("api:v1:tasks:task-automation-list"),
+ {
+ "name": "Правило сотрудника",
+ "kind": "event",
+ "trigger": "task_updated",
+ "conditions": {"operator": "and", "children": []},
+ "actions": [{"type": "set_priority", "priority": "medium"}],
+ "applies_to_all_boards": False,
+ "boards": [board.id],
+ },
+ format="json",
+ )
buttons_response = api_client.get(
reverse("api:v1:tasks:task-automation-buttons"),
{"task": task.id},
@@ -242,7 +255,10 @@ def test_regular_user_can_run_eligible_button_but_cannot_manage_rules(
format="json",
)
- assert list_response.status_code == status.HTTP_403_FORBIDDEN
+ assert list_response.status_code == status.HTTP_200_OK
+ assert create_response.status_code == status.HTTP_201_CREATED
+ assert create_response.data["created_by"]["id"] == employee.id
+ assert create_response.data["can_manage"] is True
assert buttons_response.status_code == status.HTTP_200_OK
assert [item["id"] for item in buttons_response.data] == [button.id]
assert run_response.status_code == status.HTTP_200_OK
@@ -250,6 +266,12 @@ def test_regular_user_can_run_eligible_button_but_cannot_manage_rules(
assert task.priority == "high"
+def test_automation_management_requires_authentication(api_client):
+ response = api_client.get(reverse("api:v1:tasks:task-automation-list"))
+
+ assert response.status_code == status.HTTP_401_UNAUTHORIZED
+
+
def test_manual_button_rechecks_conditions_on_run(api_client, user_factory, task_setup):
owner, board, _, _, task = task_setup
employee = user_factory(email="automation-condition@example.com")
diff --git a/frontend/src/app/tasks/page.tsx b/frontend/src/app/tasks/page.tsx
index fe204022..14125000 100644
--- a/frontend/src/app/tasks/page.tsx
+++ b/frontend/src/app/tasks/page.tsx
@@ -6640,19 +6640,19 @@ function TasksPageContent() {
Добавить колонку
- {user?.auth?.is_staff || user?.auth?.is_superuser ? (
-
- ) : null}
+ {/* TODO(tasks-automation-permissions): Gate this action by the
+ dedicated automation-management capability once introduced. */}
+
- {user?.auth?.is_staff || user?.auth?.is_superuser ? (
-
- ) : null}
+
) : null}