"""User model for authentication and authorization. Designed for single-user now but prepared for multi-user with roles in the future. """ from __future__ import annotations 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[Optional[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[Optional[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[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) password_changed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) # Soft delete support deleted_at: Mapped[Optional[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