feat: initial TrustOS platform scaffold
- FastAPI backend: auth, findings, dashboard, attack paths, footprint, AI translator, risk calculator, PDF report generator - Next.js frontend: Vault dashboard, login, findings table, finding detail with AI coach, digital footprint, reports - PostgreSQL data model: tenants, users, assets, findings, risk scores, audit reports, attack paths - Docker Compose + Dockerfiles for all services - Demo seed data: Acme Corp with 6 findings and 90-day risk score history - AI Risk Translator (OpenAI/Anthropic) with plain-English business impact - Role-based access: executive / it_admin / trustos_admin - Scope-lock engine: authorization required before any assessment Stage 1-8 complete: Phase 1 Vault Audit product ready
This commit is contained in:
202
backend/app/schemas/schemas.py
Normal file
202
backend/app/schemas/schemas.py
Normal file
@@ -0,0 +1,202 @@
|
||||
from pydantic import BaseModel, EmailStr, field_validator
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
from app.models.models import UserRole, FindingSeverity, FindingStatus, FindingCategory, AssetType
|
||||
|
||||
|
||||
# ─── Auth ────────────────────────────────────────────────────────────────────
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
email: str
|
||||
password: str
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
role: str
|
||||
tenant_id: str
|
||||
full_name: str
|
||||
|
||||
|
||||
class UserMe(BaseModel):
|
||||
id: str
|
||||
email: str
|
||||
full_name: str
|
||||
role: UserRole
|
||||
tenant_id: str
|
||||
|
||||
|
||||
# ─── Tenant ───────────────────────────────────────────────────────────────────
|
||||
|
||||
class TenantBase(BaseModel):
|
||||
name: str
|
||||
slug: str
|
||||
industry: Optional[str] = None
|
||||
size_range: Optional[str] = None
|
||||
contact_email: Optional[str] = None
|
||||
|
||||
|
||||
class TenantCreate(TenantBase):
|
||||
pass
|
||||
|
||||
|
||||
class TenantOut(TenantBase):
|
||||
id: str
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# ─── Finding ──────────────────────────────────────────────────────────────────
|
||||
|
||||
class FindingOut(BaseModel):
|
||||
id: str
|
||||
tenant_id: str
|
||||
asset_id: Optional[str] = None
|
||||
title: str
|
||||
severity: FindingSeverity
|
||||
status: FindingStatus
|
||||
category: FindingCategory
|
||||
technical_description: Optional[str] = None
|
||||
cve_id: Optional[str] = None
|
||||
cvss_score: Optional[float] = None
|
||||
affected_component: Optional[str] = None
|
||||
ai_summary: Optional[str] = None
|
||||
ai_business_impact: Optional[str] = None
|
||||
ai_impact_level: Optional[str] = None
|
||||
ai_remediation_steps: Optional[str] = None
|
||||
ai_fix_priority: Optional[str] = None
|
||||
assignee_email: Optional[str] = None
|
||||
due_date: Optional[datetime] = None
|
||||
is_top_risk: bool
|
||||
source: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class FindingCreate(BaseModel):
|
||||
title: str
|
||||
severity: FindingSeverity
|
||||
category: FindingCategory
|
||||
technical_description: Optional[str] = None
|
||||
cve_id: Optional[str] = None
|
||||
cvss_score: Optional[float] = None
|
||||
affected_component: Optional[str] = None
|
||||
asset_id: Optional[str] = None
|
||||
executive_id: Optional[str] = None
|
||||
source: str = "manual"
|
||||
|
||||
|
||||
class FindingStatusUpdate(BaseModel):
|
||||
status: FindingStatus
|
||||
resolution_note: Optional[str] = None
|
||||
assignee_email: Optional[str] = None
|
||||
due_date: Optional[datetime] = None
|
||||
|
||||
|
||||
# ─── Risk Score ───────────────────────────────────────────────────────────────
|
||||
|
||||
class RiskScoreOut(BaseModel):
|
||||
id: str
|
||||
tenant_id: str
|
||||
score_date: datetime
|
||||
overall_score: float
|
||||
score_identity: Optional[float] = None
|
||||
score_cloud: Optional[float] = None
|
||||
score_network: Optional[float] = None
|
||||
score_web: Optional[float] = None
|
||||
score_credential: Optional[float] = None
|
||||
score_digital_footprint: Optional[float] = None
|
||||
score_third_party: Optional[float] = None
|
||||
critical_count: int
|
||||
high_count: int
|
||||
medium_count: int
|
||||
low_count: int
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# ─── Dashboard ────────────────────────────────────────────────────────────────
|
||||
|
||||
class RiskCardData(BaseModel):
|
||||
id: str
|
||||
title: str
|
||||
ai_summary: Optional[str]
|
||||
ai_business_impact: Optional[str]
|
||||
ai_impact_level: Optional[str]
|
||||
ai_fix_priority: Optional[str]
|
||||
severity: str
|
||||
category: str
|
||||
|
||||
|
||||
class DashboardResponse(BaseModel):
|
||||
tenant_name: str
|
||||
current_score: float
|
||||
previous_score: Optional[float]
|
||||
score_delta: Optional[float]
|
||||
score_trend: List[dict] # [{date, score}]
|
||||
top_risks: List[RiskCardData]
|
||||
open_critical: int
|
||||
open_high: int
|
||||
open_medium: int
|
||||
total_open: int
|
||||
baseline_score: Optional[float] = None
|
||||
baseline_date: Optional[datetime] = None
|
||||
|
||||
|
||||
# ─── Audit Report ─────────────────────────────────────────────────────────────
|
||||
|
||||
class AuditReportOut(BaseModel):
|
||||
id: str
|
||||
tenant_id: str
|
||||
title: str
|
||||
report_date: datetime
|
||||
baseline_score: Optional[float]
|
||||
executive_summary: Optional[str]
|
||||
scope_description: Optional[str]
|
||||
pdf_path: Optional[str]
|
||||
is_baseline: bool
|
||||
generated_by: Optional[str]
|
||||
created_at: datetime
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class AuditReportCreate(BaseModel):
|
||||
title: str
|
||||
executive_summary: Optional[str] = None
|
||||
scope_description: Optional[str] = None
|
||||
|
||||
|
||||
# ─── Attack Path ──────────────────────────────────────────────────────────────
|
||||
|
||||
class AttackPathOut(BaseModel):
|
||||
id: str
|
||||
finding_id: str
|
||||
title: str
|
||||
ai_narrative: Optional[str]
|
||||
nodes_json: Optional[str]
|
||||
edges_json: Optional[str]
|
||||
created_at: datetime
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
# ─── Authorized Asset ─────────────────────────────────────────────────────────
|
||||
|
||||
class AuthorizedAssetCreate(BaseModel):
|
||||
value: str
|
||||
asset_type: AssetType
|
||||
description: Optional[str] = None
|
||||
authorized_by: str
|
||||
|
||||
|
||||
class AuthorizedAssetOut(BaseModel):
|
||||
id: str
|
||||
tenant_id: str
|
||||
value: str
|
||||
asset_type: AssetType
|
||||
description: Optional[str]
|
||||
authorized_by: Optional[str]
|
||||
authorized_at: datetime
|
||||
is_active: bool
|
||||
model_config = {"from_attributes": True}
|
||||
Reference in New Issue
Block a user