This commit is contained in:
parent
1fd3812768
commit
4741b641d9
@ -127,8 +127,9 @@ func loginSecurityFields(user *models.User) map[string]any {
|
||||
fields["totpConfigured"] = enabled
|
||||
}
|
||||
fields["recoveryCodesConfigured"] = configured
|
||||
fields["showRecoverySetupPrompt"] = !configured
|
||||
fields["showSecuritySetupPrompt"] = !configured || fields["externalEmailConfigured"] != true
|
||||
needsRecoverySetup := !configured && fields["externalEmailConfigured"] != true
|
||||
fields["showRecoverySetupPrompt"] = needsRecoverySetup
|
||||
fields["showSecuritySetupPrompt"] = needsRecoverySetup
|
||||
if user.EffectiveRole() == access.RoleSuperAdmin {
|
||||
required := !configured && fields["externalEmailConfigured"] != true && fields["totpConfigured"] != true
|
||||
fields["securitySetupRequired"] = required
|
||||
|
||||
@ -189,11 +189,12 @@ func TestSecuritySetupRequiresUsableCredentials(t *testing.T) {
|
||||
fields := loginSecurityFields(&user)
|
||||
emailReady := emailStatus == models.ExternalEmailStatusVerified
|
||||
codeReady := codeStatus == "valid"
|
||||
wantPrompt := !emailReady || !codeReady
|
||||
if role == access.RoleSuperAdmin {
|
||||
wantPrompt = !emailReady && !codeReady
|
||||
}
|
||||
if fields["externalEmailConfigured"] != emailReady || fields["showSecuritySetupPrompt"] != wantPrompt {
|
||||
wantPrompt := !emailReady && !codeReady
|
||||
wantRequired := role == access.RoleSuperAdmin && wantPrompt
|
||||
wantRecoveryPrompt := role != access.RoleSuperAdmin && wantPrompt
|
||||
if fields["externalEmailConfigured"] != emailReady || fields["recoveryCodesConfigured"] != codeReady ||
|
||||
fields["showSecuritySetupPrompt"] != wantPrompt || fields["showRecoverySetupPrompt"] != wantRecoveryPrompt ||
|
||||
fields["securitySetupRequired"] != wantRequired {
|
||||
t.Fatalf("unexpected security state: %#v", fields)
|
||||
}
|
||||
})
|
||||
@ -202,6 +203,49 @@ func TestSecuritySetupRequiresUsableCredentials(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonSuperAdminRecoverySetupTracksUsableOptions(t *testing.T) {
|
||||
for _, role := range []access.Role{access.RoleUser, access.RoleAdmin} {
|
||||
t.Run(string(role), func(t *testing.T) {
|
||||
engine := setupSecurityPromptTest(t)
|
||||
user := models.User{Account: "alice", Role: string(role)}
|
||||
if _, err := engine.Insert(&user); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := engine.Insert(&models.TOTPAuthenticator{UserID: user.ID, Enabled: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertPrompt := func(want bool) {
|
||||
t.Helper()
|
||||
fields := loginSecurityFields(&user)
|
||||
if fields["showSecuritySetupPrompt"] != want || fields["showRecoverySetupPrompt"] != want ||
|
||||
fields["securitySetupRequired"] != false {
|
||||
t.Fatalf("unexpected security state: %#v", fields)
|
||||
}
|
||||
}
|
||||
assertPrompt(true)
|
||||
email := models.ExternalEmail{UserID: user.ID, Status: models.ExternalEmailStatusVerified, EmailFingerprint: "fixture"}
|
||||
if _, err := engine.Insert(&email); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertPrompt(false)
|
||||
if _, err := engine.ID(email.ID).Cols("status").Update(&models.ExternalEmail{Status: models.ExternalEmailStatusDisabled}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertPrompt(true)
|
||||
code := models.RecoveryCode{UserID: user.ID, CodeHash: "fixture"}
|
||||
if _, err := engine.Insert(&code); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertPrompt(false)
|
||||
now := time.Now()
|
||||
if _, err := engine.ID(code.ID).Cols("used_at").Update(&models.RecoveryCode{UsedAt: &now}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertPrompt(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecuritySetupRejectsAnonymous(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
AcknowledgeSecuritySetup(nil, recorder, httptest.NewRequest(http.MethodPost, "/api/v1/security/setup/acknowledge", nil))
|
||||
|
||||
@ -8,7 +8,9 @@
|
||||
:show-close="false"
|
||||
class="security-setup-dialog"
|
||||
>
|
||||
<p class="setup-description">{{ isSuperAdmin ? '请至少启用一种安全验证后继续。' : '请设置安全码并绑定外部邮箱。未完成前,每次登录都会提醒。' }}</p>
|
||||
<p class="setup-description">
|
||||
{{ isSuperAdmin ? '请至少启用一种安全验证后继续。' : '安全码或外部邮箱至少配置一项。未配置时,每次登录都会提醒。' }}
|
||||
</p>
|
||||
<div class="setup-options">
|
||||
<section v-for="item in options" :key="item.key" class="setup-option">
|
||||
<div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user