asdsad
All checks were successful
Deploy / deploy-staging (push) Successful in 52s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-06-10 22:44:28 +02:00
parent a8f4a4b36a
commit cd5525ff2e
20 changed files with 1951 additions and 351 deletions

View File

@@ -0,0 +1,161 @@
<?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 = array_values(array_filter(array_map('trim', explode(',', (string) ($provisioning['member_of_csv'] ?? '')))));
$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'] ?? ''));
$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: apple-user';
$lines[] = 'objectClass: extensibleObject';
$lines[] = 'objectClass: inetOrgPerson';
$lines[] = 'objectClass: organizationalPerson';
$lines[] = 'objectClass: person';
$lines[] = 'objectClass: posixAccount';
$lines[] = 'objectClass: sambaIdmapEntry';
$lines[] = 'objectClass: sambaSamAccount';
$lines[] = 'objectClass: shadowAccount';
$lines[] = 'objectClass: top';
$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[] = 'apple-generateduid: ' . strtoupper($appleGeneratedUid);
$lines[] = 'authAuthority: ;basic;';
$lines[] = 'displayName: ' . ($displayName !== '' ? $displayName : $username);
if ($gecos !== '') {
$lines[] = 'gecos: ' . $gecos;
}
if ($givenName !== '') {
$lines[] = 'givenName: ' . $givenName;
}
$lines[] = 'loginShell: ' . $loginShell;
$lines[] = 'mail: ' . $mail;
foreach ($memberOf as $groupDn) {
$lines[] = 'memberOf: ' . $groupDn;
}
$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;
}
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;
}
}