mirror of
https://github.com/nextcloud/server.git
synced 2026-05-19 08:25:56 -04:00
fix(core): prompt for password once when installing recommended apps
Wire the password-confirmation interceptors into the recommendedapps entry point and switch the installer to a single bulk enable call so the strict password confirmation on enableApps is satisfied. Fixes #60068 -e Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
This commit is contained in:
parent
9bc1a9245c
commit
66a8d4582c
4 changed files with 69 additions and 28 deletions
|
|
@ -58,9 +58,9 @@
|
|||
<script>
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { PwdConfirmationMode } from '@nextcloud/password-confirmation'
|
||||
import { generateUrl, imagePath } from '@nextcloud/router'
|
||||
import axios from '@nextcloud/axios'
|
||||
import pLimit from 'p-limit'
|
||||
import logger from '../../logger.js'
|
||||
|
||||
import NcButton from '@nextcloud/vue/components/NcButton'
|
||||
|
|
@ -140,35 +140,41 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
installApps() {
|
||||
this.installingApps = true
|
||||
|
||||
const limit = pLimit(1)
|
||||
const installing = this.recommendedApps
|
||||
async installApps() {
|
||||
const apps = this.recommendedApps
|
||||
.filter(app => !app.active && app.isCompatible && app.canInstall && app.isSelected)
|
||||
.map(app => limit(async () => {
|
||||
logger.info(`installing ${app.id}`)
|
||||
app.loading = true
|
||||
return axios.post(generateUrl('settings/apps/enable'), { appIds: [app.id], groups: [] })
|
||||
.catch(error => {
|
||||
logger.error(`could not install ${app.id}`, { error })
|
||||
app.isSelected = false
|
||||
app.installationError = true
|
||||
})
|
||||
.then(() => {
|
||||
logger.info(`installed ${app.id}`)
|
||||
app.loading = false
|
||||
app.active = true
|
||||
})
|
||||
}))
|
||||
logger.debug(`installing ${installing.length} recommended apps`)
|
||||
Promise.all(installing)
|
||||
.then(() => {
|
||||
logger.info('all recommended apps installed, redirecting …')
|
||||
if (apps.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
window.location = this.defaultPageUrl
|
||||
this.installingApps = true
|
||||
apps.forEach(app => {
|
||||
app.loading = true
|
||||
})
|
||||
const appIds = apps.map(app => app.id)
|
||||
logger.debug(`installing ${apps.length} recommended apps`, { appIds })
|
||||
|
||||
try {
|
||||
await axios.post(
|
||||
generateUrl('settings/apps/enable'),
|
||||
{ appIds, groups: [] },
|
||||
{ confirmPassword: PwdConfirmationMode.Strict },
|
||||
)
|
||||
apps.forEach(app => {
|
||||
app.loading = false
|
||||
app.active = true
|
||||
})
|
||||
.catch(error => logger.error('could not install recommended apps', { error }))
|
||||
logger.info('all recommended apps installed, redirecting …')
|
||||
window.location = this.defaultPageUrl
|
||||
} catch (error) {
|
||||
logger.error('could not install recommended apps', { error })
|
||||
apps.forEach(app => {
|
||||
app.loading = false
|
||||
app.isSelected = false
|
||||
app.installationError = true
|
||||
})
|
||||
this.installingApps = false
|
||||
}
|
||||
},
|
||||
customIcon(appId) {
|
||||
if (!(appId in recommended) || !recommended[appId].icon) {
|
||||
|
|
|
|||
|
|
@ -4,12 +4,16 @@
|
|||
*/
|
||||
|
||||
import { getCSPNonce } from '@nextcloud/auth'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { addPasswordConfirmationInterceptors } from '@nextcloud/password-confirmation'
|
||||
import Vue from 'vue'
|
||||
|
||||
import logger from './logger.js'
|
||||
import RecommendedApps from './components/setup/RecommendedApps.vue'
|
||||
|
||||
addPasswordConfirmationInterceptors(axios)
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
__webpack_nonce__ = getCSPNonce()
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,33 @@ export function getUnifiedSearchModal() {
|
|||
return cy.get('#unified-search')
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the password-confirmation modal if it is visible.
|
||||
* Used by flows that hit endpoints requiring strict re-authentication.
|
||||
*
|
||||
* @param adminPassword Password to type into the confirmation dialog.
|
||||
*/
|
||||
export function handlePasswordConfirmation(adminPassword = 'admin') {
|
||||
const handleModal = (context: Cypress.Chainable) => {
|
||||
return context.contains('.modal-container', 'Authentication required')
|
||||
.if()
|
||||
.within(() => {
|
||||
cy.get('input[type="password"]')
|
||||
.type(adminPassword)
|
||||
cy.findByRole('button', { name: 'Confirm' })
|
||||
.click()
|
||||
})
|
||||
}
|
||||
|
||||
return cy.get('body')
|
||||
.if()
|
||||
.then(() => handleModal(cy.get('body')))
|
||||
.else()
|
||||
// Handle if inside a cy.within
|
||||
.root().closest('body')
|
||||
.then(($body) => handleModal(cy.wrap($body)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the unified search modal
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { handlePasswordConfirmation } from '../core-utils.ts'
|
||||
|
||||
type RecommendedAppsMode = 'skip' | 'install-success' | 'install-failure'
|
||||
|
||||
/**
|
||||
* DO NOT RENAME THIS FILE to .cy.ts ⚠️
|
||||
* This is not following the pattern of the other files in this folder
|
||||
|
|
@ -110,7 +114,7 @@ describe('Can install Nextcloud', { testIsolation: true, retries: 0 }, () => {
|
|||
/**
|
||||
* Shared admin setup function for the Nextcloud setup
|
||||
*/
|
||||
function sharedSetup() {
|
||||
function sharedSetup(mode: RecommendedAppsMode = 'skip') {
|
||||
const randAdmin = 'admin-' + Math.random().toString(36).substring(2, 15)
|
||||
|
||||
// mock appstore
|
||||
|
|
|
|||
Loading…
Reference in a new issue