diff --git a/core/Command/Config/ListConfigs.php b/core/Command/Config/ListConfigs.php index e530466da61e8..45358d0c161b3 100644 --- a/core/Command/Config/ListConfigs.php +++ b/core/Command/Config/ListConfigs.php @@ -25,7 +25,6 @@ use OC\Core\Command\Base; use OC\SystemConfig; use OCP\IAppConfig; -use OCP\ILazyConfig; use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -38,7 +37,6 @@ class ListConfigs extends Base { public function __construct( protected SystemConfig $systemConfig, protected IAppConfig $appConfig, - protected ILazyConfig $lazyConfig, ) { parent::__construct(); } @@ -85,20 +83,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int $configs = [ 'system' => $this->getSystemConfigs($noSensitiveValues), 'apps' => [], - 'lazy' => [], ]; foreach ($apps as $appName) { $configs['apps'][$appName] = $this->getAppConfigs($appName, $noSensitiveValues); } - foreach($this->lazyConfig->getApps() as $appId) { - $configs['lazy'][$appId] = $this->getLazyConfigs($appId, $noSensitiveValues); - } break; default: $configs = [ 'apps' => [$app => $this->getAppConfigs($app, $noSensitiveValues)], - 'lazy' => [$app => $this->getLazyConfigs($app, $noSensitiveValues)] ]; } @@ -138,7 +131,8 @@ protected function getSystemConfigs(bool $noSensitiveValues): array { * @param bool $noSensitiveValues * @return array */ - protected function getAppConfigs(string $app, bool $noSensitiveValues): array { + protected function getAppConfigs(string $app, bool $noSensitiveValues) { +// return $this->appConfig->getAllValues($app, filtered: $noSensitiveValues); if ($noSensitiveValues) { return $this->appConfig->getFilteredValues($app); } else { @@ -146,14 +140,6 @@ protected function getAppConfigs(string $app, bool $noSensitiveValues): array { } } - protected function getLazyConfigs(string $app, bool $noSensitiveValues): array { - if ($noSensitiveValues) { - return $this->lazyConfig->getFilteredValues($app); - } else { - return $this->lazyConfig->getValues($app); - } - } - /** * @param string $argumentName * @param CompletionContext $context diff --git a/core/Command/Upgrade.php b/core/Command/Upgrade.php index 30acd8f7d4d78..4a723a4c119b7 100644 --- a/core/Command/Upgrade.php +++ b/core/Command/Upgrade.php @@ -46,6 +46,7 @@ use OC\Updater; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; use OCP\IConfig; use OCP\Util; use Psr\Log\LoggerInterface; @@ -63,9 +64,7 @@ class Upgrade extends Command { public const ERROR_FAILURE = 5; public function __construct( - private IConfig $config, - private LoggerInterface $logger, - private Installer $installer, + private IConfig $config ) { parent::__construct(); } @@ -91,12 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $self = $this; - $updater = new Updater( - $this->config, - \OC::$server->getIntegrityCodeChecker(), - $this->logger, - $this->installer - ); + $updater = \OCP\Server::get(Updater::class); /** @var IEventDispatcher $dispatcher */ $dispatcher = \OC::$server->get(IEventDispatcher::class); diff --git a/core/Migrations/Version29000Date20231126110901.php b/core/Migrations/Version29000Date20231126110901.php index e853d23444e05..31dd3333dd2f8 100644 --- a/core/Migrations/Version29000Date20231126110901.php +++ b/core/Migrations/Version29000Date20231126110901.php @@ -31,30 +31,31 @@ use OCP\Migration\IOutput; use OCP\Migration\SimpleMigrationStep; -// Create new tables for the ILazyConfig API (lazyconfig). +// Create new field in appconfig for the new IAppConfig API, including lazy grouping. class Version29000Date20231126110901 extends SimpleMigrationStep { public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); - $updated = false; - - if (!$schema->hasTable('appconfig_lazy')) { - $table = $schema->createTable('appconfig_lazy'); - $table->addColumn('app_id', Types::STRING, ['length' => 32]); - $table->addColumn('extra', Types::STRING, ['length' => 32]); - $table->addColumn('config_key', Types::STRING, ['length' => 64]); - $table->addColumn('config_value', Types::TEXT); - - $table->setPrimaryKey(['app_id', 'config_key'], 'lazy_app_prim'); - $table->addIndex(['app_id', 'extra'], 'lazy_app_id_i'); - $table->addIndex(['app_id', 'extra', 'config_key'], 'lazy_app_id_key_i'); - $updated = true; + + if (!$schema->hasTable('appconfig')) { + return null; } - if (!$updated) { + $table = $schema->getTable('appconfig'); + if ($table->hasColumn('lazy_group')) { return null; } + $table->addColumn('lazy_group', Types::STRING, ['length' => 32, 'default' => '']); + $table->addColumn('sensitive', Types::SMALLINT, ['length' => 1, 'default' => '0']); + + if ($table->hasIndex('appconfig_config_key_index')) { + $table->dropIndex('appconfig_config_key_index'); + } + + $table->addIndex(['appid', 'lazy_group'], 'ac_app_lazy_i'); + $table->addIndex(['appid', 'lazy_group', 'config_key'], 'ac_app_lazy_key_i'); + return $schema; } } diff --git a/core/ajax/update.php b/core/ajax/update.php index a826b6b7e7a19..af4bb83d95bea 100644 --- a/core/ajax/update.php +++ b/core/ajax/update.php @@ -40,11 +40,14 @@ use OC\Repair\Events\RepairWarningEvent; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; +use OCP\IConfig; use OCP\IEventSource; use OCP\IEventSourceFactory; use OCP\IL10N; use OCP\ILogger; use OCP\L10N\IFactory; +use OCP\Server; if (!str_contains(@ini_get('disable_functions'), 'set_time_limit')) { @set_time_limit(0); @@ -112,9 +115,10 @@ public function handleRepairFeedback(Event $event): void { \OC_User::setIncognitoMode(true); $logger = \OC::$server->get(\Psr\Log\LoggerInterface::class); - $config = \OC::$server->getConfig(); + $config = Server::get(IConfig::class); $updater = new \OC\Updater( $config, + Server::get(IAppConfig::class), \OC::$server->getIntegrityCodeChecker(), $logger, \OC::$server->query(\OC\Installer::class) diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index 9cefd62787b2b..cedb3fbaca165 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -36,7 +36,6 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; -use OCP\ILazyConfig; use OCP\PreConditionNotMetException; /** @@ -44,7 +43,6 @@ */ class AllConfig implements IConfig { private ?IDBConnection $connection = null; - private ?ILazyConfig $lazyConfig = null; /** * 3 dimensional array with the following structure: @@ -548,38 +546,4 @@ public function getUsersForUserValueCaseInsensitive($appName, $key, $value) { public function getSystemConfig() { return $this->systemConfig; } - - public function getLazyConfig(): ILazyConfig { - if ($this->lazyConfig === null) { - $this->lazyConfig = \OCP\Server::get(ILazyConfig::class); - } - - return $this->lazyConfig; - } - - /** - * export AppConfig keys/values into LazyConfig - * - * $this->config->exportToLazy('my_app', [ - * 'first-config' => 'default_value', - * 'my-other-config' => 12, - * 'boolean-flag' => '0' - * ]); - * - * @param string $app - * @param array $configKeys an array with keys to export as index, default as value - * @since 29.0.0 - */ - public function exportToLazy(string $app, array $configKeys): void { - foreach ($configKeys as $key => $default) { - $value = $this->getAppValue($app, $key, (string)$default); - if ($this->getLazyConfig()->hasKey($app, $key) - || $default === $value) { - continue; - } - - $this->getLazyConfig()->setValueString($app, $key, $value); - $this->deleteAppValue($app, $key); - } - } } diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index 79c650705b23d..1cb9533593f49 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -1,4 +1,6 @@ * @copyright Copyright (c) 2016, ownCloud, Inc. @@ -9,6 +11,7 @@ * @author Jakob Sack * @author Joas Schilling * @author Jörn Friedrich Dreyer + * @author Maxence Lange * @author michaelletzgus * @author Morris Jobke * @author Robin Appelman @@ -30,147 +33,42 @@ * along with this program. If not, see * */ + namespace OC; -use OC\DB\Connection; -use OC\DB\OracleConnection; -use OCP\DB\QueryBuilder\IQueryBuilder; +use JsonException; +use OCP\DB\Exception as DBException; use OCP\IAppConfig; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IConfig; +use OCP\IDBConnection; +use Psr\Log\LoggerInterface; /** * This class provides an easy way for apps to store config values in the * database. */ class AppConfig implements IAppConfig { - /** @var array[] */ - protected $sensitiveValues = [ - 'circles' => [ - '/^key_pairs$/', - '/^local_gskey$/', - ], - 'external' => [ - '/^sites$/', - ], - 'integration_discourse' => [ - '/^private_key$/', - '/^public_key$/', - ], - 'integration_dropbox' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_github' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_gitlab' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_google' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_jira' => [ - '/^client_id$/', - '/^client_secret$/', - '/^forced_instance_url$/', - ], - 'integration_onedrive' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_openproject' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_reddit' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_suitecrm' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_twitter' => [ - '/^consumer_key$/', - '/^consumer_secret$/', - '/^followed_user$/', - ], - 'integration_zammad' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'notify_push' => [ - '/^cookie$/', - ], - 'spreed' => [ - '/^bridge_bot_password$/', - '/^hosted-signaling-server-(.*)$/', - '/^recording_servers$/', - '/^signaling_servers$/', - '/^signaling_ticket_secret$/', - '/^signaling_token_privkey_(.*)$/', - '/^signaling_token_pubkey_(.*)$/', - '/^sip_bridge_dialin_info$/', - '/^sip_bridge_shared_secret$/', - '/^stun_servers$/', - '/^turn_servers$/', - '/^turn_server_secret$/', - ], - 'support' => [ - '/^last_response$/', - '/^potential_subscription_key$/', - '/^subscription_key$/', - ], - 'theming' => [ - '/^imprintUrl$/', - '/^privacyUrl$/', - '/^slogan$/', - '/^url$/', - ], - 'user_ldap' => [ - '/^(s..)?ldap_agent_password$/', - ], - 'user_saml' => [ - '/^idp-x509cert$/', - ], - ]; - - /** @var Connection */ - protected $conn; - - /** @var array[] */ - private $cache = []; - - /** @var bool */ - private $configLoaded = false; - - /** - * @param Connection $conn - */ - public function __construct(Connection $conn) { - $this->conn = $conn; + private const CACHE_PREFIX = 'core/appconfig'; + private const CACHE_TTL = 3600; + private const ALL_APPS_CONFIG = '__ALL__'; + + private array $cache = []; // cached config + private array $loaded = []; // list of lazy group loaded + private ?ICache $distributedCache = null; + + /** @deprecated */ + private bool $configLoaded = false; + + public function __construct( + ICacheFactory $cacheFactory, + protected IDBConnection $connection, + private LoggerInterface $logger, + ) { + $this->loadDistributedCache($cacheFactory); } - /** - * @param string $app - * @return array - */ - private function getAppValues($app) { - $this->loadConfigValues(); - - if (isset($this->cache[$app])) { - return $this->cache[$app]; - } - - return []; - } /** * Get all apps using the config @@ -180,218 +78,659 @@ private function getAppValues($app) { * This function returns a list of all apps that have at least one * entry in the appconfig table. */ - public function getApps() { - $this->loadConfigValues(); + public function getApps(bool $loadValues = true): array { + if ($loadValues) { + $this->loadConfigAll(); + $keys = array_keys($this->cache); + sort($keys); + return $keys; + } + + $sql = $this->connection->getQueryBuilder(); + $sql->selectDistinct('appid') + ->from('appconfig') + ->orderBy('appid', 'asc') + ->groupBy('appid'); + $result = $sql->execute(); + + $rows = $result->fetchAll(); + $apps = []; + foreach ($rows as $row) { + $apps[] = $row['appid']; + } - return $this->getSortedKeys($this->cache); + return $apps; } /** * Get the available keys for an app * * @param string $app the app we are looking for + * * @return array an array of key names * * This function gets all keys of an app. Please note that the values are * not returned. */ - public function getKeys($app) { - $this->loadConfigValues(); - - if (isset($this->cache[$app])) { - return $this->getSortedKeys($this->cache[$app]); + public function getKeys(string $app): array { + if ($app === '') { + throw new \Exception('app cannot be an empty string'); } - return []; - } - - public function getSortedKeys($data) { - $keys = array_keys($data); + $this->loadConfig($app, ignoreLazyGroup: true); + $keys = array_keys($this->cache[$app] ?? []); sort($keys); return $keys; } /** - * Gets the config value + * check if a key is set in the appconfig * - * @param string $app app - * @param string $key key - * @param string $default = null, default value if the key does not exist - * @return string the value or $default + * @param string $app + * @param string $key * - * This function gets a value from the appconfig table. If the key does - * not exist the default value will be returned + * @return bool */ - public function getValue($app, $key, $default = null) { - $this->loadConfigValues(); + public function hasKey(string $app, string $key, string $lazyGroup = ''): bool { + if ($app === '') { + throw new \Exception('app cannot be an empty string'); + } + $this->loadConfig($app, $lazyGroup); - if ($this->hasKey($app, $key)) { - return $this->cache[$app][$key]; + return isset($this->cache[$app][$key]); + } + + + + + + + + + + + + /** + * @param string $app + * @param string $key + * @param string $default + * + * @inheritDoc + * @return string + * @since 29.0.0 + */ + public function getValueString(string $app, string $key, string $default = '', string $lazyGroup = ''): string { + $this->loadConfig($app); + if (!$this->hasKey($app, $key, $lazyGroup)) { + return $default; } - return $default; + return $this->cache[$app][$key]; } /** - * check if a key is set in the appconfig + * @param string $app + * @param string $key + * @param int $default * + * @inheritDoc + * @return int + * @since 29.0.0 + */ + public function getValueInt(string $app, string $key, int $default = 0, string $lazyGroup = ''): int { + return (int) $this->getValueString($app, $key, (string)$default); + } + + /** * @param string $app * @param string $key + * @param bool $default + * + * @inheritDoc * @return bool + * @since 29.0.0 */ - public function hasKey($app, $key) { - $this->loadConfigValues(); + public function getValueBool(string $app, string $key, bool $default = false, string $lazyGroup = ''): bool { + return in_array($this->getValueString($app, $key, $default ? 'true' : 'false'), ['1', 'true', 'yes']); + } - return isset($this->cache[$app][$key]); + /** + * @param string $app + * @param string $key + * @param array $default + * + * @inheritDoc + * @return array + * @since 29.0.0 + */ + public function getValueArray(string $app, string $key, array $default = [], string $lazyGroup = ''): array { + try { + $defaultJson = json_encode($default, JSON_THROW_ON_ERROR); + $value = json_decode($this->getValueString($app, $key, $defaultJson), true, JSON_THROW_ON_ERROR); + return (is_array($value)) ? $value : [$value]; + } catch (JsonException) { + return []; + } } + + + + + + + + + /** - * Sets a value. If the key did not exist before it will be created. + * @param string $app + * @param string $key + * @param string $value * - * @param string $app app - * @param string $key key - * @param string|float|int $value value - * @return bool True if the value was inserted or updated, false if the value was the same + * @inheritDoc + * @return bool + * @throws DBException + * @since 29.0.0 */ - public function setValue($app, $key, $value) { - if (!$this->hasKey($app, $key)) { - $inserted = (bool) $this->conn->insertIfNotExist('*PREFIX*appconfig', [ - 'appid' => $app, - 'configkey' => $key, - 'configvalue' => $value, - ], [ - 'appid', - 'configkey', - ]); - - if ($inserted) { - if (!isset($this->cache[$app])) { - $this->cache[$app] = []; - } + public function setValueString( + string $app, + string $key, + string $value, + bool $sensitive = false, + string $lazyGroup = '' + ): bool { + $this->loadConfig($app); + $updated = !$this->hasKey($app, $key, $lazyGroup) || $value !== $this->getValueString($app, $key, $value, $lazyGroup); + if (!$updated) { + return false; + } - $this->cache[$app][$key] = $value; - return true; + $insert = $this->connection->getQueryBuilder(); + $insert->insert('appconfig') + ->setValue('appid', $insert->createNamedParameter($app)) + ->setValue('lazy_group', $insert->createNamedParameter($lazyGroup)) + ->setValue('configkey', $insert->createNamedParameter($key)) + ->setValue('configvalue', $insert->createNamedParameter($value)); + try { + $insert->executeStatement(); + } catch (DBException $e) { + if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + throw $e; // throw exception or just log and returns false !? } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('configvalue', $update->createNamedParameter($value)) + ->set('lazy_group', $update->createNamedParameter($lazyGroup)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + $update->executeStatement(); } - $sql = $this->conn->getQueryBuilder(); - $sql->update('appconfig') - ->set('configvalue', $sql->createNamedParameter($value)) - ->where($sql->expr()->eq('appid', $sql->createNamedParameter($app))) - ->andWhere($sql->expr()->eq('configkey', $sql->createNamedParameter($key))); - - /* - * Only limit to the existing value for non-Oracle DBs: - * http://docs.oracle.com/cd/E11882_01/server.112/e26088/conditions002.htm#i1033286 - * > Large objects (LOBs) are not supported in comparison conditions. - */ - if (!($this->conn instanceof OracleConnection)) { - /* - * Only update the value when it is not the same - * Note that NULL requires some special handling. Since comparing - * against null can have special results. - */ - - if ($value === null) { - $sql->andWhere( - $sql->expr()->isNotNull('configvalue') - ); - } else { - $sql->andWhere( - $sql->expr()->orX( - $sql->expr()->isNull('configvalue'), - $sql->expr()->neq('configvalue', $sql->createNamedParameter($value), IQueryBuilder::PARAM_STR) - ) - ); - } + return true; + } + + /** + * @param string $app + * @param string $key + * @param int $value + * + * @inheritDoc + * @return bool + * @throws DBException + * @since 29.0.0 + */ + public function setValueInt(string $app, string $key, int $value, bool $sensitive = false, string $lazyGroup = ''): bool { + return $this->setValueString($app, $key, (string) $value); + } + + /** + * @param string $app + * @param string $key + * @param bool $value + * + * @inheritDoc + * @return bool + * @throws DBException + * @since 29.0.0 + */ + public function setValueBool(string $app, string $key, bool $value, bool $sensitive = false, string $lazyGroup = ''): bool { + return $this->setValueString($app, $key, $value ? 'true' : 'false'); + } + + /** + * @param string $app + * @param string $key + * @param array $value + * + * @inheritDoc + * @return bool + * @throws DBException + * @since 29.0.0 + */ + public function setValueArray(string $app, string $key, array $value, bool $sensitive = false, string $lazyGroup = ''): bool { + try { + return $this->setValueString($app, $key, json_encode($value, JSON_THROW_ON_ERROR)); + } catch (JsonException $e) { + $this->logger->warning('could not setValueArray', ['exception' => $e]); } - $changedRow = (bool) $sql->execute(); + return false; + } + + + + - $this->cache[$app][$key] = $value; - return $changedRow; + + /** + * Remove app from appconfig + * + * **note**: since 29.0.0 does not return false anymore + * + * @param string $app app + * + * Removes all keys in appconfig belonging to the app. + */ + public function deleteApp(string $app): void { + $this->loadConfig($app); + + $sql = $this->connection->getQueryBuilder(); + $sql->delete('appconfig') + ->where($sql->expr()->eq('appid', $sql->createNamedParameter($app))); + $sql->executeStatement(); + + $this->refreshCache($app); + } + + public function deleteLazyGroup(string $app, string $lazyGroup): void { } /** * Deletes a key * + * **note** since 29.0.0 does not return false anymore. + * * @param string $app app * @param string $key key - * @return boolean + * + * @since 20.0.0 */ - public function deleteKey($app, $key) { - $this->loadConfigValues(); + public function deleteKey(string $app, string $key, $lazyGroup = ''): void { + if (!$this->hasKey($app, $key, $lazyGroup) && ($this->requestLazyGroup($app, $key) === null)) { + return; // key does not exists. + } - $sql = $this->conn->getQueryBuilder(); - $sql->delete('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) - ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey'))) - ->setParameter('app', $app) - ->setParameter('configkey', $key); - $sql->execute(); + $qb = $this->connection->getQueryBuilder(); + $qb->delete('appconfig') + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))) + ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key))); + $qb->executeStatement(); unset($this->cache[$app][$key]); - return false; + $this->storeDistributedCache(); } /** - * Remove app from appconfig + * @inheritDoc + * @param string $app + * + * @since 29.0.0 + */ + public function refreshCache(string $app = ''): void { + if ($app !== '') { + unset($this->cache[$app]); + unset($this->loaded[$app]); + $this->storeDistributedCache(); + return; + } + + $this->cache = []; + $this->loaded = []; + $this->loadConfigAll(); + $this->storeDistributedCache(); + } + + private function loadConfigAll(): void { + $this->loadConfig(self::ALL_APPS_CONFIG, ignoreLazyGroup: true); + } + + /** + * @param string $app + * @param string $lazyGroup + * @param bool $ignoreLazyGroup + */ + private function loadConfig(string $app, string $lazyGroup = '', bool $ignoreLazyGroup = false): void { + if ($this->isLoaded($app, $lazyGroup)) { + return; + } + + $qb = $this->connection->getQueryBuilder(); + $qb->select('appid', 'configkey', 'configvalue', 'lazy_group') + ->from('appconfig'); + + if ($app !== self::ALL_APPS_CONFIG) { + $qb->andWhere($qb->expr()->eq('appid', $qb->createNamedParameter($app))); + } + if (!$ignoreLazyGroup) { + $qb->andWhere($qb->expr()->eq('lazy_group', $qb->createNamedParameter($lazyGroup))); + } + $result = $qb->executeQuery(); + + $rows = $result->fetchAll(); + foreach ($rows as $row) { + $this->cache[$row['appid']][$row['configkey']] = $row['configvalue']; + $this->setLoadedStatus($row['appid'], $row['lazy_group']); + } + + if ($app === self::ALL_APPS_CONFIG) { + $this->setLoadedStatus(self::ALL_APPS_CONFIG, $lazyGroup); + } + + $result->closeCursor(); + $this->storeDistributedCache(); + } + + + /** + * return assigned lazy_group from database. + * + * @param string $app + * @param string $key + * + * @return string|null NULL if key is not know + */ + private function requestLazyGroup(string $app, string $key): ?string { + $qb = $this->connection->getQueryBuilder(); + $qb->select('lazy_group') + ->from('appconfig') + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))) + ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key))); + $result = $qb->executeQuery(); + $row = $result->fetch(); + + if (!$row) { + return null; + } + + $result->closeCursor(); + + return $row['lazy_group']; + } + + private function isLoaded(string $app, string $lazyGroup): bool { + if ($app !== self::ALL_APPS_CONFIG && !array_key_exists($app, $this->cache)) { + return false; + } + + if (!in_array($lazyGroup, $this->loaded[$app] ?? [])) { + return false; + } + + return true; + } + + private function setLoadedStatus(string $app, string $lazyGroup): void { + if (in_array($lazyGroup, $this->loaded[$app] ?? [])) { + return; + } + + $this->loaded[$app][] = $lazyGroup; + } + + + + private function loadDistributedCache(ICacheFactory $cacheFactory): void { + if (!$cacheFactory->isAvailable()) { + return; + } + + $this->distributedCache = $cacheFactory->createDistributed(self::CACHE_PREFIX); + try { + $this->cache = json_decode($this->distributedCache->get('cache'), true, 32, JSON_THROW_ON_ERROR); + $this->loaded = + json_decode($this->distributedCache->get('loaded'), true, 32, JSON_THROW_ON_ERROR); + } catch (JsonException) { + $this->cache = []; + $this->loaded = []; + $this->logger->warning('AppConfig distributed cache is corrupted'); + $this->storeDistributedCache(); + } + } + + /** + * update local cache into distributed system + * + * @param bool $onlyCache + * + * @return void + */ + private function storeDistributedCache(): void { + if ($this->distributedCache === null) { + return; + } + + try { + $cache = json_encode($this->cache, JSON_THROW_ON_ERROR); + $loaded = json_encode($this->loaded, JSON_THROW_ON_ERROR); + $this->distributedCache->set('cache', $cache, self::CACHE_TTL); + $this->distributedCache->set('loaded', $loaded, self::CACHE_TTL); + } catch (JsonException) { + $this->logger->warning('...'); + return; + } + } + + + public function getAllValues(string $app, string $key = '', bool $filtered = false): array { + $this->loadConfig($app, ignoreLazyGroup: true); + // TODO: filtered=true + return $this->cache[$app]; + } + + public function searchValues(string $key, string $lazyGroup = ''): array { + $this->loadConfig(self::ALL_APPS_CONFIG, $lazyGroup); + $values = []; + foreach(array_keys($this->cache) as $app) { + if (isset($this->cache[$app][$key])) { + $values[$app] = $this->cache[$app][$key]; + } + } + + return $values; + } + + + // + // + // + // -- DEPRECATED + // + // + + /** + * Gets the config value * * @param string $app app - * @return boolean + * @param string $key key + * @param string $default = null, default value if the key does not exist * - * Removes all keys in appconfig belonging to the app. + * @return string the value or $default + * @deprecated - use getValue*() + * + * This function gets a value from the appconfig table. If the key does + * not exist the default value will be returned */ - public function deleteApp($app) { - $this->loadConfigValues(); + public function getValue($app, $key, $default = null) { + $this->loadConfig($app); - $sql = $this->conn->getQueryBuilder(); - $sql->delete('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) - ->setParameter('app', $app); - $sql->execute(); + if ($this->hasKey($app, $key)) { + return $this->cache[$app][$key]; + } - unset($this->cache[$app]); - return false; + return $default; } + /** + * Sets a value. If the key did not exist before it will be created. + * @deprecated + * + * @param string $app app + * @param string $key key + * @param string|float|int $value value + * + * @return bool True if the value was inserted or updated, false if the value was the same + */ + public function setValue($app, $key, $value) { + return $this->setValueString($app, $key, (string)$value); + } + + /** * get multiple values, either the app or key can be used as wildcard by setting it to false * * @param string|false $app * @param string|false $key + * * @return array|false + * @deprecated 29.0.0 use getAllValues() */ public function getValues($app, $key) { if (($app !== false) === ($key !== false)) { return false; } - - if ($key === false) { - return $this->getAppValues($app); + if (!$app) { return $this->searchValues($key); } else { - $appIds = $this->getApps(); - $values = array_map(function ($appId) use ($key) { - return $this->cache[$appId][$key] ?? null; - }, $appIds); - $result = array_combine($appIds, $values); - - return array_filter($result); + return $this->getAllValues($app); } } +// /** +// * @param string $app +// * +// * @return array +// */ +// private function getAppValues($app) { +// $this->loadConfigValues(); +// +// if (isset($this->cache[$app])) { +// return $this->cache[$app]; +// } +// +// return []; +// } + /** * get all values of the app or and filters out sensitive data * * @param string $app + * * @return array + * @deprecated 29.0.0 use getAllFilteredValues() */ public function getFilteredValues($app) { - $values = $this->getValues($app, false); - - if (isset($this->sensitiveValues[$app])) { - foreach ($this->sensitiveValues[$app] as $sensitiveKeyExp) { + $values = $this->getAllValues($app, filtered: true); + $sensitiveValues = [ + 'circles' => [ + '/^key_pairs$/', + '/^local_gskey$/', + ], + 'external' => [ + '/^sites$/', + ], + 'integration_discourse' => [ + '/^private_key$/', + '/^public_key$/', + ], + 'integration_dropbox' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_github' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_gitlab' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_google' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_jira' => [ + '/^client_id$/', + '/^client_secret$/', + '/^forced_instance_url$/', + ], + 'integration_onedrive' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_openproject' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_reddit' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_suitecrm' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_twitter' => [ + '/^consumer_key$/', + '/^consumer_secret$/', + '/^followed_user$/', + ], + 'integration_zammad' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'notify_push' => [ + '/^cookie$/', + ], + 'spreed' => [ + '/^bridge_bot_password$/', + '/^hosted-signaling-server-(.*)$/', + '/^recording_servers$/', + '/^signaling_servers$/', + '/^signaling_ticket_secret$/', + '/^signaling_token_privkey_(.*)$/', + '/^signaling_token_pubkey_(.*)$/', + '/^sip_bridge_dialin_info$/', + '/^sip_bridge_shared_secret$/', + '/^stun_servers$/', + '/^turn_servers$/', + '/^turn_server_secret$/', + ], + 'support' => [ + '/^last_response$/', + '/^potential_subscription_key$/', + '/^subscription_key$/', + ], + 'theming' => [ + '/^imprintUrl$/', + '/^privacyUrl$/', + '/^slogan$/', + '/^url$/', + ], + 'user_ldap' => [ + '/^(s..)?ldap_agent_password$/', + ], + 'user_saml' => [ + '/^idp-x509cert$/', + ], + ]; + + if (isset($sensitiveValues[$app])) { + foreach ($sensitiveValues[$app] as $sensitiveKeyExp) { $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values)); foreach ($sensitiveKeys as $sensitiveKey) { $values[$sensitiveKey] = IConfig::SENSITIVE_VALUE; @@ -404,6 +743,8 @@ public function getFilteredValues($app) { /** * Load all the app config values + * + * @deprecated since 29 */ protected function loadConfigValues() { if ($this->configLoaded) { @@ -412,7 +753,7 @@ protected function loadConfigValues() { $this->cache = []; - $sql = $this->conn->getQueryBuilder(); + $sql = $this->connection->getQueryBuilder(); $sql->select('*') ->from('appconfig'); $result = $sql->execute(); @@ -435,6 +776,8 @@ protected function loadConfigValues() { /** * Clear all the cached app config values * New cache will be generated next time a config value is retrieved + * + * @deprecated use clearCache(); */ public function clearCachedConfig(): void { $this->configLoaded = false; diff --git a/lib/private/LazyConfig.php b/lib/private/LazyConfig.php deleted file mode 100644 index 9b532d6c59c9c..0000000000000 --- a/lib/private/LazyConfig.php +++ /dev/null @@ -1,566 +0,0 @@ - - * - * @author Maxence Lange - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -namespace OC; - -use JsonException; -use OC\DB\Connection; -use OCP\DB\Exception as DBException; -use OCP\IConfig; -use OCP\ILazyConfig; -use Psr\Log\LoggerInterface; - -/** - * Lazy load of your app config values. - * - * store a value: - * $this->lazyConfig->setValueString('my_app', 'my_param', 'configuration_value'); - * - * retrieve a value: - * $this->lazyConfig->getValueString('my_app', 'my_param', 'default_value'); - * - * The caching is made in a way that all configured value of my_app - * are retrieved when getValue*() is called for the first time - * - * There is a way to get values only when needed, without filling the cache with rarely needed values - * when getValue*() is called with 'my_app'. A group of values can be set by using 'my_app.my_group': - * - * $this->lazyConfig->setValueString('my_app.my_group', 'my_param', 'configuration_value'); - * $this->lazyConfig->getValueString('my_app.my_group', 'my_param', 'default_value'); - * - * Values set in group 'my_group' are not cached when calling getValue*() on 'my_app', but - * only when using 'my_app.my_group'. - * - * @since 29.0.0 - */ -class LazyConfig implements ILazyConfig { - public const APP_EXTRA_SEPARATOR = '.'; - - private array $cache = []; // cached config - private array $extra = []; // list of loaded extra config - protected array $sensitiveValues = [ - 'circles' => [ - '/^key_pairs$/', - '/^local_gskey$/', - ], - 'external' => [ - '/^sites$/', - ], - 'integration_discourse' => [ - '/^private_key$/', - '/^public_key$/', - ], - 'integration_dropbox' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_github' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_gitlab' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_google' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_jira' => [ - '/^client_id$/', - '/^client_secret$/', - '/^forced_instance_url$/', - ], - 'integration_onedrive' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_openproject' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_reddit' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_suitecrm' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_twitter' => [ - '/^consumer_key$/', - '/^consumer_secret$/', - '/^followed_user$/', - ], - 'integration_zammad' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'notify_push' => [ - '/^cookie$/', - ], - 'spreed' => [ - '/^bridge_bot_password$/', - '/^hosted-signaling-server-(.*)$/', - '/^recording_servers$/', - '/^signaling_servers$/', - '/^signaling_ticket_secret$/', - '/^signaling_token_privkey_(.*)$/', - '/^signaling_token_pubkey_(.*)$/', - '/^sip_bridge_dialin_info$/', - '/^sip_bridge_shared_secret$/', - '/^stun_servers$/', - '/^turn_servers$/', - '/^turn_server_secret$/', - ], - 'support' => [ - '/^last_response$/', - '/^potential_subscription_key$/', - '/^subscription_key$/', - ], - 'theming' => [ - '/^imprintUrl$/', - '/^privacyUrl$/', - '/^slogan$/', - '/^url$/', - ], - 'user_ldap' => [ - '/^(s..)?ldap_agent_password$/', - ], - 'user_saml' => [ - '/^idp-x509cert$/', - ], - ]; - - public function __construct( - private Connection $connection, - private LoggerInterface $logger, - ) { - } - - /** - * **WARNING**: will load all lazy configs from database, unless $loadValues is false - * - * @param bool $loadValues - * - * @inheritDoc - * @return array - * @throws DBException - * @since 29.0.0 - */ - public function getApps(bool $loadValues = true): array { - if ($loadValues) { - $this->loadConfigAll(); - return array_keys($this->cache); - } - - $sql = $this->connection->getQueryBuilder(); - $sql->selectDistinct('app_id') - ->from('appconfig_lazy') - ->groupBy('app_id'); - $result = $sql->execute(); - - $rows = $result->fetchAll(); - $apps = []; - foreach ($rows as $row) { - $apps[] = $row['app_id']; - } - - return $apps; - } - - /** - * @param string $app - * - * @inheritDoc - * @return array - * @throws DBException - * @since 29.0.0 - */ - public function getKeys(string $app): array { - $appId = $this->loadConfig($app); - return array_keys($this->cache[$appId]); - } - - /** - * @param string $app - * @param string $key - * - * @inheritDoc - * @return bool - * @since 29.0.0 - */ - public function hasKey(string $app, string $key): bool { - $appId = $this->loadConfig($app); - return isset($this->cache[$appId][$key]); - } - - /** - * @param string $app - * @param string $key - * - * @inheritDoc - * @return array - * @since 29.0.0 - */ - public function getValues(string $app, string $key = ''): array { - $appId = $this->loadConfig($app, true); - - if ($key === '') { - return $this->cache[$appId] ?? []; - } - - $values = []; - foreach (($this->cache[$appId] ?? []) as $configKey => $configValue) { - if (str_starts_with($configKey, $key)) { - $values[$configKey] = $configValue; - } - } - - return $values; - } - - /** - * @param string $app - * - * @inheritDoc - * @return array - * @since 29.0.0 - */ - public function getFilteredValues(string $app): array { - $values = $this->getValues($app); - foreach (($this->sensitiveValues[$app] ?? []) as $sensitiveKeyExp) { - $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values)); - foreach ($sensitiveKeys as $sensitiveKey) { - $values[$sensitiveKey] = IConfig::SENSITIVE_VALUE; - } - } - - return $values; - } - - /** - * @param string $app - * @param string $key - * @param string $default - * - * @inheritDoc - * @return string - * @since 29.0.0 - */ - public function getValueString(string $app, string $key, string $default = ''): string { - $appId = $this->loadConfig($app); - if (!$this->hasKey($app, $key)) { - return $default; - } - - return $this->cache[$appId][$key]; - } - - /** - * @param string $app - * @param string $key - * @param int $default - * - * @inheritDoc - * @return int - * @since 29.0.0 - */ - public function getValueInt(string $app, string $key, int $default = 0): int { - return (int) $this->getValueString($app, $key, (string)$default); - } - - /** - * @param string $app - * @param string $key - * @param bool $default - * - * @inheritDoc - * @return bool - * @since 29.0.0 - */ - public function getValueBool(string $app, string $key, bool $default = false): bool { - return in_array($this->getValueString($app, $key, $default ? 'true' : 'false'), ['1', 'true', 'yes']); - } - - /** - * @param string $app - * @param string $key - * @param array $default - * - * @inheritDoc - * @return array - * @since 29.0.0 - */ - public function getValueArray(string $app, string $key, array $default = []): array { - try { - $defaultJson = json_encode($default, JSON_THROW_ON_ERROR); - $value = json_decode($this->getValueString($app, $key, $defaultJson), true, JSON_THROW_ON_ERROR); - return (is_array($value)) ? $value : [$value]; - } catch (JsonException) { - return []; - } - } - - /** - * @param string $app - * @param string $key - * @param string $value - * - * @inheritDoc - * @return bool - * @throws DBException - * @since 29.0.0 - */ - public function setValueString(string $app, string $key, string $value): bool { - $appId = $this->loadConfig($app); - $updated = !$this->hasKey($app, $key) || $value !== $this->getValueString($app, $key); - if (!$updated) { - return false; - } - - [, $extra] = $this->parseExtra($app); - $insert = $this->connection->getQueryBuilder(); - $insert->insert('appconfig_lazy') - ->setValue('app_id', $insert->createNamedParameter($appId)) - ->setValue('extra', $insert->createNamedParameter($extra)) - ->setValue('config_key', $insert->createNamedParameter($key)) - ->setValue('config_value', $insert->createNamedParameter($value)); - try { - $insert->executeStatement(); - } catch (DBException $e) { - if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { - throw $e; // throw exception or just log and returns false !? - } - - $update = $this->connection->getQueryBuilder(); - $update->update('appconfig_lazy') - ->set('config_value', $update->createNamedParameter($value)) - ->set('extra', $update->createNamedParameter($extra)) - ->where($update->expr()->eq('app_id', $update->createNamedParameter($app))) - ->andWhere($update->expr()->eq('config_key', $update->createNamedParameter($key))); - $update->executeStatement(); - } - - return true; - } - - /** - * @param string $app - * @param string $key - * @param int $value - * - * @inheritDoc - * @return bool - * @throws DBException - * @since 29.0.0 - */ - public function setValueInt(string $app, string $key, int $value): bool { - return $this->setValueString($app, $key, (string) $value); - } - - /** - * @param string $app - * @param string $key - * @param bool $value - * - * @inheritDoc - * @return bool - * @throws DBException - * @since 29.0.0 - */ - public function setValueBool(string $app, string $key, bool $value): bool { - return $this->setValueString($app, $key, $value ? 'true' : 'false'); - } - - /** - * @param string $app - * @param string $key - * @param array $value - * - * @inheritDoc - * @return bool - * @throws DBException - * @since 29.0.0 - */ - public function setValueArray(string $app, string $key, array $value): bool { - try { - return $this->setValueString($app, $key, json_encode($value, JSON_THROW_ON_ERROR)); - } catch (JsonException $e) { - $this->logger->warning('could not setValueArray', ['exception' => $e]); - } - - return false; - } - - /** - * @param string $app - * @param string $key - * - * @inheritDoc - * @throws DBException - * @since 29.0.0 - */ - public function unsetKey(string $app, string $key): void { - if (!$this->hasKey($app, $key)) { - return; - } - - $qb = $this->connection->getQueryBuilder(); - $qb->delete('appconfig_lazy') - ->where($qb->expr()->eq('app_id', $qb->createNamedParameter($app))) - ->andWhere($qb->expr()->eq('config_key', $qb->createNamedParameter($key))); - $qb->executeStatement(); - - unset($this->cache[$app][$key]); - } - - /** - * @param string $app - * - * @inheritDoc - * @throws DBException - * @since 29.0.0 - */ - public function deleteApp(string $app): void { - $appId = $this->loadConfig($app); - //[$appId,] = $this->parseExtra($app); - - $sql = $this->connection->getQueryBuilder(); - $sql->delete('appconfig_lazy') - ->where($sql->expr()->eq('app_id', $sql->createNamedParameter($appId))); - $sql->executeStatement(); - - $this->clearCache($appId); - } - - /** - * @param string $app - * - * @return string[] - */ - private function parseExtra(string $app): array { - if (!strpos($app, self::APP_EXTRA_SEPARATOR)) { - return [$app, '']; - } - - return explode(self::APP_EXTRA_SEPARATOR, $app, 2); - } - - /** - * @param string $app - * @param bool $includeExtras - * - * @return string - */ - private function loadConfig(string $app, bool $includeExtras = false): string { - [$appId, $extra] = $this->parseExtra($app); - if ($this->isLoaded($appId, $extra)) { - return $appId; - } - - if (!array_key_exists($appId, $this->cache)) { - $this->cache[$appId] = []; - $this->extra[$appId] = []; - } - - $qb = $this->connection->getQueryBuilder(); - $qb->select('config_key', 'config_value') - ->from('appconfig_lazy') - ->where($qb->expr()->eq('app_id', $qb->createNamedParameter($appId))); - - if (!$includeExtras) { - $qb->andWhere($qb->expr()->eq('extra', $qb->createNamedParameter($extra))); - } - $result = $qb->execute(); - - $rows = $result->fetchAll(); - foreach ($rows as $row) { - $this->cache[$appId][$row['config_key']] = $row['config_value']; - $this->setLoadedStatus($appId, $extra); - } - $result->closeCursor(); - - return $appId; - } - - /** - * @throws DBException - */ - private function loadConfigAll(): void { - $sql = $this->connection->getQueryBuilder(); - $sql->select('app_id', 'extra', 'config_key', 'config_value') - ->from('appconfig_lazy'); - $result = $sql->execute(); - - $rows = $result->fetchAll(); - foreach ($rows as $row) { - $this->cache[$row['app_id']][$row['config_key']] = $row['config_value']; - $this->setLoadedStatus($row['app_id'], $row['extra']); - } - } - - - private function isLoaded(string $appId, string $extra): bool { - if (!array_key_exists($appId, $this->cache)) { - return false; - } - - if (!in_array($extra, $this->extra[$appId])) { - return false; - } - - return true; - } - - private function setLoadedStatus(string $appId, string $extra): void { - if (in_array($extra, $this->extra[$appId] ?? [])) { - return; - } - - $this->extra[$appId][] = $extra; - } - - - /** - * @inheritDoc - * @param string $app - * - * @since 29.0.0 - */ - public function clearCache(string $app = ''): void { - if ($app !== '') { - unset($this->cache[$app]); - unset($this->extra[$app]); - return; - } - - $this->cache = []; - } -} diff --git a/lib/private/Server.php b/lib/private/Server.php index a7d5b137e123b..a3a714e544e3d 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -218,7 +218,6 @@ use OCP\IGroupManager; use OCP\IInitialStateService; use OCP\IL10N; -use OCP\ILazyConfig; use OCP\ILogger; use OCP\INavigationManager; use OCP\IPhoneNumberUtil; @@ -627,7 +626,6 @@ public function __construct($webRoot, \OC\Config $config) { /** @deprecated 19.0.0 */ $this->registerDeprecatedAlias('AllConfig', \OC\AllConfig::class); $this->registerAlias(\OCP\IConfig::class, \OC\AllConfig::class); - $this->registerAlias(ILazyConfig::class, LazyConfig::class); $this->registerService(\OC\SystemConfig::class, function ($c) use ($config) { return new \OC\SystemConfig($config); @@ -678,27 +676,11 @@ public function __construct($webRoot, \OC\Config $config) { $config = $c->get(\OCP\IConfig::class); if ($config->getSystemValueBool('installed', false) && !(defined('PHPUNIT_RUN') && PHPUNIT_RUN)) { - if (!$config->getSystemValueBool('log_query')) { - try { - $v = \OC_App::getAppVersions(); - } catch (\Doctrine\DBAL\Exception $e) { - // Database service probably unavailable - // Probably related to https://github.com/nextcloud/server/issues/37424 - return $arrayCacheFactory; - } - } else { - // If the log_query is enabled, we can not get the app versions - // as that does a query, which will be logged and the logging - // depends on redis and here we are back again in the same function. - $v = [ - 'log_query' => 'enabled', - ]; + $prefix = $config->getSystemValueString('cache.prefix', ''); // prefix is updated on update (core or apps) + if ($prefix === '') { + $prefix = md5(\OC_Util::getInstanceId() . '-' . implode(',', \OC_Util::getVersion()) . '-' . \OC::$SERVERROOT); + $config->setSystemValue('cache.prefix', $prefix . '-' . rand(1000, 99999)); } - $v['core'] = implode(',', \OC_Util::getVersion()); - $version = implode(',', $v); - $instanceId = \OC_Util::getInstanceId(); - $path = \OC::$SERVERROOT; - $prefix = md5($instanceId . '-' . $version . '-' . $path); return new \OC\Memcache\Factory($prefix, $c->get(LoggerInterface::class), $profiler, diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 018e4797232ac..dc0ac13fecbd4 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -58,6 +58,7 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\HintException; +use OCP\IAppConfig; use OCP\IConfig; use OCP\ILogger; use OCP\Util; @@ -73,18 +74,6 @@ * - failure(string $message) */ class Updater extends BasicEmitter { - /** @var LoggerInterface */ - private $log; - - /** @var IConfig */ - private $config; - - /** @var Checker */ - private $checker; - - /** @var Installer */ - private $installer; - private $logLevelNames = [ 0 => 'Debug', 1 => 'Info', @@ -93,14 +82,12 @@ class Updater extends BasicEmitter { 4 => 'Fatal', ]; - public function __construct(IConfig $config, - Checker $checker, - ?LoggerInterface $log, - Installer $installer) { - $this->log = $log; - $this->config = $config; - $this->checker = $checker; - $this->installer = $installer; + public function __construct( + private IConfig $config, + private IAppConfig $appConfig, + private Checker $checker, + private ?LoggerInterface $log, + private Installer $installer) { } /** @@ -164,6 +151,7 @@ public function upgrade(): bool { $this->emit('\OC\Updater', 'resetLogLevel', [ $logLevel, $this->logLevelNames[$logLevel] ]); $this->config->setSystemValue('loglevel', $logLevel); $this->config->setSystemValue('installed', true); + $this->config->setSystemValue('cache.prefix', $this->generateCachePrefix()); return $success; } @@ -368,6 +356,10 @@ protected function doAppUpgrade(): void { } } } + + + // setSystemAppValue() ? + } /** @@ -541,4 +533,12 @@ function (MigratorExecuteSqlEvent $event) use ($log): void { $log->info('\OC\Updater::finishedCheckCodeIntegrity: Finished code integrity check', ['app' => 'updater']); }); } + + public function generateCachePrefix(): string { + $this->appConfig->refreshCache(); + $v = $this->appConfig->searchValues('installed_version'); + $v['core'] = implode('.', \OC_Util::getVersion()); + $version = implode(':', $v); + return md5(\OC_Util::getInstanceId() . '-' . $version . '-' . \OC::$SERVERROOT); + } } diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index 395c1f44c033e..6e4b40b4165b5 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -63,6 +63,7 @@ use OCP\App\ManagerEvent; use OCP\Authentication\IAlternativeLogin; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; use Psr\Container\ContainerExceptionInterface; use Psr\Log\LoggerInterface; @@ -730,8 +731,9 @@ public static function getAppVersions() { static $versions; if (!$versions) { - $appConfig = \OC::$server->getAppConfig(); - $versions = $appConfig->getValues(false, 'installed_version'); + /** @var IAppConfig $appConfig */ + $appConfig = \OCP\Server::get(IAppConfig::class); + $versions = $appConfig->searchValues('installed_version'); } return $versions; } diff --git a/lib/public/IAppConfig.php b/lib/public/IAppConfig.php index cf387a8a44ccc..3c0d4d7fa7f59 100644 --- a/lib/public/IAppConfig.php +++ b/lib/public/IAppConfig.php @@ -32,14 +32,26 @@ * @since 7.0.0 */ interface IAppConfig { + + /** + * @param string $app + * + * @return array + * @since 29.0.0 + */ + public function getKeys(string $app): array; + /** * check if a key is set in the appconfig + * * @param string $app * @param string $key + * @param string $lazyGroup since 29.0.0 + * * @return bool * @since 7.0.0 */ - public function hasKey($app, $key); + public function hasKey(string $app, string $key, string $lazyGroup = ''): bool; /** * get multiply values, either the app or key can be used as wildcard by setting it to false @@ -48,6 +60,7 @@ public function hasKey($app, $key); * @param string|false $app * @return array|false * @since 7.0.0 + * @deprecated since 29.0.0 - use getAllValues() */ public function getValues($app, $key); @@ -57,16 +70,153 @@ public function getValues($app, $key); * @param string $app * @return array * @since 12.0.0 + * @deprecated since 29.0.0 - use getAllValues() */ public function getFilteredValues($app); + /** + * @param string $app + * @param string $key + * @param bool $filtered + * + * @return array + * @since 29.0.0 + */ + public function getAllValues(string $app, string $key = '', bool $filtered = false): array; + + /** + * @param string $key + * @param string $lazyGroup + * + * @return array + * @since 29.0.0 + */ + public function searchValues(string $key, string $lazyGroup = ''): array; + /** * Get all apps using the config - * @return string[] an array of app ids * * This function returns a list of all apps that have at least one * entry in the appconfig table. + * + * **WARNING**: will load all lazy configs from database, unless $loadValues is false + * + * @param bool $loadValues since 29.0.0 load all values + * + * @return string[] an array of app ids * @since 7.0.0 */ - public function getApps(); + public function getApps(bool $loadValues = true): array; + + + /** + * @param string $app + * @param string $key + * @param string $default + * @param string $lazyGroup + * + * @return string + * @since 29.0.0 + */ + public function getValueString(string $app, string $key, string $default = '', string $lazyGroup = ''): string; + + /** + * @param string $app + * @param string $key + * @param int $default + * @param string $lazyGroup + * + * @return int + * @since 29.0.0 + */ + public function getValueInt(string $app, string $key, int $default = 0, string $lazyGroup = ''): int; + + /** + * @param string $app + * @param string $key + * @param bool $default + * @param string $lazyGroup + * + * @return bool + * @since 29.0.0 + */ + public function getValueBool(string $app, string $key, bool $default = false, string $lazyGroup = ''): bool; + + /** + * @param string $app + * @param string $key + * @param array $default + * @param string $lazyGroup + * + * @return array + * @since 29.0.0 + */ + public function getValueArray(string $app, string $key, array $default = [], string $lazyGroup = ''): array; + + /** + * @param string $app + * @param string $key + * @param string $value + * @param string $lazyGroup + * @param bool $sensitive + * + * @return bool + * @since 29.0.0 + */ + public function setValueString(string $app, string $key, string $value, bool $sensitive = false, string $lazyGroup = ''): bool; + + /** + * @param string $app + * @param string $key + * @param int $value + * @param string $lazyGroup + * @param bool $sensitive + * + * @return bool + * @since 29.0.0 + */ + public function setValueInt(string $app, string $key, int $value, bool $sensitive = false, string $lazyGroup = ''): bool; + + /** + * @param string $app + * @param string $key + * @param bool $value + * @param string $lazyGroup + * @param bool $sensitive + * + * @return bool + * @since 29.0.0 + */ + public function setValueBool(string $app, string $key, bool $value, bool $sensitive = false, string $lazyGroup = ''): bool; + + /** + * @param string $app + * @param string $key + * @param array $value + * @param string $lazyGroup + * @param bool $sensitive + * + * @return bool + * @since 29.0.0 + */ + public function setValueArray(string $app, string $key, array $value, bool $sensitive = false, string $lazyGroup = ''): bool; + + /** + * @param string $app + * @param string $key + * @since 29.0.0 + */ + public function deleteKey(string $app, string $key): void; + + /** + * @param string $app + * @since 29.0.0 + */ + public function deleteApp(string $app): void; + + /** + * @param string $app + * @since 29.0.0 + */ + public function refreshCache(string $app = ''): void; } diff --git a/lib/public/ILazyConfig.php b/lib/public/ILazyConfig.php deleted file mode 100644 index cdbaff233cb66..0000000000000 --- a/lib/public/ILazyConfig.php +++ /dev/null @@ -1,171 +0,0 @@ - - * - * @author Maxence Lange - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -namespace OCP; - -/** - * @since 29.0.0 - */ -interface ILazyConfig { - /** - * @param bool $loadValues - * - * @return array - * @since 29.0.0 - */ - public function getApps(bool $loadValues = true): array; - - /** - * @param string $app - * - * @return array - * @since 29.0.0 - */ - public function getKeys(string $app): array; - - /** - * @param string $app - * @param string $key - * - * @return bool - * @since 29.0.0 - */ - public function hasKey(string $app, string $key): bool; - - /** - * @param string $app - * @param string $key - * - * @return array - * @since 29.0.0 - */ - public function getValues(string $app, string $key = ''): array; - - /** - * @param string $app - * - * @return array - * @since 29.0.0 - */ - public function getFilteredValues(string $app): array; - - /** - * @param string $app - * @param string $key - * @param string $default - * - * @return string - * @since 29.0.0 - */ - public function getValueString(string $app, string $key, string $default = ''): string; - - /** - * @param string $app - * @param string $key - * @param int $default - * - * @return int - * @since 29.0.0 - */ - public function getValueInt(string $app, string $key, int $default = 0): int; - - /** - * @param string $app - * @param string $key - * @param bool $default - * - * @return bool - * @since 29.0.0 - */ - public function getValueBool(string $app, string $key, bool $default = false): bool; - - /** - * @param string $app - * @param string $key - * @param array $default - * - * @return array - * @since 29.0.0 - */ - public function getValueArray(string $app, string $key, array $default = []): array; - - /** - * @param string $app - * @param string $key - * @param string $value - * - * @return bool - * @since 29.0.0 - */ - public function setValueString(string $app, string $key, string $value): bool; - - /** - * @param string $app - * @param string $key - * @param int $value - * - * @return bool - * @since 29.0.0 - */ - public function setValueInt(string $app, string $key, int $value): bool; - - /** - * @param string $app - * @param string $key - * @param bool $value - * - * @return bool - * @since 29.0.0 - */ - public function setValueBool(string $app, string $key, bool $value): bool; - - /** - * @param string $app - * @param string $key - * @param array $value - * - * @return bool - * @since 29.0.0 - */ - public function setValueArray(string $app, string $key, array $value): bool; - - /** - * @param string $app - * @param string $key - * @since 29.0.0 - */ - public function unsetKey(string $app, string $key): void; - - /** - * @param string $app - * @since 29.0.0 - */ - public function deleteApp(string $app): void; - - /** - * @param string $app - * @since 29.0.0 - */ - public function clearCache(string $app = ''): void; -} diff --git a/tests/Core/Command/Config/ListConfigsTest.php b/tests/Core/Command/Config/ListConfigsTest.php index a436fa342f2a6..01d5f51249480 100644 --- a/tests/Core/Command/Config/ListConfigsTest.php +++ b/tests/Core/Command/Config/ListConfigsTest.php @@ -25,7 +25,6 @@ use OC\SystemConfig; use OCP\IAppConfig; use OCP\IConfig; -use OCP\ILazyConfig; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Test\TestCase; @@ -53,16 +52,12 @@ protected function setUp(): void { $appConfig = $this->appConfig = $this->getMockBuilder(IAppConfig::class) ->disableOriginalConstructor() ->getMock(); - $lazyConfig = $this->lazyConfig = $this->getMockBuilder(ILazyConfig::class) - ->disableOriginalConstructor() - ->getMock(); $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock(); $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock(); /** @var \OC\SystemConfig $systemConfig */ /** @var \OCP\IAppConfig $appConfig */ - /** @var \OCP\ILazyConfig $lazyConfig */ - $this->command = new ListConfigs($systemConfig, $appConfig, $lazyConfig); + $this->command = new ListConfigs($systemConfig, $appConfig); } public function listData() { diff --git a/tests/lib/UpdaterTest.php b/tests/lib/UpdaterTest.php index 02b2189cdccb8..6040a62574d49 100644 --- a/tests/lib/UpdaterTest.php +++ b/tests/lib/UpdaterTest.php @@ -25,6 +25,7 @@ use OC\Installer; use OC\IntegrityCheck\Checker; use OC\Updater; +use OCP\IAppConfig; use OCP\IConfig; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; @@ -46,6 +47,9 @@ protected function setUp(): void { $this->config = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); + $this->appConfig = $this->getMockBuilder(IAppConfig::class) + ->disableOriginalConstructor() + ->getMock(); $this->logger = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->getMock(); @@ -58,6 +62,7 @@ protected function setUp(): void { $this->updater = new Updater( $this->config, + $this->appConfig, $this->checker, $this->logger, $this->installer