"""Permission delegation model — temporary permission handover between users. Allows a user to delegate their permissions to another user for a specified time period and scope. """ from __future__ import annotations import uuid from datetime import datetime from sqlalchemy import ( Boolean, CheckConstraint, DateTime, ForeignKey, func, ) from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import UUID as PGUUID from sqlalchemy.orm import Mapped, mapped_column from app.core.db import Base, TenantMixin from app.models.owned_mixin import OwnedMixin class PermissionDelegation(Base, TenantMixin, OwnedMixin): """Permission delegation — temporary handover of permissions. from_user_id delegates their permissions to to_user_id for the duration [start_at, end_at]. scope: JSONB defining which permissions are delegated. Examples: - {"all": true} — all permissions - {"entity_types": ["contact", "document"]} — specific entity types - {"permissions": ["contacts:read", "contacts:write"]} — specific permissions """ __tablename__ = "permission_delegations" __table_args__ = ( CheckConstraint( "end_at > start_at", name="ck_pd_end_after_start", ), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) from_user_id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True, ) to_user_id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True, ) start_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False ) end_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False ) scope: Mapped[dict | None] = mapped_column(JSONB, nullable=True, default=None) active: Mapped[bool] = mapped_column( Boolean, nullable=False, default=True ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now(), )