From f374de68110cd2e00d552bfe5c66e98a1e738475 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Wed, 20 Mar 2024 23:11:10 -0100 Subject: [PATCH] feat(config): frame config keys/values Signed-off-by: Maxence Lange --- lib/private/AppConfig.php | 47 ++++++++ .../Bootstrap/RegistrationContext.php | 34 ++++++ .../ConfigValues/AConfigValue.php | 106 ++++++++++++++++++ .../ConfigValues/AppConfigValue.php | 33 ++++++ .../ConfigValues/UserPreferenceValue.php | 33 ++++++ .../Bootstrap/IRegistrationContext.php | 3 + .../ConfigValues/IConfigValue.php | 52 +++++++++ 7 files changed, 308 insertions(+) create mode 100644 lib/private/AppFramework/ConfigValues/AConfigValue.php create mode 100644 lib/private/AppFramework/ConfigValues/AppConfigValue.php create mode 100644 lib/private/AppFramework/ConfigValues/UserPreferenceValue.php create mode 100644 lib/public/AppFramework/ConfigValues/IConfigValue.php diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index 518ba6ebf7a01..09dd51adc18a5 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -38,6 +38,9 @@ use InvalidArgumentException; use JsonException; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\AppFramework\ConfigValues\AppConfigValue; +use OCP\AppFramework\ConfigValues\IConfigValue; use OCP\DB\Exception as DBException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Exceptions\AppConfigIncorrectTypeException; @@ -97,6 +100,7 @@ public function __construct( protected IDBConnection $connection, protected LoggerInterface $logger, protected ICrypto $crypto, + private Coordinator $coordinator, ) { } @@ -457,6 +461,8 @@ private function getTypedValue( $this->assertParams($app, $key, valueType: $type); $this->loadConfig($lazy); + $this->compareRegisteredConfigValues($app, $key, $default, $lazy, $type); + /** * We ignore check if mixed type is requested. * If type of stored value is set as mixed, we don't filter. @@ -1506,4 +1512,45 @@ private function getSensitiveKeys(string $app): array { public function clearCachedConfig(): void { $this->clearCache(); } + + + + private function compareRegisteredConfigValues( + string $app, + string $key, + string &$default, + bool &$lazy, + int &$type + ): void { + $context = $this->coordinator->getRegistrationContext(); + $configValues = $context->getConfigValues($app, AppConfigValue::TYPE); + + if (!array_key_exists($key, $configValues)) { + if ($context->strictConfigValues($app)) { + throw new AppConfigUnknownKeyException('This key is not defined in the list of available AppConfig keys for this app'); + } + return; + } + + $configValue = $configValues[$key]; + + if ($configValue->getValueType() !== match($type) { + self::VALUE_STRING => IConfigValue::TYPE_STRING, + self::VALUE_INT => IConfigValue::TYPE_INT, + self::VALUE_FLOAT => IConfigValue::TYPE_FLOAT, + self::VALUE_BOOL => IConfigValue::TYPE_BOOL, + self::VALUE_ARRAY => IConfigValue::TYPE_ARRAY, + }) { + throw new AppConfigTypeConflictException('This key is mistyped compare to the list of available AppConfig keys for this app'); + } + + $lazy = $configValue->isLazy(); + $default = $configValue->getDefault(); + if ($configValue->isSensitive()) { + $type |= self::VALUE_SENSITIVE; + } + if ($configValue->isDeprecated()) { + $this->logger->notice('config value ' . $key . ' from ' . $app . ' is set as deprecated'); + } + } } diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index b1b2c57da555a..398c6bd5c8906 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -33,6 +33,7 @@ use OC\Support\CrashReport\Registry; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\AppFramework\ConfigValues\IConfigValue; use OCP\AppFramework\Middleware; use OCP\AppFramework\Services\InitialStateProvider; use OCP\Authentication\IAlternativeLogin; @@ -160,6 +161,7 @@ class RegistrationContext { /** @var ServiceRegistration[] */ private array $declarativeSettings = []; + private array $configValues = []; /** @var ServiceRegistration[] */ private array $teamResourceProviders = []; @@ -411,6 +413,14 @@ public function registerDeclarativeSettings(string $declarativeSettingsClass): v $declarativeSettingsClass ); } + + public function registerConfigValues(bool $strict, IConfigValue ...$configValues): void { + $this->context->registerConfigValues( + $this->appId, + $strict, + ...$configValues + ); + } }; } @@ -590,6 +600,16 @@ public function registerDeclarativeSettings(string $appId, string $declarativeSe $this->declarativeSettings[] = new ServiceRegistration($appId, $declarativeSettingsClass); } + public function registerConfigValues(string $appId, bool $strict, IConfigValue ...$configValues): void { + $values = ['_strict' => $strict]; + foreach ($configValues as $configValue) { + $values[$configValue->getConfigType()][$configValue->getKey()] = $configValue; + } + + $this->configValues[$appId] = $values; + } + + /** * @param App[] $apps */ @@ -920,4 +940,18 @@ public function getTeamResourceProviders(): array { public function getDeclarativeSettings(): array { return $this->declarativeSettings; } + + /** + * @param string $app + * @param string $configType + * + * @return array + */ + public function getConfigValues(string $app, string $configType): array { + return $this->configValues[$app][$configType] ?? []; + } + + public function strictConfigValues(string $app): bool { + return $this->configValues[$app]['_strict'] ?? false; + } } diff --git a/lib/private/AppFramework/ConfigValues/AConfigValue.php b/lib/private/AppFramework/ConfigValues/AConfigValue.php new file mode 100644 index 0000000000000..f610d429ffcbd --- /dev/null +++ b/lib/private/AppFramework/ConfigValues/AConfigValue.php @@ -0,0 +1,106 @@ + + * + * @author Maxence Lange + * + * @license AGPL-3.0 or later + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\AppFramework\ConfigValues; + +use OCP\AppFramework\ConfigValues\IConfigValue; + +abstract class AConfigValue implements IConfigValue { + private ?string $default = null; + private bool $lazy = false; + private bool $sensitive = false; + private bool $deprecated = false; + + public function __construct( + private string $key, + private int $valueType + ) { + } + + abstract public function getConfigType(): string; + + public function getKey(): string { + return $this->key; + } + + public function getValueType(): int { + return $this->valueType; + } + + public function withDefaultString(string $default): self { + $this->default = $default; + return $this; + } + + public function withDefaultInt(int $default): self { + $this->default = (string) $default; + return $this; + } + + public function withDefaultFloat(float $default): self { + $this->default = (string) $default; + return $this; + } + + public function withDefaultBool(bool $default): self { + $this->default = ($default) ? '1' : '0'; + return $this; + } + + public function withDefaultArray(array $default): self { + $this->default = json_encode($default); + return $this; + } + + public function getDefault(): string { + return $this->default; + } + + public function asLazy(bool $lazy = true): self { + $this->lazy = $lazy; + return $this; + } + + public function isLazy(): bool { + return $this->lazy; + } + + public function asSensitive(bool $sensitive = true): self { + $this->sensitive = $sensitive; + return $this; + } + + public function isSensitive(): bool { + return $this->sensitive; + } + + public function asDeprecated(bool $deprecated = true): self { + $this->deprecated = $deprecated; + return $this; + } + + public function isDeprecated(): bool { + return $this->deprecated; + } +} diff --git a/lib/private/AppFramework/ConfigValues/AppConfigValue.php b/lib/private/AppFramework/ConfigValues/AppConfigValue.php new file mode 100644 index 0000000000000..5f6555ee64cac --- /dev/null +++ b/lib/private/AppFramework/ConfigValues/AppConfigValue.php @@ -0,0 +1,33 @@ + + * + * @author Maxence Lange + * + * @license AGPL-3.0 or later + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\AppFramework\ConfigValues; + +class AppConfigValue extends AConfigValue { + public const TYPE = 'AppConfig'; + + public function getConfigType(): string { + return self::TYPE; + } +} diff --git a/lib/private/AppFramework/ConfigValues/UserPreferenceValue.php b/lib/private/AppFramework/ConfigValues/UserPreferenceValue.php new file mode 100644 index 0000000000000..47e5d09b84aea --- /dev/null +++ b/lib/private/AppFramework/ConfigValues/UserPreferenceValue.php @@ -0,0 +1,33 @@ + + * + * @author Maxence Lange + * + * @license AGPL-3.0 or later + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\AppFramework\ConfigValues; + +class UserPreferenceValue extends AConfigValue { + public const TYPE = 'UserPreference'; + + public function getConfigType(): string { + return self::TYPE; + } +} diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 09bc703e0a41c..991ecccf731c3 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -29,6 +29,7 @@ namespace OCP\AppFramework\Bootstrap; +use OCP\AppFramework\ConfigValues\IConfigValue; use OCP\AppFramework\IAppContainer; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Calendar\ICalendarProvider; @@ -410,4 +411,6 @@ public function registerSetupCheck(string $setupCheckClass): void; * @since 29.0.0 */ public function registerDeclarativeSettings(string $declarativeSettingsClass): void; + + public function registerConfigValues(bool $strict, IConfigValue ...$configValues): void; } diff --git a/lib/public/AppFramework/ConfigValues/IConfigValue.php b/lib/public/AppFramework/ConfigValues/IConfigValue.php new file mode 100644 index 0000000000000..2873e57adf5f9 --- /dev/null +++ b/lib/public/AppFramework/ConfigValues/IConfigValue.php @@ -0,0 +1,52 @@ + + * + * @author Maxence Lange + * + * @license AGPL-3.0 or later + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCP\AppFramework\ConfigValues; + +interface IConfigValue { + public const TYPE_STRING = 1; + public const TYPE_INT = 2; + public const TYPE_FLOAT = 3; + public const TYPE_BOOL = 4; + public const TYPE_ARRAY = 5; + + public function getConfigType(): string; + + public function getKey(): string; + public function getValueType(): int; + + public function withDefaultString(string $default): self; + public function withDefaultInt(int $default): self; + public function withDefaultFloat(float $default): self; + public function withDefaultBool(bool $default): self; + public function withDefaultArray(array $default): self; + public function getDefault(): ?string; + + public function asLazy(bool $lazy = true): self; + public function isLazy(): bool; + public function asSensitive(bool $sensitive = true): self; + public function isSensitive(): bool; + public function asDeprecated(bool $deprecated = true): self; + public function isDeprecated(): bool; +}