"""User model for authentication and authorization. Designed for single-user now but prepared for multi-user with roles in the future. """ from datetime import datetime from enum import Enum from typing import List, Optional from sqlalchemy import Boolean, DateTime, String, Text, text from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from .database import Base class UserRole(str, Enum): """User roles for authorization. Current implementation: single admin user. Future: can be extended with more granular roles. """ ADMIN = "admin" OPERATOR = "operator" # Future: can execute tasks but not manage users VIEWER = "viewer" # Future: read-only access class User(Base): """User model for authentication. Fields prepared for future multi-user support: - role: determines access level - is_active: allows disabling users without deletion - last_login: track user activity - password_changed_at: for password rotation policies """ __tablename__ = "users" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) username: Mapped[str] = mapped_column(String(50), unique=True, nullable=False, index=True) email: Mapped[str] = mapped_column(String(255), unique=True, nullable=True) hashed_password: Mapped[str] = mapped_column(String(255), nullable=False) # Role-based access control (prepared for future) role: Mapped[str] = mapped_column( String(20), nullable=False, server_default=text("'admin'") # Default to admin for single-user setup ) # Account status is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("1")) is_superuser: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("0")) # Display name (optional) display_name: Mapped[str] = mapped_column(String(100), nullable=True) # Timestamps 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() ) last_login: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True) password_changed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True) # Soft delete support deleted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=True) # Relationships terminal_commands: Mapped[List["TerminalCommandLog"]] = relationship( "TerminalCommandLog", back_populates="user" ) def __repr__(self) -> str: return f"" @property def is_admin(self) -> bool: """Check if user has admin privileges.""" return self.role == UserRole.ADMIN.value or self.is_superuser