187 lines
4.5 KiB
TypeScript
187 lines
4.5 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<boolean> {
|
|
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<void> {
|
|
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',
|
|
});
|