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 datetime import datetime, timedelta
|
||||||
|
|
||||||
from database import get_db
|
from database import get_db
|
||||||
from models.user import User
|
from models.user import User, UserRole
|
||||||
from models.activity import Activity, ActivityType
|
from models.activity import Activity, ActivityType
|
||||||
from models.project import ProjectMember
|
from models.project import ProjectMember
|
||||||
from schemas.activity import ActivityResponse
|
from schemas.activity import ActivityResponse
|
||||||
@@ -26,13 +26,15 @@ def get_project_activities(
|
|||||||
current_user: User = Depends(get_current_user)
|
current_user: User = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""Get activity feed for a specific project (excludes activities for deleted records)."""
|
"""Get activity feed for a specific project (excludes activities for deleted records)."""
|
||||||
# Verify user has access to the 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(
|
member = db.query(ProjectMember).filter(
|
||||||
ProjectMember.project_id == project_id,
|
ProjectMember.project_id == project_id,
|
||||||
ProjectMember.user_id == current_user.id
|
ProjectMember.user_id == current_user.id
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if not member and not current_user.is_admin:
|
if not member:
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
raise HTTPException(status_code=403, detail="Access denied to this project")
|
raise HTTPException(status_code=403, detail="Access denied to this project")
|
||||||
|
|
||||||
@@ -66,13 +68,15 @@ def get_task_activities(
|
|||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
raise HTTPException(status_code=404, detail="Task not found")
|
raise HTTPException(status_code=404, detail="Task not found")
|
||||||
|
|
||||||
# Check if user is a member of the project
|
# 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(
|
member = db.query(ProjectMember).filter(
|
||||||
ProjectMember.project_id == task.project_id,
|
ProjectMember.project_id == task.project_id,
|
||||||
ProjectMember.user_id == current_user.id
|
ProjectMember.user_id == current_user.id
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if not member and not current_user.is_admin:
|
if not member:
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
raise HTTPException(status_code=403, detail="Access denied to this task")
|
raise HTTPException(status_code=403, detail="Access denied to this task")
|
||||||
|
|
||||||
@@ -122,19 +126,27 @@ def get_recent_activities(
|
|||||||
current_user: User = Depends(get_current_user)
|
current_user: User = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""Get recent activities from all projects the user has access to (excludes activities for deleted records)."""
|
"""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
|
# 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,
|
||||||
|
limit=limit
|
||||||
|
)
|
||||||
|
return activities
|
||||||
|
|
||||||
|
# Get all projects the artist is a member of
|
||||||
project_ids = db.query(ProjectMember.project_id).filter(
|
project_ids = db.query(ProjectMember.project_id).filter(
|
||||||
ProjectMember.user_id == current_user.id
|
ProjectMember.user_id == current_user.id
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
project_ids = [pid[0] for pid in project_ids]
|
project_ids = [pid[0] for pid in project_ids]
|
||||||
|
|
||||||
if not project_ids and not current_user.is_admin:
|
if not project_ids:
|
||||||
return []
|
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 = []
|
all_activities = []
|
||||||
for project_id in project_ids:
|
for project_id in project_ids:
|
||||||
activities = ActivityService.get_activities_excluding_deleted(
|
activities = ActivityService.get_activities_excluding_deleted(
|
||||||
@@ -148,14 +160,6 @@ def get_recent_activities(
|
|||||||
# Sort by created_at and apply pagination
|
# Sort by created_at and apply pagination
|
||||||
all_activities.sort(key=lambda x: x.created_at, reverse=True)
|
all_activities.sort(key=lambda x: x.created_at, reverse=True)
|
||||||
return all_activities[skip:skip + limit]
|
return all_activities[skip:skip + limit]
|
||||||
else:
|
|
||||||
# Admin gets all activities excluding deleted records
|
|
||||||
activities = ActivityService.get_activities_excluding_deleted(
|
|
||||||
db=db,
|
|
||||||
skip=skip,
|
|
||||||
limit=limit
|
|
||||||
)
|
|
||||||
return activities
|
|
||||||
|
|
||||||
|
|
||||||
# Admin-only endpoints that include activities for deleted records
|
# Admin-only endpoints that include activities for deleted records
|
||||||
|
|||||||
Reference in New Issue
Block a user