"""Credential modal/read status/attention boundaries; synthetic secrets, no Core run.""" from dataclasses import replace import json import time import uuid from pathlib import Path import pytest from fastapi.testclient import TestClient from aim_webgui.app import create_app from aim_webgui.auth.service import Auth from aim_webgui.config import Settings from aim_webgui.workflows import Workflows from aim_webgui.credentials import presentation, service, wire from aim_webgui.errors import WebError SYNTHETIC = 'fixture-only +%! {{ 7 * 7 }} " snowman \u2603' @pytest.fixture def authz(tmp_path, monkeypatch): settings = Settings(state_dir=tmp_path/'state', public_url='https://aim.example.test', execution_enabled=True, execution_require_approval=False, execution_playbooks=('checkmk_install_agent',), execution_transport_verified=True, credentials_enabled=True).validate() auth = Auth(settings); auth.bootstrap() for name, role in [('operator', 'viewer'), ('another', 'viewer'), ('admin2', 'admin')]: auth.create_user(name, 'Synthetic-fixture-password-2026', role) with auth.store.transaction() as db: db.execute('UPDATE users SET must_change_password=0') users = {row['username']: row['id'] for row in db.execute('SELECT id,username FROM users')} for user in ('operator', 'another'): db.execute('INSERT INTO grants(user_id,customer,playbook) VALUES(?,?,?)', (users[user], 'example', 'checkmk_install_agent')) # New UX must not prepare/execute/decrypt/probe remotely on page load. from aim_webgui.adapters.core_v1 import CoreAdapter def forbidden(*args, **kwargs): raise AssertionError('Credential UX must not call Core') for name in ('preflight', 'readiness', 'reprepare'): monkeypatch.setattr(CoreAdapter, name, forbidden) return auth, users def make_job(authz, owner='operator', *, requirements=None, status='running', phase='waiting', deadline=None, canceled=False, when=None): auth, users = authz now = int(time.time()) ident = uuid.uuid4().hex plan = {'customer': 'example', 'playbook': 'checkmk_install_agent', 'targets': ['lab.example'], 'overrides': {}, 'core_request': {'key_mode': 'customer'}, 'credential_requirements': requirements if requirements is not None else ['vault_password', 'ssh_key_passphrase_or_customer_vault_value']} with auth.store.transaction() as db: db.execute('''INSERT INTO jobs(id,owner_id,plan,mode,status,created_at,scheduled_at, credential_phase,credential_deadline,cancel_requested) VALUES(?,?,?,?,?,?,?,?,?,?)''', (ident, users[owner], json.dumps(plan), 'apply', status, when or now, now, phase, now+240 if deadline is None else deadline, canceled)) return ident def client(authz, user='operator'): auth, users = authz token, session = auth.new_session(users[user]) browser = TestClient(create_app(auth.settings), base_url=auth.settings.public_url, follow_redirects=False) browser.cookies.set(auth.settings.cookie_name, token) browser.headers.update({'Origin': auth.settings.public_url, 'X-CSRF-Token': session['csrf']}) return browser def test_modal_fragment_is_eligible_owner_only_and_full_page_fallback(authz): ident = make_job(authz) with client(authz) as b: r = b.get('/jobs/'+ident) assert r.status_code == 200 assert 'data-credential-open' in r.text and '','') js = (root/'static/js/credentials.js').read_text() assert 'localStorage' not in js and 'sessionStorage' not in js assert 'dialog.showModal()' in js and 'performance.now()' in js assert 'data-credential-secret' in js and 'credentials: \'same-origin\'' in js assert "'Use custom credentials'" not in js def test_real_worker_socket_claim_is_single_use_and_never_persisted(authz, monkeypatch): """Exercise the actual existing worker wait/claim; never call Core execution.""" import threading import aim_webgui.worker as worker_module ident = make_job(authz) auth, users = authz worker = worker_module.Worker(auth.settings, Path('/synthetic-unused-config.toml')) monkeypatch.setattr(worker_module, 'revalidate', lambda *args: None) with auth.store.read() as db: row = dict(db.execute('SELECT * FROM jobs WHERE id=?',(ident,)).fetchone()) result, errors = [], [] def wait(): try: result.append(worker.wait_credentials(row)) except Exception as error: errors.append(error) thread = threading.Thread(target=wait, daemon=True);thread.start() until = time.monotonic()+3 while not (auth.settings.state_dir/'.credential.sock').exists() and time.monotonic()