Skip to content

Commit 33df8a2

Browse files
committed
feat: Convert many ::log calls to PSR-3 native logger with context attributes
Replace Horde::log in many places for better debug insights when using journalctl backend
1 parent e6bc579 commit 33df8a2

9 files changed

Lines changed: 129 additions & 27 deletions

src/Auth/AuthApiController.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Psr\Http\Message\ResponseInterface;
1010
use Psr\Http\Message\ServerRequestInterface;
1111
use Psr\Http\Server\RequestHandlerInterface;
12+
use Psr\Log\LoggerInterface;
1213
use Horde\Http\Response;
1314
use Exception;
1415
use RuntimeException;
@@ -55,7 +56,8 @@ public function __construct()
5556
} catch (Exception $e) {
5657
// If JWT bootstrap wasn't loaded, create service manually
5758
$registry = $injector->getInstance('Horde_Registry');
58-
$this->authService = new AuthenticationService($registry, null);
59+
$logger = $injector->getInstance(LoggerInterface::class);
60+
$this->authService = new AuthenticationService($registry, $logger, null);
5961
}
6062
}
6163

src/Auth/ResponsiveLoginController.php

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Psr\Http\Message\ResponseInterface;
1919
use Psr\Http\Message\ServerRequestInterface;
2020
use Psr\Http\Server\RequestHandlerInterface;
21+
use Psr\Log\LoggerInterface;
2122

2223
/**
2324
* Responsive Login Controller
@@ -35,6 +36,11 @@ class ResponsiveLoginController implements RequestHandlerInterface
3536
{
3637
use HtmlResponseTrait;
3738
use RedirectResponseTrait;
39+
40+
public function __construct(
41+
private readonly LoggerInterface $logger,
42+
) {}
43+
3844
/**
3945
* Handle the login request
4046
*
@@ -503,7 +509,7 @@ private function handleLoginPost(ServerRequestInterface $request): ResponseInter
503509
$authService = $injector?->getInstance(AuthenticationService::class);
504510
if (!$authService) {
505511
// Fallback: create service manually
506-
$authService = new AuthenticationService($registry, null);
512+
$authService = new AuthenticationService($registry, $this->logger, null);
507513
}
508514

509515
// Build authentication options
@@ -519,12 +525,13 @@ private function handleLoginPost(ServerRequestInterface $request): ResponseInter
519525
}
520526

521527
// DEBUG: Log what we got back
522-
Horde::log('LOGIN RESULT: ' . json_encode([
528+
$this->logger->debug('Login authentication result', [
523529
'success' => $result['success'],
524530
'has_access_token' => isset($result['access_token']),
525531
'has_refresh_token' => isset($result['refresh_token']),
526532
'session_id' => $result['session_id'] ?? 'NONE',
527-
]), 'DEBUG');
533+
'username' => $username,
534+
]);
528535

529536
// Authentication successful
530537
// If JWT tokens were generated, store refresh token in cookie and pass to JS
@@ -546,7 +553,11 @@ private function handleLoginPost(ServerRequestInterface $request): ResponseInter
546553
if ($jti) {
547554
// Migrate session data to JTI-based session
548555
$oldSessionId = session_id();
549-
Horde::log("LOGIN: Migrating session from $oldSessionId to JTI: $jti", 'DEBUG');
556+
$this->logger->debug('Migrating session to JWT JTI', [
557+
'old_session_id' => $oldSessionId,
558+
'new_session_id' => $jti,
559+
'username' => $username,
560+
]);
550561

551562
// Save current session data
552563
$sessionData = $_SESSION;
@@ -561,10 +572,17 @@ private function handleLoginPost(ServerRequestInterface $request): ResponseInter
561572
// Restore session data
562573
$_SESSION = $sessionData;
563574

564-
Horde::log("LOGIN: Session migrated to JTI: $jti", 'DEBUG');
575+
$this->logger->debug('Session migration completed', [
576+
'session_id' => $jti,
577+
'username' => $username,
578+
]);
565579
}
566580
} catch (Exception $e) {
567-
Horde::log("LOGIN: Failed to extract JTI for session migration: " . $e->getMessage(), 'WARN');
581+
$this->logger->warning('Failed to extract JTI for session migration', [
582+
'exception' => $e->getMessage(),
583+
'exception_class' => get_class($e),
584+
'username' => $username,
585+
]);
568586
}
569587
}
570588

src/Factory/AuthenticationServiceFactory.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Horde\Horde\Service\JwtService;
99
use Horde\Injector\Injector;
1010
use Horde_Registry;
11+
use Psr\Log\LoggerInterface;
1112
use Exception;
1213

1314
/**
@@ -35,6 +36,7 @@ class AuthenticationServiceFactory
3536
public function create(Injector $injector): AuthenticationService
3637
{
3738
$registry = $injector->getInstance('Horde_Registry');
39+
$logger = $injector->getInstance(LoggerInterface::class);
3840

3941
// Try to get JWT service (may be null if not configured)
4042
$jwtService = null;
@@ -46,6 +48,6 @@ public function create(Injector $injector): AuthenticationService
4648
// AuthenticationService will fall back to session-only mode
4749
}
4850

49-
return new AuthenticationService($registry, $jwtService);
51+
return new AuthenticationService($registry, $logger, $jwtService);
5052
}
5153
}

src/Factory/LoggerFactory.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Horde\Horde\Factory;
6+
7+
use Horde\Injector\Injector;
8+
use Psr\Log\LoggerInterface;
9+
10+
/**
11+
* Factory for PSR-3 Logger
12+
*
13+
* Creates a PSR-3 compliant logger that wraps Horde_Log.
14+
*
15+
* Copyright 2026 The Horde Project (http://www.horde.org/)
16+
*
17+
* See the enclosed file LICENSE for license information (LGPL). If you
18+
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
19+
*
20+
* @category Horde
21+
* @package Horde
22+
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
23+
*/
24+
class LoggerFactory
25+
{
26+
/**
27+
* Create PSR-3 logger instance
28+
*
29+
* @param Injector $injector
30+
* @return LoggerInterface
31+
*/
32+
public function create(Injector $injector): LoggerInterface
33+
{
34+
// Get Horde_Log_Logger instance (configured via registry)
35+
$hordeLogger = $injector->getInstance('Horde_Log_Logger');
36+
37+
// Wrap it in PSR-3 adapter
38+
return new \Horde\Log\Psr3Adapter($hordeLogger);
39+
}
40+
}

