44 lines
1.6 KiB
Python
44 lines
1.6 KiB
Python
|
|
"""Native KV rotation: replace a declared field without touching siblings.
|
||
|
|
|
||
|
|
This updates OpenBao custody only. Catalog ``rotation.owner`` remains the
|
||
|
|
provider/workload owner; this engine does not roll consumers or revoke a
|
||
|
|
provider credential. Auth-capability lanes rotate via wrap/handoff, not here.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
from secrets_engine.catalog import CatalogEntry
|
||
|
|
from secrets_engine.errors import ProvisioningError
|
||
|
|
from secrets_engine.openbao import OpenBaoClient
|
||
|
|
from secrets_engine.provision import provision_from_file
|
||
|
|
|
||
|
|
|
||
|
|
def render_rotate_plan(entry: CatalogEntry, field: str) -> str:
|
||
|
|
owner = str((entry.rotation or {}).get("owner") or "")
|
||
|
|
lines = [
|
||
|
|
f"Rotate plan for lane '{entry.id}'",
|
||
|
|
f" field: {field}",
|
||
|
|
f" target: {entry.mount}/{entry.path}",
|
||
|
|
f" owner: {owner or '-'}",
|
||
|
|
" siblings: preserved (CAS patch)",
|
||
|
|
" workload delivery: not mutated",
|
||
|
|
]
|
||
|
|
return "\n".join(lines)
|
||
|
|
|
||
|
|
|
||
|
|
def rotate_from_file(
|
||
|
|
client: OpenBaoClient, entry: CatalogEntry, field: str, file_path: Path
|
||
|
|
) -> str:
|
||
|
|
"""Replace one declared KV field. Returns the field name only."""
|
||
|
|
if not entry.stores_kv_value():
|
||
|
|
raise ProvisioningError(
|
||
|
|
f"lane '{entry.id}' is {entry.kind}; rotate native KV via this "
|
||
|
|
"command, or wrap/handoff for auth-capability material"
|
||
|
|
)
|
||
|
|
if field not in entry.fields:
|
||
|
|
raise ProvisioningError(
|
||
|
|
f"field '{field}' not declared in lane '{entry.id}' fields {entry.fields}"
|
||
|
|
)
|
||
|
|
return provision_from_file(client, entry, field, file_path)
|