API key come hash SHA-256, una per sessione, TTL rinnovabile (#41)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""API keys as SHA-256 hashes, one per session (#41)
|
||||
|
||||
Replaces the plaintext `users.api_key` column with an `api_keys` table: one
|
||||
row per login, holding only the SHA-256 hex digest, an expiry and the last
|
||||
use. Existing keys are invalidated on purpose, not migrated - the plaintext
|
||||
is exactly what must stop existing, so everybody logs in once again.
|
||||
|
||||
Revision ID: 015_api_key_hash
|
||||
Revises: 014_vision_fk_ondelete
|
||||
Create Date: 2026-10-06
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = '015_api_key_hash'
|
||||
down_revision: Union[str, None] = '014_vision_fk_ondelete'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
'api_keys',
|
||||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('key_hash', sa.String(64), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('expires_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('last_used_at', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
['user_id'], ['users.id'], name='fk_api_keys_user_id', ondelete='CASCADE'
|
||||
),
|
||||
)
|
||||
op.create_index('ix_api_keys_key_hash', 'api_keys', ['key_hash'], unique=True)
|
||||
op.create_index('ix_api_keys_user_id', 'api_keys', ['user_id'])
|
||||
|
||||
with op.batch_alter_table('users') as batch:
|
||||
batch.drop_index('ix_users_api_key')
|
||||
batch.drop_constraint('uq_users_api_key', type_='unique')
|
||||
batch.drop_column('api_key')
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
with op.batch_alter_table('users') as batch:
|
||||
batch.add_column(sa.Column('api_key', sa.String(64), nullable=True))
|
||||
batch.create_unique_constraint('uq_users_api_key', ['api_key'])
|
||||
batch.create_index('ix_users_api_key', ['api_key'])
|
||||
|
||||
op.drop_index('ix_api_keys_user_id', table_name='api_keys')
|
||||
op.drop_index('ix_api_keys_key_hash', table_name='api_keys')
|
||||
op.drop_table('api_keys')
|
||||
Reference in New Issue
Block a user