diff --git a/frontend/src/views/components/SecuritySetupPrompt/SecuritySetupPrompt.js b/frontend/src/views/components/SecuritySetupPrompt/SecuritySetupPrompt.js
index 94d300e..030caa1 100644
--- a/frontend/src/views/components/SecuritySetupPrompt/SecuritySetupPrompt.js
+++ b/frontend/src/views/components/SecuritySetupPrompt/SecuritySetupPrompt.js
@@ -10,16 +10,16 @@ export function usePage() {
&& globalStatus.userInfos.showSecuritySetupPrompt === true
&& !globalStatus.userInfos.must_change_password)
const isSuperAdmin = computed(() => globalStatus.role === 'super_admin')
- const options = computed(() => [
- { key: 'question', title: '密保问题', description: '设置只有自己知道的答案,避免使用容易猜到的个人信息。' },
- { key: 'external-email', title: '外部安全邮箱', description: '绑定可正常接收验证码的外部邮箱,并完成验证。' },
- {
- key: 'webauthn', title: '安全密钥',
- description: globalStatus.userInfos.webAuthnEnabled === true
- ? '使用支持 WebAuthn 的设备或浏览器注册安全密钥。'
- : '服务器尚未启用,需由部署管理员配置 WebAuthn 后才能注册。'
+ const options = computed(() => {
+ const items = []
+ if (!isSuperAdmin.value && !globalStatus.userInfos.recoveryCodesConfigured) {
+ items.push({ key: 'recovery', title: '安全码', description: '生成安全码并妥善保存。' })
}
- ])
+ if (!globalStatus.userInfos.externalEmailConfigured) {
+ items.push({ key: 'external-email', title: '外部邮箱', description: '绑定邮箱并完成验证。' })
+ }
+ return items
+ })
const acknowledge = async section => {
if (saving.value) return
diff --git a/frontend/src/views/components/SecuritySetupPrompt/SecuritySetupPrompt.vue b/frontend/src/views/components/SecuritySetupPrompt/SecuritySetupPrompt.vue
index f7206b2..4ad4df5 100644
--- a/frontend/src/views/components/SecuritySetupPrompt/SecuritySetupPrompt.vue
+++ b/frontend/src/views/components/SecuritySetupPrompt/SecuritySetupPrompt.vue
@@ -8,7 +8,7 @@
:show-close="false"
class="security-setup-dialog"
>
- 欢迎使用 Smail!首次登录请了解并设置以下安全选项,确认后不再自动弹出此引导。
+ {{ isSuperAdmin ? '请绑定外部邮箱。未完成前,每次登录都会提醒。' : '请设置安全码并绑定外部邮箱。未完成前,每次登录都会提醒。' }}
@@ -18,24 +18,10 @@
前往设置
-
-
- 我已了解,稍后设置
- 开始设置
+ 我知道了
+ 去设置
diff --git a/test/frontend/security-setup.test.mjs b/test/frontend/security-setup.test.mjs
index 412e08f..926f655 100644
--- a/test/frontend/security-setup.test.mjs
+++ b/test/frontend/security-setup.test.mjs
@@ -21,17 +21,17 @@ function fixture(post = async () => ({ errorNo: 0 })) {
return { store, page: createPage(computed, ref, { post }, () => store) }
}
-test('初始化超级管理员首次登录显示三项引导,强制改密时延后', () => {
+test('超级管理员只提醒邮箱,强制改密时延后', () => {
const { store, page } = fixture()
assert.equal(page.visible.value, true)
- assert.deepEqual(Array.from(page.options.value, item => item.key), ['question', 'external-email', 'webauthn'])
+ assert.deepEqual(Array.from(page.options.value, item => item.key), ['external-email'])
store.userInfos.must_change_password = 1
assert.equal(page.visible.value, false)
store.userInfos.must_change_password = 0
assert.equal(page.visible.value, true)
})
-test('确认成功后关闭并打开目标设置,刷新后的持久化状态不再弹出', async () => {
+test('确认仅关闭本次提醒,下次登录未完成设置仍提醒', async () => {
const requests = []
const { store, page } = fixture(async path => { requests.push(path); return { errorNo: 0 } })
await page.acknowledge('external-email')
@@ -42,6 +42,38 @@ test('确认成功后关闭并打开目标设置,刷新后的持久化状态
assert.deepEqual(requests, ['/api/v1/security/setup/acknowledge'])
store.userInfos = { account: 'root', showSecuritySetupPrompt: false }
assert.equal(page.visible.value, false)
+ store.settingsDrawerVisible = false
+ store.userInfos = { account: 'root', showSecuritySetupPrompt: true }
+ assert.equal(page.visible.value, true)
+})
+
+test('管理员和普通用户只显示尚未完成的安全码与邮箱', () => {
+ for (const role of ['admin', 'user']) {
+ const { store, page } = fixture()
+ store.role = role
+ assert.deepEqual(Array.from(page.options.value, item => item.key), ['recovery', 'external-email'])
+ store.userInfos.recoveryCodesConfigured = true
+ assert.deepEqual(Array.from(page.options.value, item => item.key), ['external-email'])
+ store.userInfos.externalEmailConfigured = true
+ store.userInfos.recoveryCodesConfigured = false
+ assert.deepEqual(Array.from(page.options.value, item => item.key), ['recovery'])
+ store.userInfos.recoveryCodesConfigured = true
+ store.userInfos.showSecuritySetupPrompt = false
+ assert.deepEqual(Array.from(page.options.value, item => item.key), [])
+ assert.equal(page.visible.value, false)
+ }
+})
+
+test('我知道了只关闭弹窗,去设置打开第一个未完成项', async () => {
+ const { store, page } = fixture()
+ store.role = 'user'
+ await page.acknowledge()
+ assert.equal(page.visible.value, false)
+ assert.equal(store.settingsDrawerVisible, false)
+ store.userInfos.showSecuritySetupPrompt = true
+ await page.acknowledge(page.options.value[0].key)
+ assert.equal(store.securityActiveSection, 'recovery')
+ assert.equal(store.settingsDrawerVisible, true)
})
test('保存失败保留引导,可重试且不将查看状态误记为设置完成', async () => {
@@ -58,8 +90,8 @@ test('并发点击只提交一次,切换账号后不修改新账号状态', as
let finish
let calls = 0
const { store, page } = fixture(() => { calls++; return new Promise(resolve => { finish = resolve }) })
- const pending = page.acknowledge('question')
- await page.acknowledge('webauthn')
+ const pending = page.acknowledge('recovery')
+ await page.acknowledge('external-email')
assert.equal(calls, 1)
store.userInfos = { account: 'alice', showSecuritySetupPrompt: true }
finish({ errorNo: 0 })