mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-06-20 08:00:52 +00:00
- Password hashing can take a measurable amount of time, make this more visible in the trace by capturing the computations done in the password hash in their own region. - Ref: forgejo/forgejo#6470 ## Screenshot  The upper part are where the tasks are shown (and nothing else). The bottom part is where the interesting execution tracing happens and the part where the user password hashing happens is now properly indicated/highlighted and does not need to be inferred by looking at the stack traces. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7981 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
87 lines
2.5 KiB
Go
87 lines
2.5 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
user_model "forgejo.org/models/user"
|
|
"forgejo.org/modules/setting"
|
|
"forgejo.org/modules/util"
|
|
)
|
|
|
|
// ErrUserPasswordNotSet represents a "ErrUserPasswordNotSet" kind of error.
|
|
type ErrUserPasswordNotSet struct {
|
|
UID int64
|
|
Name string
|
|
}
|
|
|
|
func (err ErrUserPasswordNotSet) Error() string {
|
|
return fmt.Sprintf("user's password isn't set [uid: %d, name: %s]", err.UID, err.Name)
|
|
}
|
|
|
|
// Unwrap unwraps this error as a ErrInvalidArgument error
|
|
func (err ErrUserPasswordNotSet) Unwrap() error {
|
|
return util.ErrInvalidArgument
|
|
}
|
|
|
|
// ErrUserPasswordInvalid represents a "ErrUserPasswordInvalid" kind of error.
|
|
type ErrUserPasswordInvalid struct {
|
|
UID int64
|
|
Name string
|
|
}
|
|
|
|
func (err ErrUserPasswordInvalid) Error() string {
|
|
return fmt.Sprintf("user's password is invalid [uid: %d, name: %s]", err.UID, err.Name)
|
|
}
|
|
|
|
// Unwrap unwraps this error as a ErrInvalidArgument error
|
|
func (err ErrUserPasswordInvalid) Unwrap() error {
|
|
return util.ErrInvalidArgument
|
|
}
|
|
|
|
// Authenticate authenticates the provided user against the DB
|
|
func Authenticate(ctx context.Context, user *user_model.User, login, password string) (*user_model.User, error) {
|
|
if user == nil {
|
|
return nil, user_model.ErrUserNotExist{Name: login}
|
|
}
|
|
|
|
if !user.IsPasswordSet() {
|
|
return nil, ErrUserPasswordNotSet{UID: user.ID, Name: user.Name}
|
|
} else if !user.ValidatePassword(ctx, password) {
|
|
return nil, ErrUserPasswordInvalid{UID: user.ID, Name: user.Name}
|
|
}
|
|
|
|
// Update password hash if server password hash algorithm have changed
|
|
// Or update the password when the salt length doesn't match the current
|
|
// recommended salt length, this in order to migrate user's salts to a more secure salt.
|
|
if user.PasswdHashAlgo != setting.PasswordHashAlgo || len(user.Salt) != user_model.SaltByteLength*2 {
|
|
if err := user.SetPassword(password); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := user_model.UpdateUserCols(ctx, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
|
|
// user could be hinted to resend confirm email.
|
|
if user.ProhibitLogin {
|
|
return nil, user_model.ErrUserProhibitLogin{
|
|
UID: user.ID,
|
|
Name: user.Name,
|
|
}
|
|
}
|
|
|
|
// attempting to login as a non-user account
|
|
if user.Type != user_model.UserTypeIndividual {
|
|
return nil, user_model.ErrUserProhibitLogin{
|
|
UID: user.ID,
|
|
Name: user.Name,
|
|
}
|
|
}
|
|
|
|
return user, nil
|
|
}
|