Files
vector/backend/alembic/versions/010_b23_policy.py
T
Sadmin a1de5794e2 fix: password_history.id → INTEGER monotonic (детерминированная обрезка)
Грабля: created_at в SQLite точен до секунды — 7 быстрых смен дают
равные метки, а secondary-сортировка по UUID-id случайна → «последние 5»
выбираются произвольно (флакущий тест + неверно хранимая история).
id теперь autoincrement (обрезка order_by id desc), UUID остаётся
как public_id. Миграция 010 обновлена (таблица только на CT108,
пересоздаётся миграцией заново — см. деплой).
2026-09-25 10:46:18 +03:00

42 lines
1.8 KiB
Python

"""B23: исполнение политики паролей + реальная ретенция журналов.
Revises: 009_e3_operations
"""
from __future__ import annotations
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql as pg
revision = '010_b23_policy'
down_revision = '009_e3_operations'
branch_labels = None
depends_on = None
def upgrade() -> None:
# Парольная политика: когда пароль был сменён в последний раз
op.add_column(
'users',
sa.Column('password_changed_at', sa.DateTime(timezone=True),
server_default=sa.text('NOW()'), nullable=True),
)
# История паролей (проверка переиспользования, password_history_count).
# id — INTEGER monotonic (обрезка «последние N» детерминирована; created_at
# в SQLite точен до секунды, UUID-порядок случайный — см. модели).
op.create_table(
'password_history',
sa.Column('id', sa.Integer, primary_key=True, autoincrement=True),
sa.Column('public_id', pg.UUID(as_uuid=True), server_default=sa.text('gen_random_uuid()')),
sa.Column('user_id', pg.UUID(as_uuid=True),
sa.ForeignKey('users.id', ondelete='CASCADE'), nullable=False),
sa.Column('hashed_password', sa.String(255), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('NOW()')),
)
op.create_index('idx_password_history_user', 'password_history', ['user_id', 'created_at'])
def downgrade() -> None:
op.drop_index('idx_password_history_user', table_name='password_history')
op.drop_table('password_history')
op.drop_column('users', 'password_changed_at')