feat(FN-011): complete Step 2 — credential model and storage interface
Fusion-Task-Id: FN-011 Fusion-Task-Lineage: 4a9aca6f-9d91-43aa-8d2a-d59657c1541a
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
"""Git provider abstraction, credentials, SSH keys, and operations."""
|
||||
|
||||
from app.git.credentials import AccessTokenCredential, CredentialStorage, GitCredential
|
||||
from app.git.provider import GitProvider
|
||||
from app.git.types import ConnectionStatus, CredentialKind, ProviderKind, SshKeyStatus
|
||||
|
||||
__all__ = [
|
||||
"AccessTokenCredential",
|
||||
"ConnectionStatus",
|
||||
"CredentialKind",
|
||||
"CredentialStorage",
|
||||
"GitCredential",
|
||||
"GitProvider",
|
||||
"ProviderKind",
|
||||
"SshKeyStatus",
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"""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 datetime, timezone
|
||||
|
||||
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(timezone.utc)
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
default_factory=lambda: datetime.now(timezone.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
|
||||
def create(self, credential: GitCredential) -> uuid.UUID:
|
||||
"""Persist *credential* and return its ID."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get(self, credential_id: uuid.UUID) -> GitCredential | None:
|
||||
"""Retrieve a credential by ID, or ``None`` if not found."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete(self, credential_id: uuid.UUID) -> None:
|
||||
"""Remove a credential by ID."""
|
||||
Reference in New Issue
Block a user