Ver código fonte

refactor(db): migrate methods off `user.go` (#7230)

Joe Chen 2 anos atrás
pai
commit
b5d47b9692

+ 14 - 9
internal/cmd/admin.go

@@ -5,6 +5,7 @@
 package cmd
 package cmd
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"reflect"
 	"reflect"
 	"runtime"
 	"runtime"
@@ -151,17 +152,21 @@ func runCreateUser(c *cli.Context) error {
 		return errors.Wrap(err, "set engine")
 		return errors.Wrap(err, "set engine")
 	}
 	}
 
 
-	if err := db.CreateUser(&db.User{
-		Name:     c.String("name"),
-		Email:    c.String("email"),
-		Password: c.String("password"),
-		IsActive: true,
-		IsAdmin:  c.Bool("admin"),
-	}); err != nil {
-		return fmt.Errorf("CreateUser: %v", err)
+	user, err := db.Users.Create(
+		context.Background(),
+		c.String("name"),
+		c.String("email"),
+		db.CreateUserOptions{
+			Password:  c.String("password"),
+			Activated: true,
+			Admin:     c.Bool("admin"),
+		},
+	)
+	if err != nil {
+		return errors.Wrap(err, "create user")
 	}
 	}
 
 
-	fmt.Printf("New user '%s' has been successfully created!\n", c.String("name"))
+	fmt.Printf("New user %q has been successfully created!\n", user.Name)
 	return nil
 	return nil
 }
 }
 
 

+ 12 - 13
internal/context/auth.go

