aeat.adapters.persistence.storage.envelope._secure_repository module¶
Generic SQL-backed envelope repository for secure objects.
Concrete domain and application repositories that wrap
adapters.persistence.storage.SecureObjectRepository
all share the same boilerplate:
adapters.persistence.storage.Envelope wrapping, namespace,
sensitivity, schema-version, Pydantic payload type, and a function that
extracts the natural id from the payload.
This module provides
adapters.persistence.storage.SecureBoundRepository, a
generic base class that captures that shared shape exactly once. Concrete
subclasses override the four class-level descriptors (namespace,
payload_type, sensitivity, schema_version) and implement
extract_identifier; they inherit envelope_path_for,
lock_target_for, load, save, delete, iter_ids, and
iter_records for free.
The base class does NOT replace
adapters.persistence.storage.SecureObjectRepository; it
composes one.
- class SecureBoundRepository(*, bucket_id=None, objects=None, settings=None)[source]¶
Bases:
GenericGeneric repository over encrypted SQL-backed envelopes for one payload type.
Subclasses MUST set class attributes:
namespace: theadapters.persistence.storage.SecureObjectRepositorynamespace string for this payload family (e.g."aeat.domain.filing.drafts").payload_type: the typed Pydantic model class wrapped by the envelope.sensitivity: theadapters.persistence.storage.SensitivityClassthat every row in this namespace MUST carry; mismatches raiseadapters.persistence.storage.ClassificationError.schema_version: the current envelope schema version this consumer expects; rows whose version differs from it raiseadapters.persistence.storage.EnvelopeVersionError.
Subclasses MUST implement
extract_identifier()so thatsave()anditer_ids()can recover the natural id from the decrypted payload.- Parameters:
bucket_id (str | None)
objects (SecureObjectRepository | None)
settings (Settings | None)
-
sensitivity:
ClassVar[SensitivityClass]¶
- extract_identifier(payload)[source]¶
Return the natural id for
payload(used as the SQL object key).Subclasses MUST override. The base implementation raises
NotImplementedError.- Return type:
str
- Parameters:
payload (T)
- classmethod payload_model()[source]¶
Return the concrete Pydantic payload model for this repository.
- Return type:
type[T]
- envelope_path_for(identifier)[source]¶
Return a logical path marker for
identifier.- Return type:
Path- Parameters:
identifier (str)
- lock_target_for(identifier)[source]¶
Return a logical lock-target marker for
identifier.SQL transactions govern actual write atomicity; this is only surfaced for diagnostic parity with file-backed repositories.
- Return type:
Path- Parameters:
identifier (str)
- property secure_object_repository: SecureObjectRepository¶
Return the concrete secure-object backend.
- Returns:
The
adapters.persistence.storage.SecureObjectRepositorybacking this logical repository.
- load(identifier)[source]¶
Return the persisted payload or
Noneif absent.- Return type:
T | None
- Parameters:
identifier (str)
- save(payload)[source]¶
Persist
payloadas an encrypted envelope row.The natural id is recovered from the payload via
extract_identifier().- Return type:
None
- Parameters:
payload (T)
- iter_ids()[source]¶
Yield every persisted identifier in storage order.
Order is the secure-object storage order (the
object_keydigest order), not the natural-id order: a caller that needs a specific order sorts the result itself. Streams one identifier at a time rather than buffering and sorting the whole namespace in memory.Fail-closed:
adapters.persistence.storage.SecureObjectRepository.list_records()scans the whole namespace and raisesSecureObjectUnreadableErrorif any row is unreadable, so a full consumption (tuple(...)) never yields a readable subset past a corrupt row.
- iter_records()[source]¶
Yield every persisted payload in storage order.
Streams each payload straight from
adapters.persistence.storage.SecureObjectRepository.list_records()without buffering the whole namespace or sorting it in memory. Order is storage-defined (theobject_keydigest order), not the natural-id order; a caller that needs a specific order sorts the result itself.Parses each row’s decrypted payload bytes directly from
list_recordsrather than routing throughload(). The SQLWHERE object_key = ?lookup insideload()cannot match the stored ciphertext whenobject_keyis anEncryptedStringcolumn (AES-256-GCM uses a random nonce, so the bind-parameter ciphertext differs from the stored ciphertext every time). Iterating directly over the decrypted rows is the correct pattern for full-scan enumeration.Fail-closed:
list_recordsscans the whole namespace and raisesSecureObjectUnreadableErrorif any row is unreadable before this generator yields a readable subset on full consumption.- Return type:
Iterator[T]