Files
duty-teller/alembic/versions/007_roles_table_and_user_role_id.py
Nikolay Tatarinov 4824450088
All checks were successful
CI / lint-and-test (push) Successful in 22s
feat: implement role-based access control for miniapp
- Introduced a new roles table in the database to manage user roles ('user' and 'admin') for access control.
- Updated the user model to include a foreign key reference to the roles table, allowing for role assignment.
- Enhanced command handlers to support the `/set_role` command for admins to assign roles to users.
- Refactored access control logic to utilize role checks instead of username/phone allowlists, improving security and maintainability.
- Updated documentation to reflect changes in access control mechanisms and role management.
- Added unit tests to ensure correct functionality of role assignment and access checks.
2026-02-20 23:58:54 +03:00

56 lines
1.8 KiB
Python

"""Add roles table and users.role_id (variant B: roles in DB).
Revision ID: 007
Revises: 006
Create Date: 2025-02-20
Roles: 'user', 'admin'. users.role_id is nullable; no role = no access except
env fallback for admins (ADMIN_USERNAMES/ADMIN_PHONES).
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "007"
down_revision: Union[str, None] = "006"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
conn = op.get_bind()
inspector = sa.inspect(conn)
tables = inspector.get_table_names()
if "roles" not in tables:
op.create_table(
"roles",
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
sa.Column("name", sa.Text(), nullable=False, unique=True),
)
# Ensure role rows exist (table may exist from create_all or partial run)
result = conn.execute(sa.text("SELECT COUNT(*) FROM roles")).scalar()
if result == 0:
op.execute(sa.text("INSERT INTO roles (name) VALUES ('user'), ('admin')"))
# SQLite does not support ALTER TABLE ADD COLUMN with FK; add column without FK.
# The ORM model keeps ForeignKey for other backends and documentation.
users_columns = [c["name"] for c in inspector.get_columns("users")]
if "role_id" not in users_columns:
op.add_column(
"users",
sa.Column("role_id", sa.Integer(), nullable=True),
)
def downgrade() -> None:
conn = op.get_bind()
inspector = sa.inspect(conn)
users_columns = [c["name"] for c in inspector.get_columns("users")]
if "role_id" in users_columns:
op.drop_column("users", "role_id")
if "roles" in inspector.get_table_names():
op.drop_table("roles")