diff options
Diffstat (limited to 'internal/db')
-rw-r--r-- | internal/db/org_team.go | 4 | ||||
-rw-r--r-- | internal/db/repo.go | 10 | ||||
-rw-r--r-- | internal/db/user.go | 110 | ||||
-rw-r--r-- | internal/db/users.go | 141 | ||||
-rw-r--r-- | internal/db/users_test.go | 40 |
5 files changed, 175 insertions, 130 deletions
diff --git a/internal/db/org_team.go b/internal/db/org_team.go index 6bdf4253..cfc4c08c 100644 --- a/internal/db/org_team.go +++ b/internal/db/org_team.go @@ -245,7 +245,9 @@ func (t *Team) RemoveRepository(repoID int64) error { return sess.Commit() } -var reservedTeamNames = []string{"new"} +var reservedTeamNames = map[string]struct{}{ + "new": {}, +} // IsUsableTeamName return an error if given name is a reserved name or pattern. func IsUsableTeamName(name string) error { diff --git a/internal/db/repo.go b/internal/db/repo.go index bb2cd52e..81a6cd00 100644 --- a/internal/db/repo.go +++ b/internal/db/repo.go @@ -1081,8 +1081,14 @@ func initRepository(e Engine, repoPath string, doer *User, repo *Repository, opt } var ( - reservedRepoNames = []string{".", ".."} - reservedRepoPatterns = []string{"*.git", "*.wiki"} + reservedRepoNames = map[string]struct{}{ + ".": {}, + "..": {}, + } + reservedRepoPatterns = []string{ + "*.git", + "*.wiki", + } ) // isRepoNameAllowed return an error if given name is a reserved name or pattern for repositories. diff --git a/internal/db/user.go b/internal/db/user.go index 4e5e4c1f..e15c9410 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -13,7 +13,6 @@ import ( "path/filepath" "strings" "time" - "unicode/utf8" "github.com/unknwon/com" log "unknwon.dev/clog/v2" @@ -59,115 +58,6 @@ func (u *User) getOrganizationCount(e Engine) (int64, error) { return e.Where("uid=?", u.ID).Count(new(OrgUser)) } -var ( - reservedUsernames = []string{"-", "explore", "create", "assets", "css", "img", "js", "less", "plugins", "debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new", ".", ".."} - reservedUserPatterns = []string{"*.keys"} -) - -type ErrNameNotAllowed struct { - args errutil.Args -} - -func IsErrNameNotAllowed(err error) bool { - _, ok := err.(ErrNameNotAllowed) - return ok -} - -func (err ErrNameNotAllowed) Value() string { - val, ok := err.args["name"].(string) - if ok { - return val - } - - val, ok = err.args["pattern"].(string) - if ok { - return val - } - - return "<value not found>" -} - -func (err ErrNameNotAllowed) Error() string { - return fmt.Sprintf("name is not allowed: %v", err.args) -} - -// isNameAllowed checks if name is reserved or pattern of name is not allowed -// based on given reserved names and patterns. -// Names are exact match, patterns can be prefix or suffix match with placeholder '*'. -func isNameAllowed(names, patterns []string, name string) error { - name = strings.TrimSpace(strings.ToLower(name)) - if utf8.RuneCountInString(name) == 0 { - return ErrNameNotAllowed{args: errutil.Args{"reason": "empty name"}} - } - - for i := range names { - if name == names[i] { - return ErrNameNotAllowed{args: errutil.Args{"reason": "reserved", "name": name}} - } - } - - for _, pat := range patterns { - if pat[0] == '*' && strings.HasSuffix(name, pat[1:]) || - (pat[len(pat)-1] == '*' && strings.HasPrefix(name, pat[:len(pat)-1])) { - return ErrNameNotAllowed{args: errutil.Args{"reason": "reserved", "pattern": pat}} - } - } - - return nil -} - -// isUsernameAllowed return an error if given name is a reserved name or pattern for users. -func isUsernameAllowed(name string) error { - return isNameAllowed(reservedUsernames, reservedUserPatterns, name) -} - -// CreateUser creates record of a new user. -// -// Deprecated: Use Users.Create instead. -func CreateUser(u *User) (err error) { - if err = isUsernameAllowed(u.Name); err != nil { - return err - } - - if Users.IsUsernameUsed(context.TODO(), u.Name) { - return ErrUserAlreadyExist{args: errutil.Args{"name": u.Name}} - } - - u.Email = strings.ToLower(u.Email) - isExist, err := IsEmailUsed(u.Email) - if err != nil { - return err - } else if isExist { - return ErrEmailAlreadyUsed{args: errutil.Args{"email": u.Email}} - } - - u.LowerName = strings.ToLower(u.Name) - u.AvatarEmail = u.Email - u.Avatar = tool.HashEmail(u.AvatarEmail) - if u.Rands, err = userutil.RandomSalt(); err != nil { - return err - } - if u.Salt, err = userutil.RandomSalt(); err != nil { - return err - } - u.Password = userutil.EncodePassword(u.Password, u.Salt) - u.MaxRepoCreation = -1 - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - if _, err = sess.Insert(u); err != nil { - return err - } else if err = os.MkdirAll(UserPath(u.Name), os.ModePerm); err != nil { - return err - } - - return sess.Commit() -} - func countUsers(e Engine) int64 { count, _ := e.Where("type=0").Count(new(User)) return count diff --git a/internal/db/users.go b/internal/db/users.go index f87dc28d..37494638 100644 --- a/internal/db/users.go +++ b/internal/db/users.go @@ -10,6 +10,7 @@ import ( "os" "strings" "time" + "unicode/utf8" "github.com/go-macaron/binding" api "github.com/gogs/go-gogs-client" @@ -234,12 +235,20 @@ func (db *users) Create(ctx context.Context, username, email string, opts Create } if db.IsUsernameUsed(ctx, username) { - return nil, ErrUserAlreadyExist{args: errutil.Args{"name": username}} + return nil, ErrUserAlreadyExist{ + args: errutil.Args{ + "name": username, + }, + } } _, err = db.GetByEmail(ctx, email) if err == nil { - return nil, ErrEmailAlreadyUsed{args: errutil.Args{"email": email}} + return nil, ErrEmailAlreadyUsed{ + args: errutil.Args{ + "email": email, + }, + } } else if !IsErrUserNotExist(err) { return nil, err } @@ -257,7 +266,7 @@ func (db *users) Create(ctx context.Context, username, email string, opts Create MaxRepoCreation: -1, IsActive: opts.Activated, IsAdmin: opts.Admin, - Avatar: cryptoutil.MD5(email), + Avatar: cryptoutil.MD5(email), // Gravatar URL uses the MD5 hash of the email, see https://en.gravatar.com/site/implement/hash/ AvatarEmail: email, } @@ -579,16 +588,6 @@ func (u *User) DisplayName() string { return u.Name } -// NewGhostUser creates and returns a fake user for people who has deleted their -// accounts. -func NewGhostUser() *User { - return &User{ - ID: -1, - Name: "Ghost", - LowerName: "ghost", - } -} - // HomeURLPath returns the URL path to the user or organization home page. // // TODO(unknwon): This is also used in templates, which should be fixed by @@ -693,3 +692,119 @@ func (u *User) GetOrganizationCount() (int64, error) { func (u *User) ShortName(length int) string { return strutil.Ellipsis(u.Name, length) } + +// NewGhostUser creates and returns a fake user for people who has deleted their +// accounts. +// +// TODO: Once migrated to unknwon.dev/i18n, pass in the `i18n.Locale` to +// translate the text to local language. +func NewGhostUser() *User { + return &User{ + ID: -1, + Name: "Ghost", + LowerName: "ghost", + } +} + +var ( + reservedUsernames = map[string]struct{}{ + "-": {}, + "explore": {}, + "create": {}, + "assets": {}, + "css": {}, + "img": {}, + "js": {}, + "less": {}, + "plugins": {}, + "debug": {}, + "raw": {}, + "install": {}, + "api": {}, + "avatar": {}, + "user": {}, + "org": {}, + "help": {}, + "stars": {}, + "issues": {}, + "pulls": {}, + "commits": {}, + "repo": {}, + "template": {}, + "admin": {}, + "new": {}, + ".": {}, + "..": {}, + } + reservedUsernamePatterns = []string{"*.keys"} +) + +type ErrNameNotAllowed struct { + args errutil.Args +} + +func IsErrNameNotAllowed(err error) bool { + _, ok := err.(ErrNameNotAllowed) + return ok +} + +func (err ErrNameNotAllowed) Value() string { + val, ok := err.args["name"].(string) + if ok { + return val + } + + val, ok = err.args["pattern"].(string) + if ok { + return val + } + + return "<value not found>" +} + +func (err ErrNameNotAllowed) Error() string { + return fmt.Sprintf("name is not allowed: %v", err.args) +} + +// isNameAllowed checks if the name is reserved or pattern of the name is not +// allowed based on given reserved names and patterns. Names are exact match, +// patterns can be prefix or suffix match with the wildcard ("*"). +func isNameAllowed(names map[string]struct{}, patterns []string, name string) error { + name = strings.TrimSpace(strings.ToLower(name)) + if utf8.RuneCountInString(name) == 0 { + return ErrNameNotAllowed{ + args: errutil.Args{ + "reason": "empty name", + }, + } + } + + if _, ok := names[name]; ok { + return ErrNameNotAllowed{ + args: errutil.Args{ + "reason": "reserved", + "name": name, + }, + } + } + + for _, pattern := range patterns { + if pattern[0] == '*' && strings.HasSuffix(name, pattern[1:]) || + (pattern[len(pattern)-1] == '*' && strings.HasPrefix(name, pattern[:len(pattern)-1])) { + return ErrNameNotAllowed{ + args: errutil.Args{ + "reason": "reserved", + "pattern": pattern, + }, + } + } + } + + return nil +} + +// isUsernameAllowed returns ErrNameNotAllowed if the given name or pattern of +// the name is not allowed as a username. +func isUsernameAllowed(name string) error { + return isNameAllowed(reservedUsernames, reservedUsernamePatterns, name) +} diff --git a/internal/db/users_test.go b/internal/db/users_test.go index 9c940f6b..2479702a 100644 --- a/internal/db/users_test.go +++ b/internal/db/users_test.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "os" + "strings" "testing" "time" @@ -211,7 +212,10 @@ func usersAuthenticate(t *testing.T, db *users) { func usersCreate(t *testing.T, db *users) { ctx := context.Background() - alice, err := db.Create(ctx, "alice", "alice@example.com", + alice, err := db.Create( + ctx, + "alice", + "alice@example.com", CreateUserOptions{ Activated: true, }, @@ -220,19 +224,32 @@ func usersCreate(t *testing.T, db *users) { t.Run("name not allowed", func(t *testing.T) { _, err := db.Create(ctx, "-", "", CreateUserOptions{}) - wantErr := ErrNameNotAllowed{args: errutil.Args{"reason": "reserved", "name": "-"}} + wantErr := ErrNameNotAllowed{ + args: errutil.Args{ + "reason": "reserved", + "name": "-", + }, + } assert.Equal(t, wantErr, err) }) t.Run("name already exists", func(t *testing.T) { _, err := db.Create(ctx, alice.Name, "", CreateUserOptions{}) - wantErr := ErrUserAlreadyExist{args: errutil.Args{"name": alice.Name}} + wantErr := ErrUserAlreadyExist{ + args: errutil.Args{ + "name": alice.Name, + }, + } assert.Equal(t, wantErr, err) }) t.Run("email already exists", func(t *testing.T) { _, err := db.Create(ctx, "bob", alice.Email, CreateUserOptions{}) - wantErr := ErrEmailAlreadyUsed{args: errutil.Args{"email": alice.Email}} + wantErr := ErrEmailAlreadyUsed{ + args: errutil.Args{ + "email": alice.Email, + }, + } assert.Equal(t, wantErr, err) }) @@ -495,3 +512,18 @@ func usersUseCustomAvatar(t *testing.T, db *users) { require.NoError(t, err) assert.True(t, alice.UseCustomAvatar) } + +func TestIsUsernameAllowed(t *testing.T) { + for name := range reservedUsernames { + t.Run(name, func(t *testing.T) { + assert.True(t, IsErrNameNotAllowed(isUsernameAllowed(name))) + }) + } + + for _, pattern := range reservedUsernamePatterns { + t.Run(pattern, func(t *testing.T) { + username := strings.ReplaceAll(pattern, "*", "alice") + assert.True(t, IsErrNameNotAllowed(isUsernameAllowed(username))) + }) + } +} |