import { execSync } from 'child_process'; /** * LDAP fixture for E2E tests * Manages OpenLDAP Docker container lifecycle */ export interface LdapConfig { container_name: string; port: number; domain: string; base_dn: string; admin_password: string; config_password: string; } /** * Start OpenLDAP container for a test workflow */ export async function startLdapServer(config: LdapConfig): Promise { const { container_name, port, domain, base_dn, admin_password, config_password, } = config; try { // Start container using docker-compose execSync( `docker run -d --name ${container_name} -p ${port}:389 ` + `-e LDAP_ORGANISATION="aInventory Test" ` + `-e LDAP_DOMAIN="${domain}" ` + `-e LDAP_BASE_DN="${base_dn}" ` + `-e LDAP_ADMIN_PASSWORD="${admin_password}" ` + `-e LDAP_CONFIG_PASSWORD="${config_password}" ` + `osixia/openldap:1.5.0`, { stdio: 'ignore' } ); // Wait for server to be ready await waitForLdapReady(config); } catch (error) { console.warn(`LDAP startup warning: ${error}`); } } /** * Stop and remove OpenLDAP container */ export async function stopLdapServer(containerName: string): Promise { try { execSync(`docker stop ${containerName}`, { stdio: 'ignore' }); execSync(`docker rm ${containerName}`, { stdio: 'ignore' }); } catch (error) { console.warn(`LDAP cleanup warning: ${error}`); } } /** * Wait for LDAP server to be ready */ export async function waitForLdapReady(config: LdapConfig, maxRetries: number = 30): Promise { const { base_dn, admin_password } = config; let retries = 0; while (retries < maxRetries) { try { // Test LDAP connectivity using ldapwhoami execSync( `docker exec ${config.container_name} ldapwhoami -H ldap://localhost:389 ` + `-D "cn=admin,${base_dn}" -w "${admin_password}"`, { stdio: 'ignore' } ); return; // Server is ready } catch { retries++; await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second } } throw new Error(`LDAP server did not become ready after ${maxRetries} seconds`); } /** * Create LDAP user for testing */ export async function createLdapUser( config: LdapConfig, username: string, password: string, email: string ): Promise { const { container_name, base_dn, admin_password } = config; const ldif = ` dn: uid=${username},ou=users,${base_dn} objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount uid: ${username} cn: ${username} sn: ${username} userPassword: ${password} mail: ${email} uidNumber: 1001 gidNumber: 1001 homeDirectory: /home/${username} `; try { execSync( `docker exec -i ${container_name} ldapadd -x -D "cn=admin,${base_dn}" -w "${admin_password}"`, { input: ldif, stdio: ['pipe', 'ignore', 'ignore'], } ); } catch (error) { console.warn(`LDAP user creation warning: ${error}`); } } /** * Verify LDAP user exists and password is correct */ export async function verifyLdapUser( config: LdapConfig, username: string, password: string ): Promise { try { const { container_name, base_dn } = config; execSync( `docker exec ${container_name} ldapwhoami -H ldap://localhost:389 ` + `-D "uid=${username},ou=users,${base_dn}" -w "${password}"`, { stdio: 'ignore' } ); return true; } catch { return false; } } /** * Seed LDAP directory with test users */ export async function seedLdapUsers( config: LdapConfig, users: Array<{ username: string; password: string; email: string }> ): Promise { for (const user of users) { await createLdapUser(config, user.username, user.password, user.email); } } /** * Get LDAP container status */ export async function getLdapStatus(containerName: string): Promise<'running' | 'stopped' | 'not_found'> { try { const result = execSync(`docker ps --filter name=${containerName} --format "{{.State}}"`, { encoding: 'utf-8', }).trim(); if (result === 'running') return 'running'; if (result === '') return 'not_found'; return 'stopped'; } catch { return 'not_found'; } } /** * Default LDAP configuration */ export const DEFAULT_LDAP_CONFIG = (workflowId: string, port: number): LdapConfig => ({ container_name: `ldap-test-${workflowId}`, port, domain: 'ainventory.local', base_dn: 'dc=ainventory,dc=local', admin_password: 'admin', config_password: 'config', });