diff options
Diffstat (limited to 'lib/private/Settings/DeclarativeManager.php')
-rw-r--r-- | lib/private/Settings/DeclarativeManager.php | 132 |
1 files changed, 114 insertions, 18 deletions
diff --git a/lib/private/Settings/DeclarativeManager.php b/lib/private/Settings/DeclarativeManager.php index 5c2b868f417..534b4b19984 100644 --- a/lib/private/Settings/DeclarativeManager.php +++ b/lib/private/Settings/DeclarativeManager.php @@ -15,6 +15,7 @@ use OCP\IAppConfig; use OCP\IConfig; use OCP\IGroupManager; use OCP\IUser; +use OCP\Security\ICrypto; use OCP\Server; use OCP\Settings\DeclarativeSettingsTypes; use OCP\Settings\Events\DeclarativeSettingsGetValueEvent; @@ -22,6 +23,7 @@ use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent; use OCP\Settings\Events\DeclarativeSettingsSetValueEvent; use OCP\Settings\IDeclarativeManager; use OCP\Settings\IDeclarativeSettingsForm; +use OCP\Settings\IDeclarativeSettingsFormWithHandlers; use Psr\Log\LoggerInterface; /** @@ -32,21 +34,26 @@ use Psr\Log\LoggerInterface; * @psalm-import-type DeclarativeSettingsFormSchemaWithoutValues from IDeclarativeSettingsForm */ class DeclarativeManager implements IDeclarativeManager { - public function __construct( - private IEventDispatcher $eventDispatcher, - private IGroupManager $groupManager, - private Coordinator $coordinator, - private IConfig $config, - private IAppConfig $appConfig, - private LoggerInterface $logger, - ) { - } + + /** @var array<string, list<IDeclarativeSettingsForm>> */ + private array $declarativeForms = []; /** * @var array<string, list<DeclarativeSettingsFormSchemaWithoutValues>> */ private array $appSchemas = []; + public function __construct( + private IEventDispatcher $eventDispatcher, + private IGroupManager $groupManager, + private Coordinator $coordinator, + private IConfig $config, + private IAppConfig $appConfig, + private LoggerInterface $logger, + private ICrypto $crypto, + ) { + } + /** * @inheritdoc */ @@ -77,11 +84,15 @@ class DeclarativeManager implements IDeclarativeManager { * @inheritdoc */ public function loadSchemas(): void { - $declarativeSettings = $this->coordinator->getRegistrationContext()->getDeclarativeSettings(); - foreach ($declarativeSettings as $declarativeSetting) { - /** @var IDeclarativeSettingsForm $declarativeSettingObject */ - $declarativeSettingObject = Server::get($declarativeSetting->getService()); - $this->registerSchema($declarativeSetting->getAppId(), $declarativeSettingObject->getSchema()); + if (empty($this->declarativeForms)) { + $declarativeSettings = $this->coordinator->getRegistrationContext()->getDeclarativeSettings(); + foreach ($declarativeSettings as $declarativeSetting) { + $app = $declarativeSetting->getAppId(); + /** @var IDeclarativeSettingsForm $declarativeForm */ + $declarativeForm = Server::get($declarativeSetting->getService()); + $this->registerSchema($app, $declarativeForm->getSchema()); + $this->declarativeForms[$app][] = $declarativeForm; + } } $this->eventDispatcher->dispatchTyped(new DeclarativeSettingsRegisterFormEvent($this)); @@ -224,6 +235,10 @@ class DeclarativeManager implements IDeclarativeManager { $storageType = $this->getStorageType($app, $fieldId); switch ($storageType) { case DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL: + $form = $this->getForm($app, $formId); + if ($form !== null && $form instanceof IDeclarativeSettingsFormWithHandlers) { + return $form->getValue($fieldId, $user); + } $event = new DeclarativeSettingsGetValueEvent($user, $app, $formId, $fieldId); $this->eventDispatcher->dispatchTyped($event); return $event->getValue(); @@ -244,31 +259,85 @@ class DeclarativeManager implements IDeclarativeManager { $storageType = $this->getStorageType($app, $fieldId); switch ($storageType) { case DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL: + $form = $this->getForm($app, $formId); + if ($form !== null && $form instanceof IDeclarativeSettingsFormWithHandlers) { + $form->setValue($fieldId, $value, $user); + break; + } + // fall back to event handling $this->eventDispatcher->dispatchTyped(new DeclarativeSettingsSetValueEvent($user, $app, $formId, $fieldId, $value)); break; case DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL: - $this->saveInternalValue($user, $app, $fieldId, $value); + $this->saveInternalValue($user, $app, $formId, $fieldId, $value); break; default: throw new Exception('Unknown storage type "' . $storageType . '"'); } } + /** + * If a declarative setting was registered as a form and not just a schema + * then this will yield the registering form. + */ + private function getForm(string $app, string $formId): ?IDeclarativeSettingsForm { + $allForms = $this->declarativeForms[$app] ?? []; + foreach ($allForms as $form) { + if ($form->getSchema()['id'] === $formId) { + return $form; + } + } + return null; + } + private function getInternalValue(IUser $user, string $app, string $formId, string $fieldId): mixed { $sectionType = $this->getSectionType($app, $fieldId); $defaultValue = $this->getDefaultValue($app, $formId, $fieldId); + + $field = $this->getSchemaField($app, $formId, $fieldId); + $isSensitive = $field !== null && isset($field['sensitive']) && $field['sensitive'] === true; + switch ($sectionType) { case DeclarativeSettingsTypes::SECTION_TYPE_ADMIN: - return $this->config->getAppValue($app, $fieldId, $defaultValue); + $value = $this->config->getAppValue($app, $fieldId, $defaultValue); + break; case DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL: - return $this->config->getUserValue($user->getUID(), $app, $fieldId, $defaultValue); + $value = $this->config->getUserValue($user->getUID(), $app, $fieldId, $defaultValue); + break; default: throw new Exception('Unknown section type "' . $sectionType . '"'); } + if ($isSensitive && $value !== '') { + try { + $value = $this->crypto->decrypt($value); + } catch (Exception $e) { + $this->logger->warning('Failed to decrypt sensitive value for field {field} in app {app}: {message}', [ + 'field' => $fieldId, + 'app' => $app, + 'message' => $e->getMessage(), + ]); + $value = $defaultValue; + } + } + return $value; } - private function saveInternalValue(IUser $user, string $app, string $fieldId, mixed $value): void { + private function saveInternalValue(IUser $user, string $app, string $formId, string $fieldId, mixed $value): void { $sectionType = $this->getSectionType($app, $fieldId); + + $field = $this->getSchemaField($app, $formId, $fieldId); + if ($field !== null && isset($field['sensitive']) && $field['sensitive'] === true && $value !== '' && $value !== 'dummySecret') { + try { + $value = $this->crypto->encrypt($value); + } catch (Exception $e) { + $this->logger->warning('Failed to decrypt sensitive value for field {field} in app {app}: {message}', [ + 'field' => $fieldId, + 'app' => $app, + 'message' => $e->getMessage()] + ); + throw new Exception('Failed to encrypt sensitive value'); + } + } + switch ($sectionType) { case DeclarativeSettingsTypes::SECTION_TYPE_ADMIN: $this->appConfig->setValueString($app, $fieldId, $value); @@ -281,6 +350,27 @@ class DeclarativeManager implements IDeclarativeManager { } } + private function getSchemaField(string $app, string $formId, string $fieldId): ?array { + $form = $this->getForm($app, $formId); + if ($form !== null) { + foreach ($form->getSchema()['fields'] as $field) { + if ($field['id'] === $fieldId) { + return $field; + } + } + } + foreach ($this->appSchemas[$app] ?? [] as $schema) { + if ($schema['id'] === $formId) { + foreach ($schema['fields'] as $field) { + if ($field['id'] === $fieldId) { + return $field; + } + } + } + } + return null; + } + private function getDefaultValue(string $app, string $formId, string $fieldId): mixed { foreach ($this->appSchemas[$app] as $schema) { if ($schema['id'] === $formId) { @@ -358,6 +448,12 @@ class DeclarativeManager implements IDeclarativeManager { ]); return false; } + if (isset($field['sensitive']) && $field['sensitive'] === true && !in_array($field['type'], [DeclarativeSettingsTypes::TEXT, DeclarativeSettingsTypes::PASSWORD])) { + $this->logger->warning('Declarative settings: sensitive field type is supported only for TEXT and PASSWORD types ({app}, {form_id}, {field_id})', [ + 'app' => $appId, 'form_id' => $formId, 'field_id' => $fieldId, + ]); + return false; + } if (!$this->validateField($appId, $formId, $field)) { return false; } |