railiance-platform/tests/test_operators_entry_retire.py
codex 7b75823095
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
ADHOC-2026-10-10: retire the deprecated operators/nextcloud entry
The founder confirmed operators/nextcloud (not /backup) is deprecated. The
operators mount is deliberately delete-free for operator-custody and absent
from platform-admin, so a silent helper performs the deletion with a one-shot
policy and a 60s orphan token scoped to that entry. It verifies the backup
sibling is unchanged and removes the token and policy afterwards.

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
2026-10-10 15:32:02 +02:00

130 lines
6 KiB
Python

"""Offline tests for the operators/nextcloud retirement helper against a fake ``bao``."""
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import textwrap
import unittest
ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / 'scripts/operators_entry_retire.py'
CONFIRM = 'RETIRE operators/nextcloud'
ONE_SHOT = 'hvs.oneshot-' + 'K3l4' * 6
# KV calls are refused unless they carry the one-shot token and its policy exists.
FAKE_BAO = textwrap.dedent(f'''\
#!/usr/bin/env python3
import json, os, sys
sf = os.environ['FAKE_BAO_STATE']
st = json.load(open(sf))
a = sys.argv[1:]
tok = os.environ.get('BAO_TOKEN', 'caller')
st['calls'].append({{'args': a, 'token': tok}})
def save(): json.dump(st, open(sf, 'w'))
def out(obj): save(); print(json.dumps(obj)); sys.exit(0)
def deny(): save(); sys.stderr.write('permission denied\\n'); sys.exit(2)
if a[:2] == ['token', 'lookup']: out({{'data': {{'policies': st['caller_policies']}}}})
if a[:2] == ['policy', 'write']: st['policy'] = open(a[3]).read(); save(); sys.exit(0)
if a[:2] == ['policy', 'delete']: st.pop('policy', None); save(); sys.exit(0)
if a[:2] == ['token', 'create']:
if st.get('fail_create'): save(); sys.exit(2)
out({{'auth': {{'client_token': '{ONE_SHOT}'}}}})
if a[:2] == ['token', 'revoke']: st['revoked'] = tok; save(); sys.exit(0)
if tok != '{ONE_SHOT}' or 'policy' not in st: deny()
if a[0] == 'read':
rest = a[-1].split('/', 2)[2]
if rest not in st['kv']: save(); sys.stderr.write('No value found\\n'); sys.exit(2)
n = st['kv'][rest]
out({{'data': {{'current_version': n, 'versions': {{str(i + 1): {{}} for i in range(n)}}}}}})
if a[0] == 'delete':
if st.get('fail_delete'): deny()
st['kv'].pop(a[1].split('/', 2)[2], None); save(); sys.exit(0)
save(); sys.exit(9)
''')
class Retire(unittest.TestCase):
def setUp(self):
self.tmp = Path(tempfile.mkdtemp())
(self.tmp / 'bin').mkdir()
(self.tmp / 'bin/bao').write_text(FAKE_BAO)
(self.tmp / 'bin/bao').chmod(0o755)
self.home = self.tmp / '.warden-attended-login' / 'session'
self.home.mkdir(parents=True)
self.state = {'calls': [], 'caller_policies': ['default', 'platform-admin'],
'kv': {'nextcloud': 2, 'nextcloud/backup': 3}}
def run_helper(self, *extra, confirm=CONFIRM):
(self.tmp / 'state.json').write_text(json.dumps(self.state))
receipt = self.tmp / f'r{len(list(self.tmp.glob("r*.json")))}.json'
env = {k: v for k, v in os.environ.items() if k not in ('BAO_TOKEN', 'VAULT_TOKEN')}
env.update(PATH=f'{self.tmp / "bin"}:{env["PATH"]}', HOME=str(self.home),
FAKE_BAO_STATE=str(self.tmp / 'state.json'))
proc = subprocess.run([sys.executable, str(SCRIPT), '--confirm', confirm, '--receipt', str(receipt), *extra],
env=env, capture_output=True, text=True)
self.assertEqual((proc.stdout, proc.stderr), ('', ''))
self.state = json.loads((self.tmp / 'state.json').read_text())
text = receipt.read_text()
self.assertNotIn(ONE_SHOT, text)
return proc.returncode, json.loads(text)
def assert_cleaned_up(self, receipt):
self.assertNotIn('policy', self.state)
self.assertTrue(receipt['policy_deleted'])
self.assertEqual(self.state.get('revoked'), ONE_SHOT)
def test_retires_entry_and_leaves_backup_untouched(self):
code, receipt = self.run_helper()
self.assertEqual((code, receipt['outcome']), (0, 'retired'))
self.assertNotIn('nextcloud', self.state['kv'])
self.assertEqual(self.state['kv']['nextcloud/backup'], 3)
self.assertEqual(receipt['entry_before'], {'current_version': 2, 'versions': 2})
self.assert_cleaned_up(receipt)
self.assertFalse(any(c['args'][0] == 'read' and '/data/' in c['args'][-1] for c in self.state['calls']))
def test_one_shot_policy_is_exact(self):
self.run_helper('--dry-run')
calls = [c for c in self.state['calls'] if c['args'][:2] == ['token', 'create']]
self.assertEqual(len(calls), 1)
args = calls[0]['args']
for flag in ('-orphan', '-policy=operators-retire-nextcloud', '-no-default-policy', '-ttl=60s'):
self.assertIn(flag, args)
def test_dry_run_deletes_nothing_and_cleans_up(self):
code, receipt = self.run_helper('--dry-run')
self.assertEqual((code, receipt['outcome']), (0, 'dry_run_would_retire'))
self.assertIn('nextcloud', self.state['kv'])
self.assert_cleaned_up(receipt)
def test_rerun_reports_already_absent(self):
self.state['kv'].pop('nextcloud')
code, receipt = self.run_helper()
self.assertEqual((code, receipt['outcome']), (0, 'already_absent'))
self.assert_cleaned_up(receipt)
def test_missing_sibling_refuses(self):
self.state['kv'].pop('nextcloud/backup')
code, receipt = self.run_helper()
self.assertEqual((code, receipt['outcome']), (2, 'sibling_absent_refusing_to_guess_layout'))
self.assertIn('nextcloud', self.state['kv'])
self.assert_cleaned_up(receipt)
def test_delete_failure_still_cleans_up(self):
self.state['fail_delete'] = True
code, receipt = self.run_helper()
self.assertEqual((code, receipt['outcome']), (4, 'delete_failed'))
self.assert_cleaned_up(receipt)
def test_refuses_without_confirmation_or_platform_admin(self):
code, receipt = self.run_helper(confirm='RETIRE')
self.assertEqual((code, receipt['outcome']), (2, 'exact_confirmation_required'))
self.state['caller_policies'] = ['operator-custody']
code, receipt = self.run_helper()
self.assertEqual((code, receipt['outcome']), (2, 'attended_platform_admin_required'))
self.assertFalse(any(c['args'][:2] == ['policy', 'write'] for c in self.state['calls']))
if __name__ == '__main__':
unittest.main()