fix(files): ending every session and making a token lock the user without holding up rows that refer to it
This commit is contained in:
@@ -1150,12 +1150,14 @@ def bump_session_epoch(db: Session, user: User) -> None:
|
||||
outlive the answer to that theft. Not committed — the caller commits it with its change.
|
||||
|
||||
The user's row is locked before the tokens go, the lock `create_token` takes too: a token made at
|
||||
the same moment either commits first and is deleted here, or waits and sees the new epoch."""
|
||||
the same moment either commits first and is deleted here, or waits and sees the new epoch. FOR NO
|
||||
KEY UPDATE (`key_share`): the two still exclude each other, but a row that only refers to the user
|
||||
(a notice, an audit line) is not held up — a token's expiry notice would deadlock with it."""
|
||||
if user.id is None:
|
||||
user.session_epoch = (user.session_epoch or 0) + 1
|
||||
return
|
||||
epoch = db.execute(
|
||||
select(User.session_epoch).where(User.id == user.id).with_for_update()
|
||||
select(User.session_epoch).where(User.id == user.id).with_for_update(key_share=True)
|
||||
).scalar_one()
|
||||
user.session_epoch = (epoch or 0) + 1
|
||||
apitokens.revoke_all(db, user.id)
|
||||
|
||||
@@ -122,9 +122,10 @@ def create_token(
|
||||
raise refused(400, "api_token_name_required")
|
||||
# The user's row, locked: two requests at once cannot both slip under the ceiling, and a
|
||||
# session ended since `current_user` read the epoch (unlocked) cannot mint a token that outlives
|
||||
# it — `bump_session_epoch` takes this lock before it revokes them all.
|
||||
# it — `bump_session_epoch` takes this lock before it revokes them all. No key update: a row that
|
||||
# only refers to the user (a folder delete's audit line) must not wait on it, or the two deadlock.
|
||||
epoch = db.execute(
|
||||
select(User.session_epoch).where(User.id == user.id).with_for_update()
|
||||
select(User.session_epoch).where(User.id == user.id).with_for_update(key_share=True)
|
||||
).scalar_one()
|
||||
if (epoch or 0) != (user.session_epoch or 0):
|
||||
raise refused(401, "not_authenticated")
|
||||
|
||||
Reference in New Issue
Block a user