@@ -175,7 +175,7 @@ func authenticatedUser(ctx *macaron.Context, sess session.Store) (_ *db.User, is
 		if conf.Auth.EnableReverseProxyAuthentication {
 		if conf.Auth.EnableReverseProxyAuthentication {
 			webAuthUser := ctx.Req.Header.Get(conf.Auth.ReverseProxyAuthenticationHeader)
 			webAuthUser := ctx.Req.Header.Get(conf.Auth.ReverseProxyAuthenticationHeader)
 			if len(webAuthUser) > 0 {
 			if len(webAuthUser) > 0 {
-				u, err := db.GetUserByName(webAuthUser)
+				user, err := db.GetUserByName(webAuthUser)
 				if err != nil {
 				if err != nil {
 					if !db.IsErrUserNotExist(err) {
 					if !db.IsErrUserNotExist(err) {
 						log.Error("Failed to get user by name: %v", err)
 						log.Error("Failed to get user by name: %v", err)
@@ -184,22 +184,21 @@ func authenticatedUser(ctx *macaron.Context, sess session.Store) (_ *db.User, is
 
 
 					// Check if enabled auto-registration.
 					// Check if enabled auto-registration.
 					if conf.Auth.EnableReverseProxyAutoRegistration {
 					if conf.Auth.EnableReverseProxyAutoRegistration {
-						u := &db.User{
-							Name:     webAuthUser,
-							Email:    gouuid.NewV4().String() + "@localhost",
-							Password: webAuthUser,
-							IsActive: true,
-						}
-						if err = db.CreateUser(u); err != nil {
-							// FIXME: should I create a system notice?
-							log.Error("Failed to create user: %v", err)
+						user, err = db.Users.Create(
+							ctx.Req.Context(),
+							webAuthUser,
+							gouuid.NewV4().String()+"@localhost",
+							db.CreateUserOptions{
+								Activated: true,
+							},
+						)
+						if err != nil {
+							log.Error("Failed to create user %q: %v", webAuthUser, err)
 							return nil, false, false
 							return nil, false, false
-						} else {
-							return u, false, false
 						}
 						}
 					}
 					}
 				}
 				}
-				return u, false, false
+				return user, false, false
 			}
 			}
 		}
 		}
 
 

+ 3 - 1
internal/db/org_team.go

@@ -245,7 +245,9 @@ func (t *Team) RemoveRepository(repoID int64) error {
 	return sess.Commit()
 	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.
 // IsUsableTeamName return an error if given name is a reserved name or pattern.
 func IsUsableTeamName(name string) error {
 func IsUsableTeamName(name string) error {

+ 8 - 2
internal/db/repo.go

@@ -1081,8 +1081,14 @@ func initRepository(e Engine, repoPath string, doer *User, repo *Repository, opt
 }
 }
 
 
 var (
 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.
 // isRepoNameAllowed return an error if given name is a reserved name or pattern for repositories.

+ 0 - 110
internal/db/user.go

@@ -13,7 +13,6 @@ import (
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
 	"time"
 	"time"
-	"unicode/utf8"
 
 
 	"github.com/unknwon/com"
 	"github.com/unknwon/com"
 	log "unknwon.dev/clog/v2"
 	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))
 	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 {
 func countUsers(e Engine) int64 {
 	count, _ := e.Where("type=0").Count(new(User))
 	count, _ := e.Where("type=0").Count(new(User))
 	return count
 	return count

+ 128 - 13
internal/db/users.go

@@ -10,6 +10,7 @@ import (
 	"os"
 	"os"
 	"strings"
 	"strings"
 	"time"
 	"time"
+	"unicode/utf8"
 
 
 	"github.com/go-macaron/binding"
 	"github.com/go-macaron/binding"
 	api "github.com/gogs/go-gogs-client"
 	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) {
 	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)
 	_, err = db.GetByEmail(ctx, email)
 	if err == nil {
 	if err == nil {
-		return nil, ErrEmailAlreadyUsed{args: errutil.Args{"email": email}}
+		return nil, ErrEmailAlreadyUsed{
+			args: errutil.Args{
+				"email": email,
+			},
+		}
 	} else if !IsErrUserNotExist(err) {
 	} else if !IsErrUserNotExist(err) {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -257,7 +266,7 @@ func (db *users) Create(ctx context.Context, username, email string, opts Create
 		MaxRepoCreation: -1,
 		MaxRepoCreation: -1,
 		IsActive:        opts.Activated,
 		IsActive:        opts.Activated,
 		IsAdmin:         opts.Admin,
 		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,
 		AvatarEmail:     email,
 	}
 	}
 
 
@@ -579,16 +588,6 @@ func (u *User) DisplayName() string {
 	return u.Name
 	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.
 // 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
 // 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 {
 func (u *User) ShortName(length int) string {
 	return strutil.Ellipsis(u.Name, length)
 	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)
+}

+ 36 - 4
internal/db/users_test.go

@@ -8,6 +8,7 @@ import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
 	"os"
 	"os"
+	"strings"
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
@@ -211,7 +212,10 @@ func usersAuthenticate(t *testing.T, db *users) {
 func usersCreate(t *testing.T, db *users) {
 func usersCreate(t *testing.T, db *users) {
 	ctx := context.Background()
 	ctx := context.Background()
 
 
-	alice, err := db.Create(ctx, "alice", "[email protected]",
+	alice, err := db.Create(
+		ctx,
+		"alice",
+		"[email protected]",
 		CreateUserOptions{
 		CreateUserOptions{
 			Activated: true,
 			Activated: true,
 		},
 		},
@@ -220,19 +224,32 @@ func usersCreate(t *testing.T, db *users) {
 
 
 	t.Run("name not allowed", func(t *testing.T) {
 	t.Run("name not allowed", func(t *testing.T) {
 		_, err := db.Create(ctx, "-", "", CreateUserOptions{})
 		_, 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)
 		assert.Equal(t, wantErr, err)
 	})
 	})
 
 
 	t.Run("name already exists", func(t *testing.T) {
 	t.Run("name already exists", func(t *testing.T) {
 		_, err := db.Create(ctx, alice.Name, "", CreateUserOptions{})
 		_, 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)
 		assert.Equal(t, wantErr, err)
 	})
 	})
 
 
 	t.Run("email already exists", func(t *testing.T) {
 	t.Run("email already exists", func(t *testing.T) {
 		_, err := db.Create(ctx, "bob", alice.Email, CreateUserOptions{})
 		_, 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)
 		assert.Equal(t, wantErr, err)
 	})
 	})
 
 
@@ -495,3 +512,18 @@ func usersUseCustomAvatar(t *testing.T, db *users) {
 	require.NoError(t, err)
 	require.NoError(t, err)
 	assert.True(t, alice.UseCustomAvatar)
 	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)))
+		})
+	}
+}

+ 12 - 13
internal/route/admin/users.go

@@ -5,6 +5,7 @@
 package admin
 package admin
 
 
 import (
 import (
+	"strconv"
 	"strings"
 	"strings"
 
 
 	"github.com/unknwon/com"
 	"github.com/unknwon/com"
@@ -77,22 +78,20 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
 		return
 		return
 	}
 	}
 
 
-	u := &db.User{
-		Name:     f.UserName,
-		Email:    f.Email,
-		Password: f.Password,
-		IsActive: true,
+	createUserOpts := db.CreateUserOptions{
+		Password:  f.Password,
+		Activated: true,
 	}
 	}
-
 	if len(f.LoginType) > 0 {
 	if len(f.LoginType) > 0 {
 		fields := strings.Split(f.LoginType, "-")
 		fields := strings.Split(f.LoginType, "-")
 		if len(fields) == 2 {
 		if len(fields) == 2 {
-			u.LoginSource = com.StrTo(fields[1]).MustInt64()
-			u.LoginName = f.LoginName
+			createUserOpts.LoginSource, _ = strconv.ParseInt(fields[1], 10, 64)
+			createUserOpts.LoginName = f.LoginName
 		}
 		}
 	}
 	}
 
 
-	if err := db.CreateUser(u); err != nil {
+	user, err := db.Users.Create(c.Req.Context(), f.UserName, f.Email, createUserOpts)
+	if err != nil {
 		switch {
 		switch {
 		case db.IsErrUserAlreadyExist(err):
 		case db.IsErrUserAlreadyExist(err):
 			c.Data["Err_UserName"] = true
 			c.Data["Err_UserName"] = true
@@ -108,15 +107,15 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
 		}
 		}
 		return
 		return
 	}
 	}
-	log.Trace("Account created by admin (%s): %s", c.User.Name, u.Name)
+	log.Trace("Account %q created by admin %q", user.Name, c.User.Name)
 
 
 	// Send email notification.
 	// Send email notification.
 	if f.SendNotify && conf.Email.Enabled {
 	if f.SendNotify && conf.Email.Enabled {
-		email.SendRegisterNotifyMail(c.Context, db.NewMailerUser(u))
+		email.SendRegisterNotifyMail(c.Context, db.NewMailerUser(user))
 	}
 	}
 
 
-	c.Flash.Success(c.Tr("admin.users.new_success", u.Name))
-	c.Redirect(conf.Server.Subpath + "/admin/users/" + com.ToStr(u.ID))
+	c.Flash.Success(c.Tr("admin.users.new_success", user.Name))
+	c.Redirect(conf.Server.Subpath + "/admin/users/" + strconv.FormatInt(user.ID, 10))
 }
 }
 
 
 func prepareUserInfo(c *context.Context) *db.User {
 func prepareUserInfo(c *context.Context) *db.User {

+ 21 - 19
internal/route/api/v1/admin/user.go

@@ -18,12 +18,12 @@ import (
 	"gogs.io/gogs/internal/userutil"
 	"gogs.io/gogs/internal/userutil"
 )
 )
 
 
-func parseLoginSource(c *context.APIContext, u *db.User, sourceID int64, loginName string) {
+func parseLoginSource(c *context.APIContext, sourceID int64) {
 	if sourceID == 0 {
 	if sourceID == 0 {
 		return
 		return
 	}
 	}
 
 
-	source, err := db.LoginSources.GetByID(c.Req.Context(), sourceID)
+	_, err := db.LoginSources.GetByID(c.Req.Context(), sourceID)
 	if err != nil {
 	if err != nil {
 		if db.IsErrLoginSourceNotExist(err) {
 		if db.IsErrLoginSourceNotExist(err) {
 			c.ErrorStatus(http.StatusUnprocessableEntity, err)
 			c.ErrorStatus(http.StatusUnprocessableEntity, err)
@@ -32,26 +32,27 @@ func parseLoginSource(c *context.APIContext, u *db.User, sourceID int64, loginNa
 		}
 		}
 		return
 		return
 	}
 	}
-
-	u.LoginSource = source.ID
-	u.LoginName = loginName
 }
 }
 
 
 func CreateUser(c *context.APIContext, form api.CreateUserOption) {
 func CreateUser(c *context.APIContext, form api.CreateUserOption) {
-	u := &db.User{
-		Name:     form.Username,
-		FullName: form.FullName,
-		Email:    form.Email,
-		Password: form.Password,
-		IsActive: true,
-	}
-
-	parseLoginSource(c, u, form.SourceID, form.LoginName)
+	parseLoginSource(c, form.SourceID)
 	if c.Written() {
 	if c.Written() {
 		return
 		return
 	}
 	}
 
 
-	if err := db.CreateUser(u); err != nil {
+	user, err := db.Users.Create(
+		c.Req.Context(),
+		form.Username,
+		form.Email,
+		db.CreateUserOptions{
+			FullName:    form.FullName,
+			Password:    form.Password,
+			LoginSource: form.SourceID,
+			LoginName:   form.LoginName,
+			Activated:   true,
+		},
+	)
+	if err != nil {
 		if db.IsErrUserAlreadyExist(err) ||
 		if db.IsErrUserAlreadyExist(err) ||
 			db.IsErrEmailAlreadyUsed(err) ||
 			db.IsErrEmailAlreadyUsed(err) ||
 			db.IsErrNameNotAllowed(err) {
 			db.IsErrNameNotAllowed(err) {
@@ -61,14 +62,14 @@ func CreateUser(c *context.APIContext, form api.CreateUserOption) {
 		}
 		}
 		return
 		return
 	}
 	}
-	log.Trace("Account created by admin %q: %s", c.User.Name, u.Name)
+	log.Trace("Account %q created by admin %q", user.Name, c.User.Name)
 
 
 	// Send email notification.
 	// Send email notification.
 	if form.SendNotify && conf.Email.Enabled {
 	if form.SendNotify && conf.Email.Enabled {
-		email.SendRegisterNotifyMail(c.Context.Context, db.NewMailerUser(u))
+		email.SendRegisterNotifyMail(c.Context.Context, db.NewMailerUser(user))
 	}
 	}
 
 
-	c.JSON(http.StatusCreated, u.APIFormat())
+	c.JSON(http.StatusCreated, user.APIFormat())
 }
 }
 
 
 func EditUser(c *context.APIContext, form api.EditUserOption) {
 func EditUser(c *context.APIContext, form api.EditUserOption) {
@@ -77,7 +78,7 @@ func EditUser(c *context.APIContext, form api.EditUserOption) {
 		return
 		return
 	}
 	}
 
 
-	parseLoginSource(c, u, form.SourceID, form.LoginName)
+	parseLoginSource(c, form.SourceID)
 	if c.Written() {
 	if c.Written() {
 		return
 		return
 	}
 	}
@@ -92,6 +93,7 @@ func EditUser(c *context.APIContext, form api.EditUserOption) {
 		u.Password = userutil.EncodePassword(u.Password, u.Salt)
 		u.Password = userutil.EncodePassword(u.Password, u.Salt)
 	}
 	}
 
 
+	u.LoginSource = form.SourceID
 	u.LoginName = form.LoginName
 	u.LoginName = form.LoginName
 	u.FullName = form.FullName
 	u.FullName = form.FullName
 	u.Email = form.Email
 	u.Email = form.Email

+ 19 - 11
internal/route/install.go

@@ -387,27 +387,35 @@ func InstallPost(c *context.Context, f form.Install) {
 
 
 	// Create admin account
 	// Create admin account
 	if len(f.AdminName) > 0 {
 	if len(f.AdminName) > 0 {
-		u := &db.User{
-			Name:     f.AdminName,
-			Email:    f.AdminEmail,
-			Password: f.AdminPasswd,
-			IsAdmin:  true,
-			IsActive: true,
-		}
-		if err := db.CreateUser(u); err != nil {
+		user, err := db.Users.Create(
+			c.Req.Context(),
+			f.AdminName,
+			f.AdminEmail,
+			db.CreateUserOptions{
+				Password:  f.AdminPasswd,
+				Activated: true,
+				Admin:     true,
+			},
+		)
+		if err != nil {
 			if !db.IsErrUserAlreadyExist(err) {
 			if !db.IsErrUserAlreadyExist(err) {
 				conf.Security.InstallLock = false
 				conf.Security.InstallLock = false
 				c.FormErr("AdminName", "AdminEmail")
 				c.FormErr("AdminName", "AdminEmail")
 				c.RenderWithErr(c.Tr("install.invalid_admin_setting", err), INSTALL, &f)
 				c.RenderWithErr(c.Tr("install.invalid_admin_setting", err), INSTALL, &f)
 				return
 				return
 			}
 			}
+
 			log.Info("Admin account already exist")
 			log.Info("Admin account already exist")
-			u, _ = db.GetUserByName(u.Name)
+			user, err = db.Users.GetByUsername(c.Req.Context(), f.AdminName)
+			if err != nil {
+				c.Error(err, "get user by name")
+				return
+			}
 		}
 		}
 
 
 		// Auto-login for admin
 		// Auto-login for admin
-		_ = c.Session.Set("uid", u.ID)
-		_ = c.Session.Set("uname", u.Name)
+		_ = c.Session.Set("uid", user.ID)
+		_ = c.Session.Set("uname", user.Name)
 	}
 	}
 
 
 	log.Info("First-time run install finished!")
 	log.Info("First-time run install finished!")

+ 20 - 16
internal/route/user/auth.go

@@ -6,6 +6,7 @@ package user
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"net/http"
 	"net/url"
 	"net/url"
 
 
 	"github.com/go-macaron/captcha"
 	"github.com/go-macaron/captcha"
@@ -311,7 +312,7 @@ func SignUpPost(c *context.Context, cpt *captcha.Captcha, f form.Register) {
 	c.Data["EnableCaptcha"] = conf.Auth.EnableRegistrationCaptcha
 	c.Data["EnableCaptcha"] = conf.Auth.EnableRegistrationCaptcha
 
 
 	if conf.Auth.DisableRegistration {
 	if conf.Auth.DisableRegistration {
-		c.Status(403)
+		c.Status(http.StatusForbidden)
 		return
 		return
 	}
 	}
 
 
@@ -332,13 +333,16 @@ func SignUpPost(c *context.Context, cpt *captcha.Captcha, f form.Register) {
 		return
 		return
 	}
 	}
 
 
-	u := &db.User{
-		Name:     f.UserName,
-		Email:    f.Email,
-		Password: f.Password,
-		IsActive: !conf.Auth.RequireEmailConfirmation,
-	}
-	if err := db.CreateUser(u); err != nil {
+	user, err := db.Users.Create(
+		c.Req.Context(),
+		f.UserName,
+		f.Email,
+		db.CreateUserOptions{
+			Password:  f.Password,
+			Activated: !conf.Auth.RequireEmailConfirmation,
+		},
+	)
+	if err != nil {
 		switch {
 		switch {
 		case db.IsErrUserAlreadyExist(err):
 		case db.IsErrUserAlreadyExist(err):
 			c.FormErr("UserName")
 			c.FormErr("UserName")
@@ -354,27 +358,27 @@ func SignUpPost(c *context.Context, cpt *captcha.Captcha, f form.Register) {
 		}
 		}
 		return
 		return
 	}
 	}
-	log.Trace("Account created: %s", u.Name)
+	log.Trace("Account created: %s", user.Name)
 
 
 	// Auto-set admin for the only user.
 	// Auto-set admin for the only user.
 	if db.CountUsers() == 1 {
 	if db.CountUsers() == 1 {
-		u.IsAdmin = true
-		u.IsActive = true
-		if err := db.UpdateUser(u); err != nil {
+		user.IsAdmin = true
+		user.IsActive = true
+		if err := db.UpdateUser(user); err != nil {
 			c.Error(err, "update user")
 			c.Error(err, "update user")
 			return
 			return
 		}
 		}
 	}
 	}
 
 
 	// Send confirmation email.
 	// Send confirmation email.
-	if conf.Auth.RequireEmailConfirmation && u.ID > 1 {
-		email.SendActivateAccountMail(c.Context, db.NewMailerUser(u))
+	if conf.Auth.RequireEmailConfirmation && user.ID > 1 {
+		email.SendActivateAccountMail(c.Context, db.NewMailerUser(user))
 		c.Data["IsSendRegisterMail"] = true
 		c.Data["IsSendRegisterMail"] = true
-		c.Data["Email"] = u.Email
+		c.Data["Email"] = user.Email
 		c.Data["Hours"] = conf.Auth.ActivateCodeLives / 60
 		c.Data["Hours"] = conf.Auth.ActivateCodeLives / 60
 		c.Success(ACTIVATE)
 		c.Success(ACTIVATE)
 
 
-		if err := c.Cache.Put(userutil.MailResendCacheKey(u.ID), 1, 180); err != nil {
+		if err := c.Cache.Put(userutil.MailResendCacheKey(user.ID), 1, 180); err != nil {
 			log.Error("Failed to put cache key 'mail resend': %v", err)
 			log.Error("Failed to put cache key 'mail resend': %v", err)
 		}
 		}
 		return
 		return