diff options
author | ᴜɴᴋɴᴡᴏɴ <u@gogs.io> | 2020-04-04 21:14:15 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-04 21:14:15 +0800 |
commit | 34145c990d4fd9f278f29cdf9c61378a75e9b934 (patch) | |
tree | 7b151bbd5aef9e487759953e3a775a82244d268d /internal/db/users.go | |
parent | 2bd9d0b9c8238ded727cd98a3ace20b53c10a44f (diff) |
lfs: implement HTTP routes (#6035)
* Bootstrap with GORM
* Fix lint error
* Set conn max lifetime to one minute
* Fallback to use gorm v1
* Define HTTP routes
* Finish authentication
* Save token updated
* Add docstring
* Finish authorization
* serveBatch rundown
* Define types in lfsutil
* Finish Batch
* authutil
* Finish basic
* Formalize response error
* Fix lint errors
* authutil: add tests
* dbutil: add tests
* lfsutil: add tests
* strutil: add tests
* Formalize 401 response
Diffstat (limited to 'internal/db/users.go')
-rw-r--r-- | internal/db/users.go | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/internal/db/users.go b/internal/db/users.go new file mode 100644 index 00000000..cadd106c --- /dev/null +++ b/internal/db/users.go @@ -0,0 +1,138 @@ +// 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 ( + "fmt" + "strings" + + "github.com/jinzhu/gorm" + "github.com/pkg/errors" + + "gogs.io/gogs/internal/errutil" +) + +// UsersStore is the persistent interface for users. +// +// NOTE: All methods are sorted in alphabetical order. +type UsersStore interface { + // Authenticate validates username and password via given login source ID. + // It returns ErrUserNotExist when the user was not found. + // + // When the "loginSourceID" is negative, it aborts the process and returns + // ErrUserNotExist if the user was not found in the database. + // + // When the "loginSourceID" is non-negative, it returns ErrLoginSourceMismatch + // if the user has different login source ID than the "loginSourceID". + // + // 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) + // 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(username string) (*User, error) +} + +var Users UsersStore + +type users struct { + *gorm.DB +} + +type ErrLoginSourceMismatch struct { + args errutil.Args +} + +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) + + var query *gorm.DB + if strings.Contains(username, "@") { + query = db.Where("email = ?", username) + } else { + query = db.Where("lower_name = ?", username) + } + + user := new(User) + err := query.First(user).Error + if err != nil && err != gorm.ErrRecordNotFound { + return nil, errors.Wrap(err, "get user") + } + + // User found in the database + if err == nil { + // Note: This check is unnecessary but to reduce user confusion at login page + // and make it more consistent from user's perspective. + if loginSourceID >= 0 && user.LoginSource != loginSourceID { + return nil, ErrLoginSourceMismatch{args: errutil.Args{"expect": loginSourceID, "actual": user.LoginSource}} + } + + // Validate password hash fetched from database for local accounts. + if user.LoginType == LoginNotype || user.LoginType == LoginPlain { + if user.ValidatePassword(password) { + return user, nil + } + + return nil, ErrUserNotExist{args: map[string]interface{}{"userID": user.ID, "name": user.Name}} + } + + source, err := LoginSources.GetByID(user.LoginSource) + if err != nil { + return nil, errors.Wrap(err, "get login source") + } + + _, err = authenticateViaLoginSource(source, username, password, false) + if err != nil { + return nil, errors.Wrap(err, "authenticate via login source") + } + return user, nil + } + + // Non-local login source is always greater than 0. + if loginSourceID <= 0 { + return nil, ErrUserNotExist{args: map[string]interface{}{"name": username}} + } + + source, err := LoginSources.GetByID(loginSourceID) + if err != nil { + return nil, errors.Wrap(err, "get login source") + } + + user, err = authenticateViaLoginSource(source, username, password, true) + if err != nil { + return nil, errors.Wrap(err, "authenticate via login source") + } + return user, nil +} + +func (db *users) GetByID(id int64) (*User, error) { + user := new(User) + err := db.Where("id = ?", id).First(user).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, ErrUserNotExist{args: map[string]interface{}{"userID": id}} + } + return nil, err + } + return user, nil +} + +func (db *users) GetByUsername(username string) (*User, error) { + user := new(User) + err := db.Where("lower_name = ?", strings.ToLower(username)).First(user).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, ErrUserNotExist{args: map[string]interface{}{"name": username}} + } + return nil, err + } + return user, nil +} |