From 444ad759803967cf8fe7f2a24d4a1edaf9b07f70 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Mon, 16 Dec 2024 17:26:38 -0100 Subject: [PATCH] fix(lexicon): convert system entry to string Signed-off-by: Maxence Lange --- core/Command/Config/App/GetConfig.php | 1 + lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + lib/private/Config/UserConfig.php | 41 ++++++++++++++++--- .../Config/Lexicon/ConfigLexiconEntry.php | 39 +++++++++++------- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/core/Command/Config/App/GetConfig.php b/core/Command/Config/App/GetConfig.php index 366e5f5c136cf..eae6818390e04 100644 --- a/core/Command/Config/App/GetConfig.php +++ b/core/Command/Config/App/GetConfig.php @@ -81,6 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // get the default value defined from lexicon + /** @psalm-suppress UndefinedInterfaceMethod */ $configValue = $this->appConfig->getValueMixed($appName, $configName, $defaultValue ?? ''); } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index aa850c8bed45f..d635a4ef1b09a 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1151,6 +1151,7 @@ 'OC\\Comments\\Manager' => $baseDir . '/lib/private/Comments/Manager.php', 'OC\\Comments\\ManagerFactory' => $baseDir . '/lib/private/Comments/ManagerFactory.php', 'OC\\Config' => $baseDir . '/lib/private/Config.php', + 'OC\\Config\\Lexicon\\CoreConfigLexicon' => $baseDir . '/lib/private/Config/Lexicon/CoreConfigLexicon.php', 'OC\\Config\\UserConfig' => $baseDir . '/lib/private/Config/UserConfig.php', 'OC\\Console\\Application' => $baseDir . '/lib/private/Console/Application.php', 'OC\\Console\\TimestampFormatter' => $baseDir . '/lib/private/Console/TimestampFormatter.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index fd4993dd5a8f5..eb733dcf8ec59 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1192,6 +1192,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Comments\\Manager' => __DIR__ . '/../../..' . '/lib/private/Comments/Manager.php', 'OC\\Comments\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/Comments/ManagerFactory.php', 'OC\\Config' => __DIR__ . '/../../..' . '/lib/private/Config.php', + 'OC\\Config\\Lexicon\\CoreConfigLexicon' => __DIR__ . '/../../..' . '/lib/private/Config/Lexicon/CoreConfigLexicon.php', 'OC\\Config\\UserConfig' => __DIR__ . '/../../..' . '/lib/private/Config/UserConfig.php', 'OC\\Console\\Application' => __DIR__ . '/../../..' . '/lib/private/Console/Application.php', 'OC\\Console\\TimestampFormatter' => __DIR__ . '/../../..' . '/lib/private/Console/TimestampFormatter.php', diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php index f12b8bbe1a150..9e8f93f89a6e5 100644 --- a/lib/private/Config/UserConfig.php +++ b/lib/private/Config/UserConfig.php @@ -712,7 +712,7 @@ private function getTypedValue( ValueType $type, ): string { $this->assertParams($userId, $app, $key); - if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type, default: $default)) { + if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, default: $default)) { return $default; // returns default if strictness of lexicon is set to WARNING (block and report) } $this->loadConfig($userId, $lazy); @@ -1047,7 +1047,7 @@ private function setTypedValue( ValueType $type, ): bool { $this->assertParams($userId, $app, $key); - if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type, $flags)) { + if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, $flags)) { return false; // returns false as database is not updated } $this->loadConfig($userId, $lazy); @@ -1823,6 +1823,7 @@ private function decryptSensitiveValue(string $userId, string $app, string $key, * @throws TypeConflictException */ private function matchAndApplyLexiconDefinition( + string $userId, string $app, string $key, bool &$lazy, @@ -1844,18 +1845,46 @@ private function matchAndApplyLexiconDefinition( } $lazy = $configValue->isLazy(); - // default from Lexicon got priority but it can still be overwritten by admin - // with 'lexicon.default.userconfig' => [. => 'my value'], in config.php - $default = $this->config->getSystemValue('lexicon.default.userconfig', [])[$app . '.' . $key] ?? $configValue->getDefault() ?? $default; $flags = $configValue->getFlags(); - if ($configValue->isDeprecated()) { $this->logger->notice('User config key ' . $app . '/' . $key . ' is set as deprecated.'); } + if ($this->hasKey($userId, $app, $key, $lazy)) { + return true; // if key exists there should be no need to extract default + } + + // default from Lexicon got priority but it can still be overwritten by admin + $default = $this->getSystemDefault($app, $configValue) ?? $configValue->getDefault() ?? $default; + return true; } + /** + * get default value set in config/config.php if stored in key: + * + * 'lexicon.default.userconfig' => [ + * => [ + * => 'my value', + * ] + * ], + * + * The entry is converted to string to fit the expected type when managing default value + * + * @param string $appId + * @param ConfigLexiconEntry $configValue + * + * @return string|null + */ + private function getSystemDefault(string $appId, ConfigLexiconEntry $configValue): ?string { + $default = $this->config->getSystemValue('lexicon.default.userconfig', [])[$appId][$configValue->getKey()] ?? null; + if ($default === null) { + return null; // no system default, using default default. + } + + return $configValue->convertToString($default); + } + /** * manage ConfigLexicon behavior based on strictness set in IConfigLexicon * diff --git a/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php b/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php index e6c6579881dba..b7a7fad56a863 100644 --- a/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php +++ b/lib/unstable/Config/Lexicon/ConfigLexiconEntry.php @@ -35,25 +35,12 @@ class ConfigLexiconEntry { public function __construct( private readonly string $key, private readonly ValueType $type, - null|string|int|float|bool|array $default = null, + private null|string|int|float|bool|array $defaultRaw = null, string $definition = '', private readonly bool $lazy = false, private readonly int $flags = 0, private readonly bool $deprecated = false, ) { - if ($default !== null) { - // in case $default is array but is not expected to be an array... - $default = ($type !== ValueType::ARRAY && is_array($default)) ? json_encode($default) : $default; - $this->default = match ($type) { - ValueType::MIXED => (string)$default, - ValueType::STRING => $this->convertFromString((string)$default), - ValueType::INT => $this->convertFromInt((int)$default), - ValueType::FLOAT => $this->convertFromFloat((float)$default), - ValueType::BOOL => $this->convertFromBool((bool)$default), - ValueType::ARRAY => $this->convertFromArray((array)$default) - }; - } - /** @psalm-suppress UndefinedClass */ if (\OC::$CLI) { // only store definition if ran from CLI $this->definition = $definition; @@ -132,9 +119,33 @@ private function convertFromArray(array $default): string { * @experimental 31.0.0 */ public function getDefault(): ?string { + if ($this->defaultRaw === null) { + return null; + } + + if ($this->default === null) { + $this->default = $this->convertToString($this->defaultRaw); + } + return $this->default; } + public function convertToString(string|int|float|bool|array $entry): string { + // in case $default is array but is not expected to be an array... + if ($this->getValueType() !== ValueType::ARRAY && is_array($entry)) { + $entry = json_encode($entry) ?? ''; + } + + return match ($this->getValueType()) { + ValueType::MIXED => (string)$entry, + ValueType::STRING => $this->convertFromString((string)$entry), + ValueType::INT => $this->convertFromInt((int)$entry), + ValueType::FLOAT => $this->convertFromFloat((float)$entry), + ValueType::BOOL => $this->convertFromBool((bool)$entry), + ValueType::ARRAY => $this->convertFromArray((array)$entry) + }; + } + /** * @inheritDoc *