Refactor user package (#33423)

and avoid global variables
pull/31840/merge
wxiaoguang 2 days ago committed by GitHub
parent a9577e0808
commit 8c4f0f02ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      models/issues/reaction.go
  2. 5
      models/user/email_address.go
  3. 5
      models/user/openid.go
  4. 59
      models/user/user.go
  5. 23
      models/user/user_system.go
  6. 3
      routers/web/user/avatar.go

@ -7,6 +7,7 @@ import (
"bytes"
"context"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
@ -321,6 +322,11 @@ func valuesUser(m map[int64]*user_model.User) []*user_model.User {
return values
}
// newMigrationOriginalUser creates and returns a fake user for external user
func newMigrationOriginalUser(name string) *user_model.User {
return &user_model.User{ID: 0, Name: name, LowerName: strings.ToLower(name)}
}
// LoadUsers loads reactions' all users
func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Repository) ([]*user_model.User, error) {
if len(list) == 0 {
@ -338,7 +344,7 @@ func (list ReactionList) LoadUsers(ctx context.Context, repo *repo_model.Reposit
for _, reaction := range list {
if reaction.OriginalAuthor != "" {
reaction.User = user_model.NewReplaceUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name()))
reaction.User = newMigrationOriginalUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name()))
} else if user, ok := userMaps[reaction.UserID]; ok {
reaction.User = user
} else {

@ -8,7 +8,6 @@ import (
"context"
"fmt"
"net/mail"
"regexp"
"strings"
"time"
@ -153,8 +152,6 @@ func UpdateEmailAddress(ctx context.Context, email *EmailAddress) error {
return err
}
var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
// ValidateEmail check if email is a valid & allowed address
func ValidateEmail(email string) error {
if err := validateEmailBasic(email); err != nil {
@ -514,7 +511,7 @@ func validateEmailBasic(email string) error {
return ErrEmailInvalid{email}
}
if !emailRegexp.MatchString(email) {
if !globalVars().emailRegexp.MatchString(email) {
return ErrEmailCharIsNotSupported{email}
}

@ -11,9 +11,6 @@ import (
"code.gitea.io/gitea/modules/util"
)
// ErrOpenIDNotExist openid is not known
var ErrOpenIDNotExist = util.NewNotExistErrorf("OpenID is unknown")
// UserOpenID is the list of all OpenID identities of a user.
// Since this is a middle table, name it OpenID is not suitable, so we ignore the lint here
type UserOpenID struct { //revive:disable-line:exported
@ -99,7 +96,7 @@ func DeleteUserOpenID(ctx context.Context, openid *UserOpenID) (err error) {
if err != nil {
return err
} else if deleted != 1 {
return ErrOpenIDNotExist
return util.NewNotExistErrorf("OpenID is unknown")
}
return nil
}

@ -14,6 +14,7 @@ import (
"path/filepath"
"regexp"
"strings"
"sync"
"time"
"unicode"
@ -417,19 +418,9 @@ func (u *User) DisplayName() string {
return u.Name
}
var emailToReplacer = strings.NewReplacer(
"\n", "",
"\r", "",
"<", "",
">", "",
",", "",
":", "",
";", "",
)
// EmailTo returns a string suitable to be put into a e-mail `To:` header.
func (u *User) EmailTo() string {
sanitizedDisplayName := emailToReplacer.Replace(u.DisplayName())
sanitizedDisplayName := globalVars().emailToReplacer.Replace(u.DisplayName())
// should be an edge case but nice to have
if sanitizedDisplayName == u.Email {
@ -526,28 +517,52 @@ func GetUserSalt() (string, error) {
if err != nil {
return "", err
}
// Returns a 32 bytes long string.
// Returns a 32-byte long string.
return hex.EncodeToString(rBytes), nil
}
// Note: The set of characters here can safely expand without a breaking change,
// but characters removed from this set can cause user account linking to break
var (
customCharsReplacement = strings.NewReplacer("Æ", "AE")
removeCharsRE = regexp.MustCompile("['`´]")
transformDiacritics = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
replaceCharsHyphenRE = regexp.MustCompile(`[\s~+]`)
)
type globalVarsStruct struct {
customCharsReplacement *strings.Replacer
removeCharsRE *regexp.Regexp
transformDiacritics transform.Transformer
replaceCharsHyphenRE *regexp.Regexp
emailToReplacer *strings.Replacer
emailRegexp *regexp.Regexp
}
var globalVars = sync.OnceValue(func() *globalVarsStruct {
return &globalVarsStruct{
// Note: The set of characters here can safely expand without a breaking change,
// but characters removed from this set can cause user account linking to break
customCharsReplacement: strings.NewReplacer("Æ", "AE"),
removeCharsRE: regexp.MustCompile("['`´]"),
transformDiacritics: transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC),
replaceCharsHyphenRE: regexp.MustCompile(`[\s~+]`),
emailToReplacer: strings.NewReplacer(
"\n", "",
"\r", "",
"<", "",
">", "",
",", "",
":", "",
";", "",
),
emailRegexp: regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"),
}
})
// NormalizeUserName only takes the name part if it is an email address, transforms it diacritics to ASCII characters.
// It returns a string with the single-quotes removed, and any other non-supported username characters are replaced with a `-` character
func NormalizeUserName(s string) (string, error) {
vars := globalVars()
s, _, _ = strings.Cut(s, "@")
strDiacriticsRemoved, n, err := transform.String(transformDiacritics, customCharsReplacement.Replace(s))
strDiacriticsRemoved, n, err := transform.String(vars.transformDiacritics, vars.customCharsReplacement.Replace(s))
if err != nil {
return "", fmt.Errorf("failed to normalize the string of provided username %q at position %d", s, n)
}
return replaceCharsHyphenRE.ReplaceAllLiteralString(removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil
return vars.replaceCharsHyphenRE.ReplaceAllLiteralString(vars.removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil
}
var (

@ -12,7 +12,6 @@ import (
const (
GhostUserID = -1
GhostUserName = "Ghost"
GhostUserLowerName = "ghost"
)
// NewGhostUser creates and returns a fake user for someone has deleted their account.
@ -20,10 +19,14 @@ func NewGhostUser() *User {
return &User{
ID: GhostUserID,
Name: GhostUserName,
LowerName: GhostUserLowerName,
LowerName: strings.ToLower(GhostUserName),
}
}
func IsGhostUserName(name string) bool {
return strings.EqualFold(name, GhostUserName)
}
// IsGhost check if user is fake user for a deleted account
func (u *User) IsGhost() bool {
if u == nil {
@ -32,20 +35,10 @@ func (u *User) IsGhost() bool {
return u.ID == GhostUserID && u.Name == GhostUserName
}
// NewReplaceUser creates and returns a fake user for external user
func NewReplaceUser(name string) *User {
return &User{
ID: 0,
Name: name,
LowerName: strings.ToLower(name),
}
}
const (
ActionsUserID = -2
ActionsUserName = "gitea-actions"
ActionsFullName = "Gitea Actions"
ActionsEmail = "teabot@gitea.io"
ActionsUserEmail = "teabot@gitea.io"
)
// NewActionsUser creates and returns a fake user for running the actions.
@ -55,8 +48,8 @@ func NewActionsUser() *User {
Name: ActionsUserName,
LowerName: ActionsUserName,
IsActive: true,
FullName: ActionsFullName,
Email: ActionsEmail,
FullName: "Gitea Actions",
Email: ActionsUserEmail,
KeepEmailPrivate: true,
LoginName: ActionsUserName,
Type: UserTypeIndividual,

@ -4,7 +4,6 @@
package user
import (
"strings"
"time"
"code.gitea.io/gitea/models/avatars"
@ -27,7 +26,7 @@ func AvatarByUserName(ctx *context.Context) {
size := int(ctx.PathParamInt64("size"))
var user *user_model.User
if strings.ToLower(userName) != user_model.GhostUserLowerName {
if !user_model.IsGhostUserName(userName) {
var err error
if user, err = user_model.GetUserByName(ctx, userName); err != nil {
if user_model.IsErrUserNotExist(err) {

Loading…
Cancel
Save