""" User, Profile, Role, and Consent models. Job ID: MTAD-IMPL-2025-11-18-CL """ from sqlalchemy import Column, String, Boolean, DateTime, Integer, ForeignKey, Text, JSON from sqlalchemy.orm import relationship from sqlalchemy.sql import func from app.database import Base from datetime import datetime class User(Base): """User entity - Core authentication and identity.""" __tablename__ = "users" id = Column(String(36), primary_key=True, index=True) email = Column(String(255), unique=True, index=True, nullable=False) password_hash = Column(String(255), nullable=False) email_verified = Column(Boolean, default=False) mfa_enabled = Column(Boolean, default=False) mfa_secret = Column(String(255), nullable=True) # Encrypted locked_until = Column(DateTime, nullable=True) failed_login_attempts = Column(Integer, default=0) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) deleted_at = Column(DateTime, nullable=True) # Relationships profile = relationship("Profile", back_populates="user", uselist=False, cascade="all, delete-orphan") roles = relationship("UserRole", back_populates="user", cascade="all, delete-orphan") consents = relationship("Consent", back_populates="user", cascade="all, delete-orphan") refresh_tokens = relationship("RefreshToken", back_populates="user", cascade="all, delete-orphan") audit_logs = relationship("AuthAuditLog", back_populates="user", cascade="all, delete-orphan") # Forum relationships forum_threads = relationship("ForumThread", back_populates="author") forum_posts = relationship("ForumPost", back_populates="author") forum_reactions = relationship("ForumReaction", back_populates="user") forum_reports = relationship("ForumReport", back_populates="reporter") # Content relationships blog_posts = relationship("BlogPost", back_populates="author") resources = relationship("Resource", back_populates="author") tribute_entries = relationship("TributeEntry", back_populates="author") # Commerce relationships orders = relationship("Order", back_populates="user", cascade="all, delete-orphan") class Profile(Base): """User Profile - Extended user information and health journey.""" __tablename__ = "profiles" id = Column(String(36), primary_key=True, index=True) user_id = Column(String(36), ForeignKey("users.id"), unique=True, nullable=False, index=True) display_name = Column(String(255), nullable=False) pseudonym = Column(String(255), nullable=True, index=True) pronouns = Column(String(50), nullable=True) avatar_url = Column(String(500), nullable=True) bio = Column(Text, nullable=True) health_journey = Column(Text, nullable=True) # PHI - Encrypted at rest consent_flags = Column(JSON, default={}) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) # Relationships user = relationship("User", back_populates="profile") class Role(Base): """Role entity - RBAC roles.""" __tablename__ = "roles" id = Column(String(36), primary_key=True, index=True) name = Column(String(100), unique=True, index=True, nullable=False) permissions = Column(JSON, default={}) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) # Relationships users = relationship("UserRole", back_populates="role", cascade="all, delete-orphan") class UserRole(Base): """User-Role mapping for RBAC.""" __tablename__ = "user_roles" id = Column(String(36), primary_key=True, index=True) user_id = Column(String(36), ForeignKey("users.id"), index=True, nullable=False) role_id = Column(String(36), ForeignKey("roles.id"), index=True, nullable=False) created_at = Column(DateTime, server_default=func.now()) # Relationships user = relationship("User", back_populates="roles") role = relationship("Role", back_populates="users") class Consent(Base): """Consent entity - User consent tracking for compliance.""" __tablename__ = "consents" id = Column(String(36), primary_key=True, index=True) user_id = Column(String(36), ForeignKey("users.id"), index=True, nullable=False) consent_type = Column(String(100), nullable=False) # e.g., marketing, analytics, data_sharing granted = Column(Boolean, default=False) granted_at = Column(DateTime, nullable=True) revoked_at = Column(DateTime, nullable=True) created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) # Relationships user = relationship("User", back_populates="consents")