This commit is contained in:
parent
998b68079a
commit
f32b81f3f3
@ -8,19 +8,7 @@ export function isRecentAuthenticationCancelled(error) {
|
||||
return error?.code === cancelledCode
|
||||
}
|
||||
|
||||
export async function requestWithRecentAuthentication(request) {
|
||||
let initialResponse
|
||||
let requiresRecentAuthentication = false
|
||||
try {
|
||||
initialResponse = await request()
|
||||
} catch (error) {
|
||||
if (error?.apiErrorCode !== recentAuthenticationRequired) throw error
|
||||
requiresRecentAuthentication = true
|
||||
}
|
||||
if (!requiresRecentAuthentication && initialResponse?.data?.code !== recentAuthenticationRequired) {
|
||||
return initialResponse
|
||||
}
|
||||
|
||||
export async function confirmRecentAuthentication() {
|
||||
let password = ''
|
||||
try {
|
||||
const result = await ElMessageBox.prompt(
|
||||
@ -50,6 +38,22 @@ export async function requestWithRecentAuthentication(request) {
|
||||
} finally {
|
||||
password = ''
|
||||
}
|
||||
}
|
||||
|
||||
export async function requestWithRecentAuthentication(request) {
|
||||
let initialResponse
|
||||
let requiresRecentAuthentication = false
|
||||
try {
|
||||
initialResponse = await request()
|
||||
} catch (error) {
|
||||
if (error?.apiErrorCode !== recentAuthenticationRequired) throw error
|
||||
requiresRecentAuthentication = true
|
||||
}
|
||||
if (!requiresRecentAuthentication && initialResponse?.data?.code !== recentAuthenticationRequired) {
|
||||
return initialResponse
|
||||
}
|
||||
|
||||
await confirmRecentAuthentication()
|
||||
|
||||
return request()
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import {ElNotification} from "element-plus";
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
|
||||
import {useGlobalStatusStore} from "@/stores/useGlobalStatusStore";
|
||||
import {confirmRecentAuthentication, isRecentAuthenticationCancelled} from '@/utils/recentAuthentication'
|
||||
|
||||
const roleOptions = {
|
||||
super_admin: { label: '超级管理员', type: 'danger' },
|
||||
@ -74,6 +75,8 @@ export function usePage() {
|
||||
|
||||
const externalEmailDialogVisible = ref(false)
|
||||
|
||||
const securityDialogOpening = ref('')
|
||||
|
||||
const editingCurrentUser = computed(() => isSameUser(selectedUser.value, globalStatus.userInfos))
|
||||
|
||||
const recoveryCodeWebManaged = computed(() => canManageRecoveryCode(selectedUser.value))
|
||||
@ -122,13 +125,35 @@ export function usePage() {
|
||||
userInfoDialog.value = true
|
||||
}
|
||||
|
||||
const openRecoveryCodeDialog = function () {
|
||||
const confirmSecurityDialogAccess = async function (dialogName) {
|
||||
if (securityDialogOpening.value) return false
|
||||
securityDialogOpening.value = dialogName
|
||||
try {
|
||||
await confirmRecentAuthentication()
|
||||
return true
|
||||
} catch (error) {
|
||||
if (!isRecentAuthenticationCancelled(error)) {
|
||||
ElNotification({
|
||||
title: lang.fail,
|
||||
message: error?.message || '身份确认失败',
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
return false
|
||||
} finally {
|
||||
securityDialogOpening.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const openRecoveryCodeDialog = async function () {
|
||||
if (!recoveryCodeWebManaged.value) return
|
||||
if (!await confirmSecurityDialogAccess('recovery-code')) return
|
||||
recoveryCodeDialogVisible.value = true
|
||||
}
|
||||
|
||||
const openExternalEmailDialog = function () {
|
||||
const openExternalEmailDialog = async function () {
|
||||
if (!selectedUser.value?.ID) return
|
||||
if (!await confirmSecurityDialogAccess('external-email')) return
|
||||
externalEmailDialogVisible.value = true
|
||||
}
|
||||
|
||||
@ -200,6 +225,7 @@ export function usePage() {
|
||||
recoveryCodeWebManaged,
|
||||
recoveryCodeDialogVisible,
|
||||
externalEmailDialogVisible,
|
||||
securityDialogOpening,
|
||||
openRecoveryCodeDialog,
|
||||
openExternalEmailDialog,
|
||||
handleSecurityChanged,
|
||||
|
||||
@ -122,6 +122,7 @@
|
||||
</div>
|
||||
<el-button
|
||||
:disabled="!recoveryCodeWebManaged"
|
||||
:loading="securityDialogOpening === 'recovery-code'"
|
||||
@click="openRecoveryCodeDialog"
|
||||
>
|
||||
{{ recoveryCodeWebManaged ? '管理安全码' : '仅支持本机管理' }}
|
||||
@ -137,7 +138,12 @@
|
||||
).label }}
|
||||
</span>
|
||||
</div>
|
||||
<el-button @click="openExternalEmailDialog">编辑外部邮箱</el-button>
|
||||
<el-button
|
||||
:loading="securityDialogOpening === 'external-email'"
|
||||
@click="openExternalEmailDialog"
|
||||
>
|
||||
编辑外部邮箱
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -201,6 +207,7 @@ const {
|
||||
recoveryCodeWebManaged,
|
||||
recoveryCodeDialogVisible,
|
||||
externalEmailDialogVisible,
|
||||
securityDialogOpening,
|
||||
openRecoveryCodeDialog,
|
||||
openExternalEmailDialog,
|
||||
handleSecurityChanged,
|
||||
|
||||
@ -75,6 +75,19 @@ test('用户安全信息使用独立弹窗并调用固定管理接口', () => {
|
||||
assert.doesNotMatch(externalSource, /\/api\/v1\/security\/external-email\/bind/)
|
||||
})
|
||||
|
||||
test('打开安全码或外部邮箱编辑弹窗前必须确认当前登录密码', () => {
|
||||
const source = read('UserManagement.js')
|
||||
assert.match(source, /confirmRecentAuthentication/)
|
||||
assert.match(source, /confirmSecurityDialogAccess\('recovery-code'\)/)
|
||||
assert.match(source, /confirmSecurityDialogAccess\('external-email'\)/)
|
||||
assert.match(source, /if \(!await confirmSecurityDialogAccess\('recovery-code'\)\) return/)
|
||||
assert.match(source, /if \(!await confirmSecurityDialogAccess\('external-email'\)\) return/)
|
||||
|
||||
const template = read('UserManagement.vue')
|
||||
assert.match(template, /securityDialogOpening === 'recovery-code'/)
|
||||
assert.match(template, /securityDialogOpening === 'external-email'/)
|
||||
})
|
||||
|
||||
test('当前用户判断优先使用用户 ID 并保留账号兼容回退', () => {
|
||||
const { isSameUser } = loadStatusHelpers()
|
||||
assert.equal(isSameUser({ ID: 7, Account: 'same' }, { id: 7, account: 'other' }), true)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user