session = $this->createMock(SessionInterface::class); $this->auth = $this->createMock(Auth::class); $this->middleware = new AuthMiddleware($this->auth); $this->stateManager = $this->createMock(RequestStateManager::class); } /** * Test: Nicht-authentifizierte Benutzer werden blockiert * OWASP A07:2021 – Identification and Authentication Failures */ public function test_blocks_unauthenticated_access(): void { // Arrange: Benutzer nicht authentifiziert $this->auth->method('isAuthenticated')->willReturn(false); $request = $this->createMock(Request::class); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); $next->expects($this->never())->method('__invoke'); // Act & Assert: Exception erwartet $this->expectException(\App\Framework\Http\Exception\HttpException::class); $this->middleware->__invoke($context, $next, $this->stateManager); } /** * Test: Authentifizierte Benutzer werden durchgelassen */ public function test_allows_authenticated_access(): void { // Arrange: Benutzer authentifiziert $this->auth->method('isAuthenticated')->willReturn(true); $request = $this->createMock(Request::class); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); $next->expects($this->once())->method('__invoke')->willReturn($context); // Act $result = $this->middleware->__invoke($context, $next, $this->stateManager); // Assert $this->assertSame($context, $result); } /** * Test: Session Hijacking Schutz * Validiert Session-Fingerprinting */ public function test_session_fingerprint_validation(): void { // Arrange: Authentifiziert aber Session-Fingerprint geändert $this->auth->method('isAuthenticated')->willReturn(true); $this->auth->method('validateSessionFingerprint')->willReturn(false); $request = $this->createMock(Request::class); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); // Act & Assert: Session Hijacking erkannt $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Session fingerprint validation failed'); $this->middleware->__invoke($context, $next, $this->stateManager); } /** * Test: Concurrent Login Detection * Erkennt verdächtige gleichzeitige Logins */ public function test_concurrent_login_detection(): void { // Arrange: Benutzer von verschiedenen IPs eingeloggt $this->auth->method('isAuthenticated')->willReturn(true); $this->auth->method('validateSessionFingerprint')->willReturn(true); $this->auth->method('detectConcurrentSessions')->willReturn(true); $request = $this->createMock(Request::class); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); // Act & Assert: Concurrent Session erkannt $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Concurrent session detected'); $this->middleware->__invoke($context, $next, $this->stateManager); } /** * Test: Session Timeout Validierung */ public function test_session_timeout_validation(): void { // Arrange: Session abgelaufen $this->auth->method('isAuthenticated')->willReturn(true); $this->auth->method('isSessionExpired')->willReturn(true); $request = $this->createMock(Request::class); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); // Act & Assert: Session Timeout $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Session expired'); $this->middleware->__invoke($context, $next, $this->stateManager); } /** * Test: Rate Limiting für Authentication Failures * Verhindert Brute Force Attacks */ public function test_authentication_rate_limiting(): void { // Arrange: Zu viele fehlgeschlagene Login-Versuche $this->auth->method('isAuthenticated')->willReturn(false); $this->auth->method('isRateLimited')->willReturn(true); $request = $this->createMock(Request::class); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); // Act & Assert: Rate Limit erreicht $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Authentication rate limit exceeded'); $this->middleware->__invoke($context, $next, $this->stateManager); } /** * Test: IP Whitelist Validation * Admin-Bereiche nur von bestimmten IPs */ public function test_ip_whitelist_validation(): void { // Arrange: IP nicht in Whitelist $this->auth->method('isAuthenticated')->willReturn(true); $this->auth->method('isIpWhitelisted')->willReturn(false); $request = $this->createRequestWithIp('192.168.1.100'); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); // Act & Assert: IP nicht erlaubt $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Access from this IP address is not allowed'); $this->middleware->__invoke($context, $next, $this->stateManager); } /** * Test: Account Lockout nach fehlgeschlagenen Versuchen */ public function test_account_lockout_after_failed_attempts(): void { // Arrange: Account gesperrt nach zu vielen Versuchen $this->auth->method('isAuthenticated')->willReturn(false); $this->auth->method('isAccountLocked')->willReturn(true); $request = $this->createMock(Request::class); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); // Act & Assert: Account gesperrt $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Account is locked due to too many failed attempts'); $this->middleware->__invoke($context, $next, $this->stateManager); } /** * Test: Two-Factor Authentication Validation */ public function test_two_factor_authentication_required(): void { // Arrange: 2FA erforderlich aber nicht bereitgestellt $this->auth->method('isAuthenticated')->willReturn(true); $this->auth->method('requires2FA')->willReturn(true); $this->auth->method('is2FAValid')->willReturn(false); $request = $this->createMock(Request::class); $context = new MiddlewareContext($request); $next = $this->createMock(Next::class); // Act & Assert: 2FA erforderlich $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Two-factor authentication required'); $this->middleware->__invoke($context, $next, $this->stateManager); } // Helper Methods private function createRequestWithIp(string $ip): Request { $request = $this->createMock(Request::class); $request->method('getClientIp')->willReturn($ip); return $request; } }