304 lines
8.1 KiB
PHP
304 lines
8.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KTXF\Event;
|
|
|
|
/**
|
|
* Security-specific event for authentication and access control events
|
|
*/
|
|
class SecurityEvent extends Event
|
|
{
|
|
// Event names
|
|
public const AUTH_SUCCESS = 'security.auth.success';
|
|
public const AUTH_FAILURE = 'security.auth.failure';
|
|
public const AUTH_LOGOUT = 'security.auth.logout';
|
|
public const TOKEN_REFRESH = 'security.token.refresh';
|
|
public const TOKEN_REVOKED = 'security.token.revoked';
|
|
|
|
public const ACCESS_DENIED = 'security.access.denied';
|
|
public const ACCESS_GRANTED = 'security.access.granted';
|
|
|
|
public const BRUTE_FORCE_DETECTED = 'security.brute_force.detected';
|
|
public const RATE_LIMIT_EXCEEDED = 'security.rate_limit.exceeded';
|
|
public const SUSPICIOUS_ACTIVITY = 'security.suspicious.activity';
|
|
|
|
public const IP_BLOCKED = 'security.ip.blocked';
|
|
public const IP_ALLOWED = 'security.ip.allowed';
|
|
public const DEVICE_BLOCKED = 'security.device.blocked';
|
|
|
|
private ?string $ipAddress = null;
|
|
private ?string $deviceFingerprint = null;
|
|
private ?string $userAgent = null;
|
|
private ?string $requestPath = null;
|
|
private ?string $requestMethod = null;
|
|
private ?string $userId = null;
|
|
private ?string $reason = null;
|
|
private int $severity = self::SEVERITY_INFO;
|
|
|
|
// Severity levels
|
|
public const SEVERITY_DEBUG = 0;
|
|
public const SEVERITY_INFO = 1;
|
|
public const SEVERITY_WARNING = 2;
|
|
public const SEVERITY_ERROR = 3;
|
|
public const SEVERITY_CRITICAL = 4;
|
|
|
|
/**
|
|
* Create a security event with common parameters
|
|
*/
|
|
public static function create(
|
|
string $name,
|
|
?string $ipAddress = null,
|
|
?string $deviceFingerprint = null,
|
|
array $data = []
|
|
): self {
|
|
$event = new self($name, $data);
|
|
$event->ipAddress = $ipAddress;
|
|
$event->deviceFingerprint = $deviceFingerprint;
|
|
|
|
// Set default severity based on event type
|
|
$event->severity = self::getSeverityForEvent($name);
|
|
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Create an authentication failure event
|
|
*/
|
|
public static function authFailure(
|
|
string $ipAddress,
|
|
?string $deviceFingerprint = null,
|
|
?string $userId = null,
|
|
?string $reason = null
|
|
): self {
|
|
$event = self::create(self::AUTH_FAILURE, $ipAddress, $deviceFingerprint, [
|
|
'userId' => $userId,
|
|
'reason' => $reason,
|
|
]);
|
|
$event->userId = $userId;
|
|
$event->reason = $reason;
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Create an authentication success event
|
|
*/
|
|
public static function authSuccess(
|
|
string $ipAddress,
|
|
?string $deviceFingerprint = null,
|
|
string $userId = null
|
|
): self {
|
|
$event = self::create(self::AUTH_SUCCESS, $ipAddress, $deviceFingerprint, [
|
|
'userId' => $userId,
|
|
]);
|
|
$event->userId = $userId;
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Create a brute force detection event
|
|
*/
|
|
public static function bruteForceDetected(
|
|
string $ipAddress,
|
|
int $failureCount,
|
|
int $windowSeconds
|
|
): self {
|
|
$event = self::create(self::BRUTE_FORCE_DETECTED, $ipAddress, null, [
|
|
'failureCount' => $failureCount,
|
|
'windowSeconds' => $windowSeconds,
|
|
]);
|
|
$event->reason = sprintf(
|
|
'%d failed attempts in %d seconds',
|
|
$failureCount,
|
|
$windowSeconds
|
|
);
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Create a rate limit exceeded event
|
|
*/
|
|
public static function rateLimitExceeded(
|
|
string $ipAddress,
|
|
int $requestCount,
|
|
int $windowSeconds,
|
|
?string $endpoint = null
|
|
): self {
|
|
$event = self::create(self::RATE_LIMIT_EXCEEDED, $ipAddress, null, [
|
|
'requestCount' => $requestCount,
|
|
'windowSeconds' => $windowSeconds,
|
|
'endpoint' => $endpoint,
|
|
]);
|
|
$event->requestPath = $endpoint;
|
|
$event->reason = sprintf(
|
|
'%d requests in %d seconds',
|
|
$requestCount,
|
|
$windowSeconds
|
|
);
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Create an access denied event
|
|
*/
|
|
public static function accessDenied(
|
|
string $ipAddress,
|
|
?string $deviceFingerprint = null,
|
|
?string $ruleId = null,
|
|
?string $reason = null
|
|
): self {
|
|
$event = self::create(self::ACCESS_DENIED, $ipAddress, $deviceFingerprint, [
|
|
'ruleId' => $ruleId,
|
|
'reason' => $reason,
|
|
]);
|
|
$event->reason = $reason;
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Get default severity for event types
|
|
*/
|
|
private static function getSeverityForEvent(string $eventName): int
|
|
{
|
|
return match ($eventName) {
|
|
self::AUTH_SUCCESS,
|
|
self::ACCESS_GRANTED,
|
|
self::TOKEN_REFRESH => self::SEVERITY_INFO,
|
|
|
|
self::AUTH_FAILURE,
|
|
self::ACCESS_DENIED,
|
|
self::AUTH_LOGOUT,
|
|
self::TOKEN_REVOKED => self::SEVERITY_WARNING,
|
|
|
|
self::RATE_LIMIT_EXCEEDED,
|
|
self::SUSPICIOUS_ACTIVITY => self::SEVERITY_ERROR,
|
|
|
|
self::BRUTE_FORCE_DETECTED,
|
|
self::IP_BLOCKED,
|
|
self::DEVICE_BLOCKED => self::SEVERITY_CRITICAL,
|
|
|
|
default => self::SEVERITY_INFO,
|
|
};
|
|
}
|
|
|
|
// Getters and setters
|
|
|
|
public function getIpAddress(): ?string
|
|
{
|
|
return $this->ipAddress;
|
|
}
|
|
|
|
public function setIpAddress(?string $ipAddress): self
|
|
{
|
|
$this->ipAddress = $ipAddress;
|
|
return $this;
|
|
}
|
|
|
|
public function getDeviceFingerprint(): ?string
|
|
{
|
|
return $this->deviceFingerprint;
|
|
}
|
|
|
|
public function setDeviceFingerprint(?string $deviceFingerprint): self
|
|
{
|
|
$this->deviceFingerprint = $deviceFingerprint;
|
|
return $this;
|
|
}
|
|
|
|
public function getUserAgent(): ?string
|
|
{
|
|
return $this->userAgent;
|
|
}
|
|
|
|
public function setUserAgent(?string $userAgent): self
|
|
{
|
|
$this->userAgent = $userAgent;
|
|
return $this;
|
|
}
|
|
|
|
public function getRequestPath(): ?string
|
|
{
|
|
return $this->requestPath;
|
|
}
|
|
|
|
public function setRequestPath(?string $requestPath): self
|
|
{
|
|
$this->requestPath = $requestPath;
|
|
return $this;
|
|
}
|
|
|
|
public function getRequestMethod(): ?string
|
|
{
|
|
return $this->requestMethod;
|
|
}
|
|
|
|
public function setRequestMethod(?string $requestMethod): self
|
|
{
|
|
$this->requestMethod = $requestMethod;
|
|
return $this;
|
|
}
|
|
|
|
public function getUserId(): ?string
|
|
{
|
|
return $this->userId;
|
|
}
|
|
|
|
public function setUserId(?string $userId): self
|
|
{
|
|
$this->userId = $userId;
|
|
return $this;
|
|
}
|
|
|
|
public function getReason(): ?string
|
|
{
|
|
return $this->reason;
|
|
}
|
|
|
|
public function setReason(?string $reason): self
|
|
{
|
|
$this->reason = $reason;
|
|
return $this;
|
|
}
|
|
|
|
public function getSeverity(): int
|
|
{
|
|
return $this->severity;
|
|
}
|
|
|
|
public function setSeverity(int $severity): self
|
|
{
|
|
$this->severity = $severity;
|
|
return $this;
|
|
}
|
|
|
|
public function getSeverityLabel(): string
|
|
{
|
|
return match ($this->severity) {
|
|
self::SEVERITY_DEBUG => 'DEBUG',
|
|
self::SEVERITY_INFO => 'INFO',
|
|
self::SEVERITY_WARNING => 'WARNING',
|
|
self::SEVERITY_ERROR => 'ERROR',
|
|
self::SEVERITY_CRITICAL => 'CRITICAL',
|
|
default => 'UNKNOWN',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Override toArray to include security-specific fields
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return array_merge(parent::toArray(), [
|
|
'ipAddress' => $this->ipAddress,
|
|
'deviceFingerprint' => $this->deviceFingerprint,
|
|
'userAgent' => $this->userAgent,
|
|
'requestPath' => $this->requestPath,
|
|
'requestMethod' => $this->requestMethod,
|
|
'userId' => $this->userId,
|
|
'reason' => $this->reason,
|
|
'severity' => $this->severity,
|
|
'severityLabel' => $this->getSeverityLabel(),
|
|
]);
|
|
}
|
|
}
|