Merge pull request #59871 from nextcloud/backport/59812/stable30

[stable30] fix: Add missing PasswordConfirmationRequired attributes
This commit is contained in:
Stephan Orbaugh 2026-04-27 15:08:51 +02:00 committed by GitHub
commit 207bd9ea5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 175 additions and 69 deletions

View file

@ -13,6 +13,7 @@ use OCA\OAuth2\Db\Client;
use OCA\OAuth2\Db\ClientMapper;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Authentication\Token\IProvider as IAuthTokenProvider;
use OCP\IL10N;
@ -40,6 +41,7 @@ class SettingsController extends Controller {
parent::__construct($appName, $request);
}
#[PasswordConfirmationRequired(strict: true)]
public function addClient(string $name,
string $redirectUri): JSONResponse {
if (filter_var($redirectUri, FILTER_VALIDATE_URL) === false) {
@ -66,6 +68,7 @@ class SettingsController extends Controller {
return new JSONResponse($result);
}
#[PasswordConfirmationRequired]
public function deleteClient(int $id): JSONResponse {
$client = $this->clientMapper->getByUid($id);

View file

@ -73,6 +73,7 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
import { loadState } from '@nextcloud/initial-state'
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
import { PwdConfirmationMode } from '@nextcloud/password-confirmation'
export default {
name: 'App',
@ -123,6 +124,7 @@ export default {
name: this.newClient.name,
redirectUri: this.newClient.redirectUri,
},
{ confirmPassword: PwdConfirmationMode.Strict },
).then(response => {
// eslint-disable-next-line vue/no-mutating-props
this.clients.push(response.data)

View file

@ -3,13 +3,17 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import axios from '@nextcloud/axios'
import Vue from 'vue'
import App from './App.vue'
import { loadState } from '@nextcloud/initial-state'
import { addPasswordConfirmationInterceptors } from '@nextcloud/password-confirmation'
Vue.prototype.t = t
Vue.prototype.OC = OC
addPasswordConfirmationInterceptors(axios)
const clients = loadState('oauth2', 'clients')
const View = Vue.extend(App)

View file

@ -93,7 +93,7 @@ class AppsController extends OCSController {
*
* 200: App enabled successfully
*/
#[PasswordConfirmationRequired]
#[PasswordConfirmationRequired(strict: true)]
public function enable(string $app): DataResponse {
try {
$this->appManager->enableApp($app);

View file

@ -556,7 +556,7 @@ class AppSettingsController extends Controller {
* @param array $groups
* @return JSONResponse
*/
#[PasswordConfirmationRequired]
#[PasswordConfirmationRequired(strict: true)]
public function enableApps(array $appIds, array $groups = []): JSONResponse {
try {
$updateRequired = false;

View file

@ -10,6 +10,7 @@ use OC\Settings\AuthorizedGroup;
use OCA\Settings\Service\AuthorizedGroupService;
use OCA\Settings\Service\NotFoundException;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\DB\Exception;
use OCP\IRequest;
@ -27,6 +28,7 @@ class AuthorizedGroupController extends Controller {
* @throws NotFoundException
* @throws Exception
*/
#[PasswordConfirmationRequired(strict: true)]
public function saveSettings(array $newGroups, string $class): DataResponse {
$currentGroups = $this->authorizedGroupService->findExistingGroupsForClass($class);

View file

@ -11,6 +11,7 @@ namespace OCA\Settings\Controller;
use OC\Authentication\TwoFactorAuth\EnforcementState;
use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
@ -31,6 +32,7 @@ class TwoFactorSettingsController extends Controller {
return new JSONResponse($this->mandatoryTwoFactor->getState());
}
#[PasswordConfirmationRequired(strict: true)]
public function update(bool $enforced, array $enforcedGroups = [], array $excludedGroups = []): JSONResponse {
$this->mandatoryTwoFactor->setState(
new EnforcementState($enforced, $enforcedGroups, $excludedGroups)

View file

@ -18,6 +18,7 @@ import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
import { generateUrl } from '@nextcloud/router'
import axios from '@nextcloud/axios'
import { showError } from '@nextcloud/dialogs'
import { PwdConfirmationMode } from '@nextcloud/password-confirmation'
import logger from '../../logger.ts'
export default {
@ -59,7 +60,7 @@ export default {
class: this.setting.class,
}
try {
await axios.post(generateUrl('/apps/settings/') + '/settings/authorizedgroups/saveSettings', data)
await axios.post(generateUrl('/apps/settings/') + '/settings/authorizedgroups/saveSettings', data, { confirmPassword: PwdConfirmationMode.Strict })
} catch (e) {
showError(t('settings', 'Unable to modify setting'))
logger.error('Unable to modify setting', e)

View file

@ -76,6 +76,7 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
import { loadState } from '@nextcloud/initial-state'
import { PwdConfirmationMode } from '@nextcloud/password-confirmation'
import sortedUniq from 'lodash/sortedUniq.js'
import uniq from 'lodash/uniq.js'
@ -156,7 +157,7 @@ export default {
enforcedGroups: this.enforcedGroups,
excludedGroups: this.excludedGroups,
}
axios.put(generateUrl('/settings/api/admin/twofactorauth'), data)
axios.put(generateUrl('/settings/api/admin/twofactorauth'), data, { confirmPassword: PwdConfirmationMode.Strict })
.then(resp => resp.data)
.then(state => {
this.state = state

View file

@ -3,9 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import axios from '@nextcloud/axios'
import { addPasswordConfirmationInterceptors } from '@nextcloud/password-confirmation'
import Vue from 'vue'
import App from './components/AdminDelegating.vue'
addPasswordConfirmationInterceptors(axios)
// bind to window
Vue.prototype.OC = OC
Vue.prototype.t = t

View file

@ -3,13 +3,17 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import axios from '@nextcloud/axios'
import { loadState } from '@nextcloud/initial-state'
import { addPasswordConfirmationInterceptors } from '@nextcloud/password-confirmation'
import Vue from 'vue'
import AdminTwoFactor from './components/AdminTwoFactor.vue'
import EncryptionSettings from './components/Encryption/EncryptionSettings.vue'
import store from './store/admin-security.js'
addPasswordConfirmationInterceptors(axios)
// eslint-disable-next-line camelcase
__webpack_nonce__ = btoa(OC.requestToken)

View file

@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import axios from '@nextcloud/axios'
import { addPasswordConfirmationInterceptors } from '@nextcloud/password-confirmation'
import Vue from 'vue'
import Vuex from 'vuex'
import VTooltipPlugin from 'v-tooltip'
@ -15,6 +17,8 @@ import { useStore } from './store/index.js'
import { getCSPNonce } from '@nextcloud/auth'
import { PiniaVuePlugin, createPinia } from 'pinia'
addPasswordConfirmationInterceptors(axios)
// CSP config for webpack dynamic chunk loading
// eslint-disable-next-line camelcase
__webpack_nonce__ = getCSPNonce()

View file

@ -50,8 +50,8 @@ export default {
get(url, options) {
return axios.get(sanitize(url), options)
},
post(url, data) {
return axios.post(sanitize(url), data)
post(url, data, options) {
return axios.post(sanitize(url), data, options)
},
patch(url, data) {
return axios.patch(sanitize(url), data)

View file

@ -9,6 +9,7 @@ import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
import { showError, showInfo } from '@nextcloud/dialogs'
import { loadState } from '@nextcloud/initial-state'
import { PwdConfirmationMode } from '@nextcloud/password-confirmation'
const state = {
apps: [],
@ -71,7 +72,7 @@ const mutations = {
enableApp(state, { appId, groups }) {
const app = state.apps.find(app => app.id === appId)
app.active = true
app.groups = groups
Vue.set(app, 'groups', [...groups])
if (app.id === 'app_api') {
state.appApiEnabled = true
}
@ -180,58 +181,82 @@ const actions = {
} else {
apps = [appId]
}
return api.requireAdmin().then((response) => {
context.commit('startLoading', apps)
context.commit('startLoading', 'install')
return api.post(generateUrl('settings/apps/enable'), { appIds: apps, groups })
.then((response) => {
context.commit('stopLoading', apps)
context.commit('stopLoading', 'install')
apps.forEach(_appId => {
context.commit('enableApp', { appId: _appId, groups })
context.commit('startLoading', apps)
context.commit('startLoading', 'install')
const previousState = {}
apps.forEach((_appId) => {
const app = context.state.apps.find((app) => app.id === _appId)
if (app) {
previousState[_appId] = {
active: app.active,
groups: [...(app.groups || [])],
}
context.commit('enableApp', { appId: _appId, groups })
}
})
return api.post(generateUrl('settings/apps/enable'), { appIds: apps, groups }, { confirmPassword: PwdConfirmationMode.Strict })
.then((response) => {
context.commit('stopLoading', apps)
context.commit('stopLoading', 'install')
// check for server health
return axios.get(generateUrl('apps/files/'))
.then(() => {
if (response.data.update_required) {
showInfo(
t(
'settings',
'The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds.',
),
{
onClick: () => window.location.reload(),
close: false,
},
)
setTimeout(function() {
location.reload()
}, 5000)
}
})
.catch(() => {
if (!Array.isArray(appId)) {
showError(t('settings', 'Error: This app cannot be enabled because it makes the server unstable'))
context.commit('setError', {
appId: apps,
error: t('settings', 'Error: This app cannot be enabled because it makes the server unstable'),
})
context.dispatch('disableApp', { appId })
}
})
})
.catch((error) => {
context.commit('stopLoading', apps)
context.commit('stopLoading', 'install')
// check for server health
return axios.get(generateUrl('apps/files/'))
.then(() => {
if (response.data.update_required) {
showInfo(
t(
'settings',
'The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds.',
),
{
onClick: () => window.location.reload(),
close: false,
},
)
setTimeout(function() {
location.reload()
}, 5000)
}
})
.catch(() => {
if (!Array.isArray(appId)) {
showError(t('settings', 'Error: This app cannot be enabled because it makes the server unstable'))
context.commit('setError', {
appId: apps,
error: t('settings', 'Error: This app cannot be enabled because it makes the server unstable'),
})
context.dispatch('disableApp', { appId })
}
apps.forEach((_appId) => {
if (previousState[_appId]) {
context.commit('enableApp', {
appId: _appId,
groups: previousState[_appId].groups,
})
if (!previousState[_appId].active) {
context.commit('disableApp', _appId)
}
}
})
.catch((error) => {
context.commit('stopLoading', apps)
context.commit('stopLoading', 'install')
const message = error.response?.data?.data?.message
if (message) {
context.commit('setError', {
appId: apps,
error: error.response.data.data.message,
error: message,
})
context.commit('APPS_API_FAILURE', { appId, error })
})
}).catch((error) => context.commit('API_FAILURE', { appId, error }))
}
})
},
forceEnableApp(context, { appId, groups }) {
let apps

4
dist/core-common.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
SPDX-License-Identifier: BSD-3-Clause
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: (MPL-2.0 OR Apache-2.0)
SPDX-FileCopyrightText: inherits developers
SPDX-FileCopyrightText: escape-html developers
SPDX-FileCopyrightText: debounce developers
SPDX-FileCopyrightText: Varun A P
SPDX-FileCopyrightText: Tobias Koppers @sokra
SPDX-FileCopyrightText: T. Jameson Little <t.jameson.little@gmail.com>
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
@ -12,11 +15,16 @@ SPDX-FileCopyrightText: Roeland Jago Douma
SPDX-FileCopyrightText: Rob Cresswell <robcresswell@pm.me>
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
SPDX-FileCopyrightText: Matt Zabriskie
SPDX-FileCopyrightText: Joyent
SPDX-FileCopyrightText: Guillaume Chau <guillaume.b.chau@gmail.com>
SPDX-FileCopyrightText: GitHub Inc.
SPDX-FileCopyrightText: Feross Aboukhadijeh
SPDX-FileCopyrightText: Evan You
SPDX-FileCopyrightText: Dr.-Ing. Mario Heiderich, Cure53 <mario@cure53.de> (https://cure53.de/)
SPDX-FileCopyrightText: David Clark
SPDX-FileCopyrightText: Christoph Wurst
SPDX-FileCopyrightText: Anthony Fu <https://github.com/antfu>
SPDX-FileCopyrightText: @nextcloud/dialogs developers
This file is generated from multiple sources. Included packages:
@ -32,6 +40,9 @@ This file is generated from multiple sources. Included packages:
- @nextcloud/capabilities
- version: 1.2.0
- license: GPL-3.0-or-later
- @nextcloud/dialogs
- version: 6.4.1
- license: AGPL-3.0-or-later
- semver
- version: 7.6.3
- license: ISC
@ -44,12 +55,24 @@ This file is generated from multiple sources. Included packages:
- @nextcloud/l10n
- version: 3.4.0
- license: GPL-3.0-or-later
- @nextcloud/logger
- version: 3.0.2
- license: GPL-3.0-or-later
- @nextcloud/password-confirmation
- version: 5.3.1
- license: MIT
- @nextcloud/router
- version: 3.0.1
- license: GPL-3.0-or-later
- @nextcloud/vue
- version: 8.29.2
- license: AGPL-3.0-or-later
- @vueuse/core
- version: 11.3.0
- license: MIT
- @vueuse/shared
- version: 11.3.0
- license: MIT
- axios
- version: 1.12.2
- license: MIT
@ -59,24 +82,48 @@ This file is generated from multiple sources. Included packages:
- css-loader
- version: 7.1.2
- license: MIT
- debounce
- version: 2.2.0
- license: MIT
- dompurify
- version: 3.2.6
- license: (MPL-2.0 OR Apache-2.0)
- escape-html
- version: 1.0.3
- license: MIT
- floating-vue
- version: 1.0.0-beta.19
- license: MIT
- focus-trap
- version: 7.6.5
- license: MIT
- ieee754
- version: 1.2.1
- license: BSD-3-Clause
- buffer
- version: 6.0.3
- license: MIT
- inherits
- version: 2.0.3
- license: ISC
- util
- version: 0.10.4
- license: MIT
- path
- version: 0.12.7
- license: MIT
- process
- version: 0.11.10
- license: MIT
- style-loader
- version: 4.0.0
- license: MIT
- tabbable
- version: 6.2.0
- license: MIT
- toastify-js
- version: 1.12.0
- license: MIT
- vue-loader
- version: 15.11.1
- license: MIT

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -6,6 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: (MPL-2.0 OR Apache-2.0)
SPDX-FileCopyrightText: inherits developers
SPDX-FileCopyrightText: escape-html developers
SPDX-FileCopyrightText: debounce developers
SPDX-FileCopyrightText: atomiks
SPDX-FileCopyrightText: Varun A P
SPDX-FileCopyrightText: Tobias Koppers @sokra
@ -73,6 +74,9 @@ This file is generated from multiple sources. Included packages:
- @nextcloud/logger
- version: 3.0.2
- license: GPL-3.0-or-later
- @nextcloud/password-confirmation
- version: 5.3.1
- license: MIT
- @nextcloud/router
- version: 3.0.1
- license: GPL-3.0-or-later
@ -106,6 +110,9 @@ This file is generated from multiple sources. Included packages:
- css-loader
- version: 7.1.2
- license: MIT
- debounce
- version: 2.2.0
- license: MIT
- dompurify
- version: 3.2.6
- license: (MPL-2.0 OR Apache-2.0)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long