Fix 2 pentest findings: unlock permission check + permanent lock preservation
SC-01: unlock_event now verifies caller has access to the calendar before revealing lock state. Previously any authenticated user could probe event existence via 404/204/403 response differences. SC-02: acquire_lock no longer overwrites permanent locks. If the owner holds a permanent lock and clicks Edit, the existing lock is returned as-is instead of being downgraded to a 5-minute temporary lock. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8f777dd15a
commit
206144d20d
@ -716,6 +716,11 @@ async def unlock_event(
|
|||||||
if not event:
|
if not event:
|
||||||
raise HTTPException(status_code=404, detail="Event not found")
|
raise HTTPException(status_code=404, detail="Event not found")
|
||||||
|
|
||||||
|
# SC-01: Verify caller has access to this calendar before revealing lock state
|
||||||
|
perm = await get_user_permission(db, event.calendar_id, current_user.id)
|
||||||
|
if perm is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Event not found")
|
||||||
|
|
||||||
lock_result = await db.execute(
|
lock_result = await db.execute(
|
||||||
select(EventLock).where(EventLock.event_id == event_id)
|
select(EventLock).where(EventLock.event_id == event_id)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -66,8 +66,20 @@ async def acquire_lock(db: AsyncSession, event_id: int, user_id: int) -> EventLo
|
|||||||
"""
|
"""
|
||||||
Atomic INSERT ON CONFLICT — acquires a 5-minute lock on the event.
|
Atomic INSERT ON CONFLICT — acquires a 5-minute lock on the event.
|
||||||
Only succeeds if no unexpired lock exists or the existing lock is held by the same user.
|
Only succeeds if no unexpired lock exists or the existing lock is held by the same user.
|
||||||
|
Permanent locks are never overwritten — if the same user holds one, it is returned as-is.
|
||||||
Returns the lock or raises 423 Locked.
|
Returns the lock or raises 423 Locked.
|
||||||
"""
|
"""
|
||||||
|
# Check for existing permanent lock first
|
||||||
|
existing = await db.execute(
|
||||||
|
select(EventLock).where(EventLock.event_id == event_id)
|
||||||
|
)
|
||||||
|
existing_lock = existing.scalar_one_or_none()
|
||||||
|
if existing_lock and existing_lock.is_permanent:
|
||||||
|
if existing_lock.locked_by == user_id:
|
||||||
|
# Owner holds permanent lock — return it without downgrading
|
||||||
|
return existing_lock
|
||||||
|
raise HTTPException(status_code=423, detail="Event is permanently locked by the calendar owner")
|
||||||
|
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
expires = now + timedelta(minutes=LOCK_DURATION_MINUTES)
|
expires = now + timedelta(minutes=LOCK_DURATION_MINUTES)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user