Build and Push / build (release) Successful in 1m16s
- Add PostgreSQL database with async SQLAlchemy - Add Alembic migrations for schema management - Add User, Role, UserPreferences, ApiKey models - Add auth endpoints: /auth/me, /auth/users, /auth/users/sync-from-authentik - Add token validation via Authentik userinfo endpoint - Add bulk user sync from Authentik admin API - Add database health check to diagnostics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
94 lines
2.5 KiB
Python
94 lines
2.5 KiB
Python
"""
|
|
Role Models
|
|
|
|
Defines domain-scoped permissions mapped from Authentik groups.
|
|
Format: {domain}:{action} (e.g., control-room:admin, media:viewer)
|
|
"""
|
|
import uuid
|
|
from typing import TYPE_CHECKING, List
|
|
|
|
from sqlalchemy import String, ForeignKey
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from src.db.database import Base
|
|
|
|
if TYPE_CHECKING:
|
|
from src.db.models.user import User
|
|
|
|
|
|
class Role(Base):
|
|
"""
|
|
Role model for domain-scoped permissions
|
|
|
|
Roles are seeded from configuration, not user-editable.
|
|
Each role maps to an Authentik group (e.g., tatlock-control-room-admin).
|
|
|
|
Domains: control-room, library, media, ai, housekeeper, developer, documents, gaming, admin
|
|
Actions: viewer, user, editor, admin (hierarchical)
|
|
"""
|
|
|
|
__tablename__ = "roles"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
)
|
|
name: Mapped[str] = mapped_column(
|
|
String(100),
|
|
unique=True,
|
|
nullable=False,
|
|
index=True,
|
|
comment="Role name in format domain:action (e.g., control-room:admin)",
|
|
)
|
|
domain: Mapped[str] = mapped_column(
|
|
String(50),
|
|
nullable=False,
|
|
index=True,
|
|
comment="Permission domain (e.g., control-room, media, ai)",
|
|
)
|
|
action: Mapped[str] = mapped_column(
|
|
String(20),
|
|
nullable=False,
|
|
comment="Permission action (viewer, user, editor, admin)",
|
|
)
|
|
authentik_group: Mapped[str | None] = mapped_column(
|
|
String(255),
|
|
nullable=True,
|
|
unique=True,
|
|
comment="Corresponding Authentik group name (e.g., tatlock-control-room-admin)",
|
|
)
|
|
|
|
# Relationships
|
|
users: Mapped[List["User"]] = relationship(
|
|
"User",
|
|
secondary="user_roles",
|
|
back_populates="roles",
|
|
lazy="selectin",
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return f"<Role {self.name}>"
|
|
|
|
|
|
class UserRole(Base):
|
|
"""
|
|
Association table for User-Role many-to-many relationship
|
|
|
|
Synced from Authentik groups during user authentication.
|
|
"""
|
|
|
|
__tablename__ = "user_roles"
|
|
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("users.id", ondelete="CASCADE"),
|
|
primary_key=True,
|
|
)
|
|
role_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True),
|
|
ForeignKey("roles.id", ondelete="CASCADE"),
|
|
primary_key=True,
|
|
)
|