From 343d038ee3a3473e5dd11f6164fe2c71bf247fb8 Mon Sep 17 00:00:00 2001 From: smail Date: Sat, 12 Sep 2026 10:49:45 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E5=9F=9F=E5=90=8D=E5=AE=8C=E6=95=B4=E9=82=AE=E7=AE=B1=E6=A0=87?= =?UTF-8?q?=E8=AF=86=E7=9B=B4=E6=8E=A5=E8=A7=A3=E6=9E=90=E6=9C=89=E6=95=88?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E4=B8=8E=E5=8D=8F=E8=AE=AE=E8=AE=A4=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/services/auth/identifier.go | 50 +++++++++++++++++------- backend/services/auth/identifier_test.go | 50 ++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 15 deletions(-) diff --git a/backend/services/auth/identifier.go b/backend/services/auth/identifier.go index b1edba6..b6a8547 100644 --- a/backend/services/auth/identifier.go +++ b/backend/services/auth/identifier.go @@ -72,24 +72,44 @@ func ResolveIdentifier(identifier string) (ResolvedIdentity, error) { Find(&bindings); err != nil { return ResolvedIdentity{}, fmt.Errorf("load login email binding: %w", err) } - if len(bindings) != 1 || bindings[0].UserID <= 0 { - return ResolvedIdentity{}, errors.New("invalid identifier") + if len(bindings) == 1 && bindings[0].UserID > 0 { + var user models.User + has, err := db.Instance.ID(bindings[0].UserID).Get(&user) + if err != nil { + return ResolvedIdentity{}, fmt.Errorf("load login user: %w", err) + } + if has && user.ID > 0 && user.Account != "" && user.Disabled == 0 { + return ResolvedIdentity{ + UserID: user.ID, + Account: user.Account, + LoginAddress: normalized, + Kind: IdentifierEmail, + }, nil + } } - var user models.User - has, err := db.Instance.ID(bindings[0].UserID).Get(&user) - if err != nil { - return ResolvedIdentity{}, fmt.Errorf("load login user: %w", err) + // 回退机制:若未绑定独立外部邮箱,但属于系统配置的域名,则按账号匹配有效系统用户 + parts := strings.SplitN(normalized, "@", 2) + if len(parts) == 2 && isConfiguredLoginDomain(parts[1]) { + account, err := validation.NormalizeAccount(parts[0]) + if err == nil { + var user models.User + has, err := db.Instance.Where("account = ? AND disabled = 0", account).Get(&user) + if err != nil { + return ResolvedIdentity{}, fmt.Errorf("load domain user: %w", err) + } + if has && user.ID > 0 && user.Account != "" { + return ResolvedIdentity{ + UserID: user.ID, + Account: user.Account, + LoginAddress: normalized, + Kind: IdentifierEmail, + }, nil + } + } } - if !has || user.ID <= 0 || user.Account == "" { - return ResolvedIdentity{}, errors.New("invalid identifier") - } - return ResolvedIdentity{ - UserID: user.ID, - Account: user.Account, - LoginAddress: normalized, - Kind: IdentifierEmail, - }, nil + + return ResolvedIdentity{}, errors.New("invalid identifier") } func parseLoginIdentifier(identifier string) (string, IdentifierKind, error) { diff --git a/backend/services/auth/identifier_test.go b/backend/services/auth/identifier_test.go index 3551d19..f7ef0d4 100644 --- a/backend/services/auth/identifier_test.go +++ b/backend/services/auth/identifier_test.go @@ -92,3 +92,53 @@ func TestNormalizeIdentifierKeepsLegacyAccountCompatibility(t *testing.T) { t.Fatalf("normalized email key = %q, err = %v", got, err) } } + + +func TestResolveIdentifierFallbackToConfiguredDomainUser(t *testing.T) { + oldConfig := config.Instance + oldDB := db.Instance + config.Instance = &config.Config{ + DbType: config.DBTypeSQLite, + DbDSN: filepath.Join(t.TempDir(), "domain_fallback.db"), + Domain: "solit.cn", + Domains: []string{"solit.cn"}, + } + if err := db.Init(""); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if db.Instance != nil && db.Instance != oldDB { + _ = db.Instance.Close() + } + db.Instance = oldDB + config.Instance = oldConfig + }) + + user := models.User{Account: "bob", Name: "Bob", Disabled: 0} + if err := SetPassword(&user, "CorrectP12"); err != nil { + t.Fatal(err) + } + if _, err := db.Instance.Insert(&user); err != nil { + t.Fatal(err) + } + + identity, err := ResolveIdentifier(" Bob@Solit.cn ") + if err != nil { + t.Fatalf("resolve configured domain user failed: %v", err) + } + if identity.Kind != IdentifierEmail || identity.UserID != user.ID || identity.Account != "bob" || identity.LoginAddress != "bob@solit.cn" { + t.Fatalf("unexpected resolved identity: %+v", identity) + } + + disabledUser := models.User{Account: "charlie", Name: "Charlie", Disabled: 1} + if _, err := db.Instance.Insert(&disabledUser); err != nil { + t.Fatal(err) + } + if _, err := ResolveIdentifier("charlie@solit.cn"); err == nil { + t.Fatal("disabled domain user must not be resolved") + } + + if _, err := ResolveIdentifier("bob@other.com"); err == nil { + t.Fatal("unconfigured domain must not be resolved") + } +} \ No newline at end of file