kv_duplicate_retire: block only on data-path grants
The founder dry-run refused on mount-wide platform/metadata/* grants held by ops-mason-survey and platform-readonly. Those cannot read the value, so they are now recorded as metadata_only_policies and no longer block retirement. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 469992@bnt-lap001 Assistant-Session: e4b8e420-df0a-441d-83ae-7f19130594d9
This commit is contained in:
parent
bed91ceaf2
commit
64ae5946d9
2 changed files with 19 additions and 4 deletions
|
|
@ -10,7 +10,10 @@ no source references it. Before the metadata delete this process proves:
|
|||
* the canonical entry exists with a live current version;
|
||||
* the duplicate exists with exactly the expected version count;
|
||||
* no live ACL policy other than platform-admin grants a non-deny capability on
|
||||
the duplicate's data or metadata path (exact, ``+`` and trailing ``*`` globs).
|
||||
the duplicate's data path (exact, ``+`` and trailing ``*`` globs). Grants on
|
||||
the metadata path only (e.g. mount-wide ``platform/metadata/*`` survey and
|
||||
read-only inventory policies) cannot read the value, so they are recorded in
|
||||
the receipt as ``metadata_only_policies`` but do not block.
|
||||
|
||||
No value is read. Run from a railiance-platform checkout:
|
||||
|
||||
|
|
@ -103,18 +106,24 @@ def run(args, receipt):
|
|||
if duplicate.get('current_version') != EXPECTED_VERSIONS or receipt['duplicate_versions'] != EXPECTED_VERSIONS:
|
||||
raise Exit(2, 'duplicate_version_unexpected')
|
||||
|
||||
paths = ['platform/data/' + DUPLICATE, 'platform/metadata/' + DUPLICATE]
|
||||
granting = {}
|
||||
data_path, metadata_path = 'platform/data/' + DUPLICATE, 'platform/metadata/' + DUPLICATE
|
||||
granting, metadata_only = {}, {}
|
||||
for name in policy_names():
|
||||
if name in EXEMPT_POLICIES:
|
||||
continue
|
||||
result = bao(['policy', 'read', name])
|
||||
if result.returncode != 0:
|
||||
raise Exit(2, 'policy_read_failed')
|
||||
hits = granting_stanzas(result.stdout.decode(), paths)
|
||||
text = result.stdout.decode()
|
||||
hits = granting_stanzas(text, [data_path])
|
||||
if hits:
|
||||
granting[name] = hits
|
||||
else:
|
||||
meta_hits = granting_stanzas(text, [metadata_path])
|
||||
if meta_hits:
|
||||
metadata_only[name] = meta_hits
|
||||
receipt['granting_policies'] = granting
|
||||
receipt['metadata_only_policies'] = metadata_only
|
||||
if granting:
|
||||
raise Exit(3, 'live_policy_grants_duplicate')
|
||||
if args.dry_run:
|
||||
|
|
|
|||
|
|
@ -101,6 +101,12 @@ class Retire(unittest.TestCase):
|
|||
self.assertEqual(list(receipt['granting_policies']), ['coding-agent'])
|
||||
self.assertIn(DUP, self.state['kv'])
|
||||
|
||||
def test_mount_wide_metadata_survey_does_not_block(self):
|
||||
self.state['policies']['platform-readonly'] = 'path "platform/metadata/*" { capabilities = ["read", "list"] }'
|
||||
code, receipt = self.run_helper('--dry-run')
|
||||
self.assertEqual((code, receipt['outcome']), (0, 'dry_run_would_retire'))
|
||||
self.assertEqual(receipt['metadata_only_policies'], {'platform-readonly': ['platform/metadata/*']})
|
||||
|
||||
def test_unexpected_versions_or_missing_canonical_refuse(self):
|
||||
self.state['kv'][DUP] = 2
|
||||
code, receipt = self.run_helper()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue