From 10e0900a893bfe6e39ffc34ae1371adf9a092031 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Mon, 8 Jun 2026 16:11:58 +0200 Subject: [PATCH] feat(08-02): create backend/api/schemas.py with CloudConnectionOut MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New cross-package Pydantic schemas module (D-10, CODE-08) - CloudConnectionOut moved here from api/admin.py to eliminate coupling - SEC-08: credentials_enc deliberately excluded; 7-field whitelist preserved - field_validator coerce_id_to_str preserves UUID→str coercion behavior - Original definition in api/admin.py left intact until plan 08-04 --- backend/api/schemas.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 backend/api/schemas.py diff --git a/backend/api/schemas.py b/backend/api/schemas.py new file mode 100644 index 0000000..f87b725 --- /dev/null +++ b/backend/api/schemas.py @@ -0,0 +1,42 @@ +"""Cross-package Pydantic response models. + +Models here are used by 2+ API packages and cannot live in a single package +without creating circular imports (D-10, RESEARCH.md Pitfall 3). +""" +from __future__ import annotations + +from datetime import datetime +from typing import Optional + +from pydantic import BaseModel, field_validator + + +class CloudConnectionOut(BaseModel): + """SEC-08: credentials_enc deliberately excluded from this response model. + + Any admin or user endpoint returning CloudConnection ORM objects MUST use + this model to prevent accidental exposure of encrypted credentials. + Safe-by-default: whitelist of allowed fields (not blacklist). + + Moved from api/admin.py to eliminate cross-package coupling between + api/cloud.py and api/admin.py (D-10, RESEARCH.md Pitfall 3). + Used by api/cloud.py and api/admin/ (after plan 08-04 admin split). + + Note: id is declared as str and coerced via validator so UUID ORM values + serialize correctly without json_encoders. + """ + + id: str + provider: str + display_name: str + status: str + connected_at: datetime + server_url: Optional[str] = None + connection_username: Optional[str] = None + model_config = {"from_attributes": True} + + @field_validator("id", mode="before") + @classmethod + def coerce_id_to_str(cls, v) -> str: + """Coerce UUID objects to str so the model validates from ORM instances.""" + return str(v)