security: preserve realm configuration and remove shell credential evaluation
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a1346b-72a4-7d40-b8bd-8b8ca346cfa2
This commit is contained in:
parent
96feef46ab
commit
30f0d27840
16 changed files with 700 additions and 430 deletions
84
tests/realm_bootstrap_native_fixture.py
Normal file
84
tests/realm_bootstrap_native_fixture.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
"""Disposable privacyIDEA 3.12.2 HTTP and database proof, no production config."""
|
||||
import contextlib
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
|
||||
|
||||
def run():
|
||||
for name in list(os.environ):
|
||||
if name.startswith('PRIVACYIDEA_'):del os.environ[name]
|
||||
logging.disable(logging.CRITICAL)
|
||||
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'sso-mfa/k8s/privacyidea'))
|
||||
import realm_bootstrap as r
|
||||
from privacyidea.app import create_app
|
||||
from privacyidea.models import db
|
||||
from privacyidea.lib.auth import create_db_admin
|
||||
from privacyidea.lib.policy import PolicyClass,set_policy
|
||||
from privacyidea.lib.resolver import get_resolver_config,save_resolver
|
||||
from privacyidea.lib.realm import set_realm,set_default_realm
|
||||
from werkzeug.serving import make_server
|
||||
with tempfile.TemporaryDirectory(prefix='realm-native-') as directory:
|
||||
root=Path(directory);(root/'enckey').write_bytes(os.urandom(96))
|
||||
cfg=root/'fixture.cfg'
|
||||
config=dict(SQLALCHEMY_DATABASE_URI='sqlite:///'+str(root/'db.sqlite'),SECRET_KEY='fixture-secret-key',
|
||||
PI_PEPPER='fixture-pepper',PI_ENCFILE=str(root/'enckey'),PI_NO_RESPONSE_SIGN=True,
|
||||
PI_AUDIT_NO_SIGN=True,PI_LOGFILE=str(root/'log'),PI_TRUSTED_JWT=[],
|
||||
PI_AUDIT_SQL_URI='sqlite:///'+str(root/'audit.sqlite'))
|
||||
cfg.write_text('\n'.join(k+'='+repr(v) for k,v in config.items()))
|
||||
app=create_app(config_name='testing',config_file=str(cfg),silent=True)
|
||||
with app.app_context():
|
||||
assert str(db.engine.url)==config['SQLALCHEMY_DATABASE_URI'];db.create_all()
|
||||
from privacyidea.lib.audit import getAudit
|
||||
from privacyidea.lib.auditmodules.sqlaudit import LogEntry
|
||||
LogEntry.__table__.create(getAudit(app.config).engine,checkfirst=True)
|
||||
create_db_admin('pi-admin',password='fixture-admin-password')
|
||||
policies=PolicyClass().policies
|
||||
server=make_server('127.0.0.1',0,app);thread=threading.Thread(target=server.serve_forever,daemon=True);thread.start()
|
||||
try:
|
||||
client=r.Client(f'http://127.0.0.1:{server.server_port}','fixture-admin-password')
|
||||
preview=r.repair(client,lambda:None)
|
||||
assert preview['configuration_writes']==0
|
||||
result=r.repair(client,lambda:'fixture-bind-password',apply=True)
|
||||
assert result['actions']==['create-resolver','create-realm','set-first-realm-default'],result
|
||||
with app.app_context():
|
||||
assert get_resolver_config(r.RESOLVER)['BINDPW']=='fixture-bind-password'
|
||||
assert PolicyClass().policies==policies
|
||||
set_policy(name='totp-self-enrollment',scope='user',action='enrollTOTP',realm='coulomb')
|
||||
set_policy(name='keycape-pending-enrollment-cancel',scope='user',action='delete',realm='coulomb',conditions=[('token','rollout_state','equals','verify',True)])
|
||||
set_policy(name='mfa-passthru-phase1',scope='authentication',action='passthru',realm='coulomb',active=False)
|
||||
policies=PolicyClass().policies
|
||||
save_resolver({'resolver':r.RESOLVER,'type':'ldapresolver','TIMEOUT':'','SIZELIMIT':'777','TLS_VERIFY':True,'CACHE_TIMEOUT':'333'})
|
||||
set_realm('other',[]);set_default_realm('other')
|
||||
before=client.snapshot()
|
||||
result=r.repair(client,lambda:(_ for _ in ()).throw(AssertionError()),apply=True)
|
||||
assert result['actions']==['fill-missing-tuning']
|
||||
after=client.snapshot()
|
||||
assert after['realms']==before['realms']
|
||||
assert after['resolver']['data']['TIMEOUT']=='5'
|
||||
for key,value in before['resolver']['data'].items():
|
||||
if key!='TIMEOUT':assert after['resolver']['data'][key]==value
|
||||
with app.app_context():
|
||||
assert get_resolver_config(r.RESOLVER)['BINDPW']=='fixture-bind-password'
|
||||
assert PolicyClass().policies==policies
|
||||
assert r.repair(client,lambda:None,apply=True)['configuration_writes']==0
|
||||
return {'native_create_and_readback':True,'native_minimal_update_preserves_omitted_fields':True,
|
||||
'native_bind_password_preserved':True,'native_hardened_policies_preserved':True,
|
||||
'native_existing_default_preserved':True,'native_repeat_is_noop':True}
|
||||
finally:server.shutdown();thread.join();server.server_close()
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
try:
|
||||
with contextlib.redirect_stdout(io.StringIO()),contextlib.redirect_stderr(io.StringIO()):result=run()
|
||||
print(json.dumps(result,sort_keys=True))
|
||||
except Exception as exc:
|
||||
import traceback
|
||||
frame=traceback.extract_tb(exc.__traceback__)[-1]
|
||||
print(json.dumps({'result':'failed','type':type(exc).__name__,'file':Path(frame.filename).name,'line':frame.lineno}))
|
||||
raise SystemExit(1)
|
||||
Loading…
Add table
Add a link
Reference in a new issue