Verify factor identity again after recovery mutation
All checks were successful
Authentication acceptance / acceptance (push) Successful in 1m6s
Authentication acceptance / provider-contract (push) Successful in 12s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
This commit is contained in:
tegwick 2026-09-13 17:30:23 +02:00
parent 3b7df9047e
commit 4d893ed3c2
3 changed files with 10 additions and 2 deletions

View file

@ -35,7 +35,7 @@ def recover(store, request):
store.record(base,expected,False) # An audit outage prevents mutation.
store.disable(request['user'],request['serial'],expected)
current=store.snapshot(request['user'],request['serial'])
if current is None or current['active']:raise RecoveryError('readback_failed')
if current is None or current['active'] or fingerprint(dict(current,active=True))!=expected:raise RecoveryError('readback_failed')
store.record(base,expected,True)
return dict(base,status='recovered',changes_applied=True,replayed=False)

View file

@ -45,3 +45,11 @@ class RecoveryTests(unittest.TestCase):
def test_replaced_inactive_factor_cannot_replay_old_recovery(self):
r=self.approval();recover(self.store,r);self.store.state['id']=2
with self.assertRaises(RecoveryError):recover(self.store,r)
def test_replacement_after_disable_cannot_report_success(self):
original=self.store.disable
def replace_after_disable(*args):
original(*args);self.store.state['id']=2
self.store.disable=replace_after_disable
with self.assertRaisesRegex(RecoveryError,'readback_failed'):recover(self.store,self.approval())
self.assertFalse(self.store.rows['support-1']['complete'])

View file

@ -29,7 +29,7 @@ state_hub_task_id: "f3287544-8412-5b1f-9d16-9f7b9e895ceb"
requires an operator verification attestation and matching metadata version,
disables one factor with a row lock, requires pre-mutation audit durability,
reads back state, records completion and reconciles retries. Reject changed or
replaced factors and reference conflicts. Eight isolated unit tests pass.
replaced factors and reference conflicts. Nine isolated unit tests pass, including replacement between disable and readback.
The installed-provider Job provider-recovery-contract-02 passed real database
and audit persistence, disable/readback and replay assertions in an isolated
fixture. Acceptance CI includes both suites. No real user's factor was changed.