"""Credential models and storage interface. Security rules: - No plaintext ``private_key`` or ``token`` fields exist on any model class. - The ``encrypted_payload`` field is opaque bytes encoded as a string. """ import abc import uuid from datetime import UTC, datetime from pydantic import BaseModel, ConfigDict, Field from app.git.types import CredentialKind class GitCredential(BaseModel): """Base credential model. Never stores plaintext secrets. The ``encrypted_payload`` field holds opaque encrypted data. """ model_config = ConfigDict(extra="forbid") id: uuid.UUID = Field(default_factory=uuid.uuid4) kind: CredentialKind encrypted_payload: str = Field(repr=False) created_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) class AccessTokenCredential(GitCredential): """Access-token credential discriminated by ``kind``.""" kind: CredentialKind = CredentialKind.access_token class CredentialStorage(abc.ABC): """Abstract storage backend for :class:`GitCredential` records.""" @abc.abstractmethod async def create(self, credential: GitCredential) -> uuid.UUID: """Persist *credential* and return its ID.""" @abc.abstractmethod async def get(self, credential_id: uuid.UUID) -> GitCredential | None: """Retrieve a credential by ID, or ``None`` if not found.""" @abc.abstractmethod async def delete(self, credential_id: uuid.UUID) -> None: """Remove a credential by ID."""