GT AI OS Community Edition v2.0.33
Security hardening release addressing CodeQL and Dependabot alerts: - Fix stack trace exposure in error responses - Add SSRF protection with DNS resolution checking - Implement proper URL hostname validation (replaces substring matching) - Add centralized path sanitization to prevent path traversal - Fix ReDoS vulnerability in email validation regex - Improve HTML sanitization in validation utilities - Fix capability wildcard matching in auth utilities - Update glob dependency to address CVE - Add CodeQL suppression comments for verified false positives 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
126
apps/tenant-backend/app/models/base.py
Normal file
126
apps/tenant-backend/app/models/base.py
Normal file
@@ -0,0 +1,126 @@
|
||||
"""
|
||||
GT 2.0 Base Model Classes - Service-Based Architecture
|
||||
|
||||
Provides Pydantic models for data serialization with the DuckDB service.
|
||||
No SQLAlchemy ORM dependency - pure Python/Pydantic models.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Optional, List, Type, TypeVar
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
# Generic type for model classes
|
||||
T = TypeVar('T', bound='BaseServiceModel')
|
||||
|
||||
|
||||
class BaseServiceModel(BaseModel):
|
||||
"""
|
||||
Base model for all GT 2.0 entities using service-based architecture.
|
||||
|
||||
Replaces SQLAlchemy models with Pydantic models + DuckDB service.
|
||||
"""
|
||||
|
||||
# Pydantic v2 configuration
|
||||
model_config = ConfigDict(
|
||||
from_attributes=True,
|
||||
validate_assignment=True,
|
||||
arbitrary_types_allowed=True,
|
||||
use_enum_values=True
|
||||
)
|
||||
|
||||
# Standard fields for all models
|
||||
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique identifier")
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation timestamp")
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow, description="Last update timestamp")
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert model to dictionary"""
|
||||
return self.model_dump()
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], data: Dict[str, Any]) -> T:
|
||||
"""Create model instance from dictionary"""
|
||||
return cls(**data)
|
||||
|
||||
@classmethod
|
||||
def from_row(cls: Type[T], row: Dict[str, Any]) -> T:
|
||||
"""Create model instance from database row"""
|
||||
# Convert database row to model, handling type conversions
|
||||
model_data = {}
|
||||
|
||||
for field_name, field_info in cls.model_fields.items():
|
||||
if field_name in row:
|
||||
value = row[field_name]
|
||||
|
||||
# Handle datetime conversion
|
||||
if field_info.annotation == datetime and isinstance(value, str):
|
||||
try:
|
||||
value = datetime.fromisoformat(value)
|
||||
except ValueError:
|
||||
value = datetime.utcnow()
|
||||
|
||||
model_data[field_name] = value
|
||||
|
||||
return cls(**model_data)
|
||||
|
||||
def update_timestamp(self):
|
||||
"""Update the updated_at timestamp"""
|
||||
self.updated_at = datetime.utcnow()
|
||||
|
||||
|
||||
class BaseCreateModel(BaseModel):
|
||||
"""Base model for creation requests"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class BaseUpdateModel(BaseModel):
|
||||
"""Base model for update requests"""
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
class BaseResponseModel(BaseServiceModel):
|
||||
"""Base model for API responses"""
|
||||
pass
|
||||
|
||||
|
||||
# Legacy compatibility - some files might still import Base
|
||||
Base = BaseServiceModel # For backwards compatibility during migration
|
||||
|
||||
|
||||
# Database service integration helpers
|
||||
class DatabaseMixin:
|
||||
"""Mixin providing database service integration methods"""
|
||||
|
||||
@classmethod
|
||||
async def get_table_name(cls) -> str:
|
||||
"""Get the database table name for this model"""
|
||||
# Convert CamelCase to snake_case and pluralize
|
||||
name = cls.__name__.lower()
|
||||
if name.endswith('y'):
|
||||
name = name[:-1] + 'ies'
|
||||
elif name.endswith('s'):
|
||||
name = name + 'es'
|
||||
else:
|
||||
name = name + 's'
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
async def create_sql(cls) -> str:
|
||||
"""Generate CREATE TABLE SQL for this model"""
|
||||
# This would generate SQL based on Pydantic field types
|
||||
# For now, return placeholder - actual schemas are in DuckDB service
|
||||
table_name = await cls.get_table_name()
|
||||
return f"-- CREATE TABLE {table_name} generated by DuckDB service"
|
||||
|
||||
async def to_sql_values(self) -> Dict[str, Any]:
|
||||
"""Convert model to SQL-safe values"""
|
||||
data = self.to_dict()
|
||||
|
||||
# Convert datetime objects to ISO strings
|
||||
for key, value in data.items():
|
||||
if isinstance(value, datetime):
|
||||
data[key] = value.isoformat()
|
||||
|
||||
return data
|
||||
Reference in New Issue
Block a user