Fix activities endpoints to match established project-access convention
get_project_activities, get_task_activities, and get_recent_activities were gating on "project member OR admin", incorrectly blocking coordinators, directors, and developers from projects they weren't explicitly added to as members. Every other project-scoped router (shots.py, assets.py) only restricts the artist role this way — everyone else has access regardless of membership. Brought activities.py in line with that convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ from typing import List, Optional
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from database import get_db
|
||||
from models.user import User
|
||||
from models.user import User, UserRole
|
||||
from models.activity import Activity, ActivityType
|
||||
from models.project import ProjectMember
|
||||
from schemas.activity import ActivityResponse
|
||||
@@ -26,15 +26,17 @@ def get_project_activities(
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Get activity feed for a specific project (excludes activities for deleted records)."""
|
||||
# Verify user has access to the project
|
||||
member = db.query(ProjectMember).filter(
|
||||
ProjectMember.project_id == project_id,
|
||||
ProjectMember.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not member and not current_user.is_admin:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=403, detail="Access denied to this project")
|
||||
# Only artists are restricted to their explicit project memberships; coordinators,
|
||||
# directors, developers, and admins have access to all projects (matches shots.py/assets.py).
|
||||
if current_user.role == UserRole.ARTIST:
|
||||
member = db.query(ProjectMember).filter(
|
||||
ProjectMember.project_id == project_id,
|
||||
ProjectMember.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not member:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=403, detail="Access denied to this project")
|
||||
|
||||
# Use ActivityService to get activities excluding deleted records
|
||||
activities = ActivityService.get_activities_excluding_deleted(
|
||||
@@ -66,15 +68,17 @@ def get_task_activities(
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
# Check if user is a member of the project
|
||||
member = db.query(ProjectMember).filter(
|
||||
ProjectMember.project_id == task.project_id,
|
||||
ProjectMember.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not member and not current_user.is_admin:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=403, detail="Access denied to this task")
|
||||
# Only artists are restricted to their explicit project memberships; coordinators,
|
||||
# directors, developers, and admins have access to all tasks (matches shots.py/assets.py).
|
||||
if current_user.role == UserRole.ARTIST:
|
||||
member = db.query(ProjectMember).filter(
|
||||
ProjectMember.project_id == task.project_id,
|
||||
ProjectMember.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not member:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=403, detail="Access denied to this task")
|
||||
|
||||
# Use ActivityService to get activities excluding deleted records
|
||||
activities = ActivityService.get_activities_excluding_deleted(
|
||||
@@ -122,34 +126,10 @@ def get_recent_activities(
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Get recent activities from all projects the user has access to (excludes activities for deleted records)."""
|
||||
# Get all projects the user is a member of
|
||||
project_ids = db.query(ProjectMember.project_id).filter(
|
||||
ProjectMember.user_id == current_user.id
|
||||
).all()
|
||||
|
||||
project_ids = [pid[0] for pid in project_ids]
|
||||
|
||||
if not project_ids and not current_user.is_admin:
|
||||
return []
|
||||
|
||||
# For non-admin users, filter by their project access
|
||||
if not current_user.is_admin:
|
||||
# Get activities from user's projects, excluding deleted records
|
||||
all_activities = []
|
||||
for project_id in project_ids:
|
||||
activities = ActivityService.get_activities_excluding_deleted(
|
||||
db=db,
|
||||
project_id=project_id,
|
||||
skip=0,
|
||||
limit=limit * 2 # Get more to account for filtering
|
||||
)
|
||||
all_activities.extend(activities)
|
||||
|
||||
# Sort by created_at and apply pagination
|
||||
all_activities.sort(key=lambda x: x.created_at, reverse=True)
|
||||
return all_activities[skip:skip + limit]
|
||||
else:
|
||||
# Admin gets all activities excluding deleted records
|
||||
# Only artists are restricted to their explicit project memberships; coordinators,
|
||||
# directors, developers, and admins see recent activity across all projects
|
||||
# (matches shots.py/assets.py).
|
||||
if current_user.role != UserRole.ARTIST:
|
||||
activities = ActivityService.get_activities_excluding_deleted(
|
||||
db=db,
|
||||
skip=skip,
|
||||
@@ -157,6 +137,30 @@ def get_recent_activities(
|
||||
)
|
||||
return activities
|
||||
|
||||
# Get all projects the artist is a member of
|
||||
project_ids = db.query(ProjectMember.project_id).filter(
|
||||
ProjectMember.user_id == current_user.id
|
||||
).all()
|
||||
|
||||
project_ids = [pid[0] for pid in project_ids]
|
||||
|
||||
if not project_ids:
|
||||
return []
|
||||
|
||||
all_activities = []
|
||||
for project_id in project_ids:
|
||||
activities = ActivityService.get_activities_excluding_deleted(
|
||||
db=db,
|
||||
project_id=project_id,
|
||||
skip=0,
|
||||
limit=limit * 2 # Get more to account for filtering
|
||||
)
|
||||
all_activities.extend(activities)
|
||||
|
||||
# Sort by created_at and apply pagination
|
||||
all_activities.sort(key=lambda x: x.created_at, reverse=True)
|
||||
return all_activities[skip:skip + limit]
|
||||
|
||||
|
||||
# Admin-only endpoints that include activities for deleted records
|
||||
@router.get("/admin/project/{project_id}/all", response_model=List[ActivityResponse])
|
||||
|
||||
Reference in New Issue
Block a user