Make note/submission edit-own and edit-others' permissions explicit

Split note:edit/note:delete and submission:edit/submission:delete into
four independent permissions each - edit_self/delete_self (acting on
your own note or submission) and edit_other/delete_other (acting on
someone else's). Previously "own" access was an unconditional, unrevokable
ownership check with no permission behind it, and a prior round had
accidentally granted coordinator submission:edit/delete by default
(inconsistent with notes, which were correctly own-only) - both are fixed
here: self-service now goes through a real, default-granted-to-everyone
permission, and acting on someone else's note/submission is an explicit
elevated grant that nobody gets by default.

The Role Management permission editor now shows "Edit Own / Delete Own /
Edit Others' / Delete Others'" as four clear, independently toggleable
options instead of one ambiguous "Edit"/"Delete" checkbox.

migrate_role_permissions.py renames the existing permission rows in place
(rather than leaving orphaned duplicates) and includes a one-time,
idempotent correction that revokes the earlier over-grant from coordinator.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 21:17:47 +08:00
parent db2c414c1a
commit 960753b3d6
5 changed files with 108 additions and 33 deletions
+26 -16
View File
@@ -1260,10 +1260,12 @@ async def update_task_note(
if not note:
raise HTTPException(status_code=404, detail="Note not found")
# Users can only update their own notes, unless they have admin permission or note:edit
if (note.user_id != current_user.id and not current_user.is_admin
and not user_has_permission(current_user, 'note', 'edit', db)):
raise HTTPException(status_code=403, detail="Not authorized to update this note")
# Editing your own note requires note:edit_self; editing someone else's requires note:edit_other
if not current_user.is_admin:
is_own = note.user_id == current_user.id
action = 'edit_self' if is_own else 'edit_other'
if not user_has_permission(current_user, 'note', action, db):
raise HTTPException(status_code=403, detail="Not authorized to update this note")
note.content = note_update.content
db.commit()
@@ -1308,10 +1310,12 @@ async def delete_task_note(
if not note:
raise HTTPException(status_code=404, detail="Note not found")
# Users can only delete their own notes, unless they have admin permission or note:delete
if (note.user_id != current_user.id and not current_user.is_admin
and not user_has_permission(current_user, 'note', 'delete', db)):
raise HTTPException(status_code=403, detail="Not authorized to delete this note")
# Deleting your own note requires note:delete_self; deleting someone else's requires note:delete_other
if not current_user.is_admin:
is_own = note.user_id == current_user.id
action = 'delete_self' if is_own else 'delete_other'
if not user_has_permission(current_user, 'note', action, db):
raise HTTPException(status_code=403, detail="Not authorized to delete this note")
db.delete(note)
db.commit()
@@ -1645,10 +1649,13 @@ async def update_task_submission(
if not submission:
raise HTTPException(status_code=404, detail="Submission not found")
# Users can only update their own submissions, unless they have admin permission or submission:edit
if (submission.user_id != current_user.id and not current_user.is_admin
and not user_has_permission(current_user, 'submission', 'edit', db)):
raise HTTPException(status_code=403, detail="Not authorized to update this submission")
# Editing your own submission requires submission:edit_self; editing
# someone else's requires submission:edit_other
if not current_user.is_admin:
is_own = submission.user_id == current_user.id
action = 'edit_self' if is_own else 'edit_other'
if not user_has_permission(current_user, 'submission', action, db):
raise HTTPException(status_code=403, detail="Not authorized to update this submission")
if submission_update.notes is not None:
submission.notes = submission_update.notes
@@ -1692,10 +1699,13 @@ async def delete_task_submission(
if not submission:
raise HTTPException(status_code=404, detail="Submission not found")
# Users can only delete their own submissions, unless they have admin permission or submission:delete
if (submission.user_id != current_user.id and not current_user.is_admin
and not user_has_permission(current_user, 'submission', 'delete', db)):
raise HTTPException(status_code=403, detail="Not authorized to delete this submission")
# Deleting your own submission requires submission:delete_self; deleting
# someone else's requires submission:delete_other
if not current_user.is_admin:
is_own = submission.user_id == current_user.id
action = 'delete_self' if is_own else 'delete_other'
if not user_has_permission(current_user, 'submission', action, db):
raise HTTPException(status_code=403, detail="Not authorized to delete this submission")
submission.deleted_at = datetime.utcnow()
submission.deleted_by = current_user.id