aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/AppConfig.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/AppConfig.php')
-rw-r--r--lib/private/AppConfig.php122
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];
+ }
}