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."""
|
"""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.provider import GitProvider
|
||||||
from app.git.types import ConnectionStatus, CredentialKind, ProviderKind, SshKeyStatus
|
from app.git.types import ConnectionStatus, CredentialKind, ProviderKind, SshKeyStatus
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
"AccessTokenCredential",
|
||||||
"ConnectionStatus",
|
"ConnectionStatus",
|
||||||
"CredentialKind",
|
"CredentialKind",
|
||||||
|
"CredentialStorage",
|
||||||
|
"GitCredential",
|
||||||
"GitProvider",
|
"GitProvider",
|
||||||
"ProviderKind",
|
"ProviderKind",
|
||||||
"SshKeyStatus",
|
"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