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-negativ
(ctx context.Context, login, password string, loginSourceID int64)
| 63 | // When the "loginSourceID" is positive, it tries to authenticate via given |
| 64 | // login source and creates a new user when not yet exists in the database. |
| 65 | func (s *UsersStore) Authenticate(ctx context.Context, login, password string, loginSourceID int64) (*User, error) { |
| 66 | login = strings.ToLower(login) |
| 67 | |
| 68 | query := s.db.WithContext(ctx) |
| 69 | if strings.Contains(login, "@") { |
| 70 | query = query.Where("email = ?", login) |
| 71 | } else { |
| 72 | query = query.Where("lower_name = ?", login) |
| 73 | } |
| 74 | |
| 75 | user := new(User) |
| 76 | err := query.First(user).Error |
| 77 | if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { |
| 78 | return nil, errors.Wrap(err, "get user") |
| 79 | } |
| 80 | |
| 81 | var authSourceID int64 // The login source ID will be used to authenticate the user |
| 82 | createNewUser := false // Whether to create a new user after successful authentication |
| 83 | |
| 84 | // User found in the database |
| 85 | if err == nil { |
| 86 | // Note: This check is unnecessary but to reduce user confusion at login page |
| 87 | // and make it more consistent from user's perspective. |
| 88 | if loginSourceID >= 0 && user.LoginSource != loginSourceID { |
| 89 | return nil, ErrLoginSourceMismatch{args: errutil.Args{"expect": loginSourceID, "actual": user.LoginSource}} |
| 90 | } |
| 91 | |
| 92 | // Validate password hash fetched from database for local accounts. |
| 93 | if user.IsLocal() { |
| 94 | if userutil.ValidatePassword(user.Password, user.Salt, password) { |
| 95 | return user, nil |
| 96 | } |
| 97 | |
| 98 | return nil, auth.ErrBadCredentials{Args: map[string]any{"login": login, "userID": user.ID}} |
| 99 | } |
| 100 | |
| 101 | authSourceID = user.LoginSource |
| 102 | |
| 103 | } else { |
| 104 | // Non-local login source is always greater than 0. |
| 105 | if loginSourceID <= 0 { |
| 106 | return nil, auth.ErrBadCredentials{Args: map[string]any{"login": login}} |
| 107 | } |
| 108 | |
| 109 | authSourceID = loginSourceID |
| 110 | createNewUser = true |
| 111 | } |
| 112 | |
| 113 | source, err := newLoginSourcesStore(s.db, loadedLoginSourceFilesStore).GetByID(ctx, authSourceID) |
| 114 | if err != nil { |
| 115 | return nil, errors.Wrap(err, "get login source") |
| 116 | } |
| 117 | |
| 118 | if !source.IsActived { |
| 119 | return nil, errors.Errorf("login source %d is not activated", source.ID) |
| 120 | } |
| 121 | |
| 122 | extAccount, err := source.Provider.Authenticate(login, password) |
nothing calls this directly
no test coverage detected