|
38 | 38 |
|
39 | 39 | use InvalidArgumentException; |
40 | 40 | use JsonException; |
| 41 | +use OC\AppFramework\Bootstrap\Coordinator; |
| 42 | +use OC\ConfigLexicon\ConfigLexiconEntry; |
| 43 | +use OCP\ConfigLexicon\IConfigLexiconEntry; |
41 | 44 | use OCP\DB\Exception as DBException; |
42 | 45 | use OCP\DB\QueryBuilder\IQueryBuilder; |
43 | 46 | use OCP\Exceptions\AppConfigIncorrectTypeException; |
@@ -82,6 +85,8 @@ class AppConfig implements IAppConfig { |
82 | 85 | private array $valueTypes = []; // type for all config values |
83 | 86 | private bool $fastLoaded = false; |
84 | 87 | private bool $lazyLoaded = false; |
| 88 | + /** @var array<array-key, array{entries: array<array-key, IConfigLexiconEntry>, strict: bool}> ['app_id' => ['strict' => bool, 'entries' => ['config_key' => IConfigLexiconEntry[]]] */ |
| 89 | + private array $configLexiconDetails = []; |
85 | 90 |
|
86 | 91 | /** |
87 | 92 | * $migrationCompleted is only needed to manage the previous structure |
@@ -457,6 +462,7 @@ private function getTypedValue( |
457 | 462 | int $type |
458 | 463 | ): string { |
459 | 464 | $this->assertParams($app, $key, valueType: $type); |
| 465 | + $this->compareRegisteredConfigValues($app, $key, $lazy, $type, $default); |
460 | 466 | $this->loadConfig($lazy); |
461 | 467 |
|
462 | 468 | /** |
@@ -748,6 +754,7 @@ private function setTypedValue( |
748 | 754 | int $type |
749 | 755 | ): bool { |
750 | 756 | $this->assertParams($app, $key); |
| 757 | + $this->compareRegisteredConfigValues($app, $key, $lazy, $type); |
751 | 758 | $this->loadConfig($lazy); |
752 | 759 |
|
753 | 760 | $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type); |
@@ -1567,4 +1574,74 @@ private function getSensitiveKeys(string $app): array { |
1567 | 1574 | public function clearCachedConfig(): void { |
1568 | 1575 | $this->clearCache(); |
1569 | 1576 | } |
| 1577 | + |
| 1578 | + /** |
| 1579 | + * verify and compare current use of config values with defined lexicon |
| 1580 | + * |
| 1581 | + * @throws AppConfigUnknownKeyException |
| 1582 | + * @throws AppConfigTypeConflictException |
| 1583 | + */ |
| 1584 | + private function compareRegisteredConfigValues( |
| 1585 | + string $app, |
| 1586 | + string $key, |
| 1587 | + bool &$lazy, |
| 1588 | + int &$type, |
| 1589 | + string &$default = '', |
| 1590 | + ): void { |
| 1591 | + $configDetails = $this->getConfigDetailsFromLexicon($app); |
| 1592 | + if (!array_key_exists($key, $configDetails['entries'])) { |
| 1593 | + if ($configDetails['strict'] === true) { |
| 1594 | + throw new AppConfigUnknownKeyException('The key ' . $app . '/' . $key . ' is not defined in the config lexicon'); |
| 1595 | + } |
| 1596 | + return; |
| 1597 | + } |
| 1598 | + |
| 1599 | + /** @var ConfigLexiconEntry $configValue */ |
| 1600 | + $configValue = $configDetails['entries'][$key]; |
| 1601 | + $type &= ~self::VALUE_SENSITIVE; |
| 1602 | + |
| 1603 | + if ($configValue->getValueType() !== match($type) { |
| 1604 | + self::VALUE_STRING => IConfigLexiconEntry::TYPE_STRING, |
| 1605 | + self::VALUE_INT => IConfigLexiconEntry::TYPE_INT, |
| 1606 | + self::VALUE_FLOAT => IConfigLexiconEntry::TYPE_FLOAT, |
| 1607 | + self::VALUE_BOOL => IConfigLexiconEntry::TYPE_BOOL, |
| 1608 | + self::VALUE_ARRAY => IConfigLexiconEntry::TYPE_ARRAY, |
| 1609 | + }) { |
| 1610 | + throw new AppConfigTypeConflictException('The key ' . $app . '/' . $key . ' is typed incorrectly in relation to the config lexicon'); |
| 1611 | + } |
| 1612 | + |
| 1613 | + $lazy = $configValue->isLazy(); |
| 1614 | + $default = $configValue->getDefault() ?? $default; |
| 1615 | + if ($configValue->isSensitive()) { |
| 1616 | + $type |= self::VALUE_SENSITIVE; |
| 1617 | + } |
| 1618 | + if ($configValue->isDeprecated()) { |
| 1619 | + $this->logger->notice('config value ' . $app . '/' . $key . ' is set as deprecated.'); |
| 1620 | + } |
| 1621 | + } |
| 1622 | + |
| 1623 | + /** |
| 1624 | + * extract details from registered $appId's config lexicon |
| 1625 | + * |
| 1626 | + * @param string $appId |
| 1627 | + * |
| 1628 | + * @return array{entries: array<array-key, IConfigLexiconEntry>, strict: bool} |
| 1629 | + */ |
| 1630 | + private function getConfigDetailsFromLexicon(string $appId): array { |
| 1631 | + if (!array_key_exists($appId, $this->configLexiconDetails)) { |
| 1632 | + $entries = []; |
| 1633 | + $bootstrapCoordinator = \OCP\Server::get(Coordinator::class); |
| 1634 | + $configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId); |
| 1635 | + foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) { |
| 1636 | + $entries[$configEntry->getKey()] = $configEntry; |
| 1637 | + } |
| 1638 | + |
| 1639 | + $this->configLexiconDetails[$appId] = [ |
| 1640 | + 'entries' => $entries, |
| 1641 | + 'strict' => $configLexicon?->isStrict() ?? false |
| 1642 | + ]; |
| 1643 | + } |
| 1644 | + |
| 1645 | + return $this->configLexiconDetails[$appId]; |
| 1646 | + } |
1570 | 1647 | } |
0 commit comments