group.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. """Group model for permission-based access control."""
  2. from __future__ import annotations
  3. from datetime import datetime
  4. from typing import TYPE_CHECKING
  5. from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Table, func
  6. from sqlalchemy.orm import Mapped, mapped_column, relationship
  7. from sqlalchemy.types import JSON
  8. from backend.app.core.database import Base
  9. if TYPE_CHECKING:
  10. from backend.app.models.user import User
  11. # Many-to-many association table between users and groups
  12. user_groups = Table(
  13. "user_groups",
  14. Base.metadata,
  15. Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True),
  16. Column("group_id", Integer, ForeignKey("groups.id", ondelete="CASCADE"), primary_key=True),
  17. )
  18. class Group(Base):
  19. """Group model for organizing users and assigning permissions.
  20. Groups contain a list of permissions that are granted to all members.
  21. Users can belong to multiple groups, and their permissions are additive.
  22. System groups (Administrators, Operators, Viewers) cannot be deleted.
  23. """
  24. __tablename__ = "groups"
  25. id: Mapped[int] = mapped_column(primary_key=True)
  26. name: Mapped[str] = mapped_column(String(100), unique=True, index=True)
  27. description: Mapped[str | None] = mapped_column(String(500), nullable=True)
  28. permissions: Mapped[list[str]] = mapped_column(JSON, default=list)
  29. is_system: Mapped[bool] = mapped_column(Boolean, default=False)
  30. created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
  31. updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now())
  32. # Relationship to users through association table
  33. users: Mapped[list[User]] = relationship(
  34. "User",
  35. secondary=user_groups,
  36. back_populates="groups",
  37. lazy="selectin",
  38. )
  39. def __repr__(self) -> str:
  40. return f"<Group {self.name}>"