From 0d8073109d1f03ce4e04f48310cb548e26aa0bdb Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 29 Jan 2026 14:59:01 +0100 Subject: [PATCH 1/3] chore: Mark IUserConfig entries as lazy where needed Signed-off-by: Marcel Klehr --- lib/AppInfo/Application.php | 10 ++-- lib/Controller/ConfigController.php | 56 ++++++++--------- lib/Controller/ZammadAPIController.php | 7 ++- .../Version040000Date20260129225956.php | 60 +++++++++++++++++++ lib/Reference/ZammadReferenceProvider.php | 15 ++--- lib/Search/ZammadSearchProvider.php | 13 ++-- lib/Service/ZammadAPIService.php | 35 ++++++----- lib/Settings/Admin.php | 2 +- lib/Settings/Personal.php | 20 +++---- 9 files changed, 140 insertions(+), 78 deletions(-) create mode 100644 lib/Migration/Version040000Date20260129225956.php diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 380fc9e..59cc920 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -22,7 +22,7 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\Collaboration\Reference\RenderReferenceEvent; -use OCP\IConfig; +use OCP\Config\IUserConfig; use OCP\IL10N; use OCP\INavigationManager; @@ -33,13 +33,13 @@ class Application extends App implements IBootstrap { public const APP_ID = 'integration_zammad'; - private IConfig $config; + private IUserConfig $userConfig; public function __construct(array $urlParams = []) { parent::__construct(self::APP_ID, $urlParams); $container = $this->getContainer(); - $this->config = $container->get(IConfig::class); + $this->userConfig = $container->get(IUserConfig::class); $manager = $container->get(INotificationManager::class); $manager->registerNotifierService(Notifier::class); @@ -63,8 +63,8 @@ public function registerNavigation(IUserSession $userSession): void { $userId = $user->getUID(); $container = $this->getContainer(); - if ($this->config->getUserValue($userId, self::APP_ID, 'navigation_enabled', '0') === '1') { - $zammadUrl = $this->config->getUserValue($userId, self::APP_ID, 'url', ''); + if ($this->userConfig->getValueString($userId, self::APP_ID, 'navigation_enabled', '0') === '1') { + $zammadUrl = $this->userConfig->getValueString($userId, self::APP_ID, 'url', ''); if ($zammadUrl !== '') { $container->get(INavigationManager::class)->add(function () use ($container, $zammadUrl) { $urlGenerator = $container->get(IURLGenerator::class); diff --git a/lib/Controller/ConfigController.php b/lib/Controller/ConfigController.php index 983829c..dd3d74c 100644 --- a/lib/Controller/ConfigController.php +++ b/lib/Controller/ConfigController.php @@ -23,6 +23,7 @@ use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; +use OCP\Config\IUserConfig; use OCP\IAppConfig; use OCP\IConfig; use OCP\IL10N; @@ -37,7 +38,7 @@ class ConfigController extends Controller { public function __construct( string $appName, IRequest $request, - private IConfig $config, + private IUserConfig $userConfig, private IAppConfig $appConfig, private IURLGenerator $urlGenerator, private IL10N $l, @@ -62,7 +63,8 @@ public function setConfig(array $values): DataResponse { if (in_array($key, ['token', 'token_type', 'url', 'oauth_state', 'redirect_uri'], true)) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } - $this->config->setUserValue($this->userId, Application::APP_ID, $key, trim($value)); + $lazy = $key !== 'url'; + $this->userConfig->setValueString($this->userId, Application::APP_ID, $key, trim($value), lazy: $lazy); } return new DataResponse([]); @@ -80,10 +82,9 @@ public function setConfig(array $values): DataResponse { public function setSensitiveConfig(array $values): DataResponse { foreach ($values as $key => $value) { if ($key === 'token' && $value !== '') { - $encryptedValue = $this->crypto->encrypt(trim($value)); - $this->config->setUserValue($this->userId, Application::APP_ID, $key, $encryptedValue); + $this->userConfig->setValueString($this->userId, Application::APP_ID, $key, $value, lazy: true, flags: IUserConfig::FLAG_SENSITIVE); } else { - $this->config->setUserValue($this->userId, Application::APP_ID, $key, trim($value)); + $this->userConfig->setValueString($this->userId, Application::APP_ID, $key, trim($value), lazy: true); } } $result = []; @@ -92,17 +93,17 @@ public function setSensitiveConfig(array $values): DataResponse { if ($values['token'] && $values['token'] !== '') { $result = $this->storeUserInfo(); } else { - $this->config->deleteUserValue($this->userId, Application::APP_ID, 'user_id'); - $this->config->deleteUserValue($this->userId, Application::APP_ID, 'user_name'); - $this->config->deleteUserValue($this->userId, Application::APP_ID, 'last_open_check'); - $this->config->deleteUserValue($this->userId, Application::APP_ID, 'token_type'); + $this->userConfig->deleteUserConfig($this->userId, Application::APP_ID, 'user_id'); + $this->userConfig->deleteUserConfig($this->userId, Application::APP_ID, 'user_name'); + $this->userConfig->deleteUserConfig($this->userId, Application::APP_ID, 'last_open_check'); + $this->userConfig->deleteUserConfig($this->userId, Application::APP_ID, 'token_type'); $result = [ 'user_name' => '', ]; } $this->zammadReferenceProvider->invalidateUserCache($this->userId); - $this->config->deleteUserValue($this->userId, Application::APP_ID, 'refresh_token'); - $this->config->deleteUserValue($this->userId, Application::APP_ID, 'token_expires_at'); + $this->userConfig->deleteUserConfig($this->userId, Application::APP_ID, 'refresh_token'); + $this->userConfig->deleteUserConfig($this->userId, Application::APP_ID, 'token_expires_at'); } if (isset($result['error'])) { return new DataResponse($result, Http::STATUS_UNAUTHORIZED); @@ -122,7 +123,8 @@ public function setAdminConfig(array $values): DataResponse { if (in_array($key, ['client_id', 'client_secret', 'oauth_instance_url'], true)) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } - $this->appConfig->setValueString(Application::APP_ID, $key, $value, lazy: true); + $lazy = $key !== 'oauth_instance_url'; + $this->appConfig->setValueString(Application::APP_ID, $key, $value, lazy: $lazy); } return new DataResponse([]); } @@ -156,17 +158,17 @@ public function setSensitiveAdminConfig(array $values): DataResponse { #[NoAdminRequired] #[NoCSRFRequired] public function oauthRedirect(string $code = '', string $state = ''): RedirectResponse { - $configState = $this->config->getUserValue($this->userId, Application::APP_ID, 'oauth_state'); + $configState = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'oauth_state', lazy: true); $clientID = $this->appConfig->getValueString(Application::APP_ID, 'client_id', lazy: true); $clientSecret = $this->appConfig->getValueString(Application::APP_ID, 'client_secret', lazy: true); // anyway, reset state - $this->config->setUserValue($this->userId, Application::APP_ID, 'oauth_state', ''); + $this->userConfig->setValueString($this->userId, Application::APP_ID, 'oauth_state', '', lazy: true); - $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); + $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); if ($clientID && $clientSecret && $configState !== '' && $configState === $state) { - $redirect_uri = $this->config->getUserValue($this->userId, Application::APP_ID, 'redirect_uri'); + $redirect_uri = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'redirect_uri', lazy: true); $result = $this->zammadAPIService->requestOAuthAccessToken($adminZammadOauthUrl, [ 'client_id' => $clientID, 'client_secret' => $clientSecret, @@ -177,16 +179,14 @@ public function oauthRedirect(string $code = '', string $state = ''): RedirectRe if (isset($result['access_token'])) { $this->zammadReferenceProvider->invalidateUserCache($this->userId); $accessToken = $result['access_token']; - $encryptedAccessToken = $accessToken === '' ? '' : $this->crypto->encrypt($accessToken); - $this->config->setUserValue($this->userId, Application::APP_ID, 'token', $encryptedAccessToken); - $this->config->setUserValue($this->userId, Application::APP_ID, 'token_type', 'oauth'); + $this->userConfig->setValueString($this->userId, Application::APP_ID, 'token', $accessToken, lazy: true, flags: IUserConfig::FLAG_SENSITIVE); + $this->userConfig->setValueString($this->userId, Application::APP_ID, 'token_type', 'oauth', lazy: true); $refreshToken = $result['refresh_token']; - $encryptedRefreshToken = $refreshToken === '' ? '' : $this->crypto->encrypt($refreshToken); - $this->config->setUserValue($this->userId, Application::APP_ID, 'refresh_token', $encryptedRefreshToken); + $this->userConfig->setValueString($this->userId, Application::APP_ID, 'refresh_token', $refreshToken, lazy: true, flags: IUserConfig::FLAG_SENSITIVE); if (isset($result['expires_in'])) { $nowTs = (new Datetime())->getTimestamp(); $expiresAt = $nowTs + (int)$result['expires_in']; - $this->config->setUserValue($this->userId, Application::APP_ID, 'token_expires_at', (string)$expiresAt); + $this->userConfig->setValueString($this->userId, Application::APP_ID, 'token_expires_at', (string)$expiresAt, lazy: true); } // get user info $this->storeUserInfo(); @@ -210,8 +210,8 @@ public function oauthRedirect(string $code = '', string $state = ''): RedirectRe * @throws PreConditionNotMetException */ private function storeUserInfo(): array { - $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); - $zammadUrl = $this->config->getUserValue($this->userId, Application::APP_ID, 'url') ?: $adminZammadOauthUrl; + $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); + $zammadUrl = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'url') ?: $adminZammadOauthUrl; if (!$zammadUrl || !preg_match('/^(https?:\/\/)?[^.]+\.[^.].*/', $zammadUrl)) { return ['error' => 'Zammad URL is invalid']; @@ -220,12 +220,12 @@ private function storeUserInfo(): array { $info = $this->zammadAPIService->request($this->userId, 'users/me'); if (isset($info['lastname'], $info['firstname'], $info['id'])) { $fullName = $info['firstname'] . ' ' . $info['lastname']; - $this->config->setUserValue($this->userId, Application::APP_ID, 'user_id', $info['id']); - $this->config->setUserValue($this->userId, Application::APP_ID, 'user_name', $fullName); + $this->userConfig->setValueString($this->userId, Application::APP_ID, 'user_id', $info['id'], lazy: true); + $this->userConfig->setValueString($this->userId, Application::APP_ID, 'user_name', $fullName, lazy: true); return ['user_name' => $fullName]; } else { - $this->config->deleteUserValue($this->userId, Application::APP_ID, 'user_id'); - $this->config->deleteUserValue($this->userId, Application::APP_ID, 'user_name'); + $this->userConfig->deleteUserConfig($this->userId, Application::APP_ID, 'user_id'); + $this->userConfig->deleteUserConfig($this->userId, Application::APP_ID, 'user_name'); return $info; } } diff --git a/lib/Controller/ZammadAPIController.php b/lib/Controller/ZammadAPIController.php index e8db9c2..04c7a3c 100644 --- a/lib/Controller/ZammadAPIController.php +++ b/lib/Controller/ZammadAPIController.php @@ -21,6 +21,7 @@ use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; +use OCP\Config\IUserConfig; use OCP\IConfig; use OCP\IRequest; use OCP\PreConditionNotMetException; @@ -30,7 +31,7 @@ class ZammadAPIController extends Controller { public function __construct( string $appName, IRequest $request, - private IConfig $config, + private IUserConfig $userConfig, private ZammadAPIService $zammadAPIService, private ?string $userId, ) { @@ -44,7 +45,7 @@ public function __construct( */ #[NoAdminRequired] public function getZammadUrl(): DataResponse { - $zammadUrl = $this->config->getUserValue($this->userId, Application::APP_ID, 'url'); + $zammadUrl = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'url'); return new DataResponse($zammadUrl); } @@ -80,7 +81,7 @@ public function getZammadAvatar(string $imageId = ''): DataDisplayResponse { */ #[NoAdminRequired] public function getNotifications(?string $since = null): DataResponse { - $hasAccessToken = $this->config->getUserValue($this->userId, Application::APP_ID, 'token') !== ''; + $hasAccessToken = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'token', lazy: true) !== ''; $zammadUrl = $this->zammadAPIService->getZammadUrl($this->userId); if (!$hasAccessToken || !preg_match('/^(https?:\/\/)?[^.]+\.[^.].*/', $zammadUrl)) { return new DataResponse('connection_impossible', Http::STATUS_BAD_REQUEST); diff --git a/lib/Migration/Version040000Date20260129225956.php b/lib/Migration/Version040000Date20260129225956.php new file mode 100644 index 0000000..f41c714 --- /dev/null +++ b/lib/Migration/Version040000Date20260129225956.php @@ -0,0 +1,60 @@ +userConfig->getUserIds(Application::APP_ID) as $userId) { + // store user config as lazy and sensitive + foreach (['token', 'refresh_token'] as $key) { + if ($this->userConfig->hasKey($userId, Application::APP_ID, $key)) { + $value = $this->userConfig->getValueString($userId, Application::APP_ID, $key); + $decryptedValue = $this->crypto->decrypt($value); + $this->userConfig->setValueString($userId, Application::APP_ID, $key, $decryptedValue, lazy: true, flags: IUserConfig::FLAG_SENSITIVE); + } + } + // store user config as lazy (except 'navigation_enabled' and 'url') + foreach (['token_type', 'token_expires_at', 'oauth_state', 'redirect_uri', 'search_enabled', 'link_preview_enabled', 'notification_enabled', 'user_id', 'user_name', 'last_open_check'] as $key) { + if ($this->userConfig->hasKey($userId, Application::APP_ID, $key)) { + $value = $this->userConfig->getValueString($userId, Application::APP_ID, $key); + $this->userConfig->setValueString($userId, Application::APP_ID, $key, $value, lazy: true); + } + } + } + + // store oauth_instance_url as non-lazy + foreach (['oauth_instance_url'] as $key) { + if ($this->appConfig->hasKey(Application::APP_ID, $key, lazy: true)) { + $this->appConfig->deleteKey(Application::APP_ID, $key); + $this->appConfig->setValueString(Application::APP_ID, $key, $value, lazy: false); + } + } + } +} diff --git a/lib/Reference/ZammadReferenceProvider.php b/lib/Reference/ZammadReferenceProvider.php index 447b420..96ee238 100644 --- a/lib/Reference/ZammadReferenceProvider.php +++ b/lib/Reference/ZammadReferenceProvider.php @@ -31,6 +31,7 @@ use OCP\Collaboration\Reference\IReferenceManager; use OCP\Collaboration\Reference\ISearchableReferenceProvider; use OCP\Collaboration\Reference\Reference; +use OCP\Config\IUserConfig; use OCP\IAppConfig; use OCP\IConfig; use OCP\IL10N; @@ -41,7 +42,7 @@ class ZammadReferenceProvider extends ADiscoverableReferenceProvider implements public function __construct( private ZammadAPIService $zammadAPIService, - private IConfig $config, + private IUserConfig $userConfig, private IAppConfig $appConfig, private IReferenceManager $referenceManager, private IURLGenerator $urlGenerator, @@ -86,7 +87,7 @@ public function getIconUrl(): string { public function getSupportedSearchProviderIds(): array { if ($this->userId !== null) { $ids = []; - $searchEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'search_enabled', '0') === '1'; + $searchEnabled = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'search_enabled', '0', lazy: true) === '1'; if ($searchEnabled) { $ids[] = 'zammad-search'; } @@ -105,7 +106,7 @@ private function isMatching(string $referenceText, string $url): bool { */ public function matchReference(string $referenceText): bool { if ($this->userId !== null) { - $linkPreviewEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'link_preview_enabled', '1') === '1'; + $linkPreviewEnabled = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'link_preview_enabled', '1', lazy: true) === '1'; if (!$linkPreviewEnabled) { return false; } @@ -115,8 +116,8 @@ public function matchReference(string $referenceText): bool { return false; } - $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); - $zammadUrl = $this->config->getUserValue($this->userId, Application::APP_ID, 'url') ?: $adminZammadOauthUrl; + $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); + $zammadUrl = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'url') ?: $adminZammadOauthUrl; return $this->isMatching($referenceText, $zammadUrl); } @@ -125,8 +126,8 @@ public function matchReference(string $referenceText): bool { * @inheritDoc */ public function resolveReference(string $referenceText): ?IReference { - $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); - $zammadUrl = $this->config->getUserValue($this->userId, Application::APP_ID, 'url') ?: $adminZammadOauthUrl; + $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); + $zammadUrl = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'url') ?: $adminZammadOauthUrl; if ($zammadUrl !== '') { $parts = $this->getLinkParts($zammadUrl, $referenceText); if ($parts !== null) { diff --git a/lib/Search/ZammadSearchProvider.php b/lib/Search/ZammadSearchProvider.php index aa3f449..95ee4bf 100644 --- a/lib/Search/ZammadSearchProvider.php +++ b/lib/Search/ZammadSearchProvider.php @@ -28,6 +28,7 @@ use OCA\Zammad\AppInfo\Application; use OCA\Zammad\Service\ZammadAPIService; use OCP\App\IAppManager; +use OCP\Config\IUserConfig; use OCP\IAppConfig; use OCP\IConfig; use OCP\IDateTimeFormatter; @@ -44,7 +45,7 @@ class ZammadSearchProvider implements IProvider, IExternalProvider { public function __construct( private IAppManager $appManager, private IL10N $l10n, - private IConfig $config, + private IUserConfig $userConfig, private IAppConfig $appConfig, private IURLGenerator $urlGenerator, private IDateTimeFormatter $dateTimeFormatter, @@ -95,16 +96,16 @@ public function search(IUser $user, ISearchQuery $query): SearchResult { $offset = $query->getCursor(); $offset = $offset ? intval($offset) : 0; - $theme = $this->config->getUserValue($user->getUID(), 'accessibility', 'theme'); + $theme = $this->userConfig->getValueString($user->getUID(), 'accessibility', 'theme'); $thumbnailUrl = ($theme === 'dark') ? $this->urlGenerator->imagePath(Application::APP_ID, 'app.svg') : $this->urlGenerator->imagePath(Application::APP_ID, 'app-dark.svg'); - $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); - $zammadUrl = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'url') ?: $adminZammadOauthUrl; - $hasAccessToken = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'token') !== ''; + $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); + $zammadUrl = $this->userConfig->getValueString($user->getUID(), Application::APP_ID, 'url') ?: $adminZammadOauthUrl; + $hasAccessToken = $this->userConfig->getValueString($user->getUID(), Application::APP_ID, 'token', lazy: true) !== ''; - $searchEnabled = $this->config->getUserValue($user->getUID(), Application::APP_ID, 'search_enabled', '0') === '1'; + $searchEnabled = $this->userConfig->getValueString($user->getUID(), Application::APP_ID, 'search_enabled', '0', lazy: true) === '1'; if (!$hasAccessToken || !$searchEnabled) { return SearchResult::paginated($this->getName(), [], 0); } diff --git a/lib/Service/ZammadAPIService.php b/lib/Service/ZammadAPIService.php index c04b1a9..c0db03f 100644 --- a/lib/Service/ZammadAPIService.php +++ b/lib/Service/ZammadAPIService.php @@ -19,6 +19,7 @@ use GuzzleHttp\Exception\ServerException; use OCA\Zammad\AppInfo\Application; use OCP\AppFramework\Http; +use OCP\Config\IUserConfig; use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\IAppConfig; @@ -45,7 +46,7 @@ public function __construct( private IUserManager $userManager, private LoggerInterface $logger, private IL10N $l10n, - private IConfig $config, + private IUserConfig $userConfig, private IAppConfig $appConfig, private INotificationManager $notificationManager, private ICrypto $crypto, @@ -57,8 +58,8 @@ public function __construct( } public function getZammadUrl(string $userId): string { - $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); - return $this->config->getUserValue($userId, Application::APP_ID, 'url') ?: $adminZammadOauthUrl; + $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); + return $this->userConfig->getValueString($userId, Application::APP_ID, 'url') ?: $adminZammadOauthUrl; } /** @@ -79,12 +80,12 @@ public function checkOpenTickets(): void { * @throws PreConditionNotMetException */ private function checkOpenTicketsForUser(string $userId): void { - $accessToken = $this->config->getUserValue($userId, Application::APP_ID, 'token'); - $notificationEnabled = ($this->config->getUserValue($userId, Application::APP_ID, 'notification_enabled', '0') === '1'); + $accessToken = $this->userConfig->getValueString($userId, Application::APP_ID, 'token', lazy: true); + $notificationEnabled = ($this->userConfig->getValueString($userId, Application::APP_ID, 'notification_enabled', '0', lazy: true) === '1'); if ($accessToken && $notificationEnabled) { $zammadUrl = $this->getZammadUrl($userId); if ($zammadUrl) { - $lastNotificationCheck = $this->config->getUserValue($userId, Application::APP_ID, 'last_open_check'); + $lastNotificationCheck = $this->userConfig->getValueString($userId, Application::APP_ID, 'last_open_check', lazy: true); $lastNotificationCheck = $lastNotificationCheck === '' ? null : $lastNotificationCheck; // get the zammad user ID $me = $this->request($userId, 'users/me'); @@ -94,7 +95,7 @@ private function checkOpenTicketsForUser(string $userId): void { $notifications = $this->getNotifications($userId, $lastNotificationCheck); if (!isset($notifications['error']) && count($notifications) > 0) { $lastNotificationCheck = $notifications[0]['updated_at']; - $this->config->setUserValue($userId, Application::APP_ID, 'last_open_check', $lastNotificationCheck); + $this->userConfig->setValueString($userId, Application::APP_ID, 'last_open_check', $lastNotificationCheck, lazy: true); $nbOpen = 0; foreach ($notifications as $n) { if (!isset($n['state_id'], $n['owner_id'])) { @@ -455,9 +456,9 @@ public function request( ): array { $zammadUrl = $this->getZammadUrl($userId); $this->checkTokenExpiration($userId); - $accessToken = $this->config->getUserValue($userId, Application::APP_ID, 'token'); + $accessToken = $this->userConfig->getValueString($userId, Application::APP_ID, 'token', lazy: true); $accessToken = $accessToken === '' ? '' : $this->crypto->decrypt($accessToken); - $authType = $this->config->getUserValue($userId, Application::APP_ID, 'token_type'); + $authType = $this->userConfig->getValueString($userId, Application::APP_ID, 'token_type', lazy: true); try { $url = $zammadUrl . '/api/v1/' . $endPoint; $authHeader = ($authType === 'access') ? 'Token token=' : 'Bearer '; @@ -526,8 +527,8 @@ public function request( } private function checkTokenExpiration(string $userId): void { - $refreshToken = $this->config->getUserValue($userId, Application::APP_ID, 'refresh_token'); - $expireAt = $this->config->getUserValue($userId, Application::APP_ID, 'token_expires_at'); + $refreshToken = $this->userConfig->getValueString($userId, Application::APP_ID, 'refresh_token', lazy: true); + $expireAt = $this->userConfig->getValueString($userId, Application::APP_ID, 'token_expires_at', lazy: true); if ($refreshToken !== '' && $expireAt !== '') { $nowTs = (new Datetime())->getTimestamp(); $expireAt = (int)$expireAt; @@ -541,9 +542,8 @@ private function checkTokenExpiration(string $userId): void { private function refreshToken(string $userId): bool { $clientID = $this->appConfig->getValueString(Application::APP_ID, 'client_id', lazy: true); $clientSecret = $this->appConfig->getValueString(Application::APP_ID, 'client_secret', lazy: true); - $refreshToken = $this->config->getUserValue($userId, Application::APP_ID, 'refresh_token'); - $refreshToken = $refreshToken === '' ? '' : $this->crypto->decrypt($refreshToken); - $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); + $refreshToken = $this->userConfig->getValueString($userId, Application::APP_ID, 'refresh_token', lazy: true); + $adminZammadOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); if (!$refreshToken) { $this->logger->error('No Zammad refresh token found', ['app' => Application::APP_ID]); return false; @@ -556,16 +556,15 @@ private function refreshToken(string $userId): bool { ], 'POST'); if (isset($result['access_token'])) { $accessToken = $result['access_token']; - $encryptedAccessToken = $accessToken === '' ? '' : $this->crypto->encrypt($accessToken); - $this->config->setUserValue($userId, Application::APP_ID, 'token', $encryptedAccessToken); + $this->userConfig->setValueString($userId, Application::APP_ID, 'token', $accessToken, lazy: true, flags: IUserConfig::FLAG_SENSITIVE); // TODO check if we need to store the refresh token here // $refreshToken = $result['refresh_token']; // $encryptedRefreshToken = $refreshToken === '' ? '' : $this->crypto->encrypt($refreshToken); - // $this->config->setUserValue($userId, Application::APP_ID, 'refresh_token', $encryptedRefreshToken); + // $this->($userId, Application::APP_ID, 'refresh_token', $encryptedRefreshToken); if (isset($result['expires_in'])) { $nowTs = (new Datetime())->getTimestamp(); $expiresAt = $nowTs + (int)$result['expires_in']; - $this->config->setUserValue($userId, Application::APP_ID, 'token_expires_at', (string)$expiresAt); + $this->userConfig->setValueString($userId, Application::APP_ID, 'token_expires_at', (string)$expiresAt, lazy: true); } return true; } else { diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index a47c5f9..e3577e9 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -24,7 +24,7 @@ public function getForm(): TemplateResponse { $clientID = $this->appConfig->getValueString(Application::APP_ID, 'client_id', lazy: true); $clientSecret = $this->appConfig->getValueString(Application::APP_ID, 'client_secret', lazy: true); - $oauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); + $oauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); $adminLinkPreviewEnabled = $this->appConfig->getValueString(Application::APP_ID, 'link_preview_enabled', '1', lazy: true) === '1'; $adminConfig = [ diff --git a/lib/Settings/Personal.php b/lib/Settings/Personal.php index 323961c..dfd7745 100644 --- a/lib/Settings/Personal.php +++ b/lib/Settings/Personal.php @@ -5,6 +5,7 @@ use OCA\Zammad\AppInfo\Application; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; +use OCP\Config\IUserConfig; use OCP\IAppConfig; use OCP\IConfig; @@ -14,7 +15,7 @@ class Personal implements ISettings { public function __construct( - private IConfig $config, + private IUserConfig $userConfig, private IAppConfig $appConfig, private IInitialState $initialStateService, private ICrypto $crypto, @@ -29,16 +30,15 @@ public function getForm(): TemplateResponse { // for OAuth $clientID = $this->appConfig->getValueString(Application::APP_ID, 'client_id', lazy: true); $clientSecret = $this->appConfig->getValueString(Application::APP_ID, 'client_secret', lazy: true); - $adminOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url', lazy: true); + $adminOauthUrl = $this->appConfig->getValueString(Application::APP_ID, 'oauth_instance_url'); - $token = $this->config->getUserValue($this->userId, Application::APP_ID, 'token'); - $token = $token === '' ? '' : $this->crypto->decrypt($token); - $userName = $this->config->getUserValue($this->userId, Application::APP_ID, 'user_name'); - $url = $this->config->getUserValue($this->userId, Application::APP_ID, 'url') ?: $adminOauthUrl; - $searchEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'search_enabled', '0') === '1'; - $notificationEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'notification_enabled', '0') === '1'; - $navigationEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'navigation_enabled', '0') === '1'; - $linkPreviewEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'link_preview_enabled', '1') === '1'; + $token = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'token', lazy: true); + $userName = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'user_name', lazy: true); + $url = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'url') ?: $adminOauthUrl; + $searchEnabled = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'search_enabled', '0', lazy: true) === '1'; + $notificationEnabled = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'notification_enabled', '0', lazy: true) === '1'; + $navigationEnabled = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'navigation_enabled', '0') === '1'; + $linkPreviewEnabled = $this->userConfig->getValueString($this->userId, Application::APP_ID, 'link_preview_enabled', '1', lazy: true) === '1'; $userConfig = [ // don't expose the token to the user From d3a36d1cdc40b02fb151ede107341bc7a53ce7e1 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 29 Jan 2026 15:07:14 +0100 Subject: [PATCH 2/3] fix: Fix missed instance Signed-off-by: Marcel Klehr --- lib/Service/ZammadAPIService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Service/ZammadAPIService.php b/lib/Service/ZammadAPIService.php index c0db03f..b58c7ae 100644 --- a/lib/Service/ZammadAPIService.php +++ b/lib/Service/ZammadAPIService.php @@ -116,7 +116,7 @@ private function checkOpenTicketsForUser(string $userId): void { } } elseif (isset($me['error-code']) && $me['error-code'] === Http::STATUS_UNAUTHORIZED) { // Auth token seems to no longer be valid, wipe it and don't retry - $this->config->deleteUserValue($userId, Application::APP_ID, 'token'); + $this->userConfig->deleteUserConfig($userId, Application::APP_ID, 'token'); } } } From 124b9b32b531b487b441de8de68d0228cb8fcebf Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Thu, 29 Jan 2026 15:07:47 +0100 Subject: [PATCH 3/3] Fix: Run cs:fix Signed-off-by: Marcel Klehr --- lib/Controller/ConfigController.php | 1 - lib/Controller/ZammadAPIController.php | 1 - lib/Migration/Version040000Date20260129225956.php | 2 +- lib/Reference/ZammadReferenceProvider.php | 1 - lib/Search/ZammadSearchProvider.php | 1 - lib/Service/ZammadAPIService.php | 1 - lib/Settings/Personal.php | 1 - 7 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/Controller/ConfigController.php b/lib/Controller/ConfigController.php index dd3d74c..97ad2d9 100644 --- a/lib/Controller/ConfigController.php +++ b/lib/Controller/ConfigController.php @@ -25,7 +25,6 @@ use OCP\AppFramework\Http\RedirectResponse; use OCP\Config\IUserConfig; use OCP\IAppConfig; -use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; diff --git a/lib/Controller/ZammadAPIController.php b/lib/Controller/ZammadAPIController.php index 04c7a3c..cd5f50a 100644 --- a/lib/Controller/ZammadAPIController.php +++ b/lib/Controller/ZammadAPIController.php @@ -22,7 +22,6 @@ use OCP\AppFramework\Http\DataResponse; use OCP\Config\IUserConfig; -use OCP\IConfig; use OCP\IRequest; use OCP\PreConditionNotMetException; diff --git a/lib/Migration/Version040000Date20260129225956.php b/lib/Migration/Version040000Date20260129225956.php index f41c714..25a48ad 100644 --- a/lib/Migration/Version040000Date20260129225956.php +++ b/lib/Migration/Version040000Date20260129225956.php @@ -31,7 +31,7 @@ public function __construct( * @param array $options */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { - foreach($this->userConfig->getUserIds(Application::APP_ID) as $userId) { + foreach ($this->userConfig->getUserIds(Application::APP_ID) as $userId) { // store user config as lazy and sensitive foreach (['token', 'refresh_token'] as $key) { if ($this->userConfig->hasKey($userId, Application::APP_ID, $key)) { diff --git a/lib/Reference/ZammadReferenceProvider.php b/lib/Reference/ZammadReferenceProvider.php index 96ee238..6fa83e1 100644 --- a/lib/Reference/ZammadReferenceProvider.php +++ b/lib/Reference/ZammadReferenceProvider.php @@ -33,7 +33,6 @@ use OCP\Collaboration\Reference\Reference; use OCP\Config\IUserConfig; use OCP\IAppConfig; -use OCP\IConfig; use OCP\IL10N; use OCP\IURLGenerator; use OCP\PreConditionNotMetException; diff --git a/lib/Search/ZammadSearchProvider.php b/lib/Search/ZammadSearchProvider.php index 95ee4bf..e5a379b 100644 --- a/lib/Search/ZammadSearchProvider.php +++ b/lib/Search/ZammadSearchProvider.php @@ -30,7 +30,6 @@ use OCP\App\IAppManager; use OCP\Config\IUserConfig; use OCP\IAppConfig; -use OCP\IConfig; use OCP\IDateTimeFormatter; use OCP\IL10N; use OCP\IURLGenerator; diff --git a/lib/Service/ZammadAPIService.php b/lib/Service/ZammadAPIService.php index b58c7ae..1da110d 100644 --- a/lib/Service/ZammadAPIService.php +++ b/lib/Service/ZammadAPIService.php @@ -25,7 +25,6 @@ use OCP\IAppConfig; use OCP\ICache; use OCP\ICacheFactory; -use OCP\IConfig; use OCP\IL10N; use OCP\IUser; use OCP\IUserManager; diff --git a/lib/Settings/Personal.php b/lib/Settings/Personal.php index dfd7745..afd9c9b 100644 --- a/lib/Settings/Personal.php +++ b/lib/Settings/Personal.php @@ -7,7 +7,6 @@ use OCP\AppFramework\Services\IInitialState; use OCP\Config\IUserConfig; use OCP\IAppConfig; -use OCP\IConfig; use OCP\Security\ICrypto; use OCP\Settings\ISettings;