From 9fa4b3986a4a11fefb97bf23c9874494aedb9bc8 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Mon, 15 Jul 2024 15:08:24 +0200 Subject: [PATCH] fix(config): Fix incorrect config handling Signed-off-by: provokateurin --- appinfo/routes.php | 2 + lib/Controller/ConfigController.php | 38 +++++- lib/Settings/Admin.php | 4 +- lib/Settings/Personal.php | 3 +- package-lock.json | 185 +++++++++++++++++----------- package.json | 1 + src/components/AdminSettings.vue | 45 ++++--- src/components/PersonalSettings.vue | 69 +++++++---- 8 files changed, 229 insertions(+), 118 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 7a92333..b08d9b1 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -13,7 +13,9 @@ 'routes' => [ ['name' => 'config#oauthRedirect', 'url' => '/oauth-redirect', 'verb' => 'GET'], ['name' => 'config#setConfig', 'url' => '/config', 'verb' => 'PUT'], + ['name' => 'config#setSensitiveConfig', 'url' => '/sensitive-config', 'verb' => 'PUT'], ['name' => 'config#setAdminConfig', 'url' => '/admin-config', 'verb' => 'PUT'], + ['name' => 'config#setSensitiveAdminConfig', 'url' => '/sensitive-admin-config', 'verb' => 'PUT'], ['name' => 'config#popupSuccessPage', 'url' => '/popup-success', 'verb' => 'GET'], ['name' => 'gitlabAPI#getEvents', 'url' => '/events', 'verb' => 'GET'], diff --git a/lib/Controller/ConfigController.php b/lib/Controller/ConfigController.php index 489055d..ce82fac 100644 --- a/lib/Controller/ConfigController.php +++ b/lib/Controller/ConfigController.php @@ -23,7 +23,6 @@ use OCP\AppFramework\Services\IInitialState; use OCP\IConfig; use OCP\IL10N; - use OCP\IRequest; use OCP\IURLGenerator; use OCP\PreConditionNotMetException; @@ -46,11 +45,27 @@ public function __construct(string $appName, * set config values * @NoAdminRequired * - * @param array $values - * @return DataResponse * @throws PreConditionNotMetException */ public function setConfig(array $values): DataResponse { + foreach ($values as $key => $value) { + if ($key === 'url' || $key === 'token') { + return new DataResponse([], Http::STATUS_BAD_REQUEST); + } + + $this->config->setUserValue($this->userId, Application::APP_ID, $key, $value); + } + + return new DataResponse([]); + } + + /** + * @PasswordConfirmationRequired + * @NoAdminRequired + * + * @throws PreConditionNotMetException + */ + public function setSensitiveConfig(array $values): DataResponse { // revoke the oauth token if needed if (isset($values['token']) && $values['token'] === '') { $tokenType = $this->config->getUserValue($this->userId, Application::APP_ID, 'token_type'); @@ -62,6 +77,7 @@ public function setConfig(array $values): DataResponse { foreach ($values as $key => $value) { $this->config->setUserValue($this->userId, Application::APP_ID, $key, $value); } + $result = []; if (isset($values['token'])) { @@ -100,6 +116,20 @@ public function setConfig(array $values): DataResponse { * @return DataResponse */ public function setAdminConfig(array $values): DataResponse { + foreach ($values as $key => $value) { + if ($key === 'client_id' || $key === 'client_secret' || $key === 'oauth_instance_url') { + return new DataResponse([], Http::STATUS_BAD_REQUEST); + } + + $this->config->setAppValue(Application::APP_ID, $key, $value); + } + return new DataResponse(1); + } + + /** + * @PasswordConfirmationRequired + */ + public function setSensitiveAdminConfig(array $values): DataResponse { foreach ($values as $key => $value) { $this->config->setAppValue(Application::APP_ID, $key, $value); } @@ -153,7 +183,7 @@ public function oauthRedirect(string $code = '', string $state = ''): RedirectRe $refreshToken = $result['refresh_token'] ?? ''; if (isset($result['expires_in'])) { $nowTs = (new Datetime())->getTimestamp(); - $expiresAt = $nowTs + (int) $result['expires_in']; + $expiresAt = $nowTs + (int)$result['expires_in']; $this->config->setUserValue($this->userId, Application::APP_ID, 'token_expires_at', strval($expiresAt)); } $this->config->setUserValue($this->userId, Application::APP_ID, 'url', $adminOauthUrl); diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index a570cd7..0aa6f9e 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -6,7 +6,6 @@ use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; use OCP\IConfig; - use OCP\Settings\ISettings; class Admin implements ISettings { @@ -20,7 +19,8 @@ public function __construct(private IConfig $config, */ public function getForm(): TemplateResponse { $clientID = $this->config->getAppValue(Application::APP_ID, 'client_id'); - $clientSecret = $this->config->getAppValue(Application::APP_ID, 'client_secret'); + // Do not expose the saved client secret to the user + $clientSecret = $this->config->getAppValue(Application::APP_ID, 'client_secret') !== '' ? 'dummyToken' : ''; $oauthUrl = $this->config->getAppValue(Application::APP_ID, 'oauth_instance_url'); $usePopup = $this->config->getAppValue(Application::APP_ID, 'use_popup', '0') === '1'; $adminLinkPreviewEnabled = $this->config->getAppValue(Application::APP_ID, 'link_preview_enabled', '1') === '1'; diff --git a/lib/Settings/Personal.php b/lib/Settings/Personal.php index 1c42dc2..7051969 100644 --- a/lib/Settings/Personal.php +++ b/lib/Settings/Personal.php @@ -20,7 +20,8 @@ public function __construct(private IConfig $config, * @return TemplateResponse */ public function getForm(): TemplateResponse { - $token = $this->config->getUserValue($this->userId, Application::APP_ID, 'token'); + // Do not expose the saved token to the user + $token = $this->config->getUserValue($this->userId, Application::APP_ID, 'token') !== '' ? 'dummyToken' : ''; $searchEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'search_enabled', '0') === '1'; $searchIssuesEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'search_issues_enabled', '0') === '1'; $searchMRsEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'search_mrs_enabled', '0') === '1'; diff --git a/package-lock.json b/package-lock.json index 8ccfc7f..93581c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,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", @@ -2947,42 +2948,31 @@ "integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==" }, "node_modules/@nextcloud/auth": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@nextcloud/auth/-/auth-2.2.1.tgz", - "integrity": "sha512-zYtgrg9NMZfN8kmL5JPCsh5jDhpTCEslhnZWMvbhTiQ7hrOnji/67ok6VMK0CTJ1a92Vr67Ow72lW7YRX69zEA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@nextcloud/auth/-/auth-2.3.0.tgz", + "integrity": "sha512-PCkRJbML9sXvBENY43vTIERIZJFk2azu08IK6zYOnOZ7cFkD1QlFJtdTCZTImQLg01IXhIm0j0ExEdatHoqz7g==", + "license": "GPL-3.0-or-later", "dependencies": { - "@nextcloud/event-bus": "^3.1.0" + "@nextcloud/event-bus": "^3.2.0" }, "engines": { "node": "^20.0.0", - "npm": "^9.0.0" + "npm": "^10.0.0" } }, "node_modules/@nextcloud/axios": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@nextcloud/axios/-/axios-2.4.0.tgz", - "integrity": "sha512-ARGzT9p45L0sjRIV3JZWGPtMbwgxd4eEMcMJNn58NA7UQIsMkTwHb5pXQjL+5elXY9zp/JMz7n/7SHTp0bkuXQ==", - "dependencies": { - "@nextcloud/auth": "^2.1.0", - "@nextcloud/router": "^2.1.2", - "axios": "^1.4.0" - }, - "engines": { - "node": "^20.0.0", - "npm": "^9.0.0" - } - }, - "node_modules/@nextcloud/axios/node_modules/@nextcloud/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-2.2.0.tgz", - "integrity": "sha512-M4AVGnB5tt3MYO5RpH/R2jq7z/nW05AmRhk4Lh68krVwRIYGo8pgNikKrPGogHd2Q3UgzF5Py1drHz3uuV99bQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@nextcloud/axios/-/axios-2.5.0.tgz", + "integrity": "sha512-82LQ5PZA0ZVUnS8QiGoAGOR5kE7EKD84qEEgeZJ+Y7p5iljwi3AT6niQuP7YuHjt3MKM+6jQiyghZk5SquiszQ==", + "license": "GPL-3.0", "dependencies": { - "@nextcloud/typings": "^1.7.0", - "core-js": "^3.6.4" + "@nextcloud/auth": "^2.3.0", + "@nextcloud/router": "^3.0.1", + "axios": "^1.6.8" }, "engines": { "node": "^20.0.0", - "npm": "^9.0.0" + "npm": "^10.0.0" } }, "node_modules/@nextcloud/babel-config": { @@ -3177,35 +3167,24 @@ } }, "node_modules/@nextcloud/event-bus": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-3.1.0.tgz", - "integrity": "sha512-purXQsXbhbmpcDsbDuR0i7vwUgOsqnIUa7QAD3lV/UZUkUT94SmxBM5LgQ8iV8TQBWWleEwQHy5kYfHeTGF9wg==", - "dependencies": { - "semver": "^7.5.1" - }, - "engines": { - "node": "^16.0.0", - "npm": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@nextcloud/event-bus/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-3.3.1.tgz", + "integrity": "sha512-VBYJspOVk5aZopgZwCUoMKFqcTLCNel2TLvtu0HMPV2gR5ZLPiPAKbkyKkYTh+Sd5QB1gR6l3STTv1gyal0soQ==", + "license": "GPL-3.0-or-later", "dependencies": { - "yallist": "^4.0.0" + "@types/node": "^20.12.12", + "semver": "^7.6.2" }, "engines": { - "node": ">=10" + "node": "^20.0.0", + "npm": "^10.0.0" } }, "node_modules/@nextcloud/event-bus/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3213,11 +3192,6 @@ "node": ">=10" } }, - "node_modules/@nextcloud/event-bus/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/@nextcloud/files": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.1.0.tgz", @@ -3314,6 +3288,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.1.0", "resolved": "https://registry.npmjs.org/@nextcloud/paths/-/paths-2.1.0.tgz", @@ -3323,9 +3335,10 @@ } }, "node_modules/@nextcloud/router": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-3.0.0.tgz", - "integrity": "sha512-RlPrOPw94yT9rmt3+2sUs2cmWzqhX5eFW+i/EHymJEKgURVtnqCcXjIcAiLTfgsCCdAS1hGapBL8j8rhHk1FHQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-3.0.1.tgz", + "integrity": "sha512-Ci/uD3x8OKHdxSqXL6gRJ+mGJOEXjeiHjj7hqsZqVTsT7kOrCjDf0/J8z5RyLlokKZ0IpSe+hGxgi3YB7Gpw3Q==", + "license": "GPL-3.0-or-later", "dependencies": { "@nextcloud/typings": "^1.7.0" }, @@ -3917,6 +3930,21 @@ "@types/ms": "*" } }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, + "node_modules/@types/escape-html": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/escape-html/-/escape-html-1.0.4.tgz", + "integrity": "sha512-qZ72SFTgUAZ5a7Tj6kf2SHLetiH5S6f8G5frB2SPQ3EyF02kxdyBFf4Tz4banE3xCgGnKgWLt//a6VuYHKYJTg==", + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "8.56.5", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz", @@ -4069,9 +4097,10 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "20.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", + "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } @@ -4200,6 +4229,12 @@ "resolved": "https://registry.npmjs.org/@types/toastify-js/-/toastify-js-1.12.3.tgz", "integrity": "sha512-9RjLlbAHMSaae/KZNHGv19VG4gcLIm3YjvacCXBtfMfYn26h76YP5oxXI8k26q4iKXCB9LNfv18lsoS0JnFPTg==" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", @@ -5392,7 +5427,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", @@ -5411,11 +5447,12 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -6620,6 +6657,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7371,6 +7409,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -7565,9 +7604,10 @@ } }, "node_modules/dompurify": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.9.tgz", - "integrity": "sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ==" + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==", + "license": "(MPL-2.0 OR Apache-2.0)" }, "node_modules/domutils": { "version": "3.1.0", @@ -9279,15 +9319,16 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -9311,6 +9352,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -15013,7 +15055,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, "node_modules/pseudomap": { "version": "1.0.2", diff --git a/package.json b/package.json index fdf7138..e948281 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,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 3930d60..3989491 100644 --- a/src/components/AdminSettings.vue +++ b/src/components/AdminSettings.vue @@ -86,6 +86,7 @@ import { delay } from '../utils.js' import { showSuccess, showError } from '@nextcloud/dialogs' import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' +import { confirmPassword } from '@nextcloud/password-confirmation' export default { name: 'AdminSettings', @@ -118,23 +119,9 @@ export default { methods: { onCheckboxChanged(newValue, key) { this.state[key] = newValue - this.saveOptions({ [key]: this.state[key] ? '1' : '0' }) - }, - onInput() { - delay(() => { - this.saveOptions({ - client_id: this.state.client_id, - client_secret: this.state.client_secret, - oauth_instance_url: this.state.oauth_instance_url, - }) - }, 2000)() - }, - saveOptions(values) { - const req = { - values, - } - const url = generateUrl('/apps/integration_gitlab/admin-config') - axios.put(url, req).then((response) => { + axios.put(generateUrl('/apps/integration_gitlab/admin-config'), { + values: { [key]: this.state[key] ? '1' : '0' }, + }).then((response) => { showSuccess(t('integration_gitlab', 'GitLab admin options saved')) }).catch((error) => { showError( @@ -144,6 +131,30 @@ export default { console.debug(error) }) }, + onInput() { + delay(async () => { + await confirmPassword() + + const values = { + client_id: this.state.client_id, + oauth_instance_url: this.state.oauth_instance_url, + } + if (this.state.client_secret !== 'dummyToken') { + values.client_secret = this.state.client_secret + } + axios.put(generateUrl('/apps/integration_gitlab/sensitive-admin-config'), { + values, + }).then((response) => { + showSuccess(t('integration_gitlab', 'GitLab admin options saved')) + }).catch((error) => { + showError( + t('integration_gitlab', 'Failed to save GitLab admin options') + + ': ' + (error.response?.request?.responseText ?? ''), + ) + console.debug(error) + }) + }, 2000)() + }, }, } diff --git a/src/components/PersonalSettings.vue b/src/components/PersonalSettings.vue index a744af9..6d8ca6f 100644 --- a/src/components/PersonalSettings.vue +++ b/src/components/PersonalSettings.vue @@ -108,6 +108,8 @@ import { showSuccess, showError } from '@nextcloud/dialogs' import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' +import { confirmPassword } from '@nextcloud/password-confirmation' +import '@nextcloud/password-confirmation/dist/style.css' export default { name: 'PersonalSettings', @@ -165,26 +167,44 @@ export default { }, methods: { - onLogoutClick() { - this.state.token = '' - this.saveOptions({ token: '' }) + async onLogoutClick() { + if (await this.saveSensitiveOptions({ token: '' })) { + this.state.token = '' + } }, - onCheckboxChanged(newValue, key) { + async onCheckboxChanged(newValue, key) { this.state[key] = newValue - this.saveOptions({ [key]: this.state[key] ? '1' : '0' }) + + this.loading = true + + try { + await axios.put(generateUrl('/apps/integration_gitlab/config'), { + values: { [key]: this.state[key] ? '1' : '0' }, + }) + showSuccess(t('integration_gitlab', 'GitLab options saved')) + } catch (error) { + showError(t('integration_gitlab', 'Failed to save GitLab options') + ': ' + (error.response?.data?.error ?? '')) + console.debug(error) + } + + this.loading = false }, onInput() { - this.loading = true delay(() => { - this.saveOptions({ url: this.state.url }) + this.saveSensitiveOptions({ url: this.state.url }) }, 2000)() }, - saveOptions(values) { - const req = { - values, - } - const url = generateUrl('/apps/integration_gitlab/config') - axios.put(url, req).then((response) => { + async saveSensitiveOptions(values) { + await confirmPassword() + + this.loading = true + + try { + const req = { + values, + } + const url = generateUrl('/apps/integration_gitlab/sensitive-config') + const response = await axios.put(url, req) if (response.data.user_name !== undefined) { this.state.user_name = response.data.user_name this.state.user_displayname = response.data.user_displayname @@ -196,15 +216,14 @@ export default { } else { showSuccess(t('integration_gitlab', 'GitLab options saved')) } - }).catch((error) => { - showError( - t('integration_gitlab', 'Failed to save GitLab options') - + ': ' + (error.response?.data?.error ?? ''), - ) + this.loading = false + return true + } catch (error) { + showError(t('integration_gitlab', 'Failed to save GitLab options') + ': ' + (error.response?.data?.error ?? '')) console.debug(error) - }).then(() => { this.loading = false - }) + return false + } }, onConnectClick() { if (this.showOAuth) { @@ -215,10 +234,14 @@ export default { }, connectWithToken() { this.loading = true - this.saveOptions({ - token: this.state.token, + const values = { url: this.state.url, - }) + } + // Do not overwrite the saved token if it is just the dummy token + if (this.state.token !== 'dummyToken') { + values.token = this.state.token + } + this.saveSensitiveOptions(values) }, connectWithOauth() { if (this.state.use_popup) {