Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Exception\Authentication;
|
||||
|
||||
use App\Framework\Exception\ErrorCode;
|
||||
use App\Framework\Exception\ExceptionContext;
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
|
||||
/**
|
||||
* Ausnahme für gesperrte Benutzerkonten
|
||||
*
|
||||
* Verwendet OWASP-konforme Nachrichten für Account-Sperrungen
|
||||
*/
|
||||
final class AccountLockedException extends FrameworkException
|
||||
{
|
||||
/**
|
||||
* @param string $identifier Benutzer-Identifikator
|
||||
* @param int $failedAttempts Anzahl der Fehlversuche die zur Sperrung führten
|
||||
* @param int $lockDurationMinutes Dauer der Sperrung in Minuten
|
||||
* @param \Throwable|null $previous Vorherige Ausnahme
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $identifier,
|
||||
public readonly int $failedAttempts,
|
||||
public readonly int $lockDurationMinutes = 15,
|
||||
?\Throwable $previous = null
|
||||
) {
|
||||
// OWASP-konforme Nachricht mit Platzhaltern
|
||||
$message = "User {$this->identifier} account locked after {$this->failedAttempts} failed attempts";
|
||||
|
||||
$unlockTime = new \DateTimeImmutable("+{$this->lockDurationMinutes} minutes");
|
||||
|
||||
$context = ExceptionContext::forOperation('authentication.account_lock', 'Auth')
|
||||
->withData([
|
||||
'user_identifier' => $this->identifier,
|
||||
'failed_attempts' => $this->failedAttempts,
|
||||
'lock_duration_minutes' => $this->lockDurationMinutes,
|
||||
'unlock_time' => $unlockTime->format('Y-m-d H:i:s'),
|
||||
'event_identifier' => "authn_account_locked:{$this->identifier},{$this->failedAttempts}",
|
||||
'category' => 'authentication',
|
||||
'requires_alert' => true, // Account-Sperrungen sind immer alert-würdig
|
||||
])
|
||||
->withMetadata([
|
||||
'security_event' => true,
|
||||
'owasp_compliant' => true,
|
||||
'log_level' => 'WARN',
|
||||
'critical_security_event' => true,
|
||||
]);
|
||||
|
||||
parent::__construct(
|
||||
message: $message,
|
||||
context: $context,
|
||||
code: 423, // Locked
|
||||
previous: $previous,
|
||||
errorCode: ErrorCode::AUTH_ACCOUNT_LOCKED,
|
||||
retryAfter: $this->lockDurationMinutes * 60 // Retry nach Sperrzeit in Sekunden
|
||||
);
|
||||
}
|
||||
|
||||
// === Factory Methods für verschiedene Sperr-Szenarien ===
|
||||
|
||||
public static function tooManyFailedAttempts(string $identifier, int $attempts = 5): self
|
||||
{
|
||||
return new self($identifier, $attempts, 15); // 15 Minuten Standard-Sperrzeit
|
||||
}
|
||||
|
||||
public static function suspiciousActivity(string $identifier, int $lockDurationMinutes = 60): self
|
||||
{
|
||||
return new self($identifier, 0, $lockDurationMinutes);
|
||||
}
|
||||
|
||||
public static function administrativeLock(string $identifier): self
|
||||
{
|
||||
return new self($identifier, 0, 0); // 0 = permanente Sperrung
|
||||
}
|
||||
|
||||
public static function bruteForceDetected(string $identifier, int $attempts): self
|
||||
{
|
||||
return new self($identifier, $attempts, 120); // 2 Stunden bei Brute Force
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt OWASP-konforme Event-Daten zurück
|
||||
*/
|
||||
public function getSecurityEventData(): array
|
||||
{
|
||||
return [
|
||||
'event_identifier' => "authn_account_locked:{$this->identifier},{$this->failedAttempts}",
|
||||
'description' => "User {$this->identifier} account locked after {$this->failedAttempts} failed attempts",
|
||||
'category' => 'authentication',
|
||||
'log_level' => 'WARN',
|
||||
'requires_alert' => true,
|
||||
'user_identifier' => $this->identifier,
|
||||
'failed_attempts' => $this->failedAttempts,
|
||||
'lock_duration_minutes' => $this->lockDurationMinutes,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob die Sperrung permanent ist
|
||||
*/
|
||||
public function isPermanentLock(): bool
|
||||
{
|
||||
return $this->lockDurationMinutes === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die Entsperrzeit
|
||||
*/
|
||||
public function getUnlockTime(): ?\DateTimeImmutable
|
||||
{
|
||||
if ($this->isPermanentLock()) {
|
||||
return null; // Permanente Sperrung
|
||||
}
|
||||
|
||||
return new \DateTimeImmutable("+{$this->lockDurationMinutes} minutes");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt verbleibende Sperrzeit in Sekunden zurück
|
||||
*/
|
||||
public function getRemainingLockTimeSeconds(): ?int
|
||||
{
|
||||
$unlockTime = $this->getUnlockTime();
|
||||
if ($unlockTime === null) {
|
||||
return null; // Permanente Sperrung
|
||||
}
|
||||
|
||||
$now = new \DateTimeImmutable();
|
||||
$diff = $unlockTime->getTimestamp() - $now->getTimestamp();
|
||||
|
||||
return max(0, $diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Account-Sperrungen erfordern immer Alerts
|
||||
*/
|
||||
public function requiresAlert(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user