generated from jric11/baseProject
35 lines
1.7 KiB
Python
35 lines
1.7 KiB
Python
import json,subprocess
|
|
from application_harness import Application,ROOT,PHP
|
|
app=Application();workers=[]
|
|
def race(operation,seed):
|
|
group=[]
|
|
for state in seed['states']:
|
|
p=subprocess.Popen(PHP+['tests/mfa_worker.php',operation],cwd=ROOT,env=app.env,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True)
|
|
workers.append(p);group.append(p)
|
|
p.stdin.write(json.dumps({'state':state,'recovery':seed['recovery']})+'\n');p.stdin.flush()
|
|
for p in group:assert p.stdout.readline().strip()=='READY'
|
|
for p in group:p.stdin.write('go\n');p.stdin.flush()
|
|
result=[]
|
|
for p in group:
|
|
out,err=p.communicate(timeout=30)
|
|
assert p.returncode==0,err
|
|
result.append(json.loads(out))
|
|
return result
|
|
try:
|
|
seed=json.loads(subprocess.check_output(PHP+['tests/mfa_worker.php','seed'],cwd=ROOT,env=app.env,text=True))
|
|
before=app.fixture('mfaState','admin@example.test')
|
|
result=race('verify',seed)
|
|
after=app.fixture('mfaState','admin@example.test')
|
|
assert sorted(r['ok'] for r in result)==[False,True] and len(after['recovery'])==len(before['recovery'])-1,result
|
|
print('PASS: Concurrent recovery attempts consume a single-use code exactly once.')
|
|
result=race('replace',seed)
|
|
after=app.fixture('mfaState','admin@example.test')
|
|
assert sorted(r['ok'] for r in result)==[False,True],result
|
|
assert len(after['recovery'])==10 and after['factor']['secret_cipher']!=before['factor']['secret_cipher']
|
|
print('PASS: Competing authorized replacements commit exactly one complete factor swap.')
|
|
print('2 MFA concurrency checks passed.')
|
|
finally:
|
|
for p in workers:
|
|
if p.poll() is None:p.kill();p.wait()
|
|
app.close()
|