synology_apm_repo.sdk.profiles.secrets module

OS-keyring-backed secret storage for a profile’s credential fields.

keyring is imported lazily, inside each function, matching storage/s3.py’s/storage/azure.py’s own lazy-import convention: keyring is always installed (a required dependency), but a caller that only lists/reads non-secret profile metadata shouldn’t pay for importing it.

No plaintext fallback exists anywhere in this module, by design — if no real keyring backend is available, every function here raises ProfileSecretBackendUnavailableError rather than writing a secret to disk or silently discarding it.

Grouping scheme: one keyring item per profile — service name f"{_SERVICE_PREFIX}/{profile_name}" (visibly groups a profile’s own row in Keychain/Credential Manager/Secret Service), fixed username (_SECRET_USERNAME), and a JSON object of every secret field currently set (access_key/secret_key/credential/password, depending on backend) as the item’s password. Because every field shares one item, set_secrets must read-modify-write: it merges its caller’s fields onto whatever’s already stored rather than overwriting the whole item, so setting one field never clobbers a sibling field set earlier on the same profile.

synology_apm_repo.sdk.profiles.secrets.set_secrets(profile_name, secrets)

Merge secrets (a subset of some BackendKind’s secret_fields_for(kind), values already resolved — an absent field is simply not written) into profile_name’s single keyring item, on top of whatever fields are already stored there. profiles/ __init__.py::save_profile is the sole caller and already pre-filters to secret_fields_for(kind) before calling, so no field this doesn’t already know how to store could reach it.

synology_apm_repo.sdk.profiles.secrets.get_secrets(profile_name)

Every secret field currently stored for profile_name — a field with no stored value (never set, or the ambient-credential-chain case) is simply absent from the returned dict, not an error.

synology_apm_repo.sdk.profiles.secrets.delete_secrets(profile_name)

Remove every secret field stored for profile_name, in one item. A profile that never had one (PasswordDeleteError) is not an error here — the end state (“no secret stored”) is already what was asked for.