$config */ public function __construct( private readonly array $config, ) { } /** * @param array $request * @param array $provisioning */ public function build(array $request, array $provisioning): string { $username = (string) ($request['username'] ?? ''); $usersDn = (string) ($this->config['ldap']['users_dn'] ?? ''); $uidNumber = trim((string) ($provisioning['uid_number'] ?? '')); $gidNumber = trim((string) ($provisioning['gid_number'] ?? '')); $homeDirectory = trim((string) ($provisioning['home_directory'] ?? '')); $loginShell = trim((string) ($provisioning['login_shell'] ?? ($this->config['ldap']['default_login_shell'] ?? '/bin/sh'))); $memberOf = $this->parseGroupDns((string) ($provisioning['member_of_list'] ?? '')); $displayName = trim((string) ($provisioning['display_name'] ?? $request['display_name'] ?? '')); $givenName = trim((string) ($request['given_name'] ?? '')); $familyName = trim((string) ($request['family_name'] ?? '')); $cn = trim((string) ($provisioning['cn'] ?? ($displayName !== '' ? $displayName : $username))); $gecos = trim((string) ($provisioning['gecos'] ?? ($displayName !== '' ? $displayName : trim($givenName . ' ' . $familyName)))); $mail = trim((string) ($request['email'] ?? '')); $appleGeneratedUid = trim((string) ($provisioning['apple_generateduid'] ?? $this->generateUuidV4())); $sambaSid = trim((string) ($provisioning['samba_sid'] ?? '')); $appleSchemaEnabled = (bool) ($this->config['ldap']['apple_schema_enabled'] ?? false); $shadowExpire = (string) ($this->config['ldap']['shadow_expire'] ?? 1); $shadowFlag = (string) ($this->config['ldap']['shadow_flag'] ?? 0); $shadowInactive = (string) ($this->config['ldap']['shadow_inactive'] ?? 0); $shadowMax = (string) ($this->config['ldap']['shadow_max'] ?? 99999); $shadowMin = (string) ($this->config['ldap']['shadow_min'] ?? 0); $shadowWarning = (string) ($this->config['ldap']['shadow_warning'] ?? 7); $placeholderPassword = $this->generatePlaceholderPassword(); $passwordHash = $this->cryptSha512($placeholderPassword); $warnings = []; if ($uidNumber === '') { $warnings[] = '# WARNUNG: uidNumber fehlt und muss vor dem LDAP-Import gesetzt werden.'; } if ($gidNumber === '') { $warnings[] = '# WARNUNG: gidNumber fehlt und muss vor dem LDAP-Import gesetzt werden.'; } if ($sambaSid === '') { $warnings[] = '# WARNUNG: sambaSID fehlt. Viele NAS-Setups erwarten diesen Wert.'; } if ($memberOf === []) { $memberOf = array_values(array_map('strval', (array) ($this->config['ldap']['default_groups'] ?? []))); } $lines = [ 'version: 1', '', ]; foreach ($warnings as $warning) { $lines[] = $warning; } if ($warnings !== []) { $lines[] = '# Das Passwort unten ist ein zufälliger Platzhalter für den inaktiven Zustand.'; $lines[] = '# Platzhalter-Passwort: ' . $placeholderPassword; $lines[] = ''; } $lines[] = 'dn: uid=' . $username . ',' . $usersDn; $lines[] = 'objectClass: inetOrgPerson'; $lines[] = 'objectClass: organizationalPerson'; $lines[] = 'objectClass: person'; $lines[] = 'objectClass: posixAccount'; $lines[] = 'objectClass: sambaSamAccount'; $lines[] = 'objectClass: shadowAccount'; $lines[] = 'objectClass: top'; if ($appleSchemaEnabled) { $lines[] = 'objectClass: apple-user'; $lines[] = 'objectClass: extensibleObject'; } $lines[] = 'cn: ' . $cn; if ($gidNumber !== '') { $lines[] = 'gidNumber: ' . $gidNumber; } $lines[] = 'homeDirectory: ' . $homeDirectory; if ($sambaSid !== '') { $lines[] = 'sambaSID: ' . $sambaSid; } $lines[] = 'sn: ' . ($familyName !== '' ? $familyName : $username); $lines[] = 'uid: ' . $username; if ($uidNumber !== '') { $lines[] = 'uidNumber: ' . $uidNumber; } $lines[] = 'displayName: ' . ($displayName !== '' ? $displayName : $username); if ($appleSchemaEnabled) { $lines[] = 'apple-generateduid: ' . strtoupper($appleGeneratedUid); $lines[] = 'authAuthority: ;basic;'; } if ($gecos !== '') { $lines[] = 'gecos: ' . $gecos; } if ($givenName !== '') { $lines[] = 'givenName: ' . $givenName; } $lines[] = 'loginShell: ' . $loginShell; $lines[] = 'mail: ' . $mail; foreach ($memberOf as $groupDn) { $lines[] = 'memberOf: ' . $groupDn; } $lines[] = 'pwdLastSet: 0'; $lines[] = 'sambaAcctFlags: [U ]'; $lines[] = 'sambaLMPassword: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; $lines[] = 'sambaPasswordHistory: 0000000000000000000000000000000000000000000000000000000000000000'; $lines[] = 'sambaPwdLastSet: 0'; $lines[] = 'shadowExpire: ' . $shadowExpire; $lines[] = 'shadowFlag: ' . $shadowFlag; $lines[] = 'shadowInactive: ' . $shadowInactive; $lines[] = 'shadowLastChange: 1'; $lines[] = 'shadowMax: ' . $shadowMax; $lines[] = 'shadowMin: ' . $shadowMin; $lines[] = 'shadowWarning: ' . $shadowWarning; $lines[] = 'userPassword: ' . $passwordHash; return implode(PHP_EOL, $lines) . PHP_EOL; } /** * @return array */ private function parseGroupDns(string $value): array { $lines = preg_split('/\r\n|\r|\n/', $value) ?: []; return array_values(array_filter(array_map('trim', $lines))); } private function generateUuidV4(): string { $bytes = random_bytes(16); $bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40); $bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80); return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($bytes), 4)); } private function generatePlaceholderPassword(): string { return bin2hex(random_bytes(12)); } private function cryptSha512(string $password): string { $salt = '$6$' . bin2hex(random_bytes(8)) . '$'; $hash = crypt($password, $salt); return str_starts_with($hash, '$6$') ? '{CRYPT}' . $hash : $hash; } }