Connect P04 audited recovery to fresh-MFA platform browser flow
All checks were successful
Authentication acceptance / acceptance (push) Successful in 1m14s
Authentication acceptance / provider-contract (push) Successful in 13s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
This commit is contained in:
tegwick 2026-09-13 21:05:33 +02:00
parent 317d897b85
commit cb51584f58
5 changed files with 256 additions and 2 deletions

View file

@ -8,6 +8,11 @@ def fingerprint(snapshot):
return hashlib.sha256(json.dumps(snapshot,sort_keys=True,separators=(',',':')).encode()).hexdigest()
def recover(store, request):
from contextlib import nullcontext
with store.operation_lock() if hasattr(store,"operation_lock") else nullcontext():
return _recover(store,request)
def _recover(store, request):
for key in ('user','serial','realm','actor','reference'):
value=request.get(key)
if not isinstance(value,str) or not value or len(value)>200 or not re.fullmatch(r'[A-Za-z0-9_.@:/-]+',value):raise RecoveryError('invalid_request')
@ -42,6 +47,25 @@ def recover(store, request):
class ProviderStore:
def __init__(self, app, realm='coulomb'):
self.app=app;self.realm=realm
def operation_lock(self):
from contextlib import contextmanager
@contextmanager
def locked():
from privacyidea.models import db
from sqlalchemy import text
if db.engine.dialect.name != 'postgresql':
yield;return
with db.engine.connect() as connection:
connection.execute(text('SELECT pg_advisory_lock(4912040036)'))
try:yield
finally:connection.execute(text('SELECT pg_advisory_unlock(4912040036)'))
return locked()
def serials(self,user):
from privacyidea.lib.token import get_tokens
from privacyidea.lib.user import User
values=get_tokens(user=User(user,self.realm))
if len(values)>20:raise RecoveryError('too_many_factors')
return [token.token.serial for token in values]
def _token(self,user,serial):
from privacyidea.lib.token import get_tokens
from privacyidea.lib.user import User