From 3828df2b4e9fc7b9eb29592546e7d1d94c91a30c Mon Sep 17 00:00:00 2001 From: Git'Fellow <12234510+solracsf@users.noreply.github.com> Date: Fri, 27 Mar 2026 07:54:51 +0100 Subject: [PATCH] refactor: move to ITimeFactory Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com> --- lib/Controller/Id4meController.php | 2 +- lib/Controller/LoginController.php | 2 +- lib/Model/Token.php | 20 ++++++++++++-------- lib/Service/DiscoveryService.php | 6 ++++-- lib/Service/TokenService.php | 12 +++++++----- lib/User/Backend.php | 8 +++++--- tests/unit/Service/DiscoveryServiceTest.php | 6 +++++- 7 files changed, 35 insertions(+), 21 deletions(-) diff --git a/lib/Controller/Id4meController.php b/lib/Controller/Id4meController.php index 2d3d0b62..9ddfa043 100644 --- a/lib/Controller/Id4meController.php +++ b/lib/Controller/Id4meController.php @@ -322,7 +322,7 @@ public function code(string $state = '', string $code = '', string $scope = '') } // Set last password confirm to the future as we don't have passwords to confirm against with SSO - $this->session->set('last-password-confirm', strtotime('+4 year', time())); + $this->session->set('last-password-confirm', $this->timeFactory->getTime() + 4 * 365 * 24 * 3600); return new RedirectResponse($this->serverVersion->getMajorVersion() >= 32 ? $this->urlGenerator->linkToDefaultPageUrl() : \OC_Util::getDefaultPageUrl()); } diff --git a/lib/Controller/LoginController.php b/lib/Controller/LoginController.php index 9835620d..89da8442 100644 --- a/lib/Controller/LoginController.php +++ b/lib/Controller/LoginController.php @@ -667,7 +667,7 @@ public function code(string $state = '', string $code = '', string $scope = '', $this->config->setUserValue($user->getUID(), Application::APP_ID, 'had_token_once', '1'); // Set last password confirm to the future as we don't have passwords to confirm against with SSO - $this->session->set('last-password-confirm', strtotime('+4 year', time())); + $this->session->set('last-password-confirm', $this->timeFactory->getTime() + 4 * 365 * 24 * 3600); // for backchannel logout try { diff --git a/lib/Model/Token.php b/lib/Model/Token.php index f5f66c00..73962ce6 100644 --- a/lib/Model/Token.php +++ b/lib/Model/Token.php @@ -9,6 +9,7 @@ namespace OCA\UserOIDC\Model; use JsonSerializable; +use OCP\AppFramework\Utility\ITimeFactory; class Token implements JsonSerializable { @@ -20,13 +21,16 @@ class Token implements JsonSerializable { private int $createdAt; private ?int $providerId; - public function __construct(array $tokenData) { + public function __construct( + array $tokenData, + private ITimeFactory $timeFactory, + ) { $this->idToken = $tokenData['id_token'] ?? null; $this->accessToken = $tokenData['access_token']; $this->expiresIn = $tokenData['expires_in']; $this->refreshExpiresIn = $tokenData['refresh_expires_in'] ?? null; $this->refreshToken = $tokenData['refresh_token'] ?? null; - $this->createdAt = $tokenData['created_at'] ?? time(); + $this->createdAt = $tokenData['created_at'] ?? $this->timeFactory->getTime(); $this->providerId = $tokenData['provider_id'] ?? null; } @@ -44,7 +48,7 @@ public function getExpiresIn(): int { public function getExpiresInFromNow(): int { $expiresAt = $this->createdAt + $this->expiresIn; - return $expiresAt - time(); + return $expiresAt - $this->timeFactory->getTime(); } public function getRefreshExpiresIn(): ?int { @@ -58,7 +62,7 @@ public function getRefreshExpiresInFromNow(): int { return 0; } $refreshExpiresAt = $this->createdAt + $this->refreshExpiresIn; - return $refreshExpiresAt - time(); + return $refreshExpiresAt - $this->timeFactory->getTime(); } public function getRefreshToken(): ?string { @@ -70,11 +74,11 @@ public function getProviderId(): ?int { } public function isExpired(): bool { - return time() > ($this->createdAt + $this->expiresIn); + return $this->timeFactory->getTime() > ($this->createdAt + $this->expiresIn); } public function isExpiring(): bool { - return time() > ($this->createdAt + (int)($this->expiresIn / 2)); + return $this->timeFactory->getTime() > ($this->createdAt + (int)($this->expiresIn / 2)); } public function refreshIsExpired(): bool { @@ -82,7 +86,7 @@ public function refreshIsExpired(): bool { if ($this->refreshExpiresIn === null) { return false; } - return time() > ($this->createdAt + $this->refreshExpiresIn); + return $this->timeFactory->getTime() > ($this->createdAt + $this->refreshExpiresIn); } public function refreshIsExpiring(): bool { @@ -90,7 +94,7 @@ public function refreshIsExpiring(): bool { if ($this->refreshExpiresIn === null) { return false; } - return time() > ($this->createdAt + (int)($this->refreshExpiresIn / 2)); + return $this->timeFactory->getTime() > ($this->createdAt + (int)($this->refreshExpiresIn / 2)); } public function getCreatedAt() { diff --git a/lib/Service/DiscoveryService.php b/lib/Service/DiscoveryService.php index c24506ce..c9e0e42e 100644 --- a/lib/Service/DiscoveryService.php +++ b/lib/Service/DiscoveryService.php @@ -13,6 +13,7 @@ use OCA\UserOIDC\Helper\HttpClientHelper; use OCA\UserOIDC\Vendor\Firebase\JWT\JWK; use OCA\UserOIDC\Vendor\Firebase\JWT\JWT; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\ICache; use OCP\ICacheFactory; use OCP\IConfig; @@ -44,6 +45,7 @@ public function __construct( private HttpClientHelper $clientService, private ProviderService $providerService, private IConfig $config, + private ITimeFactory $timeFactory, ICacheFactory $cacheFactory, ) { $this->cache = $cacheFactory->createDistributed('user_oidc'); @@ -75,7 +77,7 @@ public function obtainDiscovery(Provider $provider): array { */ public function obtainJWK(Provider $provider, string $tokenToDecode, bool $useCache = true): array { $lastJwksRefresh = $this->providerService->getSetting($provider->getId(), ProviderService::SETTING_JWKS_CACHE_TIMESTAMP); - if ($lastJwksRefresh !== '' && $useCache && (int)$lastJwksRefresh > time() - self::INVALIDATE_JWKS_CACHE_AFTER_SECONDS) { + if ($lastJwksRefresh !== '' && $useCache && (int)$lastJwksRefresh > $this->timeFactory->getTime() - self::INVALIDATE_JWKS_CACHE_AFTER_SECONDS) { $rawJwks = $this->providerService->getSetting($provider->getId(), ProviderService::SETTING_JWKS_CACHE); $rawJwks = json_decode($rawJwks, true); $this->logger->debug('[obtainJWK] jwks cache content', ['jwks_cache' => $rawJwks]); @@ -87,7 +89,7 @@ public function obtainJWK(Provider $provider, string $tokenToDecode, bool $useCa // cache jwks $this->providerService->setSetting($provider->getId(), ProviderService::SETTING_JWKS_CACHE, $responseBody); $this->logger->debug('[obtainJWK] setting cache', ['jwks_cache' => $responseBody]); - $this->providerService->setSetting($provider->getId(), ProviderService::SETTING_JWKS_CACHE_TIMESTAMP, strval(time())); + $this->providerService->setSetting($provider->getId(), ProviderService::SETTING_JWKS_CACHE_TIMESTAMP, strval($this->timeFactory->getTime())); } $fixedJwks = $this->fixJwksAlg($rawJwks, $tokenToDecode); diff --git a/lib/Service/TokenService.php b/lib/Service/TokenService.php index e965b864..30e2318c 100644 --- a/lib/Service/TokenService.php +++ b/lib/Service/TokenService.php @@ -20,6 +20,7 @@ use OCP\App\IAppManager; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Authentication\Exceptions\ExpiredTokenException; use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\Authentication\Exceptions\WipeTokenException; @@ -67,11 +68,12 @@ public function __construct( private DiscoveryService $discoveryService, private ProviderMapper $providerMapper, private ILockingProvider $lockingProvider, + private ITimeFactory $timeFactory, ) { } public function storeToken(array $tokenData): Token { - $token = new Token($tokenData); + $token = new Token($tokenData, $this->timeFactory); $this->session->set(self::SESSION_TOKEN_KEY, json_encode($token, JSON_THROW_ON_ERROR)); $this->logger->debug('[TokenService] Store token in the session', ['session_id' => $this->session->getId()]); return $token; @@ -93,7 +95,7 @@ public function getToken(bool $refreshIfExpired = true): ?Token { return null; } - $token = new Token(json_decode($sessionData, true, 512, JSON_THROW_ON_ERROR)); + $token = new Token(json_decode($sessionData, true, 512, JSON_THROW_ON_ERROR), $this->timeFactory); // token is still valid if (!$token->isExpired()) { $this->logger->debug('[TokenService] getToken: token is still valid, it expires in ' . strval($token->getExpiresInFromNow()) . ' and refresh expires in ' . strval($token->getRefreshExpiresInFromNow())); @@ -225,7 +227,7 @@ public function refresh(Token $token): Token { // the token expiration and the moment it attempted to acquire the lock $sessionData = $this->session->get(self::SESSION_TOKEN_KEY); if ($sessionData) { - $currentToken = new Token(json_decode($sessionData, true, 512, JSON_THROW_ON_ERROR)); + $currentToken = new Token(json_decode($sessionData, true, 512, JSON_THROW_ON_ERROR), $this->timeFactory); if (!$currentToken->isExpired()) { $this->logger->debug('[TokenService] Token already refreshed by another request'); return $currentToken; @@ -368,7 +370,7 @@ public function getExchangedToken(string $targetAudience, array $extraScopes = [ $bodyArray, ['provider_id' => $loginToken->getProviderId()], ); - return new Token($tokenData); + return new Token($tokenData, $this->timeFactory); } catch (ClientException|ServerException $e) { $response = $e->getResponse(); $body = (string)$response->getBody(); @@ -439,6 +441,6 @@ public function getTokenFromOidcProviderApp(string $userId, string $targetAudien 'refresh_expires_in' => method_exists($generationEvent, 'getRefreshExpiresIn') ? $generationEvent->getRefreshExpiresIn() : $generationEvent->getExpiresIn(), - ]); + ], $this->timeFactory); } } diff --git a/lib/User/Backend.php b/lib/User/Backend.php index afd90e89..dd7b8365 100644 --- a/lib/User/Backend.php +++ b/lib/User/Backend.php @@ -22,6 +22,7 @@ use OCA\UserOIDC\User\Validator\SelfEncodedValidator; use OCA\UserOIDC\User\Validator\UserInfoValidator; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Authentication\IApacheBackend; use OCP\DB\Exception; use OCP\EventDispatcher\GenericEvent; @@ -69,6 +70,7 @@ public function __construct( private LdapService $ldapService, private IUserManager $userManager, private ServerVersion $serverVersion, + private ITimeFactory $timeFactory, ) { } @@ -348,12 +350,12 @@ public function getCurrentUserId(): string { } } - $this->session->set('last-password-confirm', strtotime('+4 year', time())); + $this->session->set('last-password-confirm', $this->timeFactory->getTime() + 4 * 365 * 24 * 3600); $this->setSessionUser($userId); return $userId; } elseif ($this->userExists($tokenUserId)) { $this->checkFirstLogin($tokenUserId); - $this->session->set('last-password-confirm', strtotime('+4 year', time())); + $this->session->set('last-password-confirm', $this->timeFactory->getTime() + 4 * 365 * 24 * 3600); $this->setSessionUser($tokenUserId); return $tokenUserId; } else { @@ -375,7 +377,7 @@ public function getCurrentUserId(): string { return ''; } $this->checkFirstLogin($tokenUserId); - $this->session->set('last-password-confirm', strtotime('+4 year', time())); + $this->session->set('last-password-confirm', $this->timeFactory->getTime() + 4 * 365 * 24 * 3600); $this->setSessionUser($tokenUserId); return $tokenUserId; } diff --git a/tests/unit/Service/DiscoveryServiceTest.php b/tests/unit/Service/DiscoveryServiceTest.php index c3ac0e78..5a17452d 100644 --- a/tests/unit/Service/DiscoveryServiceTest.php +++ b/tests/unit/Service/DiscoveryServiceTest.php @@ -11,6 +11,7 @@ use OCA\UserOIDC\Helper\HttpClientHelper; use OCA\UserOIDC\Service\DiscoveryService; use OCA\UserOIDC\Service\ProviderService; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\ICacheFactory; use OCP\IConfig; use PHPUnit\Framework\Assert; @@ -30,6 +31,8 @@ class DiscoveryServiceTest extends TestCase { private $config; /** @var ICacheFactory|MockObject */ private $cacheFactory; + /** @var ITimeFactory|MockObject */ + private $timeFactory; /** @var DiscoveryService */ private $discoveryService; @@ -39,8 +42,9 @@ public function setUp(): void { $this->clientHelper = $this->createMock(HttpClientHelper::class); $this->providerService = $this->createMock(ProviderService::class); $this->config = $this->createMock(IConfig::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); $this->cacheFactory = $this->createMock(ICacheFactory::class); - $this->discoveryService = new DiscoveryService($this->logger, $this->clientHelper, $this->providerService, $this->config, $this->cacheFactory); + $this->discoveryService = new DiscoveryService($this->logger, $this->clientHelper, $this->providerService, $this->config, $this->timeFactory, $this->cacheFactory); } /**