Bruno Charest 5bc12d0729
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Add terminal session management with heartbeat monitoring, idle timeout detection, session reuse logic, and command history panel UI with search and filtering capabilities
2025-12-18 13:49:40 -05:00

89 lines
3.1 KiB
Python

"""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"<User id={self.id} username={self.username} role={self.role}>"
@property
def is_admin(self) -> bool:
"""Check if user has admin privileges."""
return self.role == UserRole.ADMIN.value or self.is_superuser