github.com/RichardKnop/go-oauth2-server@v1.0.5-0.20201019163316-d02a401490d0/oauth/user.go (about)

     1  package oauth
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/RichardKnop/go-oauth2-server/models"
    10  	"github.com/RichardKnop/go-oauth2-server/util"
    11  	pass "github.com/RichardKnop/go-oauth2-server/util/password"
    12  	"github.com/RichardKnop/uuid"
    13  	"github.com/jinzhu/gorm"
    14  )
    15  
    16  var (
    17  	// MinPasswordLength defines minimum password length
    18  	MinPasswordLength = 6
    19  
    20  	// ErrPasswordTooShort ...
    21  	ErrPasswordTooShort = fmt.Errorf(
    22  		"Password must be at least %d characters long",
    23  		MinPasswordLength,
    24  	)
    25  	// ErrUserNotFound ...
    26  	ErrUserNotFound = errors.New("User not found")
    27  	// ErrInvalidUserPassword ...
    28  	ErrInvalidUserPassword = errors.New("Invalid user password")
    29  	// ErrCannotSetEmptyUsername ...
    30  	ErrCannotSetEmptyUsername = errors.New("Cannot set empty username")
    31  	// ErrUserPasswordNotSet ...
    32  	ErrUserPasswordNotSet = errors.New("User password not set")
    33  	// ErrUsernameTaken ...
    34  	ErrUsernameTaken = errors.New("Username taken")
    35  )
    36  
    37  // UserExists returns true if user exists
    38  func (s *Service) UserExists(username string) bool {
    39  	_, err := s.FindUserByUsername(username)
    40  	return err == nil
    41  }
    42  
    43  // FindUserByUsername looks up a user by username
    44  func (s *Service) FindUserByUsername(username string) (*models.OauthUser, error) {
    45  	// Usernames are case insensitive
    46  	user := new(models.OauthUser)
    47  	notFound := s.db.Where("username = LOWER(?)", username).
    48  		First(user).RecordNotFound()
    49  
    50  	// Not found
    51  	if notFound {
    52  		return nil, ErrUserNotFound
    53  	}
    54  
    55  	return user, nil
    56  }
    57  
    58  // CreateUser saves a new user to database
    59  func (s *Service) CreateUser(roleID, username, password string) (*models.OauthUser, error) {
    60  	return s.createUserCommon(s.db, roleID, username, password)
    61  }
    62  
    63  // CreateUserTx saves a new user to database using injected db object
    64  func (s *Service) CreateUserTx(tx *gorm.DB, roleID, username, password string) (*models.OauthUser, error) {
    65  	return s.createUserCommon(tx, roleID, username, password)
    66  }
    67  
    68  // SetPassword sets a user password
    69  func (s *Service) SetPassword(user *models.OauthUser, password string) error {
    70  	return s.setPasswordCommon(s.db, user, password)
    71  }
    72  
    73  // SetPasswordTx sets a user password in a transaction
    74  func (s *Service) SetPasswordTx(tx *gorm.DB, user *models.OauthUser, password string) error {
    75  	return s.setPasswordCommon(tx, user, password)
    76  }
    77  
    78  // AuthUser authenticates user
    79  func (s *Service) AuthUser(username, password string) (*models.OauthUser, error) {
    80  	// Fetch the user
    81  	user, err := s.FindUserByUsername(username)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	// Check that the password is set
    87  	if !user.Password.Valid {
    88  		return nil, ErrUserPasswordNotSet
    89  	}
    90  
    91  	// Verify the password
    92  	if pass.VerifyPassword(user.Password.String, password) != nil {
    93  		return nil, ErrInvalidUserPassword
    94  	}
    95  
    96  	return user, nil
    97  }
    98  
    99  // UpdateUsername ...
   100  func (s *Service) UpdateUsername(user *models.OauthUser, username string) error {
   101  	if username == "" {
   102  		return ErrCannotSetEmptyUsername
   103  	}
   104  
   105  	return s.updateUsernameCommon(s.db, user, username)
   106  }
   107  
   108  // UpdateUsernameTx ...
   109  func (s *Service) UpdateUsernameTx(tx *gorm.DB, user *models.OauthUser, username string) error {
   110  	return s.updateUsernameCommon(tx, user, username)
   111  }
   112  
   113  func (s *Service) createUserCommon(db *gorm.DB, roleID, username, password string) (*models.OauthUser, error) {
   114  	// Start with a user without a password
   115  	user := &models.OauthUser{
   116  		MyGormModel: models.MyGormModel{
   117  			ID:        uuid.New(),
   118  			CreatedAt: time.Now().UTC(),
   119  		},
   120  		RoleID:   util.StringOrNull(roleID),
   121  		Username: strings.ToLower(username),
   122  		Password: util.StringOrNull(""),
   123  	}
   124  
   125  	// If the password is being set already, create a bcrypt hash
   126  	if password != "" {
   127  		if len(password) < MinPasswordLength {
   128  			return nil, ErrPasswordTooShort
   129  		}
   130  		passwordHash, err := pass.HashPassword(password)
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  		user.Password = util.StringOrNull(string(passwordHash))
   135  	}
   136  
   137  	// Check the username is available
   138  	if s.UserExists(user.Username) {
   139  		return nil, ErrUsernameTaken
   140  	}
   141  
   142  	// Create the user
   143  	if err := db.Create(user).Error; err != nil {
   144  		return nil, err
   145  	}
   146  	return user, nil
   147  }
   148  
   149  func (s *Service) setPasswordCommon(db *gorm.DB, user *models.OauthUser, password string) error {
   150  	if len(password) < MinPasswordLength {
   151  		return ErrPasswordTooShort
   152  	}
   153  
   154  	// Create a bcrypt hash
   155  	passwordHash, err := pass.HashPassword(password)
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	// Set the password on the user object
   161  	return db.Model(user).UpdateColumns(models.OauthUser{
   162  		Password:    util.StringOrNull(string(passwordHash)),
   163  		MyGormModel: models.MyGormModel{UpdatedAt: time.Now().UTC()},
   164  	}).Error
   165  }
   166  
   167  func (s *Service) updateUsernameCommon(db *gorm.DB, user *models.OauthUser, username string) error {
   168  	if username == "" {
   169  		return ErrCannotSetEmptyUsername
   170  	}
   171  	return db.Model(user).UpdateColumn("username", strings.ToLower(username)).Error
   172  }