diff options
Diffstat (limited to 'lib/private/AppConfig.php')
-rw-r--r-- | lib/private/AppConfig.php | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index dc9bac7745d..bb439218015 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -11,6 +11,10 @@ namespace OC; use InvalidArgumentException; use JsonException; +use NCU\Config\Lexicon\ConfigLexiconEntry; +use NCU\Config\Lexicon\ConfigLexiconStrictness; +use NCU\Config\Lexicon\IConfigLexicon; +use OC\AppFramework\Bootstrap\Coordinator; use OCP\DB\Exception as DBException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Exceptions\AppConfigIncorrectTypeException; @@ -55,6 +59,8 @@ class AppConfig implements IAppConfig { private array $valueTypes = []; // type for all config values private bool $fastLoaded = false; private bool $lazyLoaded = false; + /** @var array<array-key, array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */ + private array $configLexiconDetails = []; /** * $migrationCompleted is only needed to manage the previous structure @@ -430,6 +436,9 @@ class AppConfig implements IAppConfig { int $type, ): string { $this->assertParams($app, $key, valueType: $type); + if (!$this->compareRegisteredConfigValues($app, $key, $lazy, $type, $default)) { + return $default; // returns default if strictness of lexicon is set to WARNING (block and report) + } $this->loadConfig($app, $lazy); /** @@ -721,6 +730,9 @@ class AppConfig implements IAppConfig { int $type, ): bool { $this->assertParams($app, $key); + if (!$this->compareRegisteredConfigValues($app, $key, $lazy, $type)) { + return false; // returns false as database is not updated + } $this->loadConfig(null, $lazy); $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type); @@ -1559,4 +1571,114 @@ class AppConfig implements IAppConfig { public function clearCachedConfig(): void { $this->clearCache(); } + + /** + * verify and compare current use of config values with defined lexicon + * + * @throws AppConfigUnknownKeyException + * @throws AppConfigTypeConflictException + * @return bool TRUE if everything is fine compared to lexicon or lexicon does not exist + */ + private function compareRegisteredConfigValues( + string $app, + string $key, + bool &$lazy, + int &$type, + string &$default = '', + ): bool { + if (in_array($key, + [ + 'enabled', + 'installed_version', + 'types', + ])) { + return true; // we don't break stuff for this list of config keys. + } + $configDetails = $this->getConfigDetailsFromLexicon($app); + if (!array_key_exists($key, $configDetails['entries'])) { + return $this->applyLexiconStrictness( + $configDetails['strictness'], + 'The app config key ' . $app . '/' . $key . ' is not defined in the config lexicon' + ); + } + + /** @var ConfigLexiconEntry $configValue */ + $configValue = $configDetails['entries'][$key]; + $type &= ~self::VALUE_SENSITIVE; + + $appConfigValueType = $configValue->getValueType()->toAppConfigFlag(); + if ($type === self::VALUE_MIXED) { + $type = $appConfigValueType; // we overwrite if value was requested as mixed + } elseif ($appConfigValueType !== $type) { + throw new AppConfigTypeConflictException('The app config key ' . $app . '/' . $key . ' is typed incorrectly in relation to the config lexicon'); + } + + $lazy = $configValue->isLazy(); + $default = $configValue->getDefault() ?? $default; // default from Lexicon got priority + if ($configValue->isFlagged(self::FLAG_SENSITIVE)) { + $type |= self::VALUE_SENSITIVE; + } + if ($configValue->isDeprecated()) { + $this->logger->notice('App config key ' . $app . '/' . $key . ' is set as deprecated.'); + } + + return true; + } + + /** + * manage ConfigLexicon behavior based on strictness set in IConfigLexicon + * + * @param ConfigLexiconStrictness|null $strictness + * @param string $line + * + * @return bool TRUE if conflict can be fully ignored, FALSE if action should be not performed + * @throws AppConfigUnknownKeyException if strictness implies exception + * @see IConfigLexicon::getStrictness() + */ + private function applyLexiconStrictness( + ?ConfigLexiconStrictness $strictness, + string $line = '', + ): bool { + if ($strictness === null) { + return true; + } + + switch ($strictness) { + case ConfigLexiconStrictness::IGNORE: + return true; + case ConfigLexiconStrictness::NOTICE: + $this->logger->notice($line); + return true; + case ConfigLexiconStrictness::WARNING: + $this->logger->warning($line); + return false; + } + + throw new AppConfigUnknownKeyException($line); + } + + /** + * extract details from registered $appId's config lexicon + * + * @param string $appId + * + * @return array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness} + */ + private function getConfigDetailsFromLexicon(string $appId): array { + if (!array_key_exists($appId, $this->configLexiconDetails)) { + $entries = []; + $bootstrapCoordinator = \OCP\Server::get(Coordinator::class); + $configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId); + foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) { + $entries[$configEntry->getKey()] = $configEntry; + } + + $this->configLexiconDetails[$appId] = [ + 'entries' => $entries, + 'strictness' => $configLexicon?->getStrictness() ?? ConfigLexiconStrictness::IGNORE + ]; + } + + return $this->configLexiconDetails[$appId]; + } } |