"""Provider enrollment hook: password-only self-service cannot replace an active factor. Loaded explicitly by provider configuration, so a missing module fails startup. Pending setup can be regenerated/confirmed; active-factor replacement goes through fresh-MFA audited recovery. Admin actions retain their existing provider policies. """ def check(request, action): from flask import g from privacyidea.lib.error import PolicyError from privacyidea.lib.user import User from privacyidea.lib.token import get_tokens principal = g.logged_in_user if principal.get('role') != 'user': return True user = User(principal.get('username', ''), principal.get('realm', '')) if user.is_empty() or action != 'init': raise PolicyError('Use the account recovery process to replace an active authenticator.') tokens = get_tokens(user=user, active=True) if any(token.token.rollout_state not in {'verify', 'clientwait', 'pending'} for token in tokens): raise PolicyError('An active authenticator already exists. Use the account recovery process to replace it.') serial = request.all_data.get('serial') if serial: matches = [token for token in tokens if token.token.serial == serial] if len(matches) != 1 or matches[0].token.rollout_state != 'verify': raise PolicyError('Only your unfinished authenticator setup can be confirmed or regenerated.') return True