103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
"""
|
|
Credential persistence abstractions for Mail Intake.
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
This module defines the generic persistence contract used to store and
|
|
retrieve authentication credentials across Mail Intake components.
|
|
|
|
The ``CredentialStore`` abstraction establishes a strict separation
|
|
between credential *lifecycle management* and credential *storage*.
|
|
Authentication providers are responsible for acquiring, validating,
|
|
refreshing, and revoking credentials, while concrete store
|
|
implementations are responsible solely for persistence concerns.
|
|
|
|
By remaining agnostic to credential structure, serialization format,
|
|
and storage backend, this module enables multiple persistence
|
|
strategies—such as local files, in-memory caches, distributed stores,
|
|
or secrets managers—without coupling authentication logic to any
|
|
specific storage mechanism.
|
|
"""
|
|
|
|
|
|
from abc import ABC, abstractmethod
|
|
from typing import Generic, Optional, TypeVar
|
|
|
|
T = TypeVar("T")
|
|
|
|
|
|
class CredentialStore(ABC, Generic[T]):
|
|
"""
|
|
Abstract base class defining a generic persistence interface for
|
|
authentication credentials.
|
|
|
|
Notes:
|
|
**Responsibilities:**
|
|
|
|
- Provide persistent storage separating life-cycle management from storage mechanics
|
|
- Keep implementation focused only on persistence
|
|
|
|
**Constraints:**
|
|
|
|
- The store is intentionally agnostic to:
|
|
- The concrete credential type being stored
|
|
- The serialization format used to persist credentials
|
|
- The underlying storage backend or durability guarantees
|
|
"""
|
|
|
|
@abstractmethod
|
|
def load(self) -> Optional[T]:
|
|
"""
|
|
Load previously persisted credentials.
|
|
|
|
Returns:
|
|
Optional[T]:
|
|
An instance of type ``T`` if credentials are available and
|
|
loadable; otherwise ``None``.
|
|
|
|
Notes:
|
|
**Guarantees:**
|
|
|
|
- Implementations should return ``None`` when no credentials are present or when stored credentials cannot be successfully decoded or deserialized
|
|
- The store must not attempt to validate, refresh, or otherwise interpret the returned credentials
|
|
"""
|
|
|
|
@abstractmethod
|
|
def save(self, credentials: T) -> None:
|
|
"""
|
|
Persist credentials to the underlying storage backend.
|
|
|
|
Args:
|
|
credentials (T):
|
|
The credential object to persist.
|
|
|
|
Notes:
|
|
**Lifecycle:**
|
|
|
|
- This method is invoked when credentials are newly obtained or have been refreshed and are known to be valid at the time of persistence
|
|
|
|
**Responsibilities:**
|
|
|
|
- Ensuring durability appropriate to the deployment context
|
|
- Applying encryption or access controls where required
|
|
- Overwriting any previously stored credentials
|
|
"""
|
|
|
|
@abstractmethod
|
|
def clear(self) -> None:
|
|
"""
|
|
Remove any persisted credentials from the store.
|
|
|
|
Notes:
|
|
**Lifecycle:**
|
|
|
|
- This method is called when credentials are known to be invalid, revoked, corrupted, or otherwise unusable
|
|
- Must ensure that no stale authentication material remains accessible
|
|
|
|
**Guarantees:**
|
|
|
|
- Implementations should treat this operation as idempotent
|
|
"""
|