src/Middleware/JwtSession.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Psr\Http\Message\ResponseInterface;
99
use Psr\Http\Server\MiddlewareInterface;
1010
use Psr\Http\Server\RequestHandlerInterface;
11+
use Psr\Log\LoggerInterface;
1112
use Exception;
1213
use Horde;
1314

@@ -28,13 +29,20 @@
2829
*/
2930
class JwtSession implements MiddlewareInterface
3031
{
32+
public function __construct(
33+
private readonly LoggerInterface $logger,
34+
) {}
35+
3136
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
3237
{
3338
// Check for JWT refresh token in cookie
3439
$cookies = $request->getCookieParams();
3540
$jwtRefreshToken = $cookies['horde_jwt_refresh'] ?? null;
3641

37-
Horde::log("JwtSession middleware: horde_jwt_refresh cookie " . ($jwtRefreshToken ? "present" : "absent"), 'DEBUG');
42+
$this->logger->debug('JWT session middleware checking refresh token', [
43+
'has_cookie' => $jwtRefreshToken !== null,
44+
'session_status' => session_status(),
45+
]);
3846

3947
if ($jwtRefreshToken) {
4048
// Parse JWT to get JTI (without full validation - just decode)
@@ -54,13 +62,19 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
5462
// Set session ID BEFORE any session is started
5563
if (session_status() === PHP_SESSION_NONE) {
5664
session_id($jti);
57-
Horde::log("JwtSession middleware: Set session_id to JTI: $jti", 'DEBUG');
65+
$this->logger->debug('JWT session ID set from refresh token', [
66+
'jti' => $jti,
67+
'previous_session_status' => 'none',
68+
]);
5869
// Session will be started by HordeCoreMiddleware
5970
}
6071
}
6172
} catch (Exception $e) {
6273
// Invalid JWT, ignore and let normal session handling proceed
63-
Horde::log("JwtSession middleware: Failed to parse JWT: " . $e->getMessage(), 'DEBUG');
74+
$this->logger->debug('Failed to parse JWT refresh token', [
75+
'exception' => $e->getMessage(),
76+
'exception_class' => get_class($e),
77+
]);
6478
}
6579
}
6680
}

