Some checks failed
CI / style (push) Has been cancelled
CI / build-client (push) Has been cancelled
CI / build-amd64 (push) Has been cancelled
CI / build-tester (push) Has been cancelled
CI / test (cockroachdb) (push) Has been cancelled
CI / test (mariadb) (push) Has been cancelled
CI / test (postgres) (push) Has been cancelled
CI / test (sqlite) (push) Has been cancelled
CI / build-arm64 (push) Has been cancelled
CI / build-armv7 (push) Has been cancelled
CI / publish-images (push) Has been cancelled
CI / publish-release (push) Has been cancelled
Backend: - Add User model with username, hashed password, role, is_active - Add passlib[bcrypt] and python-jose[cryptography] dependencies - Create auth.py with JWT utilities, password hashing, role checking - Add /auth endpoints: status, setup, login, me, users CRUD - Add role-based permission system (viewer/operator/editor/admin) - Environment: SPOOLMAN_AUTH_ENABLED, SPOOLMAN_AUTH_SECRET_KEY - Migration for user table Frontend: - Add AuthProvider context with login/logout/setup - Add LoginPage component with setup mode for first user - Add ProtectedRoute wrapper for auth-required pages - Add axios interceptor to attach JWT token to requests - Add User Management tab in Settings (admin only) - Add translation keys for auth UI When SPOOLMAN_AUTH_ENABLED=true: - Users must login to access the app - First visit shows setup page to create admin account - Role-based access control for future endpoint protection Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
43 lines
1.3 KiB
Python
43 lines
1.3 KiB
Python
"""Add user authentication table.
|
|
|
|
Revision ID: d4e5f6g7h8i9
|
|
Revises: c3d4e5f6g7h8
|
|
Create Date: 2025-01-16 01:00:00.000000
|
|
|
|
"""
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision = "d4e5f6g7h8i9"
|
|
down_revision = "c3d4e5f6g7h8"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"user",
|
|
sa.Column("id", sa.Integer(), nullable=False),
|
|
sa.Column("username", sa.String(length=64), nullable=False),
|
|
sa.Column("hashed_password", sa.String(length=128), nullable=False),
|
|
sa.Column(
|
|
"role",
|
|
sa.String(length=16),
|
|
nullable=False,
|
|
server_default="viewer",
|
|
comment="Role: 'admin', 'editor', 'operator', 'viewer'.",
|
|
),
|
|
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.text("1")),
|
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index(op.f("ix_user_id"), "user", ["id"], unique=False)
|
|
op.create_index(op.f("ix_user_username"), "user", ["username"], unique=True)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index(op.f("ix_user_username"), table_name="user")
|
|
op.drop_index(op.f("ix_user_id"), table_name="user")
|
|
op.drop_table("user")
|