"use strict"; // End-to-end UI check of chj.users against an ISOLATED Windows test machine (Windows OpenSSH). // Creates and deletes the temporary account "chjui1" and changes sshd_config through CH-J (with restart and // rollback guard), then restores the inherited policy. Credentials only from the environment: // CHJ_USERS_WINDOWS_HOST, CHJ_USERS_WINDOWS_USER, CHJ_USERS_WINDOWS_PASSWORD, CHJ_USERS_WINDOWS_FINGERPRINT // Run: npx electron scripts/smokeUsersUiWindows.js [screenshot-dir] const { app, BrowserWindow, ipcMain, protocol } = require("electron"); const assert = require("node:assert/strict"); const fs = require("node:fs"); const os = require("node:os"); const path = require("node:path"); const { hardenSession } = require("../src/main/bootstrap/createMainWindow"); const { PluginRegistry } = require("../src/main/plugins/pluginRegistry"); const { PluginRuntime } = require("../src/main/plugins/pluginRuntime"); const { SessionManager } = require("../src/main/sessions/sessionManager"); const { PrivilegedExecutor } = require("../src/main/sessions/privilegedExec"); const { UserAdminService, createUserAdminApi } = require("../src/main/users/userAdminService"); const { WindowsUserAdminService } = require("../src/main/users/windowsUserAdminService"); const { WindowsExecutor } = require("../src/main/sessions/windowsPrivilegedExec"); const { detectPlatform } = require("../src/main/sessions/platformDetect"); const { KeyGeneratorService } = require("../src/main/keys/keyGeneratorService"); const env = process.env; const root = fs.mkdtempSync(path.join(os.tmpdir(), "chj-users-ui-win-")); const shots = process.argv.find((arg, index) => index > 1 && !arg.startsWith("-") && !arg.endsWith(".js")) || null; app.setPath("userData", root); protocol.registerSchemesAsPrivileged([{ scheme: "chj-plugin", privileges: { standard: true, secure: true, supportFetchAPI: true } }]); const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const errors = []; let window; const js = (source) => window.webContents.executeJavaScript(source); async function until(source, timeout = 60000) { const started = Date.now(); while (Date.now() - started < timeout) { if (await js(`(() => { try { return (${source}); } catch { return false; } })()`)) return; await delay(100); } throw new Error(`UI condition timed out: ${source}\nstatus: ${await js("document.querySelector('#status').textContent")}`); } async function shot(name) { if (!shots) return; fs.mkdirSync(shots, { recursive: true }); fs.writeFileSync(path.join(shots, `${name}.png`), (await window.webContents.capturePage()).toPNG()); } async function confirm() { await until("document.querySelector('#confirmDialog').open"); await js("document.querySelector('#confirmForm').requestSubmit()"); } async function selectUser(name) { await until(`document.querySelector('button[data-username="${name}"]')`); await js(`document.querySelector('button[data-username="${name}"]').click()`); await until(`document.querySelector('#detailTitle').textContent === "${name}" && !document.querySelector('#detailSubtitle').textContent.includes("Načítám")`); } async function run() { assert.ok(env.CHJ_USERS_WINDOWS_HOST && env.CHJ_USERS_WINDOWS_PASSWORD && env.CHJ_USERS_WINDOWS_FINGERPRINT, "CHJ_USERS_WINDOWS_* not set"); await app.whenReady(); hardenSession(); const fingerprint = Buffer.from(`${env.CHJ_USERS_WINDOWS_FINGERPRINT}=`, "base64").toString("hex"); const profileService = { get: () => ({ id: "win", label: "Windows test", host: env.CHJ_USERS_WINDOWS_HOST, port: 22, username: env.CHJ_USERS_WINDOWS_USER || "codex", authMethod: "password" }), getHostKey: () => fingerprint, trustHostKey() {}, getStoredPassword: () => env.CHJ_USERS_WINDOWS_PASSWORD, markUsed() {} }; const sessionManager = new SessionManager({ profileService, latencyMonitor: { start: () => () => {} } }); await sessionManager.connect({ sessionId: "ui", profileId: "win" }); const windowsService = new WindowsUserAdminService({ sessionManager, windowsExecutor: new WindowsExecutor({ sessionManager }) }); const userAdmin = createUserAdminApi(new UserAdminService({ sessionManager, privilegedExecutor: new PrivilegedExecutor({ sessionManager }), windowsService, detectPlatform })); const registry = new PluginRegistry(root, null, { bundledRoot: path.join(__dirname, "../src/main/firstPartyPlugins") }); const runtime = new PluginRuntime({ BrowserWindow, registry, sessionManager, userAdmin, keyGeneratorService: new KeyGeneratorService(), preload: path.join(__dirname, "../src/preload/pluginPreload.js"), getMainWindow: () => null, isVaultUnlocked: () => true }); runtime.registerIpc(ipcMain); runtime.open("chj.users"); window = runtime.windows.get("chj.users"); window.webContents.on("console-message", (event) => { if (event.level === "error") errors.push(event.message); }); window.setSize(1320, 900); await until("document.querySelectorAll('#usersBody button[data-username]').length > 0"); assert.match(await js("document.querySelector('#privilegeBadge').textContent"), /správce Windows \(zvýšený token\)/); assert.equal(await js("document.querySelector('#elevatedButton').hidden"), true, "no sudo button on Windows"); assert.equal(await js("Array.from(document.querySelectorAll('thead th')).map(n => n.textContent).join('|')"), "Účet|SID (RID)|Stav|Administrators|SSH (CH-J)"); await shot("w01-list"); const inventory = await userAdmin.inventory("ui"); const leftover = inventory.users.find((user) => user.username === "chjui1"); if (leftover) await userAdmin.manage("ui", { action: "delete", sid: leftover.sid, username: "chjui1" }); await js("document.querySelector('#reloadButton').click()"); await until("!document.querySelector('button[data-username=\"chjui1\"]') && document.querySelector('#status').textContent.includes('Načteno')"); const allowGroups = /OpenSSH Users/i.test(inventory.groups.map((group) => group.name).join(",")) ? "S-1-5-32-585" : null; await js("document.querySelector('#createButton').click()"); await until("document.querySelector('#createDialog').open"); assert.equal(await js("document.querySelector('#createShell').closest('label').hidden"), true, "no shell on Windows"); assert.equal(await js("document.querySelector('#createDescriptionLabel').hidden"), false); await js(`document.querySelector('#createUsername').value = 'chjui1'; document.querySelector('#createComment').value = 'CH-J UI'; document.querySelector('#createDescription').value = 'UI smoke'; document.querySelector('#createPassword').value = 'Ui-Win-Pass-123!'; document.querySelector('#createPassword2').value = 'Ui-Win-Pass-123!'; ${allowGroups ? `Array.from(document.querySelector('#createGroups').options).forEach(o => { o.selected = o.value === '${allowGroups}'; });` : ""}`); await shot("w02-create"); await js("document.querySelector('#createForm').requestSubmit()"); await until("document.querySelector('#detailTitle').textContent === 'chjui1' && document.querySelector('#detailSubtitle').textContent.includes('S-1-5-21-')", 90000); await shot("w03-account"); await js("document.querySelector('#tab-groups').click()"); assert.equal(await js("document.querySelector('#tab-groups').textContent"), "Skupiny a správci"); assert.match(await js("document.querySelector('#panel-groups').textContent"), /Administrators[\s\S]*neznamená automaticky zvýšený token/); await shot("w04-groups"); await js("document.querySelector('#tab-ssh').click()"); await until("document.querySelector('#panel-ssh').textContent.includes('Skutečně IPv4')"); await shot("w05-ssh"); await js(`document.querySelector('input[name="policy-password"][value="no"]').click(); document.querySelector('.policy-form').requestSubmit();`); await confirm(); await until("document.querySelector('#status').textContent.includes('potvrdilo nové SSH přihlášení') || document.querySelector('#status').classList.contains('error')", 240000); assert.match(await js("document.querySelector('#status').textContent"), /potvrdilo nové SSH přihlášení/); await until("document.querySelector('button[data-username=\"chjui1\"]').closest('tr').textContent.includes('heslo ne')"); await shot("w06-ssh-applied"); await js(`document.querySelector('input[name="policy-password"][value="inherit"]').click(); document.querySelector('.policy-form').requestSubmit();`); await confirm(); await until("document.querySelector('#status').textContent.includes('potvrdilo nové SSH přihlášení') || document.querySelector('#status').classList.contains('error')", 240000); await js("document.querySelector('#tab-keys').click()"); await shot("w07-keys"); await js("document.querySelector('#tab-diagnostics').click()"); assert.match(await js("document.querySelector('#panel-diagnostics').textContent"), /Výchozí SSH shell/); await shot("w08-diagnostics"); await selectUser("chjui1"); await js("document.querySelector('#tab-account').click()"); await js("Array.from(document.querySelectorAll('#panel-account button')).find(b => b.textContent.includes('Odstranit')).click()"); await until("document.querySelector('#confirmDialog').open"); await js("document.querySelector('#confirmTyped').value = 'chjui1'; document.querySelector('#confirmForm').requestSubmit();"); await until("!document.querySelector('button[data-username=\"chjui1\"]')", 120000); await shot("w09-after-delete"); assert.deepEqual(errors, [], "no renderer errors"); await sessionManager.disconnectAll(); console.log("chj.users Windows UI smoke passed"); } run().then(() => { fs.rmSync(root, { recursive: true, force: true }); app.exit(0); }, (error) => { console.error(error); fs.rmSync(root, { recursive: true, force: true }); app.exit(1); });