Files
leocrm/app/models/guest_user.py
T

61 lines
2.1 KiB
Python
Raw Normal View History

"""Guest User model — for time-limited guest access via entity permissions."""
from __future__ import annotations
import uuid
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, Index, String, func
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.orm import Mapped, mapped_column
from app.core.db import Base, TenantMixin
class GuestUser(Base, TenantMixin):
"""Guest user with time-limited access to shared entities.
Guests are invited by tenant admins and can only access entities
that have explicit entity_permissions with principal_type='guest'.
"""
__tablename__ = "guest_users"
__table_args__ = (
Index("ix_guest_users_email_tenant", "email", "tenant_id", unique=True),
Index("ix_guest_users_status", "status", "tenant_id"),
Index("ix_guest_users_invited_by", "invited_by"),
)
id: Mapped[uuid.UUID] = mapped_column(
PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
email: Mapped[str] = mapped_column(String(255), nullable=False)
name: Mapped[str] = mapped_column(String(255), nullable=False)
password_hash: Mapped[str | None] = mapped_column(
String(255), nullable=True, default=None
)
tenant_id: Mapped[uuid.UUID] = mapped_column(
PGUUID(as_uuid=True),
ForeignKey("tenants.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
invited_by: Mapped[uuid.UUID | None] = mapped_column(
PGUUID(as_uuid=True),
ForeignKey("users.id", ondelete="SET NULL"),
nullable=True,
)
status: Mapped[str] = mapped_column(
String(20), nullable=False, default="invited"
) # 'invited', 'active', 'expired', 'revoked'
expires_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True, default=None
)
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(),
)