184 lines
6.6 KiB
PHP
184 lines
6.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App;
|
|
|
|
final class RegistrationLdifBuilder
|
|
{
|
|
/**
|
|
* @param array<string, mixed> $config
|
|
*/
|
|
public function __construct(
|
|
private readonly array $config,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $request
|
|
* @param array<string, mixed> $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<int, string>
|
|
*/
|
|
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;
|
|
}
|
|
}
|