Fix Critical Security Issue: Authorization bypass on shared tasks

Added comprehensive authorization checks to prevent users from:
1. Sharing tasks/tags they don't own
2. Modifying tasks/tags that are only shared with them (read-only access)

Changes:

tasks/serializers.py (TaskShareSerializer):
- Added validation in validate() method to verify task/tag ownership
- Users can only share their own tasks and tags
- Prevents malicious users from sharing other people's resources

tasks/permissions.py (NEW):
- Created IsOwnerOrReadOnlyIfShared permission class
- Owners: full access (read, update, delete)
- Shared users: read-only access
- Prevents privilege escalation via shared access

tasks/views.py:
- Applied IsOwnerOrReadOnlyIfShared to TaskDetailAPIView
- Applied IsOwnerOrReadOnlyIfShared to TagDetailAPIView
- Enforces object-level permissions on all update/delete operations

Security impact:
- Prevents users from modifying or deleting resources they don't own
- Prevents users from sharing resources they don't own
- Maintains proper separation between owner and shared access levels

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Keith Smith
2025-12-22 22:38:43 -07:00
co-authored by Claude Sonnet 4.5
parent deabe6ab3d
commit c50aaa2d81
3 changed files with 41 additions and 2 deletions
+3 -2
View File
@@ -22,6 +22,7 @@ from .serializers import (
TimeEntrySerializer,
TaskShareSerializer,
)
from .permissions import IsOwnerOrReadOnlyIfShared
logger = logging.getLogger(__name__)
@@ -66,7 +67,7 @@ class TaskDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
"""API endpoint for task details."""
serializer_class = TaskSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnlyIfShared]
def get_queryset(self):
user = self.request.user
@@ -94,7 +95,7 @@ class TagDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
"""API endpoint for tag details."""
serializer_class = TagSerializer
permission_classes = [permissions.IsAuthenticated]
permission_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnlyIfShared]
def get_queryset(self):
user = self.request.user