From f6dbd420e9a93bb2ebdaee4b7b5dce4e7b444d6f Mon Sep 17 00:00:00 2001 From: Julien Veyssier Date: Thu, 3 Oct 2024 16:05:40 +0200 Subject: [PATCH] encrypt api keys and basic passwords for app config and user settings Signed-off-by: Julien Veyssier --- appinfo/info.xml | 2 +- .../Version030102Date20241003155512.php | 89 +++++++++++++++++++ lib/Service/OpenAiSettingsService.php | 58 +++++++++--- 3 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 lib/Migration/Version030102Date20241003155512.php diff --git a/appinfo/info.xml b/appinfo/info.xml index b9244b93..512d42e9 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -79,7 +79,7 @@ Negative: Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/). ]]> - 3.1.2 + 3.1.3 agpl Julien Veyssier OpenAi diff --git a/lib/Migration/Version030102Date20241003155512.php b/lib/Migration/Version030102Date20241003155512.php new file mode 100644 index 00000000..048a1caf --- /dev/null +++ b/lib/Migration/Version030102Date20241003155512.php @@ -0,0 +1,89 @@ +config->getAppValue(Application::APP_ID, $key); + if ($value !== '') { + $encryptedValue = $this->crypto->encrypt($value); + $this->config->setAppValue(Application::APP_ID, $key, $encryptedValue); + } + } + + // user api keys and passwords + $qbUpdate = $this->connection->getQueryBuilder(); + $qbUpdate->update('preferences') + ->set('configvalue', $qbUpdate->createParameter('updateValue')) + ->where( + $qbUpdate->expr()->eq('appid', $qbUpdate->createNamedParameter(Application::APP_ID, IQueryBuilder::PARAM_STR)) + ) + ->andWhere( + $qbUpdate->expr()->eq('userid', $qbUpdate->createParameter('updateUserId')) + ) + ->andWhere( + $qbUpdate->expr()->eq('configkey', $qbUpdate->createParameter('updateConfigKey')) + ); + + $qbSelect = $this->connection->getQueryBuilder(); + $qbSelect->select('userid', 'configvalue', 'configkey') + ->from('preferences') + ->where( + $qbSelect->expr()->eq('appid', $qbSelect->createNamedParameter(Application::APP_ID, IQueryBuilder::PARAM_STR)) + ); + + $or = $qbSelect->expr()->orx(); + $or->add($qbSelect->expr()->eq('configkey', $qbSelect->createNamedParameter('api_key', IQueryBuilder::PARAM_STR))); + $or->add($qbSelect->expr()->eq('configkey', $qbSelect->createNamedParameter('basic_password', IQueryBuilder::PARAM_STR))); + $qbSelect->andWhere($or); + + $qbSelect->andWhere( + $qbSelect->expr()->nonEmptyString('configvalue') + ) + ->andWhere( + $qbSelect->expr()->isNotNull('configvalue') + ); + $req = $qbSelect->executeQuery(); + while ($row = $req->fetch()) { + $userId = $row['userid']; + $configKey = $row['configkey']; + $storedClearValue = $row['configvalue']; + $encryptedValue = $this->crypto->encrypt($storedClearValue); + $qbUpdate->setParameter('updateConfigKey', $configKey, IQueryBuilder::PARAM_STR); + $qbUpdate->setParameter('updateValue', $encryptedValue, IQueryBuilder::PARAM_STR); + $qbUpdate->setParameter('updateUserId', $userId, IQueryBuilder::PARAM_STR); + $qbUpdate->executeStatement(); + } + $req->closeCursor(); + } +} diff --git a/lib/Service/OpenAiSettingsService.php b/lib/Service/OpenAiSettingsService.php index 97743ddd..1b33c535 100644 --- a/lib/Service/OpenAiSettingsService.php +++ b/lib/Service/OpenAiSettingsService.php @@ -26,6 +26,8 @@ use Exception; use OCA\OpenAi\AppInfo\Application; use OCP\IConfig; +use OCP\PreConditionNotMetException; +use OCP\Security\ICrypto; class OpenAiSettingsService { private const ADMIN_CONFIG_TYPES = [ @@ -57,7 +59,10 @@ class OpenAiSettingsService { ]; - public function __construct(private IConfig $config) { + public function __construct( + private IConfig $config, + private ICrypto $crypto, + ) { } @@ -66,9 +71,11 @@ public function __construct(private IConfig $config) { /** * @return string + * @throws Exception */ public function getAdminApiKey(): string { - return $this->config->getAppValue(Application::APP_ID, 'api_key'); + $encryptedApiKey = $this->config->getAppValue(Application::APP_ID, 'api_key'); + return $encryptedApiKey === '' ? '' : $this->crypto->decrypt($encryptedApiKey); } /** @@ -76,11 +83,16 @@ public function getAdminApiKey(): string { * @param null|string $userId * @param boolean $fallBackOnAdminValue * @return string + * @throws Exception */ public function getUserApiKey(?string $userId, bool $fallBackOnAdminValue = false): string { $fallBackApiKey = $fallBackOnAdminValue ? $this->getAdminApiKey() : ''; - $userApiKey = $userId === null ? $fallBackApiKey : ($this->config->getUserValue($userId, Application::APP_ID, 'api_key', $fallBackApiKey) ?: $fallBackApiKey); - return $userApiKey; + if ($userId === null) { + return $fallBackApiKey; + } + $encryptedUserApiKey = $this->config->getUserValue($userId, Application::APP_ID, 'api_key'); + $userApiKey = $encryptedUserApiKey === '' ? '' : $this->crypto->decrypt($encryptedUserApiKey); + return $userApiKey ?: $fallBackApiKey; } /** @@ -186,11 +198,16 @@ public function getUserBasicUser(?string $userId, bool $fallBackOnAdminValue = t * @param string|null $userId * @param bool $fallBackOnAdminValue * @return string + * @throws Exception */ public function getUserBasicPassword(?string $userId, bool $fallBackOnAdminValue = true): string { - $fallBackBasicPassword = $fallBackOnAdminValue ? $this->config->getAppValue(Application::APP_ID, 'basic_password', '') : ''; - $basicPassword = $userId === null ? $fallBackBasicPassword : ($this->config->getUserValue($userId, Application::APP_ID, 'basic_password', $fallBackBasicPassword) ?: $fallBackBasicPassword); - return $basicPassword; + $fallBackBasicPassword = $fallBackOnAdminValue ? $this->getAdminBasicPassword() : ''; + if ($userId === null) { + return $fallBackBasicPassword; + } + $encryptedUserBasicPassword = $this->config->getUserValue($userId, Application::APP_ID, 'basic_password'); + $userBasicPassword = $encryptedUserBasicPassword === '' ? '' : $this->crypto->decrypt($encryptedUserBasicPassword); + return $userBasicPassword ?: $fallBackBasicPassword; } /** @@ -204,9 +221,11 @@ public function getAdminBasicUser(): string { /** * Get admin basic password * @return string + * @throws Exception */ public function getAdminBasicPassword(): string { - return $this->config->getAppValue(Application::APP_ID, 'basic_password', ''); + $encryptedBasicPassword = $this->config->getAppValue(Application::APP_ID, 'basic_password', ''); + return $encryptedBasicPassword === '' ? '' : $this->crypto->decrypt($encryptedBasicPassword); } /** @@ -324,16 +343,27 @@ public function setQuotas(array $quotas): void { */ public function setAdminApiKey(string $apiKey): void { // No need to validate. As long as it's a string, we're happy campers - $this->config->setAppValue(Application::APP_ID, 'api_key', $apiKey); + if ($apiKey === '') { + $this->config->setAppValue(Application::APP_ID, 'api_key', ''); + } else { + $encryptedApiKey = $this->crypto->encrypt($apiKey); + $this->config->setAppValue(Application::APP_ID, 'api_key', $encryptedApiKey); + } } /** * @param string $userId * @param string $apiKey + * @throws PreConditionNotMetException */ public function setUserApiKey(string $userId, string $apiKey): void { // No need to validate. As long as it's a string, we're happy campers - $this->config->setUserValue($userId, Application::APP_ID, 'api_key', $apiKey); + if ($apiKey === '') { + $this->config->setUserValue($userId, Application::APP_ID, 'api_key', ''); + } else { + $encryptedApiKey = $this->crypto->encrypt($apiKey); + $this->config->setUserValue($userId, Application::APP_ID, 'api_key', $encryptedApiKey); + } } /** @@ -443,13 +473,15 @@ public function setAdminBasicUser(string $basicUser): void { * @return void */ public function setAdminBasicPassword(string $basicPassword): void { - $this->config->setAppValue(Application::APP_ID, 'basic_password', $basicPassword); + $encryptedBasicPassword = $basicPassword === '' ? '' : $this->crypto->encrypt($basicPassword); + $this->config->setAppValue(Application::APP_ID, 'basic_password', $encryptedBasicPassword); } /** * @param string $userId * @param string $basicUser * @return void + * @throws PreConditionNotMetException */ public function setUserBasicUser(string $userId, string $basicUser): void { $this->config->setUserValue($userId, Application::APP_ID, 'basic_user', $basicUser); @@ -459,9 +491,11 @@ public function setUserBasicUser(string $userId, string $basicUser): void { * @param string $userId * @param string $basicPassword * @return void + * @throws PreConditionNotMetException */ public function setUserBasicPassword(string $userId, string $basicPassword): void { - $this->config->setUserValue($userId, Application::APP_ID, 'basic_password', $basicPassword); + $encryptedBasicPassword = $basicPassword === '' ? '' : $this->crypto->encrypt($basicPassword); + $this->config->setUserValue($userId, Application::APP_ID, 'basic_password', $encryptedBasicPassword); } /**