Files
gt-ai-os-community/apps/control-panel-backend/app/models/session.py
HackWeasel 310491a557 GT AI OS Community v2.0.33 - Add NVIDIA NIM and Nemotron agents
- Updated python_coding_microproject.csv to use NVIDIA NIM Kimi K2
- Updated kali_linux_shell_simulator.csv to use NVIDIA NIM Kimi K2
  - Made more general-purpose (flexible targets, expanded tools)
- Added nemotron-mini-agent.csv for fast local inference via Ollama
- Added nemotron-agent.csv for advanced reasoning via Ollama
- Added wiki page: Projects for NVIDIA NIMs and Nemotron
2025-12-12 17:47:14 -05:00

91 lines
3.8 KiB
Python

"""
Session database model for server-side session tracking.
OWASP/NIST Compliant Session Management (Issue #264):
- Server-side session state is authoritative
- Tracks idle timeout (30 min) and absolute timeout (8 hours)
- Session token hash stored (never plaintext)
"""
from datetime import datetime
from typing import Optional, Dict, Any
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
import uuid
from app.core.database import Base
class Session(Base):
"""Server-side session model for OWASP/NIST compliant session management"""
__tablename__ = "sessions"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
session_token_hash = Column(String(64), unique=True, nullable=False, index=True) # SHA-256 hash
# Session timing (NIST SP 800-63B compliant)
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
last_activity_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
absolute_expires_at = Column(DateTime(timezone=True), nullable=False)
# Session metadata for security auditing
ip_address = Column(String(45), nullable=True) # IPv6 compatible
user_agent = Column(Text, nullable=True)
tenant_id = Column(Integer, ForeignKey("tenants.id"), nullable=True, index=True)
# Session state
is_active = Column(Boolean, default=True, nullable=False)
revoked_at = Column(DateTime(timezone=True), nullable=True)
revoke_reason = Column(String(50), nullable=True) # 'logout', 'idle_timeout', 'absolute_timeout', 'admin_revoke', 'password_change', 'cleanup_stale'
ended_at = Column(DateTime(timezone=True), nullable=True) # When session ended (any reason: logout, timeout, etc.)
app_type = Column(String(20), default='control_panel', nullable=False) # 'control_panel' or 'tenant_app'
# Relationships
user = relationship("User", back_populates="sessions")
tenant = relationship("Tenant", backref="sessions")
def __repr__(self):
return f"<Session(id={self.id}, user_id={self.user_id}, is_active={self.is_active})>"
def to_dict(self) -> Dict[str, Any]:
"""Convert session to dictionary (excluding sensitive data)"""
return {
"id": str(self.id),
"user_id": self.user_id,
"tenant_id": self.tenant_id,
"created_at": self.created_at.isoformat() if self.created_at else None,
"last_activity_at": self.last_activity_at.isoformat() if self.last_activity_at else None,
"absolute_expires_at": self.absolute_expires_at.isoformat() if self.absolute_expires_at else None,
"ip_address": self.ip_address,
"is_active": self.is_active,
"revoked_at": self.revoked_at.isoformat() if self.revoked_at else None,
"revoke_reason": self.revoke_reason,
"ended_at": self.ended_at.isoformat() if self.ended_at else None,
"app_type": self.app_type,
}
@property
def is_expired(self) -> bool:
"""Check if session is expired (either idle or absolute)"""
if not self.is_active:
return True
now = datetime.now(self.absolute_expires_at.tzinfo) if self.absolute_expires_at.tzinfo else datetime.utcnow()
# Check absolute timeout
if now >= self.absolute_expires_at:
return True
# Check idle timeout (30 minutes)
from datetime import timedelta
idle_timeout = timedelta(minutes=30)
idle_expires_at = self.last_activity_at + idle_timeout
if now >= idle_expires_at:
return True
return False