"""Run the in-pod adapter against actual privacyIDEA 3.12.2 models on SQLite. Install that exact package in a disposable environment. No native cluster or production config is used; this does not replace deployed-image acceptance. """ import importlib.metadata import importlib.util import io from pathlib import Path import sys import pytest pytest.importorskip('privacyidea') if importlib.metadata.version('privacyIDEA') != '3.12.2': pytest.skip('adapter verification requires privacyIDEA 3.12.2', allow_module_level=True) from flask import Flask from privacyidea.models import Admin, Policy, db from privacyidea.lib.crypto import hash_with_pepper from privacyidea.lib.auth import verify_db_admin import privacyidea.cli ROOT=Path(__file__).resolve().parents[1] PASSWORD='SyntheticAdminCredential12345678901234567890' @pytest.fixture def native(tmp_path,monkeypatch): spec=importlib.util.spec_from_file_location('admin_remote_native',ROOT/'sso-mfa/k8s/privacyidea/bootstrap-admin-remote.py') remote=importlib.util.module_from_spec(spec);spec.loader.exec_module(remote) app=Flask('disposable-bootstrap') app.config.update(SQLALCHEMY_DATABASE_URI='sqlite:///'+str(tmp_path/'db.sqlite'), SQLALCHEMY_TRACK_MODIFICATIONS=False,PI_PEPPER='synthetic-pepper',SECRET_KEY='synthetic-key') db.init_app(app) with app.app_context(): db.create_all() monkeypatch.setattr(privacyidea.cli,'create_silent_app',lambda:app) def invoke(password=PASSWORD): import json monkeypatch.setattr(sys,'stdin',io.TextIOWrapper(io.BytesIO(json.dumps({'password':password}).encode()))) return remote.main() import logging previous_disable = logging.root.manager.disable try: yield app,remote,invoke finally: logging.disable(previous_disable) def test_real_model_creation_and_repeat_preserve_password_hash(native): app,_,invoke=native assert invoke()=={'result':'created','mfa_verified':False} with app.app_context(): before=Admin.query.filter_by(username='pi-admin').one().password assert verify_db_admin('pi-admin',PASSWORD) assert [a.username for a in Admin.query.all()]==['pi-admin'] assert Policy.query.one().adminuser=='pi-admin' assert invoke()=={'result':'verified','mfa_verified':False} with app.app_context(): assert Admin.query.one().password==before def test_real_model_mismatch_never_resets_password(native): app,remote,invoke=native invoke() with app.app_context(): before=Admin.query.one().password with pytest.raises(remote.Refused):invoke('DifferentSyntheticPassword1234567890123456') with app.app_context(): assert Admin.query.one().password==before def test_real_legacy_broad_policy_refused_before_account_creation(native): app,remote,invoke=native with app.app_context(): db.session.add(Policy(name='pi-admin-all-rights',scope='admin',action='*'));db.session.commit() with pytest.raises(remote.Refused):invoke() with app.app_context(): assert Admin.query.count()==0 assert Policy.query.one().adminuser=='' def test_real_other_admin_requires_explicit_policy_migration(native): app,remote,invoke=native with app.app_context(): db.session.add(Admin(username='other-admin',password=hash_with_pepper(PASSWORD)));db.session.commit() with pytest.raises(remote.Refused):invoke() with app.app_context(): assert [a.username for a in Admin.query.all()]==['other-admin'] assert Policy.query.count()==0 def test_real_failed_policy_insert_rolls_back_account(native,monkeypatch): app,remote,invoke=native original=db.session.add def fail_policy(obj,*args,**kwargs): if isinstance(obj,Policy):raise RuntimeError('synthetic-failure') return original(obj,*args,**kwargs) monkeypatch.setattr(db.session,'add',fail_policy) with pytest.raises(remote.Refused):invoke() with app.app_context(): assert Admin.query.count()==0 assert Policy.query.count()==0 def test_real_concurrent_account_conflict_never_updates_existing_row(native,monkeypatch): import privacyidea.lib.auth app,remote,invoke=native with app.app_context(): db.session.add(Admin(username='pi-admin',password=hash_with_pepper('ExistingSyntheticPassword1234567890123456'))) db.session.commit();before=Admin.query.one().password # Simulate an account appearing after the initial absence observation. monkeypatch.setattr(privacyidea.lib.auth,'list_db_admin',lambda:[]) with pytest.raises(remote.Refused):invoke() with app.app_context(): assert Admin.query.one().password==before assert Policy.query.count()==0 def test_real_external_admin_realm_requires_owner_profile(native): app,remote,invoke=native app.config['SUPERUSER_REALM']=['directory-admins'] with pytest.raises(remote.Refused):invoke() with app.app_context(): assert Admin.query.count()==0 assert Policy.query.count()==0