aboutsummaryrefslogtreecommitdiff
path: root/internal/db/users.go
diff options
context:
space:
mode:
authorᴜɴᴋɴᴡᴏɴ <u@gogs.io>2020-04-04 21:14:15 +0800
committerGitHub <noreply@github.com>2020-04-04 21:14:15 +0800
commit34145c990d4fd9f278f29cdf9c61378a75e9b934 (patch)
tree7b151bbd5aef9e487759953e3a775a82244d268d /internal/db/users.go
parent2bd9d0b9c8238ded727cd98a3ace20b53c10a44f (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.go138
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
+}