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

This commit is contained in:
2026-06-15 21:36:36 +02:00
parent 94ea3c58af
commit 5408a2bb89
5 changed files with 118 additions and 15 deletions

View File

@@ -190,7 +190,18 @@ body {
} }
.login-password-row input { .login-password-row input {
padding-right: 56px; padding-right: 64px;
}
.login-password-row input::-ms-reveal,
.login-password-row input::-ms-clear {
display: none;
}
.login-password-row input::-webkit-credentials-auto-fill-button,
.login-password-row input::-webkit-textfield-decoration-container {
visibility: hidden;
pointer-events: none;
} }
.login-password-toggle { .login-password-toggle {
@@ -201,13 +212,43 @@ body {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 36px; width: 34px;
height: 36px; height: 34px;
border: 0; border: 1px solid rgba(15, 23, 42, 0.16);
border-radius: 999px; border-radius: 999px;
background: transparent; background: rgba(15, 23, 42, 0.78);
color: rgba(15, 23, 42, 0.56); color: rgba(255, 255, 255, 0.96);
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.18);
cursor: pointer; cursor: pointer;
transition: background-color 160ms ease, transform 160ms ease, box-shadow 160ms ease;
}
.login-password-toggle:hover {
background: rgba(15, 23, 42, 0.92);
transform: translateY(-50%) scale(1.03);
}
.login-password-toggle:focus-visible {
outline: 2px solid rgba(14, 165, 233, 0.55);
outline-offset: 2px;
}
.login-password-icon {
width: 17px;
height: 17px;
display: block;
}
.login-password-icon-hide {
display: none;
}
.login-password-toggle.is-visible .login-password-icon-show {
display: none;
}
.login-password-toggle.is-visible .login-password-icon-hide {
display: block;
} }
.login-field textarea { .login-field textarea {

View File

@@ -13,6 +13,7 @@ document.addEventListener('DOMContentLoaded', () => {
const isHidden = input.type === 'password'; const isHidden = input.type === 'password';
input.type = isHidden ? 'text' : 'password'; input.type = isHidden ? 'text' : 'password';
toggle.setAttribute('aria-label', isHidden ? 'Passwort verbergen' : 'Passwort anzeigen'); toggle.setAttribute('aria-label', isHidden ? 'Passwort verbergen' : 'Passwort anzeigen');
toggle.classList.toggle('is-visible', isHidden);
}); });
}); });
}); });

View File

@@ -143,9 +143,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<label class="login-field"> <label class="login-field">
<span>Passwort</span> <span>Passwort</span>
<div class="login-password-row"> <div class="login-password-row">
<input type="password" name="password" value="" required data-password-field> <input type="password" name="password" value="<?= htmlspecialchars($form['password'], ENT_QUOTES) ?>" required data-password-field>
<button class="login-password-toggle" type="button" data-password-toggle aria-label="Passwort anzeigen"> <button class="login-password-toggle" type="button" data-password-toggle aria-label="Passwort anzeigen">
<span aria-hidden="true">◉</span> <svg class="login-password-icon login-password-icon-show" viewBox="0 0 24 24" aria-hidden="true">
<path d="M2 12s3.6-6 10-6 10 6 10 6-3.6 6-10 6-10-6-10-6Z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="12" cy="12" r="3" fill="none" stroke="currentColor" stroke-width="1.8"/>
</svg>
<svg class="login-password-icon login-password-icon-hide" viewBox="0 0 24 24" aria-hidden="true">
<path d="M3 3l18 18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
<path d="M10.6 5.2A11 11 0 0 1 12 5c6.4 0 10 7 10 7a18.8 18.8 0 0 1-3.1 3.8M6.7 6.7C4 8.5 2 12 2 12a18.4 18.4 0 0 0 10 6 10.8 10.8 0 0 0 4.1-.8" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.9 9.9A3 3 0 0 0 14.1 14.1" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</svg>
</button> </button>
</div> </div>
</label> </label>
@@ -153,9 +161,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<label class="login-field"> <label class="login-field">
<span>Passwort wiederholen</span> <span>Passwort wiederholen</span>
<div class="login-password-row"> <div class="login-password-row">
<input type="password" name="password_confirm" value="" required data-password-field> <input type="password" name="password_confirm" value="<?= htmlspecialchars($form['password_confirm'], ENT_QUOTES) ?>" required data-password-field>
<button class="login-password-toggle" type="button" data-password-toggle aria-label="Passwort anzeigen"> <button class="login-password-toggle" type="button" data-password-toggle aria-label="Passwort anzeigen">
<span aria-hidden="true">◉</span> <svg class="login-password-icon login-password-icon-show" viewBox="0 0 24 24" aria-hidden="true">
<path d="M2 12s3.6-6 10-6 10 6 10 6-3.6 6-10 6-10-6-10-6Z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="12" cy="12" r="3" fill="none" stroke="currentColor" stroke-width="1.8"/>
</svg>
<svg class="login-password-icon login-password-icon-hide" viewBox="0 0 24 24" aria-hidden="true">
<path d="M3 3l18 18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
<path d="M10.6 5.2A11 11 0 0 1 12 5c6.4 0 10 7 10 7a18.8 18.8 0 0 1-3.1 3.8M6.7 6.7C4 8.5 2 12 2 12a18.4 18.4 0 0 0 10 6 10.8 10.8 0 0 0 4.1-.8" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.9 9.9A3 3 0 0 0 14.1 14.1" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</svg>
</button> </button>
</div> </div>
</label> </label>

