feat(ssh): change SSH key uniqueness to per-user scope
Previously, SSH key fingerprints were globally unique across all users, preventing the same key from being registered by different users. This change makes fingerprint uniqueness scoped to individual users. - Remove global unique constraints on payload and fingerprint columns - Add composite unique constraint on (user_id, fingerprint) - Make add_ssh_key operation idempotent for same user - Return tuple (SSHKey, is_new) from service to indicate creation status - Update API to return 200 for existing keys, 201 for new keys BREAKING CHANGE: API behavior changed - duplicate key addition now returns 200 OK instead of 409 Conflict. Service method signature changed from returning SSHKey to tuple[SSHKey, bool].
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
"""Per-user SSH key fingerprint uniqueness.
|
||||
|
||||
Revision ID: a1b2c3d4e5f6
|
||||
Revises: 8f2d9e4a7c1b
|
||||
Create Date: 2026-04-24 10:00:00.000000
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a1b2c3d4e5f6'
|
||||
down_revision = '8f2d9e4a7c1b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Drop the global unique constraint on payload
|
||||
op.drop_constraint('ssh_keys_payload_key', 'ssh_keys', type_='unique')
|
||||
|
||||
# Drop the global unique index on fingerprint
|
||||
op.drop_index('ix_ssh_keys_fingerprint', table_name='ssh_keys')
|
||||
|
||||
# Create a non-unique index on fingerprint for query performance
|
||||
op.create_index(op.f('ix_ssh_keys_fingerprint'), 'ssh_keys', ['fingerprint'], unique=False)
|
||||
|
||||
# Add composite unique constraint for per-user fingerprint uniqueness
|
||||
op.create_unique_constraint('uix_user_fingerprint', 'ssh_keys', ['user_id', 'fingerprint'])
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Drop the composite unique constraint
|
||||
op.drop_constraint('uix_user_fingerprint', 'ssh_keys', type_='unique')
|
||||
|
||||
# Drop the non-unique index
|
||||
op.drop_index(op.f('ix_ssh_keys_fingerprint'), table_name='ssh_keys')
|
||||
|
||||
# Recreate the global unique index
|
||||
op.create_index('ix_ssh_keys_fingerprint', 'ssh_keys', ['fingerprint'], unique=True)
|
||||
|
||||
# Recreate the global unique constraint on payload
|
||||
op.create_unique_constraint('ssh_keys_payload_key', 'ssh_keys', ['payload'])
|
||||
Reference in New Issue
Block a user