logout отзывает предъявленный токен по jti (in-memory denylist до exp); смена и сброс пароля инкрементят users.token_version (claim ver в JWT, сверка в auth/deps) — все прежние сессии отзываются. Своё устройство при смене пароля остаётся в сессии (перевыдача cookie). Миграция 0013. #57 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
45 lines
1.6 KiB
Python
45 lines
1.6 KiB
Python
"""Пользователь: версия сессий (token_version) для отзыва JWT.
|
|
|
|
Идемпотентна: на свежей БД столбец создаёт 0001 (create_all из актуальных моделей) -> no-op;
|
|
на существующей БД добавляет столбец. render_as_batch включён в env.py (для SQLite).
|
|
|
|
Инкремент token_version отзывает все ранее выданные токены пользователя (см. auth/deps, #57).
|
|
|
|
Revision ID: 0013_user_token_version
|
|
Revises: 0012_match_finish_draft
|
|
Create Date: 2026-09-13
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from sqlalchemy import inspect
|
|
|
|
from alembic import op
|
|
|
|
revision: str = "0013_user_token_version"
|
|
down_revision: Union[str, None] = "0012_match_finish_draft"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
bind = op.get_bind()
|
|
insp = inspect(bind)
|
|
cols = {c["name"] for c in insp.get_columns("users")}
|
|
with op.batch_alter_table("users") as b:
|
|
if "token_version" not in cols:
|
|
b.add_column(
|
|
sa.Column(
|
|
"token_version", sa.Integer(), nullable=False, server_default="0"
|
|
)
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
bind = op.get_bind()
|
|
insp = inspect(bind)
|
|
cols = {c["name"] for c in insp.get_columns("users")}
|
|
with op.batch_alter_table("users") as b:
|
|
if "token_version" in cols:
|
|
b.drop_column("token_version")
|