统一协议邮箱标识认证处理
Some checks are pending
Security Checks / security (push) Waiting to run

This commit is contained in:
smail 2026-08-30 23:03:36 +08:00
parent e603295df3
commit 3a6582c722
5 changed files with 58 additions and 9 deletions

View File

@ -79,12 +79,6 @@ func (a action) User(session *gopop.Session, username string) error {
session.Ctx = tc
}
log.WithContext(session.Ctx).Debug("POP3 USER received")
infos := strings.Split(username, "@")
if len(infos) > 1 {
username = infos[0]
}
log.WithContext(session.Ctx).Debug("POP3 user name normalized")
session.User = username

View File

@ -62,3 +62,13 @@ func Test_email(t *testing.T) {
fmt.Printf("%s", b.String())
}
func TestActionUserPreservesFullEmailIdentifier(t *testing.T) {
session := &gopop.Session{}
if err := (action{}).User(session, "Alice@Example.com"); err != nil {
t.Fatal(err)
}
if session.User != "Alice@Example.com" {
t.Fatalf("POP3 user identifier = %q, want full email identifier", session.User)
}
}

View File

@ -62,7 +62,6 @@ func (s *Session) Auth(mech string) (sasl.Server, error) {
}
func (s *Session) AuthPlain(username, pwd string) error {
username = auth.NormalizeAccount(username)
user, err := auth.AuthenticateProtocol("smtp", s.RemoteAddress.String(), username, pwd)
if err != nil {
log.WithContext(s.Ctx).Info("SMTP authentication failed")

View File

@ -106,3 +106,46 @@ func TestHasAuthRequiresMailboxOwnershipForAdministrators(t *testing.T) {
t.Fatal("administrator bypassed mailbox ownership")
}
}
func TestProtocolAuthenticationAcceptsConfiguredFullEmailIdentifier(t *testing.T) {
oldConfig := config.Instance
oldDB := db.Instance
config.Instance = &config.Config{
DbType: config.DBTypeSQLite,
DbDSN: filepath.Join(t.TempDir(), "full-email-auth.db"),
Domain: "example.com",
Domains: []string{"example.com"},
}
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: "alice", Name: "Alice"}
if err := SetPassword(&user, "CorrectP12"); err != nil {
t.Fatal(err)
}
if _, err := db.Instance.Insert(&user); err != nil {
t.Fatal(err)
}
for _, protocol := range []string{"smtp", "imap", "pop3"} {
got, err := AuthenticateProtocol(protocol, "192.0.2.10:12345", " Alice@Example.com ", "CorrectP12")
if err != nil {
t.Fatalf("%s full email authentication failed: %v", protocol, err)
}
if got == nil || got.Account != "alice" {
t.Fatalf("%s authenticated user = %+v", protocol, got)
}
}
if _, err := AuthenticateProtocol("imap", "192.0.2.10:12345", "alice@other.example", "CorrectP12"); err == nil {
t.Fatal("unconfigured email domain should be rejected")
}
}

View File

@ -42,7 +42,10 @@ var (
)
func AuthenticateProtocol(protocol string, remoteAddress string, account string, plain string) (*models.User, error) {
normalizedAccount := strings.ToLower(NormalizeAccount(account))
normalizedAccount := strings.ToLower(strings.TrimSpace(account))
if resolvedAccount, err := NormalizeIdentifier(account); err == nil {
normalizedAccount = resolvedAccount
}
normalizedRemote := protocolRemoteHost(remoteAddress)
pairKey := hashedProtocolLoginKey("pair", protocol, normalizedRemote, normalizedAccount)
accountKey := hashedProtocolLoginKey("account", normalizedAccount)
@ -51,7 +54,7 @@ func AuthenticateProtocol(protocol string, remoteAddress string, account string,
if protocolPairAttempts.blocked(pairKey) || protocolAccountAttempts.blocked(accountKey) || protocolRemoteAttempts.blocked(remoteKey) {
return nil, errors.New("invalid credentials")
}
user, err := Authenticate(account, plain)
user, err := AuthenticateIdentifier(account, plain)
if err != nil {
protocolPairAttempts.recordFailure(pairKey)
protocolAccountAttempts.recordFailure(accountKey)