68 lines
2.2 KiB
Go
68 lines
2.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"git.pcux.cn/xuans/Smail/db"
|
|
"git.pcux.cn/xuans/Smail/models"
|
|
"git.pcux.cn/xuans/Smail/services/notification"
|
|
)
|
|
|
|
// 通知身份拥有独立用户名空间,不会回退到普通协议或网页认证。
|
|
func authenticateNotification(protocol, remoteAddress, account, plain string) (*models.User, *ProtocolSession, error) {
|
|
account = strings.ToLower(strings.TrimSpace(account))
|
|
identifier := strings.TrimPrefix(account, notification.UsernamePrefix)
|
|
remote := protocolRemoteHost(remoteAddress)
|
|
pairKey := hashedProtocolLoginKey("pair", "notification", remote, identifier)
|
|
accountKey := hashedProtocolLoginKey("account", identifier)
|
|
remoteKey := hashedProtocolLoginKey("remote", remote)
|
|
fail := func() (*models.User, *ProtocolSession, error) {
|
|
protocolPairAttempts.recordFailure(pairKey)
|
|
protocolAccountAttempts.recordFailure(accountKey)
|
|
protocolRemoteAttempts.recordFailure(remoteKey)
|
|
return nil, nil, notification.ErrDenied
|
|
}
|
|
if protocolPairAttempts.blocked(pairKey) || protocolAccountAttempts.blocked(accountKey) || protocolRemoteAttempts.blocked(remoteKey) {
|
|
return nil, nil, notification.ErrDenied
|
|
}
|
|
if protocol != "smtp" || db.Instance == nil {
|
|
return fail()
|
|
}
|
|
identifier, err := NormalizeProtocolIdentifier(identifier)
|
|
if err != nil {
|
|
return fail()
|
|
}
|
|
identity, err := ResolveIdentifier(identifier)
|
|
if err != nil || identity.UserID <= 0 {
|
|
return fail()
|
|
}
|
|
var user models.User
|
|
found, err := db.Instance.ID(identity.UserID).Get(&user)
|
|
if err != nil || !found {
|
|
return fail()
|
|
}
|
|
grant, err := notification.Authenticate(db.Instance, user, plain)
|
|
if err != nil {
|
|
return fail()
|
|
}
|
|
session := &ProtocolSession{user: user, notification: grant}
|
|
if err := session.Check(); err != nil {
|
|
return fail()
|
|
}
|
|
protocolPairAttempts.clear(pairKey)
|
|
protocolAccountAttempts.clear(accountKey)
|
|
return &user, session, nil
|
|
}
|
|
|
|
func (s *ProtocolSession) IsNotification() bool {
|
|
return s != nil && s.notification != nil
|
|
}
|
|
|
|
func (s *ProtocolSession) ReserveNotification(recipients int) error {
|
|
if !s.IsNotification() || s.Check() != nil {
|
|
return notification.ErrDenied
|
|
}
|
|
return s.notification.Reserve(db.Instance, recipients, time.Now())
|
|
}
|