From c101dcd94b252ef8d1da4cd884c19511696d896b Mon Sep 17 00:00:00 2001 From: Edward Ly Date: Thu, 14 Nov 2024 18:09:42 -0800 Subject: [PATCH 1/2] feat(admin): add password confirmation when saving credentials Signed-off-by: Edward Ly --- lib/Controller/DocusignController.php | 2 ++ package-lock.json | 39 +++++++++++++++++++++++++++ package.json | 1 + src/components/AdminSettings.vue | 4 ++- 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/Controller/DocusignController.php b/lib/Controller/DocusignController.php index 2118665..33d9b70 100644 --- a/lib/Controller/DocusignController.php +++ b/lib/Controller/DocusignController.php @@ -21,6 +21,7 @@ use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\IConfig; @@ -111,6 +112,7 @@ public function signStandalone(int $fileId, array $targetEmails = [], array $tar * @param array $values * @return DataResponse */ + #[PasswordConfirmationRequired] #[FrontpageRoute(verb: 'PUT', url: '/docusign-config')] public function setDocusignConfig(array $values): DataResponse { foreach ($values as $key => $value) { diff --git a/package-lock.json b/package-lock.json index 365a19e..4b52674 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@nextcloud/initial-state": "^2.1.0", "@nextcloud/l10n": "^2.2.0", "@nextcloud/moment": "^1.3.1", + "@nextcloud/password-confirmation": "^5.1.1", "@nextcloud/router": "^3.0.0", "@nextcloud/vue": "^8.8.1", "@nextcloud/vue-dashboard": "^2.0.1", @@ -2616,6 +2617,44 @@ "npm": "^9.0.0" } }, + "node_modules/@nextcloud/password-confirmation": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@nextcloud/password-confirmation/-/password-confirmation-5.1.1.tgz", + "integrity": "sha512-UlQcjVe/fr/JaJ6TWaRM+yBLIEZRU6RWMy0JoExcA6UVJs2HJrRIyVMuiCLuIYlH23ReJH+z7zFI3+V7vdeJ1Q==", + "license": "MIT", + "dependencies": { + "@nextcloud/axios": "^2.5.0", + "@nextcloud/l10n": "^3.1.0", + "@nextcloud/router": "^3.0.1" + }, + "engines": { + "node": "^20.0.0", + "npm": "^10.0.0" + }, + "peerDependencies": { + "@nextcloud/vue": "^8.0.0", + "vue": "^2.7.16" + } + }, + "node_modules/@nextcloud/password-confirmation/node_modules/@nextcloud/l10n": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nextcloud/l10n/-/l10n-3.1.0.tgz", + "integrity": "sha512-unciqr8QSJ29vFBw9S1bquyoj1PTWHszNL8tcUNuxUAYpq0hX+8o7rpB5gimELA4sj4m9+VCJwgLtBZd1Yj0lg==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@nextcloud/router": "^3.0.1", + "@nextcloud/typings": "^1.8.0", + "@types/dompurify": "^3.0.5", + "@types/escape-html": "^1.0.4", + "dompurify": "^3.1.2", + "escape-html": "^1.0.3", + "node-gettext": "^3.0.0" + }, + "engines": { + "node": "^20.0.0", + "npm": "^10.0.0" + } + }, "node_modules/@nextcloud/paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@nextcloud/paths/-/paths-2.2.1.tgz", diff --git a/package.json b/package.json index 9a43032..ddc8d26 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@nextcloud/initial-state": "^2.1.0", "@nextcloud/l10n": "^2.2.0", "@nextcloud/moment": "^1.3.1", + "@nextcloud/password-confirmation": "^5.1.1", "@nextcloud/router": "^3.0.0", "@nextcloud/vue": "^8.8.1", "@nextcloud/vue-dashboard": "^2.0.1", diff --git a/src/components/AdminSettings.vue b/src/components/AdminSettings.vue index d61c6fd..19c1429 100644 --- a/src/components/AdminSettings.vue +++ b/src/components/AdminSettings.vue @@ -77,6 +77,7 @@ import { generateUrl } from '@nextcloud/router' import axios from '@nextcloud/axios' import { delay } from '../utils.js' import { showSuccess, showError } from '@nextcloud/dialogs' +import { confirmPassword } from '@nextcloud/password-confirmation' export default { name: 'AdminSettings', @@ -126,7 +127,8 @@ export default { methods: { onFieldInput() { this.loading = true - delay(() => { + delay(async () => { + await confirmPassword() this.saveOptions({ docusign_client_id: this.state.docusign_client_id, docusign_client_secret: this.state.docusign_client_secret, From d710b57e89dc9123a844a36458c2afaae2e7d850 Mon Sep 17 00:00:00 2001 From: Edward Ly Date: Thu, 14 Nov 2024 21:58:19 -0800 Subject: [PATCH 2/2] fix(admin): hide saved credentials from user, require client credentials when needed Signed-off-by: Edward Ly --- lib/Settings/Admin.php | 4 ++-- src/components/AdminSettings.vue | 27 +++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index f6dcaff..719ea53 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -84,8 +84,8 @@ public function getForm(): TemplateResponse { $userEmail = $this->config->getAppValue(Application::APP_ID, 'docusign_user_email'); $adminConfig = [ - 'docusign_client_id' => $clientID, - 'docusign_client_secret' => $clientSecret, + 'docusign_client_id' => $clientID ? 'dummyClientNumber' : '', + 'docusign_client_secret' => $clientSecret ? 'dummyClientSecret' : '', 'docusign_token' => $token !== '', 'docusign_user_name' => $userName, 'docusign_user_email' => $userEmail, diff --git a/src/components/AdminSettings.vue b/src/components/AdminSettings.vue index 19c1429..e699ca1 100644 --- a/src/components/AdminSettings.vue +++ b/src/components/AdminSettings.vue @@ -129,10 +129,15 @@ export default { this.loading = true delay(async () => { await confirmPassword() - this.saveOptions({ - docusign_client_id: this.state.docusign_client_id, - docusign_client_secret: this.state.docusign_client_secret, - }) + + const values = {} + if (this.state.docusign_client_id !== 'dummyClientNumber') { + values.docusign_client_id = this.state.docusign_client_id + } + if (this.state.docusign_client_secret !== 'dummyClientSecret') { + values.docusign_client_secret = this.state.docusign_client_secret + } + this.saveOptions(values) }, 2000)() }, saveOptions(values) { @@ -155,6 +160,20 @@ export default { }) }, onOAuthClick() { + let dummyValueProvided = false + if (this.state.docusign_client_id === 'dummyClientNumber') { + this.state.docusign_client_id = '' + dummyValueProvided = true + } + if (this.state.docusign_client_secret === 'dummyClientSecret') { + this.state.docusign_client_secret = '' + dummyValueProvided = true + } + if (dummyValueProvided) { + showError(t('integration_docusign', 'For security reasons, please enter your client credentials again')) + return + } + const oauthState = Math.random().toString(36).substring(3) const scopes = [ 'signature',