View File

@@ -13,12 +13,21 @@ $token = trim((string) ($_GET['token'] ?? $_POST['token'] ?? ''));
$record = $service->validate($token); $record = $service->validate($token);
$message = null; $message = null;
$error = null; $error = null;
$form = [
'password' => '',
'password_confirm' => '',
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$form = [
'password' => (string) ($_POST['password'] ?? ''),
'password_confirm' => (string) ($_POST['password_confirm'] ?? ''),
];
$result = $service->complete( $result = $service->complete(
$token, $token,
(string) ($_POST['password'] ?? ''), $form['password'],
(string) ($_POST['password_confirm'] ?? '') $form['password_confirm']
); );
if ($result['success'] ?? false) { if ($result['success'] ?? false) {
@@ -58,12 +67,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<input type="hidden" name="token" value="<?= htmlspecialchars($token, ENT_QUOTES) ?>"> <input type="hidden" name="token" value="<?= htmlspecialchars($token, ENT_QUOTES) ?>">
<label class="login-field"> <label class="login-field">
<span>Neues Passwort</span> <span>Neues Passwort</span>
<input type="password" name="password" required> <input type="password" name="password" value="<?= htmlspecialchars($form['password'], ENT_QUOTES) ?>" required>
</label> </label>
<label class="login-field"> <label class="login-field">
<span>Neues Passwort wiederholen</span> <span>Neues Passwort wiederholen</span>
<input type="password" name="password_confirm" required> <input type="password" name="password_confirm" value="<?= htmlspecialchars($form['password_confirm'], ENT_QUOTES) ?>" required>
</label> </label>
<div class="login-actions"> <div class="login-actions">

View File

@@ -163,14 +163,16 @@ final class RegistrationService
$request['provisioning']['status'] = 'ldap_created_inactive'; $request['provisioning']['status'] = 'ldap_created_inactive';
$request['provisioning']['ldif_path'] = $this->writeLdifDraft($request['id'], $username, $this->ldifBuilder->build($request, $provisioning)); $request['provisioning']['ldif_path'] = $this->writeLdifDraft($request['id'], $username, $this->ldifBuilder->build($request, $provisioning));
} catch (\Throwable $exception) { } catch (\Throwable $exception) {
$errorReference = $this->logProvisioningFailure($request, $provisioning, $exception);
$request['password_secret'] = $passwordSecret; $request['password_secret'] = $passwordSecret;
$request['status'] = 'provisioning_failed'; $request['status'] = 'provisioning_failed';
$request['provisioning']['status'] = 'provisioning_failed'; $request['provisioning']['status'] = 'provisioning_failed';
$request['provisioning']['error'] = $exception->getMessage(); $request['provisioning']['error'] = $exception->getMessage();
$request['provisioning']['error_reference'] = $errorReference;
return [ return [
'success' => false, 'success' => false,
'errors' => ['Die Registrierung konnte im Moment nicht gespeichert werden. Bitte versuche es später erneut.'], 'errors' => ['Die Registrierung konnte im Moment nicht gespeichert werden. Bitte versuche es später erneut. Referenz: ' . $errorReference],
'request' => $this->store->create($request), 'request' => $this->store->create($request),
]; ];
} }
@@ -379,4 +381,38 @@ final class RegistrationService
return $this->projectRoot . '/' . ltrim($path, '/'); return $this->projectRoot . '/' . ltrim($path, '/');
} }
/**
* @param array<string, mixed> $request
* @param array<string, mixed> $provisioning
*/
private function logProvisioningFailure(array $request, array $provisioning, \Throwable $exception): string
{
$reference = 'REG-' . gmdate('YmdHis') . '-' . substr(bin2hex(random_bytes(4)), 0, 8);
$path = $this->resolveProjectPath('data/registration-provisioning-errors.log');
$directory = dirname($path);
if (!is_dir($directory)) {
mkdir($directory, 0775, true);
}
$payload = [
'reference' => $reference,
'created_at' => gmdate('c'),
'message' => $exception->getMessage(),
'username' => (string) ($request['username'] ?? ''),
'email' => (string) ($request['email'] ?? ''),
'display_name' => (string) ($request['display_name'] ?? ''),
'origin' => $request['origin'] ?? [],
'provisioning' => $provisioning,
];
file_put_contents(
$path,
json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . PHP_EOL,
FILE_APPEND | LOCK_EX
);
return $reference;
}
} }