feat: implement role-based access control for miniapp
All checks were successful
CI / lint-and-test (push) Successful in 22s

- 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.
This commit is contained in:
2026-02-20 23:58:54 +03:00
parent d02d0a1835
commit 4824450088
18 changed files with 554 additions and 83 deletions

View File

@@ -10,6 +10,17 @@ class Base(DeclarativeBase):
pass
class Role(Base):
"""Role for access control: 'user' (miniapp access), 'admin' (admin actions)."""
__tablename__ = "roles"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(Text, nullable=False, unique=True)
users: Mapped[list["User"]] = relationship("User", back_populates="role")
class User(Base):
"""Telegram user and display name; may have telegram_user_id=None for import-only users."""
@@ -27,7 +38,11 @@ class User(Base):
name_manually_edited: Mapped[bool] = mapped_column(
Boolean, nullable=False, server_default="0", default=False
)
role_id: Mapped[int | None] = mapped_column(
Integer, ForeignKey("roles.id"), nullable=True
)
role: Mapped["Role | None"] = relationship("Role", back_populates="users")
duties: Mapped[list["Duty"]] = relationship("Duty", back_populates="user")