Files
service_manager/backend/app/models/user.py

136 lines
4.4 KiB
Python

"""
User Model - ServiceManagerWeb
Modelo para usuarios del sistema (internos y clientes)
"""
from sqlalchemy import String, Boolean, DateTime, ForeignKey, Text, ARRAY
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.dialects.postgresql import UUID, ENUM
from typing import Optional, List
import enum
import uuid
from datetime import datetime
from app.core.database import Base
class UserRole(str, enum.Enum):
"""Roles de usuario en el sistema."""
# Staff interno
ADMIN = "ADMIN" # Control total
SUPPORT_MANAGER = "SUPPORT_MANAGER" # Gestión de equipos y SLAs
AGENT = "AGENT" # Atención de tickets
AUDITOR = "AUDITOR" # Solo lectura para auditoría
# Clientes
CLIENT_ADMIN = "CLIENT_ADMIN" # Admin de organización cliente
CLIENT_USER = "CLIENT_USER" # Usuario final cliente
class User(Base):
"""Modelo de Usuario."""
__tablename__ = "users"
# Relación con tenant
tenant_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("tenants.id", ondelete="CASCADE"),
nullable=False
)
# Información básica
email: Mapped[str] = mapped_column(String(320), nullable=False)
first_name: Mapped[str] = mapped_column(String(100), nullable=False)
last_name: Mapped[str] = mapped_column(String(100), nullable=False)
avatar_url: Mapped[Optional[str]] = mapped_column(String(500))
# Autenticación
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
role: Mapped[UserRole] = mapped_column(ENUM(UserRole, name="user_role_enum"), nullable=False)
# 2FA (opcional para staff interno)
totp_secret: Mapped[Optional[str]] = mapped_column(String(32))
totp_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
backup_codes: Mapped[Optional[List[str]]] = mapped_column(ARRAY(String))
# Estado
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
email_verified: Mapped[bool] = mapped_column(Boolean, default=False)
last_login: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
last_activity: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
# Preferencias
language: Mapped[str] = mapped_column(String(10), default="es")
timezone: Mapped[str] = mapped_column(String(50), default="UTC")
notifications_email: Mapped[bool] = mapped_column(Boolean, default=True)
# Relaciones
tenant: Mapped["Tenant"] = relationship("Tenant", back_populates="users")
created_tickets: Mapped[List["Ticket"]] = relationship(
"Ticket",
back_populates="created_by_user",
foreign_keys="Ticket.created_by"
)
assigned_tickets: Mapped[List["Ticket"]] = relationship(
"Ticket",
back_populates="assigned_to_user",
foreign_keys="Ticket.assigned_to"
)
# Unique constraint por tenant
__table_args__ = (
{"postgresql_tablespace": "users"},
)
def __repr__(self) -> str:
return f"<User(id={self.id}, email='{self.email}', role='{self.role}')>"
@property
def full_name(self) -> str:
"""Get user's full name."""
return f"{self.first_name} {self.last_name}"
@property
def is_staff(self) -> bool:
"""Check if user is internal staff."""
return self.role in [
UserRole.ADMIN,
UserRole.SUPPORT_MANAGER,
UserRole.AGENT,
UserRole.AUDITOR
]
@property
def is_client(self) -> bool:
"""Check if user is a client."""
return self.role in [
UserRole.CLIENT_ADMIN,
UserRole.CLIENT_USER
]
@property
def can_manage_users(self) -> bool:
"""Check if user can manage other users."""
return self.role in [
UserRole.ADMIN,
UserRole.SUPPORT_MANAGER,
UserRole.CLIENT_ADMIN
]
@property
def can_manage_tickets(self) -> bool:
"""Check if user can manage tickets."""
return self.role in [
UserRole.ADMIN,
UserRole.SUPPORT_MANAGER,
UserRole.AGENT
]
@property
def requires_2fa(self) -> bool:
"""Check if 2FA is required for this user."""
# 2FA opcional para staff interno, no requerido para clientes
return self.is_staff