aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Borysenko <andrey18106x@gmail.com>2025-05-22 20:14:33 +0300
committerAndrey Borysenko <andrey18106x@gmail.com>2025-05-28 20:43:04 +0300
commit799433233879ddfced00b0881958fa7593fc5f4b (patch)
tree49971a408ca1f0755f54bd4e3b76465029d3687f
parent1d4b899244a35799b74c942fc191af6b5135b52f (diff)
downloadnextcloud-server-799433233879ddfced00b0881958fa7593fc5f4b.tar.gz
nextcloud-server-799433233879ddfced00b0881958fa7593fc5f4b.zip
feat: add support for sensitive Declarative settings values encryption
Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
-rw-r--r--apps/settings/appinfo/routes.php1
-rw-r--r--apps/settings/lib/Controller/CommonSettingsTrait.php8
-rw-r--r--apps/settings/lib/Controller/DeclarativeSettingsController.php40
-rw-r--r--apps/settings/lib/ResponseDefinitions.php1
-rw-r--r--apps/settings/openapi-full.json137
-rw-r--r--apps/settings/openapi.json137
-rw-r--r--apps/settings/src/components/DeclarativeSettings/DeclarativeSection.vue15
-rw-r--r--apps/settings/src/main-declarative-settings-forms.ts1
-rw-r--r--apps/testing/lib/Settings/DeclarativeSettingsForm.php30
-rw-r--r--lib/private/Settings/DeclarativeManager.php71
-rw-r--r--lib/public/Settings/IDeclarativeSettingsForm.php1
-rw-r--r--openapi.json137
-rw-r--r--tests/lib/Settings/DeclarativeManagerTest.php38
13 files changed, 610 insertions, 7 deletions
diff --git a/apps/settings/appinfo/routes.php b/apps/settings/appinfo/routes.php
index 5038cf33348..b0ce73113f4 100644
--- a/apps/settings/appinfo/routes.php
+++ b/apps/settings/appinfo/routes.php
@@ -67,6 +67,7 @@ return [
],
'ocs' => [
['name' => 'DeclarativeSettings#setValue', 'url' => '/settings/api/declarative/value', 'verb' => 'POST', 'root' => ''],
+ ['name' => 'DeclarativeSettings#setSensitiveValue', 'url' => '/settings/api/declarative/value-sensitive', 'verb' => 'POST', 'root' => ''],
['name' => 'DeclarativeSettings#getForms', 'url' => '/settings/api/declarative/forms', 'verb' => 'GET', 'root' => ''],
],
];
diff --git a/apps/settings/lib/Controller/CommonSettingsTrait.php b/apps/settings/lib/Controller/CommonSettingsTrait.php
index 56760c10f81..67398d1357b 100644
--- a/apps/settings/lib/Controller/CommonSettingsTrait.php
+++ b/apps/settings/lib/Controller/CommonSettingsTrait.php
@@ -144,6 +144,14 @@ trait CommonSettingsTrait {
$this->declarativeSettingsManager->loadSchemas();
$declarativeSettings = $this->declarativeSettingsManager->getFormsWithValues($user, $type, $section);
+ foreach ($declarativeSettings as &$form) {
+ foreach ($form['fields'] as &$field) {
+ if (isset($field['sensitive']) && $field['sensitive'] === true && !empty($field['value'])) {
+ $field['value'] = 'dummySecret';
+ }
+ }
+ }
+
if ($type === 'personal') {
$settings = array_values($this->settingsManager->getPersonalSettings($section));
if ($section === 'theming') {
diff --git a/apps/settings/lib/Controller/DeclarativeSettingsController.php b/apps/settings/lib/Controller/DeclarativeSettingsController.php
index eb9d45839de..4e4bee4043c 100644
--- a/apps/settings/lib/Controller/DeclarativeSettingsController.php
+++ b/apps/settings/lib/Controller/DeclarativeSettingsController.php
@@ -15,6 +15,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
use OCA\Settings\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCSController;
@@ -53,6 +54,45 @@ class DeclarativeSettingsController extends OCSController {
*/
#[NoAdminRequired]
public function setValue(string $app, string $formId, string $fieldId, mixed $value): DataResponse {
+ return $this->saveValue($app, $formId, $fieldId, $value);
+ }
+
+ /**
+ * Sets a declarative settings value.
+ * Password confirmation is required for sensitive values.
+ *
+ * @param string $app ID of the app
+ * @param string $formId ID of the form
+ * @param string $fieldId ID of the field
+ * @param mixed $value Value to be saved
+ * @return DataResponse<Http::STATUS_OK, null, array{}>
+ * @throws NotLoggedInException Not logged in or not an admin user
+ * @throws NotAdminException Not logged in or not an admin user
+ * @throws OCSBadRequestException Invalid arguments to save value
+ *
+ * 200: Value set successfully
+ */
+ #[NoAdminRequired]
+ #[PasswordConfirmationRequired]
+ public function setSensitiveValue(string $app, string $formId, string $fieldId, mixed $value): DataResponse {
+ return $this->saveValue($app, $formId, $fieldId, $value);
+ }
+
+ /**
+ * Sets a declarative settings value.
+ *
+ * @param string $app ID of the app
+ * @param string $formId ID of the form
+ * @param string $fieldId ID of the field
+ * @param mixed $value Value to be saved
+ * @return DataResponse<Http::STATUS_OK, null, array{}>
+ * @throws NotLoggedInException Not logged in or not an admin user
+ * @throws NotAdminException Not logged in or not an admin user
+ * @throws OCSBadRequestException Invalid arguments to save value
+ *
+ * 200: Value set successfully
+ */
+ private function saveValue(string $app, string $formId, string $fieldId, mixed $value): DataResponse {
$user = $this->userSession->getUser();
if ($user === null) {
throw new NotLoggedInException();
diff --git a/apps/settings/lib/ResponseDefinitions.php b/apps/settings/lib/ResponseDefinitions.php
index 0f3d6bffccf..12adefda91f 100644
--- a/apps/settings/lib/ResponseDefinitions.php
+++ b/apps/settings/lib/ResponseDefinitions.php
@@ -20,6 +20,7 @@ namespace OCA\Settings;
* default: mixed,
* options?: list<string|array{name: string, value: mixed}>,
* value: string|int|float|bool|list<string>,
+ * sensitive?: boolean,
* }
*
* @psalm-type SettingsDeclarativeForm = array{
diff --git a/apps/settings/openapi-full.json b/apps/settings/openapi-full.json
index e12598a2584..d592051507b 100644
--- a/apps/settings/openapi-full.json
+++ b/apps/settings/openapi-full.json
@@ -169,6 +169,9 @@
}
}
]
+ },
+ "sensitive": {
+ "type": "boolean"
}
}
},
@@ -373,6 +376,140 @@
}
}
},
+ "/ocs/v2.php/settings/api/declarative/value-sensitive": {
+ "post": {
+ "operationId": "declarative_settings-set-sensitive-value",
+ "summary": "Sets a declarative settings value. Password confirmation is required for sensitive values.",
+ "description": "This endpoint requires password confirmation",
+ "tags": [
+ "declarative_settings"
+ ],
+ "security": [
+ {
+ "bearer_auth": []
+ },
+ {
+ "basic_auth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "app",
+ "formId",
+ "fieldId",
+ "value"
+ ],
+ "properties": {
+ "app": {
+ "type": "string",
+ "description": "ID of the app"
+ },
+ "formId": {
+ "type": "string",
+ "description": "ID of the form"
+ },
+ "fieldId": {
+ "type": "string",
+ "description": "ID of the field"
+ },
+ "value": {
+ "type": "object",
+ "description": "Value to be saved"
+ }
+ }
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "OCS-APIRequest",
+ "in": "header",
+ "description": "Required to be true for the API request to pass",
+ "required": true,
+ "schema": {
+ "type": "boolean",
+ "default": true
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Value set successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "ocs"
+ ],
+ "properties": {
+ "ocs": {
+ "type": "object",
+ "required": [
+ "meta",
+ "data"
+ ],
+ "properties": {
+ "meta": {
+ "$ref": "#/components/schemas/OCSMeta"
+ },
+ "data": {
+ "nullable": true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Not logged in or not an admin user",
+ "content": {
+ "text/plain": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid arguments to save value",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "ocs"
+ ],
+ "properties": {
+ "ocs": {
+ "type": "object",
+ "required": [
+ "meta",
+ "data"
+ ],
+ "properties": {
+ "meta": {
+ "$ref": "#/components/schemas/OCSMeta"
+ },
+ "data": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/ocs/v2.php/settings/api/declarative/forms": {
"get": {
"operationId": "declarative_settings-get-forms",
diff --git a/apps/settings/openapi.json b/apps/settings/openapi.json
index f81b18bdc70..f38960aa244 100644
--- a/apps/settings/openapi.json
+++ b/apps/settings/openapi.json
@@ -169,6 +169,9 @@
}
}
]
+ },
+ "sensitive": {
+ "type": "boolean"
}
}
},
@@ -332,6 +335,140 @@
}
}
},
+ "/ocs/v2.php/settings/api/declarative/value-sensitive": {
+ "post": {
+ "operationId": "declarative_settings-set-sensitive-value",
+ "summary": "Sets a declarative settings value. Password confirmation is required for sensitive values.",
+ "description": "This endpoint requires password confirmation",
+ "tags": [
+ "declarative_settings"
+ ],
+ "security": [
+ {
+ "bearer_auth": []
+ },
+ {
+ "basic_auth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "app",
+ "formId",
+ "fieldId",
+ "value"
+ ],
+ "properties": {
+ "app": {
+ "type": "string",
+ "description": "ID of the app"
+ },
+ "formId": {
+ "type": "string",
+ "description": "ID of the form"
+ },
+ "fieldId": {
+ "type": "string",
+ "description": "ID of the field"
+ },
+ "value": {
+ "type": "object",
+ "description": "Value to be saved"
+ }
+ }
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "OCS-APIRequest",
+ "in": "header",
+ "description": "Required to be true for the API request to pass",
+ "required": true,
+ "schema": {
+ "type": "boolean",
+ "default": true
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Value set successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "ocs"
+ ],
+ "properties": {
+ "ocs": {
+ "type": "object",
+ "required": [
+ "meta",
+ "data"
+ ],
+ "properties": {
+ "meta": {
+ "$ref": "#/components/schemas/OCSMeta"
+ },
+ "data": {
+ "nullable": true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Not logged in or not an admin user",
+ "content": {
+ "text/plain": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid arguments to save value",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "ocs"
+ ],
+ "properties": {
+ "ocs": {
+ "type": "object",
+ "required": [
+ "meta",
+ "data"
+ ],
+ "properties": {
+ "meta": {
+ "$ref": "#/components/schemas/OCSMeta"
+ },
+ "data": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/ocs/v2.php/settings/api/declarative/forms": {
"get": {
"operationId": "declarative_settings-get-forms",
diff --git a/apps/settings/src/components/DeclarativeSettings/DeclarativeSection.vue b/apps/settings/src/components/DeclarativeSettings/DeclarativeSection.vue
index 334739337e1..9ee1680516e 100644
--- a/apps/settings/src/components/DeclarativeSettings/DeclarativeSection.vue
+++ b/apps/settings/src/components/DeclarativeSettings/DeclarativeSection.vue
@@ -119,6 +119,7 @@ import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
import NcInputField from '@nextcloud/vue/components/NcInputField'
import NcSelect from '@nextcloud/vue/components/NcSelect'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
+import { confirmPassword } from '@nextcloud/password-confirmation'
export default {
name: 'DeclarativeSection',
@@ -202,9 +203,19 @@ export default {
}
},
- updateDeclarativeSettingsValue(formField, value = null) {
+ async updateDeclarativeSettingsValue(formField, value = null) {
try {
- return axios.post(generateOcsUrl('settings/api/declarative/value'), {
+ let url = generateOcsUrl('settings/api/declarative/value')
+ if (formField?.sensitive === true) {
+ url = generateOcsUrl('settings/api/declarative/value-sensitive')
+ try {
+ await confirmPassword()
+ } catch (err) {
+ showError(t('settings', 'Password confirmation is required'))
+ return
+ }
+ }
+ return axios.post(url, {
app: this.formApp,
formId: this.form.id.replace(this.formApp + '_', ''), // Remove app prefix to send clean form id
fieldId: formField.id,
diff --git a/apps/settings/src/main-declarative-settings-forms.ts b/apps/settings/src/main-declarative-settings-forms.ts
index 6e2d71b69ca..d6f5973baea 100644
--- a/apps/settings/src/main-declarative-settings-forms.ts
+++ b/apps/settings/src/main-declarative-settings-forms.ts
@@ -20,6 +20,7 @@ interface DeclarativeFormField {
options: Array<unknown>|null,
value: unknown,
default: unknown,
+ sensitive: boolean,
}
interface DeclarativeForm {
diff --git a/apps/testing/lib/Settings/DeclarativeSettingsForm.php b/apps/testing/lib/Settings/DeclarativeSettingsForm.php
index dd83e9c9e89..55e44cbcbea 100644
--- a/apps/testing/lib/Settings/DeclarativeSettingsForm.php
+++ b/apps/testing/lib/Settings/DeclarativeSettingsForm.php
@@ -169,6 +169,36 @@ class DeclarativeSettingsForm implements IDeclarativeSettingsForm {
],
],
],
+ [
+ 'id' => 'test_sensitive_field',
+ 'title' => 'Sensitive text field',
+ 'description' => 'Set some secure value setting that is stored encrypted',
+ 'type' => DeclarativeSettingsTypes::TEXT,
+ 'label' => 'Sensitive field',
+ 'placeholder' => 'Set secure value',
+ 'default' => '',
+ 'sensitive' => true, // only for TEXT, PASSWORD types
+ ],
+ [
+ 'id' => 'test_sensitive_field_2',
+ 'title' => 'Sensitive password field',
+ 'description' => 'Set some password setting that is stored encrypted',
+ 'type' => DeclarativeSettingsTypes::PASSWORD,
+ 'label' => 'Sensitive field',
+ 'placeholder' => 'Set secure value',
+ 'default' => '',
+ 'sensitive' => true, // only for TEXT, PASSWORD types
+ ],
+ [
+ 'id' => 'test_non_sensitive_field',
+ 'title' => 'Password field',
+ 'description' => 'Set some password setting',
+ 'type' => DeclarativeSettingsTypes::PASSWORD,
+ 'label' => 'Password field',
+ 'placeholder' => 'Set secure value',
+ 'default' => '',
+ 'sensitive' => false,
+ ],
],
];
}
diff --git a/lib/private/Settings/DeclarativeManager.php b/lib/private/Settings/DeclarativeManager.php
index dea0c678f20..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;
@@ -49,6 +50,7 @@ class DeclarativeManager implements IDeclarativeManager {
private IConfig $config,
private IAppConfig $appConfig,
private LoggerInterface $logger,
+ private ICrypto $crypto,
) {
}
@@ -266,7 +268,7 @@ class DeclarativeManager implements IDeclarativeManager {
$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 . '"');
@@ -290,18 +292,52 @@ class DeclarativeManager implements IDeclarativeManager {
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);
@@ -314,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) {
@@ -391,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;
}
diff --git a/lib/public/Settings/IDeclarativeSettingsForm.php b/lib/public/Settings/IDeclarativeSettingsForm.php
index d471cdf4a93..419905b7b23 100644
--- a/lib/public/Settings/IDeclarativeSettingsForm.php
+++ b/lib/public/Settings/IDeclarativeSettingsForm.php
@@ -27,6 +27,7 @@ namespace OCP\Settings;
* label?: string,
* default: mixed,
* options?: list<string|array{name: string, value: mixed}>,
+ * sensitive?: boolean,
* }
*
* @psalm-type DeclarativeSettingsFormFieldWithValue = DeclarativeSettingsFormField&array{
diff --git a/openapi.json b/openapi.json
index 2c7ed4b0a1f..cf1199fe6e7 100644
--- a/openapi.json
+++ b/openapi.json
@@ -3981,6 +3981,9 @@
}
}
]
+ },
+ "sensitive": {
+ "type": "boolean"
}
}
},
@@ -27197,6 +27200,140 @@
}
}
},
+ "/ocs/v2.php/settings/api/declarative/value-sensitive": {
+ "post": {
+ "operationId": "settings-declarative_settings-set-sensitive-value",
+ "summary": "Sets a declarative settings value. Password confirmation is required for sensitive values.",
+ "description": "This endpoint requires password confirmation",
+ "tags": [
+ "settings/declarative_settings"
+ ],
+ "security": [
+ {
+ "bearer_auth": []
+ },
+ {
+ "basic_auth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "app",
+ "formId",
+ "fieldId",
+ "value"
+ ],
+ "properties": {
+ "app": {
+ "type": "string",
+ "description": "ID of the app"
+ },
+ "formId": {
+ "type": "string",
+ "description": "ID of the form"
+ },
+ "fieldId": {
+ "type": "string",
+ "description": "ID of the field"
+ },
+ "value": {
+ "type": "object",
+ "description": "Value to be saved"
+ }
+ }
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "OCS-APIRequest",
+ "in": "header",
+ "description": "Required to be true for the API request to pass",
+ "required": true,
+ "schema": {
+ "type": "boolean",
+ "default": true
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Value set successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "ocs"
+ ],
+ "properties": {
+ "ocs": {
+ "type": "object",
+ "required": [
+ "meta",
+ "data"
+ ],
+ "properties": {
+ "meta": {
+ "$ref": "#/components/schemas/OCSMeta"
+ },
+ "data": {
+ "nullable": true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Not logged in or not an admin user",
+ "content": {
+ "text/plain": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Invalid arguments to save value",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": [
+ "ocs"
+ ],
+ "properties": {
+ "ocs": {
+ "type": "object",
+ "required": [
+ "meta",
+ "data"
+ ],
+ "properties": {
+ "meta": {
+ "$ref": "#/components/schemas/OCSMeta"
+ },
+ "data": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/ocs/v2.php/settings/api/declarative/forms": {
"get": {
"operationId": "settings-declarative_settings-get-forms",
diff --git a/tests/lib/Settings/DeclarativeManagerTest.php b/tests/lib/Settings/DeclarativeManagerTest.php
index 522264acd8c..d279dfa46f0 100644
--- a/tests/lib/Settings/DeclarativeManagerTest.php
+++ b/tests/lib/Settings/DeclarativeManagerTest.php
@@ -18,6 +18,7 @@ use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IUser;
+use OCP\Security\ICrypto;
use OCP\Settings\DeclarativeSettingsTypes;
use OCP\Settings\Events\DeclarativeSettingsSetValueEvent;
use OCP\Settings\IDeclarativeManager;
@@ -50,6 +51,9 @@ class DeclarativeManagerTest extends TestCase {
/** @var LoggerInterface|MockObject */
private $logger;
+ /** @var ICrypto|MockObject */
+ private $crypto;
+
/** @var IUser|MockObject */
private $user;
@@ -215,6 +219,36 @@ class DeclarativeManagerTest extends TestCase {
],
],
],
+ [
+ 'id' => 'test_sensitive_field',
+ 'title' => 'Sensitive text field',
+ 'description' => 'Set some secure value setting that is stored encrypted',
+ 'type' => DeclarativeSettingsTypes::TEXT,
+ 'label' => 'Sensitive field',
+ 'placeholder' => 'Set secure value',
+ 'default' => '',
+ 'sensitive' => true, // only for TEXT, PASSWORD types
+ ],
+ [
+ 'id' => 'test_sensitive_field_2',
+ 'title' => 'Sensitive password field',
+ 'description' => 'Set some password setting that is stored encrypted',
+ 'type' => DeclarativeSettingsTypes::PASSWORD,
+ 'label' => 'Sensitive field',
+ 'placeholder' => 'Set secure value',
+ 'default' => '',
+ 'sensitive' => true, // only for TEXT, PASSWORD types
+ ],
+ [
+ 'id' => 'test_non_sensitive_field',
+ 'title' => 'Password field',
+ 'description' => 'Set some password setting',
+ 'type' => DeclarativeSettingsTypes::PASSWORD,
+ 'label' => 'Password field',
+ 'placeholder' => 'Set secure value',
+ 'default' => '',
+ 'sensitive' => false,
+ ],
],
];
@@ -229,6 +263,7 @@ class DeclarativeManagerTest extends TestCase {
$this->config = $this->createMock(IConfig::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->logger = $this->createMock(LoggerInterface::class);
+ $this->crypto = $this->createMock(ICrypto::class);
$this->declarativeManager = new DeclarativeManager(
$this->eventDispatcher,
@@ -236,7 +271,8 @@ class DeclarativeManagerTest extends TestCase {
$this->coordinator,
$this->config,
$this->appConfig,
- $this->logger
+ $this->logger,
+ $this->crypto,
);
$this->user = $this->createMock(IUser::class);