fix(order): prevent duplicate subscriptions and repair invite gifts
Build docker and publish / build (20.15.1) (push) Successful in 5m6s
Build docker and publish / build (20.15.1) (push) Successful in 5m6s
This commit is contained in:
@@ -2,6 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/mail"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/server/internal/config"
|
||||
@@ -15,11 +16,10 @@ func IsEmailDomainWhitelisted(email, whitelistCSV string) bool {
|
||||
if whitelistCSV == "" {
|
||||
return false
|
||||
}
|
||||
parts := strings.SplitN(email, "@", 2)
|
||||
if len(parts) != 2 {
|
||||
_, domain, ok := parseStrictEmail(email)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
domain := strings.ToLower(strings.TrimSpace(parts[1]))
|
||||
for _, d := range strings.Split(whitelistCSV, ",") {
|
||||
if strings.ToLower(strings.TrimSpace(d)) == domain {
|
||||
return true
|
||||
@@ -32,10 +32,16 @@ func ShouldGrantTrialForEmail(register config.RegisterConfig, email string) bool
|
||||
if !register.EnableTrial {
|
||||
return false
|
||||
}
|
||||
if !IsValidTrialEmail(email) {
|
||||
return false
|
||||
}
|
||||
// 无论白名单是否启用,泛域名邮箱(含 + 别名或 Gmail 点号)始终拒绝赠送
|
||||
if IsDisposableAlias(email) {
|
||||
return false
|
||||
}
|
||||
if isConfusableGmailDomain(emailDomain(email)) {
|
||||
return false
|
||||
}
|
||||
if !register.EnableTrialEmailWhitelist {
|
||||
return true
|
||||
}
|
||||
@@ -52,11 +58,10 @@ func ShouldGrantTrialForEmail(register config.RegisterConfig, email string) bool
|
||||
// For Gmail-like domains, local part containing "." or "+" is rejected.
|
||||
// For all other domains, only "+" alias is rejected.
|
||||
func IsDisposableAlias(email string) bool {
|
||||
parts := strings.SplitN(strings.ToLower(strings.TrimSpace(email)), "@", 2)
|
||||
if len(parts) != 2 {
|
||||
local, domain, ok := parseStrictEmail(email)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
local, domain := parts[0], parts[1]
|
||||
|
||||
// All domains: reject + alias
|
||||
if strings.ContainsRune(local, '+') {
|
||||
@@ -74,11 +79,10 @@ func IsDisposableAlias(email string) bool {
|
||||
// Removes dots from local part for Gmail-like providers (gmail.com, googlemail.com).
|
||||
func NormalizeEmail(email string) string {
|
||||
email = strings.ToLower(strings.TrimSpace(email))
|
||||
parts := strings.SplitN(email, "@", 2)
|
||||
if len(parts) != 2 {
|
||||
local, domain, ok := parseStrictEmail(email)
|
||||
if !ok {
|
||||
return email
|
||||
}
|
||||
local, domain := parts[0], parts[1]
|
||||
|
||||
// Strip + alias
|
||||
if idx := strings.IndexByte(local, '+'); idx != -1 {
|
||||
@@ -101,6 +105,51 @@ func isGmailLikeDomain(domain string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func IsValidTrialEmail(email string) bool {
|
||||
local, domain, ok := parseStrictEmail(email)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return local != "" && domain != ""
|
||||
}
|
||||
|
||||
func parseStrictEmail(email string) (local, domain string, ok bool) {
|
||||
email = strings.ToLower(strings.TrimSpace(email))
|
||||
if email == "" || strings.ContainsAny(email, " \t\r\n") {
|
||||
return "", "", false
|
||||
}
|
||||
addr, err := mail.ParseAddress(email)
|
||||
if err != nil || addr.Address != email || addr.Name != "" {
|
||||
return "", "", false
|
||||
}
|
||||
parts := strings.Split(addr.Address, "@")
|
||||
if len(parts) != 2 {
|
||||
return "", "", false
|
||||
}
|
||||
local = strings.TrimSpace(parts[0])
|
||||
domain = strings.Trim(strings.TrimSpace(parts[1]), ".")
|
||||
if local == "" || domain == "" || strings.Contains(domain, "..") || !strings.Contains(domain, ".") {
|
||||
return "", "", false
|
||||
}
|
||||
return local, domain, true
|
||||
}
|
||||
|
||||
func emailDomain(email string) string {
|
||||
_, domain, ok := parseStrictEmail(email)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return domain
|
||||
}
|
||||
|
||||
func isConfusableGmailDomain(domain string) bool {
|
||||
switch strings.ToLower(strings.TrimSpace(domain)) {
|
||||
case "gmaial.com", "gmial.com", "gmai.com", "gamil.com", "gmal.com", "gmail.co", "gmail.con":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NormalizedEmailHasTrial returns true if any user with the same normalized email
|
||||
// already holds a trial subscription. Only performs the cross-user DB check when
|
||||
// normalization actually changes the email (i.e., dots removed or + alias stripped).
|
||||
|
||||
Reference in New Issue
Block a user