fix(*): 邮箱与系统公告 - 合并 PR #417 并修复迁移冲突和提醒关闭逻辑
保留历史数据库迁移谱系与公告按需加载,复用邮件配置快照校验注册域名,补充白名单及公告回归测试。
This commit is contained in:
@@ -158,6 +158,55 @@ func TestMigrateSchemaV4AddsResourceUploadKeyToExistingSchema(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateSchemaRepairsLegacyAssetFoldersMigrationOrder(t *testing.T) {
|
||||
db, err := Open(Config{Driver: "sqlite", DSN: "file:migration-legacy-v6-order?mode=memory&cache=shared"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.AutoMigrate(&model.Resource{}, &model.Asset{}, &model.AssetFolder{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.AutoMigrate(&schemaMigration{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, item := range schemaMigrations[:5] {
|
||||
if err := db.Create(&schemaMigration{Version: item.version, Name: item.name, Checksum: item.checksum, AppliedAt: time.Now().UTC()}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := db.Create(&schemaMigration{Version: 6, Name: "asset_library_folders", Checksum: assetLibraryFoldersChecksum, AppliedAt: time.Now().UTC()}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := MigrateSchema(db); err != nil {
|
||||
t.Fatalf("repair legacy migration order: %v", err)
|
||||
}
|
||||
if !db.Migrator().HasColumn(&model.Resource{}, "playback_status") || !db.Migrator().HasColumn(&model.Resource{}, "playback_object_key") || !db.Migrator().HasColumn(&model.Resource{}, "playback_error") {
|
||||
t.Fatal("legacy database did not receive resource playback columns")
|
||||
}
|
||||
status, err := ReadSchemaStatus(db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !status.Ready || status.Current != CurrentSchemaVersion {
|
||||
t.Fatalf("unexpected repaired schema status: %#v", status)
|
||||
}
|
||||
var applied schemaMigration
|
||||
if err := db.First(&applied, "version = ?", 6).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if applied.Name != "asset_library_folders" || applied.Checksum != assetLibraryFoldersChecksum {
|
||||
t.Fatalf("historical migration 6 must be preserved: %#v", applied)
|
||||
}
|
||||
var playback schemaMigration
|
||||
if err := db.First(&playback, "version = ?", 7).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if playback.Name != "resource_playback_variant" || playback.Checksum != resourcePlaybackChecksum {
|
||||
t.Fatalf("migration 7 must supply playback schema: %#v", playback)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateSchemaRollsBackFailedMigration(t *testing.T) {
|
||||
db, err := Open(Config{Driver: "sqlite", DSN: "file:migration-rollback?mode=memory&cache=shared"})
|
||||
if err != nil {
|
||||
|
||||
@@ -459,8 +459,8 @@ func validAnnouncementLevel(level model.AnnouncementLevel) bool {
|
||||
func normalizeAnnouncementInput(req CreateAnnouncementRequest) (string, string, model.AnnouncementLevel, error) {
|
||||
title := strings.TrimSpace(req.Title)
|
||||
content := strings.TrimSpace(req.Content)
|
||||
if title == "" || content == "" {
|
||||
return "", "", "", BadAuthRequest("请填写公告标题和正文")
|
||||
if title == "" {
|
||||
return "", "", "", BadAuthRequest("请填写公告标题")
|
||||
}
|
||||
if utf8.RuneCountInString(title) > 120 {
|
||||
return "", "", "", BadAuthRequest("公告标题不能超过 120 个字符")
|
||||
|
||||
@@ -262,6 +262,19 @@ func TestAnnouncementPublishRejectsInvalidInput(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnouncementPublishAllowsEmptyContent(t *testing.T) {
|
||||
svc, _ := newAnnouncementImageTestService(t)
|
||||
admin := &model.User{ID: "admin", Role: model.UserRoleAdmin, Status: model.UserStatusActive}
|
||||
|
||||
announcement, err := svc.CreateAnnouncement(admin, CreateAnnouncementRequest{Title: "仅标题公告", Content: " ", Level: model.AnnouncementLevelInfo})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if announcement.Title != "仅标题公告" || announcement.Content != "" {
|
||||
t.Fatalf("announcement = %+v", announcement)
|
||||
}
|
||||
}
|
||||
|
||||
func newAnnouncementImageTestService(t *testing.T) (*Service, *gorm.DB) {
|
||||
t.Helper()
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
|
||||
@@ -129,6 +129,9 @@ func (s *Service) Register(req RegisterRequest) (*AuthSessionResult, error) {
|
||||
if email == "" {
|
||||
return nil, BadAuthRequest("请输入邮箱")
|
||||
}
|
||||
if err := s.validateRegistrationEmailDomain(email); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verifiedCode, err = s.VerifyRegistrationEmailCode(email, req.EmailCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -28,41 +28,56 @@ const emailSettingKey = "email"
|
||||
const registrationEmailPurpose = "registration"
|
||||
const registrationCodeTTL = 10 * time.Minute
|
||||
|
||||
var defaultRegistrationEmailDomains = []string{
|
||||
"gmail.com",
|
||||
"163.com",
|
||||
"126.com",
|
||||
"qq.com",
|
||||
"outlook.com",
|
||||
"hotmail.com",
|
||||
"icloud.com",
|
||||
"yahoo.com",
|
||||
"foxmail.com",
|
||||
}
|
||||
|
||||
type EmailSettingRequest struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Encryption string `json:"encryption"`
|
||||
FromEmail string `json:"fromEmail"`
|
||||
FromName string `json:"fromName"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Encryption string `json:"encryption"`
|
||||
FromEmail string `json:"fromEmail"`
|
||||
FromName string `json:"fromName"`
|
||||
RegistrationAllowedDomains []string `json:"registrationAllowedDomains"`
|
||||
}
|
||||
|
||||
type PublicEmailSetting struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Encryption string `json:"encryption"`
|
||||
FromEmail string `json:"fromEmail"`
|
||||
FromName string `json:"fromName"`
|
||||
FromNameInherited bool `json:"fromNameInherited"`
|
||||
HasPassword bool `json:"hasPassword"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Encryption string `json:"encryption"`
|
||||
FromEmail string `json:"fromEmail"`
|
||||
FromName string `json:"fromName"`
|
||||
FromNameInherited bool `json:"fromNameInherited"`
|
||||
HasPassword bool `json:"hasPassword"`
|
||||
RegistrationAllowedDomains []string `json:"registrationAllowedDomains"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type emailSettingValue struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Encryption string `json:"encryption"`
|
||||
FromEmail string `json:"fromEmail"`
|
||||
FromName string `json:"fromName"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Encryption string `json:"encryption"`
|
||||
FromEmail string `json:"fromEmail"`
|
||||
FromName string `json:"fromName"`
|
||||
RegistrationAllowedDomains []string `json:"registrationAllowedDomains"`
|
||||
}
|
||||
|
||||
func (s *Service) AdminEmailSetting(actor *model.User) (*PublicEmailSetting, error) {
|
||||
@@ -84,7 +99,15 @@ func (s *Service) UpdateEmailSetting(actor *model.User, req EmailSettingRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
next := normalizeEmailSetting(emailSettingValue{Enabled: req.Enabled, Host: req.Host, Port: req.Port, Username: req.Username, Password: req.Password, Encryption: req.Encryption, FromEmail: req.FromEmail, FromName: req.FromName})
|
||||
allowedDomains := req.RegistrationAllowedDomains
|
||||
if allowedDomains == nil {
|
||||
allowedDomains = current.RegistrationAllowedDomains
|
||||
}
|
||||
next := normalizeEmailSetting(emailSettingValue{
|
||||
Enabled: req.Enabled, Host: req.Host, Port: req.Port, Username: req.Username, Password: req.Password,
|
||||
Encryption: req.Encryption, FromEmail: req.FromEmail, FromName: req.FromName,
|
||||
RegistrationAllowedDomains: allowedDomains,
|
||||
})
|
||||
if next.Password == "" {
|
||||
next.Password = current.Password
|
||||
}
|
||||
@@ -146,6 +169,9 @@ func (s *Service) SendRegistrationEmailCode(rawEmail string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateRegistrationEmailDomain(email, setting.RegistrationAllowedDomains); err != nil {
|
||||
return err
|
||||
}
|
||||
if !setting.Enabled {
|
||||
return Forbidden("平台尚未启用注册邮件,请联系管理员")
|
||||
}
|
||||
@@ -249,6 +275,9 @@ func (s *Service) readEmailSetting() (*model.SystemSetting, emailSettingValue, e
|
||||
}
|
||||
|
||||
func validateEmailSetting(value emailSettingValue) error {
|
||||
if err := validateRegistrationEmailDomains(value.RegistrationAllowedDomains); err != nil {
|
||||
return err
|
||||
}
|
||||
if !value.Enabled {
|
||||
return nil
|
||||
}
|
||||
@@ -273,6 +302,10 @@ func normalizeEmailSetting(value emailSettingValue) emailSettingValue {
|
||||
value.Password = strings.TrimSpace(value.Password)
|
||||
value.FromEmail = normalizeEmail(value.FromEmail)
|
||||
value.FromName = strings.TrimSpace(value.FromName)
|
||||
if value.RegistrationAllowedDomains == nil {
|
||||
value.RegistrationAllowedDomains = append([]string(nil), defaultRegistrationEmailDomains...)
|
||||
}
|
||||
value.RegistrationAllowedDomains = normalizeEmailDomains(value.RegistrationAllowedDomains)
|
||||
if value.Port == 0 {
|
||||
value.Port = 587
|
||||
}
|
||||
@@ -287,7 +320,11 @@ func normalizeEmailSetting(value emailSettingValue) emailSettingValue {
|
||||
func (s *Service) publicEmailSetting(setting *model.SystemSetting, value emailSettingValue) *PublicEmailSetting {
|
||||
inherited := value.FromName == "" || value.FromName == defaultAppearanceBrandName
|
||||
value = resolveEmailSender(value, s.appearanceBrandName())
|
||||
result := &PublicEmailSetting{Enabled: value.Enabled, Host: value.Host, Port: value.Port, Username: value.Username, Encryption: value.Encryption, FromEmail: value.FromEmail, FromName: value.FromName, FromNameInherited: inherited, HasPassword: value.Password != ""}
|
||||
result := &PublicEmailSetting{
|
||||
Enabled: value.Enabled, Host: value.Host, Port: value.Port, Username: value.Username, Encryption: value.Encryption,
|
||||
FromEmail: value.FromEmail, FromName: value.FromName, FromNameInherited: inherited, HasPassword: value.Password != "",
|
||||
RegistrationAllowedDomains: value.RegistrationAllowedDomains,
|
||||
}
|
||||
if setting != nil {
|
||||
result.UpdatedBy = setting.UpdatedBy
|
||||
result.CreatedAt = setting.CreatedAt
|
||||
@@ -296,6 +333,75 @@ func (s *Service) publicEmailSetting(setting *model.SystemSetting, value emailSe
|
||||
return result
|
||||
}
|
||||
|
||||
func normalizeEmailDomains(values []string) []string {
|
||||
result := make([]string, 0, len(values))
|
||||
seen := make(map[string]bool, len(values))
|
||||
for _, raw := range values {
|
||||
value := strings.ToLower(strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(raw), "@")))
|
||||
value = strings.TrimSuffix(value, ".")
|
||||
if value == "" || seen[value] {
|
||||
continue
|
||||
}
|
||||
seen[value] = true
|
||||
result = append(result, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func validateRegistrationEmailDomains(allowed []string) error {
|
||||
if len(allowed) > 100 {
|
||||
return BadAuthRequest("电子邮件域名白名单最多填写 100 项")
|
||||
}
|
||||
for _, domain := range allowed {
|
||||
if !validEmailDomain(domain) {
|
||||
return BadAuthRequest("电子邮件域名格式不正确:" + domain)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validEmailDomain(value string) bool {
|
||||
if len(value) > 253 || !strings.Contains(value, ".") || strings.HasPrefix(value, ".") || strings.HasSuffix(value, ".") {
|
||||
return false
|
||||
}
|
||||
for _, label := range strings.Split(value, ".") {
|
||||
if label == "" || len(label) > 63 || strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") {
|
||||
return false
|
||||
}
|
||||
for _, char := range label {
|
||||
if (char < 'a' || char > 'z') && (char < '0' || char > '9') && char != '-' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Service) validateRegistrationEmailDomain(email string) error {
|
||||
_, setting, err := s.readEmailSetting()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return validateRegistrationEmailDomain(email, setting.RegistrationAllowedDomains)
|
||||
}
|
||||
|
||||
func validateRegistrationEmailDomain(email string, allowedDomains []string) error {
|
||||
parts := strings.Split(email, "@")
|
||||
if len(parts) != 2 {
|
||||
return BadAuthRequest("邮箱格式不正确")
|
||||
}
|
||||
domain := strings.ToLower(parts[1])
|
||||
if len(allowedDomains) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, allowed := range allowedDomains {
|
||||
if domain == allowed {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return BadAuthRequest("该邮箱域名不在管理员设置的白名单内")
|
||||
}
|
||||
|
||||
func resolveEmailSender(value emailSettingValue, brandName string) emailSettingValue {
|
||||
if value.FromName == "" || value.FromName == defaultAppearanceBrandName {
|
||||
value.FromName = brandName
|
||||
|
||||
134
backend/internal/service/email_domain_test.go
Normal file
134
backend/internal/service/email_domain_test.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"infinite-canvas/backend/internal/model"
|
||||
)
|
||||
|
||||
func TestRegistrationDomainPolicyChecksSendingAndRegistration(t *testing.T) {
|
||||
svc, db := newPasswordResetTestService(t)
|
||||
admin := &model.User{ID: "admin", Username: "admin", Role: model.UserRoleAdmin, Status: model.UserStatusActive}
|
||||
if err := db.Create(admin).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Create(&model.SystemSetting{Key: registrationSettingKey, ValueJSON: `{"enabled":true}`}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var deliveredCode string
|
||||
sends := 0
|
||||
svc.mailSender = func(_ emailSettingValue, _, _ string, body string) error {
|
||||
sends++
|
||||
deliveredCode = codeFromEmailBody(body)
|
||||
return nil
|
||||
}
|
||||
if err := svc.SendRegistrationEmailCode("member@blocked.example"); err == nil || !strings.Contains(err.Error(), "白名单") {
|
||||
t.Fatalf("blocked domain must not receive registration code: %v", err)
|
||||
}
|
||||
var codes int64
|
||||
if err := db.Model(&model.EmailVerificationCode{}).Count(&codes).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sends != 0 || codes != 0 {
|
||||
t.Fatalf("blocked send created side effects: sends=%d codes=%d", sends, codes)
|
||||
}
|
||||
if err := svc.SendRegistrationEmailCode("member@example.com"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if deliveredCode == "" || sends != 1 {
|
||||
t.Fatal("allowed domain did not receive a code")
|
||||
}
|
||||
// A code issued before an administrator tightens the policy must not bypass it.
|
||||
if _, err := svc.UpdateEmailSetting(admin, EmailSettingRequest{RegistrationAllowedDomains: []string{"gmail.com"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := svc.Register(RegisterRequest{Username: "member", Email: "member@example.com", Password: "strong-password", EmailCode: deliveredCode}); err == nil || !strings.Contains(err.Error(), "白名单") {
|
||||
t.Fatalf("registration must recheck current domain policy: %v", err)
|
||||
}
|
||||
count, err := svc.repo.UserCount()
|
||||
if err != nil || count != 1 {
|
||||
t.Fatalf("blocked registration created a user: count=%d err=%v", count, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmailDomainSettingsRoundTripAndValidation(t *testing.T) {
|
||||
svc, _ := newPasswordResetTestService(t)
|
||||
admin := &model.User{ID: "admin", Role: model.UserRoleAdmin, Status: model.UserStatusActive}
|
||||
setting, err := svc.UpdateEmailSetting(admin, EmailSettingRequest{RegistrationAllowedDomains: []string{" @GMAIL.COM. ", "gmail.com"}})
|
||||
if err != nil || setting == nil || len(setting.RegistrationAllowedDomains) != 1 || setting.RegistrationAllowedDomains[0] != "gmail.com" {
|
||||
t.Fatalf("normalization failed: setting=%+v err=%v", setting, err)
|
||||
}
|
||||
if _, err := svc.UpdateEmailSetting(admin, EmailSettingRequest{RegistrationAllowedDomains: []string{"bad_domain"}}); err == nil {
|
||||
t.Fatal("invalid whitelist accepted while SMTP is disabled")
|
||||
}
|
||||
setting, err = svc.AdminEmailSetting(admin)
|
||||
if err != nil || setting == nil || len(setting.RegistrationAllowedDomains) != 1 || setting.RegistrationAllowedDomains[0] != "gmail.com" {
|
||||
t.Fatalf("failed update changed the saved whitelist: setting=%+v err=%v", setting, err)
|
||||
}
|
||||
if _, err := svc.UpdateEmailSetting(admin, EmailSettingRequest{RegistrationAllowedDomains: []string{}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
setting, err = svc.AdminEmailSetting(admin)
|
||||
if err != nil || setting == nil || setting.RegistrationAllowedDomains == nil || len(setting.RegistrationAllowedDomains) != 0 {
|
||||
t.Fatalf("explicit empty whitelist must survive persistence: setting=%+v err=%v", setting, err)
|
||||
}
|
||||
if err := svc.validateRegistrationEmailDomain("member@custom.example"); err != nil {
|
||||
t.Fatalf("empty saved whitelist should allow custom domains: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeEmailSettingDefaultsToMainstreamRegistrationDomains(t *testing.T) {
|
||||
setting := normalizeEmailSetting(emailSettingValue{})
|
||||
for _, domain := range []string{"gmail.com", "163.com", "126.com", "qq.com", "outlook.com", "hotmail.com", "icloud.com", "yahoo.com", "foxmail.com"} {
|
||||
if !containsEmailDomain(setting.RegistrationAllowedDomains, domain) {
|
||||
t.Fatalf("default allowed domains = %#v, missing %q", setting.RegistrationAllowedDomains, domain)
|
||||
}
|
||||
}
|
||||
|
||||
setting = normalizeEmailSetting(emailSettingValue{RegistrationAllowedDomains: []string{}})
|
||||
if len(setting.RegistrationAllowedDomains) != 0 {
|
||||
t.Fatalf("explicit empty allowed domains = %#v, want unrestricted", setting.RegistrationAllowedDomains)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRegistrationEmailDomainUsesExactWhitelistMatch(t *testing.T) {
|
||||
allowed := []string{"gmail.com", "example.org"}
|
||||
for _, email := range []string{"member@gmail.com", "member@example.org"} {
|
||||
if err := validateRegistrationEmailDomain(email, allowed); err != nil {
|
||||
t.Fatalf("%s should be allowed: %v", email, err)
|
||||
}
|
||||
}
|
||||
for _, test := range []struct {
|
||||
email string
|
||||
want string
|
||||
}{
|
||||
{email: "member@fakegmail.com", want: "不在管理员设置的白名单"},
|
||||
{email: "member@blocked.example", want: "不在管理员设置的白名单"},
|
||||
} {
|
||||
if err := validateRegistrationEmailDomain(test.email, allowed); err == nil || !strings.Contains(err.Error(), test.want) {
|
||||
t.Fatalf("validateRegistrationEmailDomain(%q) error = %v", test.email, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRegistrationEmailDomainsRejectsInvalidDomain(t *testing.T) {
|
||||
if err := validateRegistrationEmailDomains([]string{"invalid_domain"}); err == nil {
|
||||
t.Fatal("invalid domain should be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRegistrationEmailDomainAllowsAnyDomainWhenWhitelistIsEmpty(t *testing.T) {
|
||||
if err := validateRegistrationEmailDomain("member@example.org", nil); err != nil {
|
||||
t.Fatalf("empty whitelist should not restrict registration: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func containsEmailDomain(values []string, target string) bool {
|
||||
for _, value := range values {
|
||||
if value == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -21,7 +21,8 @@ func TestPasswordResetChangesPasswordConsumesCodeAndRevokesSessions(t *testing.T
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
user := model.User{ID: "user-1", Username: "creator", Email: "creator@example.com", DisplayName: "Creator", Role: model.UserRoleUser, Status: model.UserStatusActive, PasswordHash: oldHash}
|
||||
// Existing users can reset passwords even outside the registration whitelist.
|
||||
user := model.User{ID: "user-1", Username: "creator", Email: "creator@external.example", DisplayName: "Creator", Role: model.UserRoleUser, Status: model.UserStatusActive, PasswordHash: oldHash}
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -255,7 +256,7 @@ func newPasswordResetTestService(t *testing.T) (*Service, *gorm.DB) {
|
||||
if err := db.AutoMigrate(&model.User{}, &model.AuthSession{}, &model.EmailVerificationCode{}, &model.SystemSetting{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
settingJSON, err := json.Marshal(emailSettingValue{Enabled: true, Host: "smtp.example.com", Port: 587, Encryption: "starttls", FromEmail: "noreply@example.com", FromName: "影策"})
|
||||
settingJSON, err := json.Marshal(emailSettingValue{Enabled: true, Host: "smtp.example.com", Port: 587, Encryption: "starttls", FromEmail: "noreply@example.com", FromName: "影策", RegistrationAllowedDomains: []string{"example.com"}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user