src/Service/AuthenticationService.php

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Horde\Core\Auth\Jwt\GeneratedJwt;
88
use Horde_Registry;
9+
use Psr\Log\LoggerInterface;
910
use Exception;
1011
use Horde;
1112

@@ -58,11 +59,13 @@ class AuthenticationService
5859
{
5960
/**
6061
* @param Horde_Registry $registry Horde registry
62+
* @param LoggerInterface $logger PSR-3 logger
6163
* @param JwtService|null $jwtService JWT service (optional, for dual-mode)
6264
*/
6365
public function __construct(
6466
private readonly Horde_Registry $registry,
65-
private readonly ?JwtService $jwtService = null
67+
private readonly LoggerInterface $logger,
68+
private readonly ?JwtService $jwtService = null,
6669
) {}
6770

6871
/**
@@ -108,13 +111,23 @@ public function authenticate(string $username, string $password, array $options
108111
$authCredentials = array_merge($authCredentials, $options['auth_params']);
109112
}
110113

111-
Horde::log("AUTHENTICATE: Attempting auth for username=$username", 'DEBUG');
114+
$this->logger->debug('Authentication attempt', [
115+
'username' => $username,
116+
'generate_jwt' => $generateJwt,
117+
'has_jwt_service' => $this->jwtService !== null,
118+
]);
112119
$authResult = $auth->authenticate($username, $authCredentials);
113-
Horde::log("AUTHENTICATE: Auth result=" . ($authResult ? 'true' : 'false') . " for username=$username", 'DEBUG');
120+
$this->logger->debug('Authentication result', [
121+
'username' => $username,
122+
'success' => $authResult,
123+
]);
114124

115125
// Check if authentication actually succeeded
116126
if (!$authResult) {
117-
Horde::log("AUTHENTICATE: Authentication failed for username=$username", 'ERR');
127+
$this->logger->error('Authentication failed', [
128+
'username' => $username,
129+
'reason' => 'auth_backend_returned_false',
130+
]);
118131
return [
119132
'success' => false,
120133
'error' => 'Authentication failed',
@@ -125,11 +138,11 @@ public function authenticate(string $username, string $password, array $options
125138
$credentials = $auth->getCredential('credentials') ?: ['password' => $password];
126139
$userId = $username;
127140

128-
Horde::log("AUTHENTICATE: username=$username, generateJwt=$generateJwt, hasJwtService=" . ($this->jwtService !== null ? 'yes' : 'no'), 'DEBUG');
129-
130141
// If JWT enabled, generate tokens
131142
if ($generateJwt && $this->jwtService !== null) {
132-
Horde::log("AUTHENTICATE: Generating JWT tokens", 'DEBUG');
143+
$this->logger->debug('Generating JWT tokens', [
144+
'username' => $username,
145+
]);
133146

134147
// Generate JWT tokens
135148
$jwtClaims = $this->buildJwtClaims($username, $options);
@@ -152,7 +165,12 @@ public function authenticate(string $username, string $password, array $options
152165
['refresh_jti' => $jti] // Link access token to session
153166
));
154167

155-
Horde::log("AUTHENTICATE: JWT tokens generated, session_id=" . session_id() . ", jti=$jti", 'DEBUG');
168+
$this->logger->debug('JWT tokens generated', [
169+
'username' => $username,
170+
'session_id' => session_id(),
171+
'jti' => $jti,
172+
'access_token_expires_at' => $accessToken->expiresAt,
173+
]);
156174

157175
return [
158176
'success' => true,

test/Unit/Factory/AuthenticationServiceFactoryTest.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Horde\Horde\Service\AuthenticationService;
1212
use Horde\Horde\Service\JwtService;
1313
use Horde\Injector\Injector;
14+
use Psr\Log\LoggerInterface;
1415
use Horde_Registry;
1516

1617
/**
@@ -37,11 +38,16 @@ protected function setUp(): void
3738
// Create mock registry
3839
$this->registry = $this->createMock(Horde_Registry::class);
3940

41+
// Create mock logger
42+
$logger = $this->createMock(LoggerInterface::class);
43+
4044
// Create mock injector
4145
$this->injector = $this->createMock(Injector::class);
4246
$this->injector->method('getInstance')
43-
->with('Horde_Registry')
44-
->willReturn($this->registry);
47+
->willReturnMap([
48+
['Horde_Registry', $this->registry],
49+
[LoggerInterface::class, $logger],
50+
]);
4551
}
4652

4753
protected function tearDown(): void

test/Unit/Service/AuthenticationServiceJtiSessionTest.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Horde\Horde\Service\JwtService;
1010
use Horde\Core\Auth\Jwt\GeneratedJwt;
1111
use Horde\Core\Auth\Jwt\VerifiedJwt;
12+
use Psr\Log\LoggerInterface;
1213
use Horde_Registry;
1314
use Exception;
1415
use Horde_Auth;
@@ -60,7 +61,7 @@ public function testRefreshTokenValidatesSession(): void
6061
->willReturn($mockVerified);
6162

6263
// Create service
63-
$authService = new AuthenticationService($registry, $jwtService);
64+
$authService = new AuthenticationService($registry, $this->createMock(LoggerInterface::class), $jwtService);
6465

6566
// Try to refresh
6667
$result = $authService->refreshToken('fake-jwt-token');
@@ -135,7 +136,7 @@ public function testRefreshTokenValidatesJtiMatchesSessionId(): void
135136
->willReturn($mockAccessToken);
136137

137138
// Create service
138-
$authService = new AuthenticationService($registry, $jwtService);
139+
$authService = new AuthenticationService($registry, $this->createMock(LoggerInterface::class), $jwtService);
139140

140141
// Try to refresh with attacker's token
141142
// The method will:
@@ -169,7 +170,7 @@ public function testLogoutClearsAuth(): void
169170
->method('clearAuth');
170171

171172
// Create service
172-
$authService = new AuthenticationService($registry, null);
173+
$authService = new AuthenticationService($registry, $this->createMock(LoggerInterface::class), null);
173174

174175
// Logout
175176
$authService->logout();
@@ -247,7 +248,7 @@ public function testIssueTokensForAuthenticatedUserCreatesJtiSession(): void
247248
->willReturn($mockAccessToken);
248249

249250
// Create service
250-
$authService = new AuthenticationService($registry, $jwtService);
251+
$authService = new AuthenticationService($registry, $this->createMock(LoggerInterface::class), $jwtService);
251252

252253
// Issue tokens
253254
$result = $authService->issueTokensForAuthenticatedUser('testuser');

test/Unit/Service/AuthenticationServiceSessionRegistryTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Horde\Horde\Service\JwtService;
1010
use Horde\Core\Auth\Jwt\VerifiedJwt;
1111
use Horde\Core\Auth\Jwt\GeneratedJwt;
12+
use Psr\Log\LoggerInterface;
1213
use Horde_Registry;
1314

1415
/**
@@ -69,7 +70,7 @@ public function testRefreshTokenChecksSessionRegistry(): void
6970
->willReturn('testuser');
7071

7172
// Create service
72-
$authService = new AuthenticationService($registry, $jwtService);
73+
$authService = new AuthenticationService($registry, $this->createMock(LoggerInterface::class), $jwtService);
7374

7475
// Refresh token
7576
$result = $authService->refreshToken('fake-jwt-token');
@@ -113,7 +114,7 @@ public function testLogoutDestroysSessionRegistry(): void
113114
->method('clearAuth');
114115

115116
// Create service
116-
$authService = new AuthenticationService($registry, null);
117+
$authService = new AuthenticationService($registry, $this->createMock(LoggerInterface::class), null);
117118

118119
// Logout
119120
$authService->logout();

0 commit comments

Comments
 (0)