db: add tests for users (#6116)

* Add new methods

* Use Users.Create to replace previous hack

* Reduce side effect

* Do not clear tables when test failed

* test_users_Authenticate

* Rename constant

* test_users_Create

* test_users_GetByEmail

* test_users_GetByID

* test_users_GetByUsername
pull/6119/head
ᴜɴᴋɴᴡᴏɴ 2020-04-18 12:07:30 +08:00 committed by GitHub
parent fa497b1633
commit 9d64d222a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 506 additions and 112 deletions

View File

@ -38,7 +38,7 @@ func Test_accessTokens(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
err := clearTables(db.DB, tables...)
err := clearTables(t, db.DB, tables...)
if err != nil {
t.Fatal(err)
}

View File

@ -15,32 +15,6 @@ import (
// |______//____ >\___ >__|
// \/ \/
type ErrUserAlreadyExist struct {
Name string
}
func IsErrUserAlreadyExist(err error) bool {
_, ok := err.(ErrUserAlreadyExist)
return ok
}
func (err ErrUserAlreadyExist) Error() string {
return fmt.Sprintf("user already exists [name: %s]", err.Name)
}
type ErrEmailAlreadyUsed struct {
Email string
}
func IsErrEmailAlreadyUsed(err error) bool {
_, ok := err.(ErrEmailAlreadyUsed)
return ok
}
func (err ErrEmailAlreadyUsed) Error() string {
return fmt.Sprintf("e-mail has been used [email: %s]", err.Email)
}
type ErrUserOwnRepos struct {
UID int64
}

View File

@ -37,7 +37,7 @@ func Test_lfs(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
err := clearTables(db.DB, tables...)
err := clearTables(t, db.DB, tables...)
if err != nil {
t.Fatal(err)
}

View File

@ -40,7 +40,7 @@ func Test_loginSources(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
err := clearTables(db.DB, tables...)
err := clearTables(t, db.DB, tables...)
if err != nil {
t.Fatal(err)
}
@ -119,10 +119,10 @@ func test_loginSources_DeleteByID(t *testing.T, db *loginSources) {
}
// Create a user that uses this login source
user := &User{
_, err = (&users{DB: db.DB}).Create(CreateUserOpts{
Name: "alice",
LoginSource: source.ID,
}
err = db.DB.Create(user).Error
})
if err != nil {
t.Fatal(err)
}

View File

@ -42,7 +42,11 @@ func TestMain(m *testing.M) {
}
// clearTables removes all rows from given tables.
func clearTables(db *gorm.DB, tables ...interface{}) error {
func clearTables(t *testing.T, db *gorm.DB, tables ...interface{}) error {
if t.Failed() {
return nil
}
for _, t := range tables {
err := db.Delete(t).Error
if err != nil {

View File

@ -209,6 +209,8 @@ var _ UsersStore = (*MockUsersStore)(nil)
type MockUsersStore struct {
MockAuthenticate func(username, password string, loginSourceID int64) (*User, error)
MockCreate func(opts CreateUserOpts) (*User, error)
MockGetByEmail func(email string) (*User, error)
MockGetByID func(id int64) (*User, error)
MockGetByUsername func(username string) (*User, error)
}
@ -217,6 +219,14 @@ func (m *MockUsersStore) Authenticate(username, password string, loginSourceID i
return m.MockAuthenticate(username, password, loginSourceID)
}
func (m *MockUsersStore) Create(opts CreateUserOpts) (*User, error) {
return m.MockCreate(opts)
}
func (m *MockUsersStore) GetByEmail(email string) (*User, error) {
return m.MockGetByEmail(email)
}
func (m *MockUsersStore) GetByID(id int64) (*User, error) {
return m.MockGetByID(id)
}

View File

@ -12,6 +12,8 @@ import (
"xorm.io/builder"
"xorm.io/xorm"
"gogs.io/gogs/internal/errutil"
)
var (
@ -99,7 +101,7 @@ func (org *User) RemoveOrgRepo(repoID int64) error {
// CreateOrganization creates record of a new organization.
func CreateOrganization(org, owner *User) (err error) {
if err = IsUsableUsername(org.Name); err != nil {
if err = isUsernameAllowed(org.Name); err != nil {
return err
}
@ -107,7 +109,7 @@ func CreateOrganization(org, owner *User) (err error) {
if err != nil {
return err
} else if isExist {
return ErrUserAlreadyExist{org.Name}
return ErrUserAlreadyExist{args: errutil.Args{"name": org.Name}}
}
org.LowerName = strings.ToLower(org.Name)
@ -177,7 +179,7 @@ func GetOrgByName(name string) (*User, error) {
}
u := &User{
LowerName: strings.ToLower(name),
Type: USER_TYPE_ORGANIZATION,
Type: UserOrganization,
}
has, err := x.Get(u)
if err != nil {

View File

@ -32,7 +32,7 @@ func Test_perms(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
err := clearTables(db.DB, tables...)
err := clearTables(t, db.DB, tables...)
if err != nil {
t.Fatal(err)
}

View File

@ -75,7 +75,7 @@ type createRepoOpts struct {
// create creates a new repository record in the database. Fields of "repo" will be updated
// in place upon insertion. It returns ErrNameNotAllowed when the repository name is not allowed,
// or returns ErrRepoAlreadyExist when a repository with same name already exists for the owner.
// or ErrRepoAlreadyExist when a repository with same name already exists for the owner.
func (db *repos) create(ownerID int64, opts createRepoOpts) (*Repository, error) {
err := isRepoNameAllowed(opts.Name)
if err != nil {

View File

@ -35,7 +35,7 @@ func Test_repos(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
err := clearTables(db.DB, tables...)
err := clearTables(t, db.DB, tables...)
if err != nil {
t.Fatal(err)
}

View File

@ -36,7 +36,7 @@ func Test_twoFactors(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
err := clearTables(db.DB, tables...)
err := clearTables(t, db.DB, tables...)
if err != nil {
t.Fatal(err)
}

View File

@ -42,8 +42,8 @@ const USER_AVATAR_URL_PREFIX = "avatars"
type UserType int
const (
USER_TYPE_INDIVIDUAL UserType = iota // Historic reason to make it starts at 0.
USER_TYPE_ORGANIZATION
UserIndividual UserType = iota // Historic reason to make it starts at 0.
UserOrganization
)
// User represents the object of individual and member of organization.
@ -53,10 +53,10 @@ type User struct {
Name string `xorm:"UNIQUE NOT NULL" gorm:"NOT NULL"`
FullName string
// Email is the primary email address (to be used for communication)
Email string `xorm:"NOT NULL" gorm:"NOT NULL"`
Passwd string `xorm:"NOT NULL" gorm:"NOT NULL"`
LoginType LoginType
LoginSource int64 `xorm:"NOT NULL DEFAULT 0" gorm:"NOT NULL;DEFAULT:0"`
Email string `xorm:"NOT NULL" gorm:"NOT NULL"`
Passwd string `xorm:"NOT NULL" gorm:"NOT NULL"`
LoginType LoginType // TODO: Remove me https://github.com/gogs/gogs/issues/6117.
LoginSource int64 `xorm:"NOT NULL DEFAULT 0" gorm:"NOT NULL;DEFAULT:0"`
LoginName string
Type UserType
OwnedOrgs []*User `xorm:"-" gorm:"-" json:"-"`
@ -321,8 +321,8 @@ func (u *User) NewGitSig() *git.Signature {
}
}
// EncodePasswd encodes password to safe format.
func (u *User) EncodePasswd() {
// EncodePassword encodes password to safe format.
func (u *User) EncodePassword() {
newPasswd := pbkdf2.Key([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
u.Passwd = fmt.Sprintf("%x", newPasswd)
}
@ -330,7 +330,7 @@ func (u *User) EncodePasswd() {
// ValidatePassword checks if given password matches the one belongs to the user.
func (u *User) ValidatePassword(passwd string) bool {
newUser := &User{Passwd: passwd, Salt: u.Salt}
newUser.EncodePasswd()
newUser.EncodePassword()
return subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(newUser.Passwd)) == 1
}
@ -388,7 +388,7 @@ func (u *User) IsWriterOfRepo(repo *Repository) bool {
// IsOrganization returns true if user is actually a organization.
func (u *User) IsOrganization() bool {
return u.Type == USER_TYPE_ORGANIZATION
return u.Type == UserOrganization
}
// IsUserOrgOwner returns true if user is in the owner team of given organization.
@ -448,7 +448,7 @@ func (u *User) GetOrganizations(showPrivate bool) error {
}
u.Orgs = make([]*User, 0, len(orgIDs))
if err = x.Where("type = ?", USER_TYPE_ORGANIZATION).In("id", orgIDs).Find(&u.Orgs); err != nil {
if err = x.Where("type = ?", UserOrganization).In("id", orgIDs).Find(&u.Orgs); err != nil {
return err
}
return nil
@ -555,13 +555,15 @@ func isNameAllowed(names, patterns []string, name string) error {
return nil
}
func IsUsableUsername(name string) error {
// 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 = IsUsableUsername(u.Name); err != nil {
if err = isUsernameAllowed(u.Name); err != nil {
return err
}
@ -569,7 +571,7 @@ func CreateUser(u *User) (err error) {
if err != nil {
return err
} else if isExist {
return ErrUserAlreadyExist{u.Name}
return ErrUserAlreadyExist{args: errutil.Args{"name": u.Name}}
}
u.Email = strings.ToLower(u.Email)
@ -577,7 +579,7 @@ func CreateUser(u *User) (err error) {
if err != nil {
return err
} else if isExist {
return ErrEmailAlreadyUsed{u.Email}
return ErrEmailAlreadyUsed{args: errutil.Args{"email": u.Email}}
}
u.LowerName = strings.ToLower(u.Name)
@ -589,7 +591,7 @@ func CreateUser(u *User) (err error) {
if u.Salt, err = GetUserSalt(); err != nil {
return err
}
u.EncodePasswd()
u.EncodePassword()
u.MaxRepoCreation = -1
sess := x.NewSession()
@ -680,7 +682,7 @@ func VerifyActiveEmailCode(code, email string) *EmailAddress {
// ChangeUserName changes all corresponding setting from old user name to new one.
func ChangeUserName(u *User, newUserName string) (err error) {
if err = IsUsableUsername(newUserName); err != nil {
if err = isUsernameAllowed(newUserName); err != nil {
return err
}
@ -688,7 +690,7 @@ func ChangeUserName(u *User, newUserName string) (err error) {
if err != nil {
return err
} else if isExist {
return ErrUserAlreadyExist{newUserName}
return ErrUserAlreadyExist{args: errutil.Args{"name": newUserName}}
}
if err = ChangeUsernameInPullRequests(u.Name, newUserName); err != nil {
@ -723,7 +725,7 @@ func updateUser(e Engine, u *User) error {
if err != nil {
return err
} else if has {
return ErrEmailAlreadyUsed{u.Email}
return ErrEmailAlreadyUsed{args: errutil.Args{"email": u.Email}}
}
if len(u.AvatarEmail) == 0 {
@ -904,8 +906,8 @@ func DeleteInactivateUsers() (err error) {
}
// UserPath returns the path absolute path of user repositories.
func UserPath(userName string) string {
return filepath.Join(conf.Repository.Root, strings.ToLower(userName))
func UserPath(username string) string {
return filepath.Join(conf.Repository.Root, strings.ToLower(username))
}
func GetUserByKeyID(keyID int64) (*User, error) {
@ -919,25 +921,6 @@ func GetUserByKeyID(keyID int64) (*User, error) {
return user, nil
}
var _ errutil.NotFound = (*ErrUserNotExist)(nil)
type ErrUserNotExist struct {
args map[string]interface{}
}
func IsErrUserNotExist(err error) bool {
_, ok := err.(ErrUserNotExist)
return ok
}
func (err ErrUserNotExist) Error() string {
return fmt.Sprintf("user does not exist: %v", err.args)
}
func (ErrUserNotExist) NotFound() bool {
return true
}
func getUserByID(e Engine, id int64) (*User, error) {
u := new(User)
has, err := e.ID(id).Get(u)
@ -1047,6 +1030,7 @@ func ValidateCommitsWithEmails(oldCommits []*git.Commit) []*UserCommit {
}
// GetUserByEmail returns the user object by given e-mail if exists.
// Deprecated: Use Users.GetByEmail instead.
func GetUserByEmail(email string) (*User, error) {
if len(email) == 0 {
return nil, ErrUserNotExist{args: map[string]interface{}{"email": email}}

View File

@ -9,16 +9,17 @@ import (
"strings"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/errutil"
)
// EmailAdresses is the list of all email addresses of a user. Can contain the
// EmailAddresses is the list of all email addresses of a user. Can contain the
// primary email address, but is not obligatory.
type EmailAddress struct {
ID int64
UID int64 `xorm:"INDEX NOT NULL"`
Email string `xorm:"UNIQUE NOT NULL"`
IsActivated bool
IsPrimary bool `xorm:"-" json:"-"`
UID int64 `xorm:"INDEX NOT NULL" gorm:"INDEX"`
Email string `xorm:"UNIQUE NOT NULL" gorm:"UNIQUE"`
IsActivated bool `gorm:"NOT NULL;DEFAULT:FALSE"`
IsPrimary bool `xorm:"-" gorm:"-" json:"-"`
}
// GetEmailAddresses returns all email addresses belongs to given user.
@ -68,7 +69,7 @@ func isEmailUsed(e Engine, email string) (bool, error) {
}
// We need to check primary email of users as well.
return e.Where("type=?", USER_TYPE_INDIVIDUAL).And("email=?", email).Get(new(User))
return e.Where("type=?", UserIndividual).And("email=?", email).Get(new(User))
}
// IsEmailUsed returns true if the email has been used.
@ -82,7 +83,7 @@ func addEmailAddress(e Engine, email *EmailAddress) error {
if err != nil {
return err
} else if used {
return ErrEmailAlreadyUsed{email.Email}
return ErrEmailAlreadyUsed{args: errutil.Args{"email": email.Email}}
}
_, err = e.Insert(email)
@ -105,7 +106,7 @@ func AddEmailAddresses(emails []*EmailAddress) error {
if err != nil {
return err
} else if used {
return ErrEmailAlreadyUsed{emails[i].Email}
return ErrEmailAlreadyUsed{args: errutil.Args{"email": emails[i].Email}}
}
}

View File

@ -7,10 +7,12 @@ package db
import (
"fmt"
"strings"
"time"
"github.com/jinzhu/gorm"
"github.com/pkg/errors"
"gogs.io/gogs/internal/cryptoutil"
"gogs.io/gogs/internal/errutil"
)
@ -30,15 +32,35 @@ type UsersStore interface {
// When the "loginSourceID" is positive, it tries to authenticate via given
// login source and creates a new user when not yet exists in the database.
Authenticate(username, password string, loginSourceID int64) (*User, error)
// Create creates a new user and persist to database.
// It returns ErrUserAlreadyExist when a user with same name already exists,
// or ErrEmailAlreadyUsed if the email has been used by another user.
Create(opts CreateUserOpts) (*User, error)
// GetByEmail returns the user (not organization) with given email.
// It ignores records with unverified emails and returns ErrUserNotExist when not found.
GetByEmail(email string) (*User, error)
// GetByID returns the user with given ID. It returns ErrUserNotExist when not found.
GetByID(id int64) (*User, error)
// GetByUsername returns the user with given username. It returns ErrUserNotExist
// when not found.
// GetByUsername returns the user with given username. It returns ErrUserNotExist when not found.
GetByUsername(username string) (*User, error)
}
var Users UsersStore
// NOTE: This is a GORM create hook.
func (u *User) BeforeCreate() {
u.CreatedUnix = gorm.NowFunc().Unix()
u.UpdatedUnix = u.CreatedUnix
}
// NOTE: This is a GORM query hook.
func (u *User) AfterFind() {
u.Created = time.Unix(u.CreatedUnix, 0).Local()
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
}
var _ UsersStore = (*users)(nil)
type users struct {
*gorm.DB
}
@ -51,14 +73,14 @@ func (err ErrLoginSourceMismatch) Error() string {
return fmt.Sprintf("login source mismatch: %v", err.args)
}
func (db *users) Authenticate(username, password string, loginSourceID int64) (*User, error) {
username = strings.ToLower(username)
func (db *users) Authenticate(login, password string, loginSourceID int64) (*User, error) {
login = strings.ToLower(login)
var query *gorm.DB
if strings.Contains(username, "@") {
query = db.Where("email = ?", username)
if strings.Contains(login, "@") {
query = db.Where("email = ?", login)
} else {
query = db.Where("lower_name = ?", username)
query = db.Where("lower_name = ?", login)
}
user := new(User)
@ -89,7 +111,7 @@ func (db *users) Authenticate(username, password string, loginSourceID int64) (*
return nil, errors.Wrap(err, "get login source")
}
_, err = authenticateViaLoginSource(source, username, password, false)
_, err = authenticateViaLoginSource(source, login, password, false)
if err != nil {
return nil, errors.Wrap(err, "authenticate via login source")
}
@ -98,7 +120,7 @@ func (db *users) Authenticate(username, password string, loginSourceID int64) (*
// Non-local login source is always greater than 0.
if loginSourceID <= 0 {
return nil, ErrUserNotExist{args: map[string]interface{}{"name": username}}
return nil, ErrUserNotExist{args: map[string]interface{}{"login": login}}
}
source, err := LoginSources.GetByID(loginSourceID)
@ -106,19 +128,154 @@ func (db *users) Authenticate(username, password string, loginSourceID int64) (*
return nil, errors.Wrap(err, "get login source")
}
user, err = authenticateViaLoginSource(source, username, password, true)
user, err = authenticateViaLoginSource(source, login, password, true)
if err != nil {
return nil, errors.Wrap(err, "authenticate via login source")
}
return user, nil
}
type CreateUserOpts struct {
Name string
Email string
Password string
LoginSource int64
Activated bool
}
type ErrUserAlreadyExist struct {
args errutil.Args
}
func IsErrUserAlreadyExist(err error) bool {
_, ok := err.(ErrUserAlreadyExist)
return ok
}
func (err ErrUserAlreadyExist) Error() string {
return fmt.Sprintf("user already exists: %v", err.args)
}
type ErrEmailAlreadyUsed struct {
args errutil.Args
}
func IsErrEmailAlreadyUsed(err error) bool {
_, ok := err.(ErrEmailAlreadyUsed)
return ok
}
func (err ErrEmailAlreadyUsed) Email() string {
email, ok := err.args["email"].(string)
if ok {
return email
}
return "<email not found>"
}
func (err ErrEmailAlreadyUsed) Error() string {
return fmt.Sprintf("email has been used: %v", err.args)
}
func (db *users) Create(opts CreateUserOpts) (*User, error) {
err := isUsernameAllowed(opts.Name)
if err != nil {
return nil, err
}
_, err = db.GetByUsername(opts.Name)
if err == nil {
return nil, ErrUserAlreadyExist{args: errutil.Args{"name": opts.Name}}
} else if !IsErrUserNotExist(err) {
return nil, err
}
_, err = db.GetByEmail(opts.Email)
if err == nil {
return nil, ErrEmailAlreadyUsed{args: errutil.Args{"email": opts.Email}}
} else if !IsErrUserNotExist(err) {
return nil, err
}
user := &User{
LowerName: strings.ToLower(opts.Name),
Name: opts.Name,
Email: opts.Email,
Passwd: opts.Password,
LoginSource: opts.LoginSource,
MaxRepoCreation: -1,
IsActive: opts.Activated,
Avatar: cryptoutil.MD5(opts.Email),
AvatarEmail: opts.Email,
}
user.Rands, err = GetUserSalt()
if err != nil {
return nil, err
}
user.Salt, err = GetUserSalt()
if err != nil {
return nil, err
}
user.EncodePassword()
return user, db.DB.Create(user).Error
}
var _ errutil.NotFound = (*ErrUserNotExist)(nil)
type ErrUserNotExist struct {
args errutil.Args
}
func IsErrUserNotExist(err error) bool {
_, ok := err.(ErrUserNotExist)
return ok
}
func (err ErrUserNotExist) Error() string {
return fmt.Sprintf("user does not exist: %v", err.args)
}
func (ErrUserNotExist) NotFound() bool {
return true
}
func (db *users) GetByEmail(email string) (*User, error) {
email = strings.ToLower(email)
if len(email) == 0 {
return nil, ErrUserNotExist{args: errutil.Args{"email": email}}
}
// First try to find the user by primary email
user := new(User)
err := db.Where("email = ? AND type = ? AND is_active = ?", email, UserIndividual, true).First(user).Error
if err == nil {
return user, nil
} else if !gorm.IsRecordNotFoundError(err) {
return nil, err
}
// Otherwise, check activated email addresses
emailAddress := new(EmailAddress)
err = db.Where("email = ? AND is_activated = ?", email, true).First(emailAddress).Error
if err != nil {
if gorm.IsRecordNotFoundError(err) {
return nil, ErrUserNotExist{args: errutil.Args{"email": email}}
}
return nil, err
}
return db.GetByID(emailAddress.UID)
}
func (db *users) GetByID(id int64) (*User, error) {
user := new(User)
err := db.Where("id = ?", id).First(user).Error
if err != nil {
if gorm.IsRecordNotFoundError(err) {
return nil, ErrUserNotExist{args: map[string]interface{}{"userID": id}}
return nil, ErrUserNotExist{args: errutil.Args{"userID": id}}
}
return nil, err
}
@ -130,7 +287,7 @@ func (db *users) GetByUsername(username string) (*User, error) {
err := db.Where("lower_name = ?", strings.ToLower(username)).First(user).Error
if err != nil {
if gorm.IsRecordNotFoundError(err) {
return nil, ErrUserNotExist{args: map[string]interface{}{"name": username}}
return nil, ErrUserNotExist{args: errutil.Args{"name": username}}
}
return nil, err
}

262
internal/db/users_test.go Normal file
View File

@ -0,0 +1,262 @@
// Copyright 2020 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package db
import (
"testing"
"time"
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
"gogs.io/gogs/internal/errutil"
)
func Test_users(t *testing.T) {
if testing.Short() {
t.Skip()
}
t.Parallel()
tables := []interface{}{new(User), new(EmailAddress)}
db := &users{
DB: initTestDB(t, "users", tables...),
}
for _, tc := range []struct {
name string
test func(*testing.T, *users)
}{
{"Authenticate", test_users_Authenticate},
{"Create", test_users_Create},
{"GetByEmail", test_users_GetByEmail},
{"GetByID", test_users_GetByID},
{"GetByUsername", test_users_GetByUsername},
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
err := clearTables(t, db.DB, tables...)
if err != nil {
t.Fatal(err)
}
})
tc.test(t, db)
})
}
}
// TODO: Only local account is tested, tests for external account will be added
// along with addressing https://github.com/gogs/gogs/issues/6115.
func test_users_Authenticate(t *testing.T, db *users) {
password := "pa$$word"
alice, err := db.Create(CreateUserOpts{
Name: "alice",
Email: "alice@example.com",
Password: password,
})
if err != nil {
t.Fatal(err)
}
t.Run("user not found", func(t *testing.T) {
_, err := db.Authenticate("bob", password, -1)
expErr := ErrUserNotExist{args: map[string]interface{}{"login": "bob"}}
assert.Equal(t, expErr, err)
})
t.Run("invalid password", func(t *testing.T) {
_, err := db.Authenticate(alice.Name, "bad_password", -1)
expErr := ErrUserNotExist{args: map[string]interface{}{"userID": alice.ID, "name": alice.Name}}
assert.Equal(t, expErr, err)
})
t.Run("via email and password", func(t *testing.T) {
user, err := db.Authenticate(alice.Email, password, -1)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, alice.Name, user.Name)
})
t.Run("via username and password", func(t *testing.T) {
user, err := db.Authenticate(alice.Name, password, -1)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, alice.Name, user.Name)
})
}
func test_users_Create(t *testing.T, db *users) {
alice, err := db.Create(CreateUserOpts{
Name: "alice",
Email: "alice@example.com",
Activated: true,
})
if err != nil {
t.Fatal(err)
}
t.Run("name not allowed", func(t *testing.T) {
_, err := db.Create(CreateUserOpts{
Name: "-",
})
expErr := ErrNameNotAllowed{args: errutil.Args{"reason": "reserved", "name": "-"}}
assert.Equal(t, expErr, err)
})
t.Run("name already exists", func(t *testing.T) {
_, err := db.Create(CreateUserOpts{
Name: alice.Name,
})
expErr := ErrUserAlreadyExist{args: errutil.Args{"name": alice.Name}}
assert.Equal(t, expErr, err)
})
t.Run("email already exists", func(t *testing.T) {
_, err := db.Create(CreateUserOpts{
Name: "bob",
Email: alice.Email,
})
expErr := ErrEmailAlreadyUsed{args: errutil.Args{"email": alice.Email}}
assert.Equal(t, expErr, err)
})
user, err := db.GetByUsername(alice.Name)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, gorm.NowFunc().Format(time.RFC3339), user.Created.Format(time.RFC3339))
assert.Equal(t, gorm.NowFunc().Format(time.RFC3339), user.Updated.Format(time.RFC3339))
}
func test_users_GetByEmail(t *testing.T, db *users) {
t.Run("empty email", func(t *testing.T) {
_, err := db.GetByEmail("")
expErr := ErrUserNotExist{args: errutil.Args{"email": ""}}
assert.Equal(t, expErr, err)
})
t.Run("ignore organization", func(t *testing.T) {
// TODO: Use Orgs.Create to replace SQL hack when the method is available.
org, err := db.Create(CreateUserOpts{
Name: "gogs",
Email: "gogs@exmaple.com",
})
if err != nil {
t.Fatal(err)
}
err = db.Exec(`UPDATE user SET type = ? WHERE id = ?`, UserOrganization, org.ID).Error
if err != nil {
t.Fatal(err)
}
_, err = db.GetByEmail(org.Email)
expErr := ErrUserNotExist{args: errutil.Args{"email": org.Email}}
assert.Equal(t, expErr, err)
})
t.Run("by primary email", func(t *testing.T) {
alice, err := db.Create(CreateUserOpts{
Name: "alice",
Email: "alice@exmaple.com",
})
if err != nil {
t.Fatal(err)
}
_, err = db.GetByEmail(alice.Email)
expErr := ErrUserNotExist{args: errutil.Args{"email": alice.Email}}
assert.Equal(t, expErr, err)
// Mark user as activated
// TODO: Use UserEmails.Verify to replace SQL hack when the method is available.
err = db.Exec(`UPDATE user SET is_active = ? WHERE id = ?`, true, alice.ID).Error
if err != nil {
t.Fatal(err)
}
user, err := db.GetByEmail(alice.Email)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, alice.Name, user.Name)
})
t.Run("by secondary email", func(t *testing.T) {
bob, err := db.Create(CreateUserOpts{
Name: "bob",
Email: "bob@example.com",
})
if err != nil {
t.Fatal(err)
}
// TODO: Use UserEmails.Create to replace SQL hack when the method is available.
email2 := "bob2@exmaple.com"
err = db.Exec(`INSERT INTO email_address (uid, email) VALUES (?, ?)`, bob.ID, email2).Error
if err != nil {
t.Fatal(err)
}
_, err = db.GetByEmail(email2)
expErr := ErrUserNotExist{args: errutil.Args{"email": email2}}
assert.Equal(t, expErr, err)
// TODO: Use UserEmails.Verify to replace SQL hack when the method is available.
err = db.Exec(`UPDATE email_address SET is_activated = ? WHERE email = ?`, true, email2).Error
if err != nil {
t.Fatal(err)
}
user, err := db.GetByEmail(email2)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, bob.Name, user.Name)
})
}
func test_users_GetByID(t *testing.T, db *users) {
alice, err := db.Create(CreateUserOpts{
Name: "alice",
Email: "alice@exmaple.com",
})
if err != nil {
t.Fatal(err)
}
user, err := db.GetByID(alice.ID)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, alice.Name, user.Name)
_, err = db.GetByID(404)
expErr := ErrUserNotExist{args: errutil.Args{"userID": int64(404)}}
assert.Equal(t, expErr, err)
}
func test_users_GetByUsername(t *testing.T, db *users) {
alice, err := db.Create(CreateUserOpts{
Name: "alice",
Email: "alice@exmaple.com",
})
if err != nil {
t.Fatal(err)
}
user, err := db.GetByUsername(alice.Name)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, alice.Name, user.Name)
_, err = db.GetByUsername("bad_username")
expErr := ErrUserNotExist{args: errutil.Args{"name": "bad_username"}}
assert.Equal(t, expErr, err)
}

View File

@ -21,7 +21,7 @@ func Organizations(c *context.Context) {
c.Data["PageIsAdminOrganizations"] = true
route.RenderUserSearch(c, &route.UserSearchOptions{
Type: db.USER_TYPE_ORGANIZATION,
Type: db.UserOrganization,
Counter: db.CountOrganizations,
Ranger: db.Organizations,
PageSize: conf.UI.Admin.OrgPagingNum,

View File

@ -30,7 +30,7 @@ func Users(c *context.Context) {
c.Data["PageIsAdminUsers"] = true
route.RenderUserSearch(c, &route.UserSearchOptions{
Type: db.USER_TYPE_INDIVIDUAL,
Type: db.UserIndividual,
Counter: db.CountUsers,
Ranger: db.ListUsers,
PageSize: conf.UI.Admin.UserPagingNum,
@ -196,7 +196,7 @@ func EditUserPost(c *context.Context, f form.AdminEditUser) {
c.Error(err, "get user salt")
return
}
u.EncodePasswd()
u.EncodePassword()
}
u.LoginName = f.LoginName

View File

@ -90,7 +90,7 @@ func EditUser(c *context.APIContext, form api.EditUserOption) {
c.Error(err, "get user salt")
return
}
u.EncodePasswd()
u.EncodePassword()
}
u.LoginName = form.LoginName

View File

@ -27,7 +27,7 @@ func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *
Website: apiForm.Website,
Location: apiForm.Location,
IsActive: true,
Type: db.USER_TYPE_ORGANIZATION,
Type: db.UserOrganization,
}
if err := db.CreateOrganization(org, user); err != nil {
if db.IsErrUserAlreadyExist(err) ||

View File

@ -46,7 +46,7 @@ func AddEmail(c *context.APIContext, form api.CreateEmailOption) {
if err := db.AddEmailAddresses(emails); err != nil {
if db.IsErrEmailAlreadyUsed(err) {
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("email address has been used: "+err.(db.ErrEmailAlreadyUsed).Email))
c.ErrorStatus(http.StatusUnprocessableEntity, errors.New("email address has been used: "+err.(db.ErrEmailAlreadyUsed).Email()))
} else {
c.Error(err, "add email addresses")
}

View File

@ -19,7 +19,7 @@ import (
func Search(c *context.APIContext) {
opts := &db.SearchUserOptions{
Keyword: c.Query("q"),
Type: db.USER_TYPE_INDIVIDUAL,
Type: db.UserIndividual,
PageSize: com.StrTo(c.Query("limit")).MustInt(),
}
if opts.PageSize == 0 {

View File

@ -138,7 +138,7 @@ func ExploreUsers(c *context.Context) {
c.Data["PageIsExploreUsers"] = true
RenderUserSearch(c, &UserSearchOptions{
Type: db.USER_TYPE_INDIVIDUAL,
Type: db.UserIndividual,
Counter: db.CountUsers,
Ranger: db.ListUsers,
PageSize: conf.UI.ExplorePagingNum,
@ -153,7 +153,7 @@ func ExploreOrganizations(c *context.Context) {
c.Data["PageIsExploreOrganizations"] = true
RenderUserSearch(c, &UserSearchOptions{
Type: db.USER_TYPE_ORGANIZATION,
Type: db.UserOrganization,
Counter: db.CountOrganizations,
Ranger: db.Organizations,
PageSize: conf.UI.ExplorePagingNum,

View File

@ -32,7 +32,7 @@ func CreatePost(c *context.Context, f form.CreateOrg) {
org := &db.User{
Name: f.OrgName,
IsActive: true,
Type: db.USER_TYPE_ORGANIZATION,
Type: db.UserOrganization,
}
if err := db.CreateOrganization(org, c.User); err != nil {

View File

@ -553,7 +553,7 @@ func ResetPasswdPost(c *context.Context) {
c.Error(err, "get user salt")
return
}
u.EncodePasswd()
u.EncodePassword()
if err := db.UpdateUser(u); err != nil {
c.Error(err, "update user")
return

View File

@ -207,7 +207,7 @@ func SettingsPasswordPost(c *context.Context, f form.ChangePassword) {
c.Errorf(err, "get user salt")
return
}
c.User.EncodePasswd()
c.User.EncodePassword()
if err := db.UpdateUser(c.User); err != nil {
c.Errorf(err, "update user")
return