Add native rotate and persistent lane overlay states
rotate replaces one declared KV field through the merge-safe patch path and never prints the value. Overlay states active/suspended/deactivated/ compromised live under the evidence directory. compromise/reactivate and successful suspend/deactivate/revoke update that overlay; exec/wrap/ handoff/provision refuse non-active lanes. Provider-side rotation stays with rotation.owner. Production remains fail-closed. Assistant: grok Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
This commit is contained in:
parent
85d4548035
commit
72d3327c28
12 changed files with 596 additions and 20 deletions
43
src/secrets_engine/rotate.py
Normal file
43
src/secrets_engine/rotate.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
"""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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue