mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 13:28:25 +00:00 
			
		
		
		
	[Refactor] Passwort Hash/Set (#14282)
* move SaltGeneration into HashPasswort and rename it to what it does
* Migration: Where Password is Valid with Empty String delete it
* prohibit empty password hash
* let SetPassword("") unset pwd stuff
			
			
This commit is contained in:
		@@ -349,12 +349,11 @@ func runChangePassword(c *cli.Context) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if user.Salt, err = models.GetUserSalt(); err != nil {
 | 
						if err = user.SetPassword(c.String("password")); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	user.HashPassword(c.String("password"))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := models.UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
 | 
						if err = models.UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -771,8 +771,10 @@ func UserSignIn(username, password string) (*User, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				// Update password hash if server password hash algorithm have changed
 | 
									// Update password hash if server password hash algorithm have changed
 | 
				
			||||||
				if user.PasswdHashAlgo != setting.PasswordHashAlgo {
 | 
									if user.PasswdHashAlgo != setting.PasswordHashAlgo {
 | 
				
			||||||
					user.HashPassword(password)
 | 
										if err = user.SetPassword(password); err != nil {
 | 
				
			||||||
					if err := UpdateUserCols(user, "passwd", "passwd_hash_algo"); err != nil {
 | 
											return nil, err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if err = UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
 | 
				
			||||||
						return nil, err
 | 
											return nil, err
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -277,6 +277,8 @@ var migrations = []Migration{
 | 
				
			|||||||
	NewMigration("Add scope and nonce columns to oauth2_grant table", addScopeAndNonceColumnsToOAuth2Grant),
 | 
						NewMigration("Add scope and nonce columns to oauth2_grant table", addScopeAndNonceColumnsToOAuth2Grant),
 | 
				
			||||||
	// v165 -> v166
 | 
						// v165 -> v166
 | 
				
			||||||
	NewMigration("Convert hook task type from char(16) to varchar(16) and trim the column", convertHookTaskTypeToVarcharAndTrim),
 | 
						NewMigration("Convert hook task type from char(16) to varchar(16) and trim the column", convertHookTaskTypeToVarcharAndTrim),
 | 
				
			||||||
 | 
						// v166 -> v167
 | 
				
			||||||
 | 
						NewMigration("Where Password is Valid with Empty String delete it", recalculateUserEmptyPWD),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCurrentDBVersion returns the current db version
 | 
					// GetCurrentDBVersion returns the current db version
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										115
									
								
								models/migrations/v166.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								models/migrations/v166.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 The Gitea 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 migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/sha256"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/crypto/argon2"
 | 
				
			||||||
 | 
						"golang.org/x/crypto/bcrypt"
 | 
				
			||||||
 | 
						"golang.org/x/crypto/pbkdf2"
 | 
				
			||||||
 | 
						"golang.org/x/crypto/scrypt"
 | 
				
			||||||
 | 
						"xorm.io/builder"
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func recalculateUserEmptyPWD(x *xorm.Engine) (err error) {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							algoBcrypt = "bcrypt"
 | 
				
			||||||
 | 
							algoScrypt = "scrypt"
 | 
				
			||||||
 | 
							algoArgon2 = "argon2"
 | 
				
			||||||
 | 
							algoPbkdf2 = "pbkdf2"
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type User struct {
 | 
				
			||||||
 | 
							ID                 int64  `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
							Passwd             string `xorm:"NOT NULL"`
 | 
				
			||||||
 | 
							PasswdHashAlgo     string `xorm:"NOT NULL DEFAULT 'argon2'"`
 | 
				
			||||||
 | 
							MustChangePassword bool   `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
 | 
							LoginType          int
 | 
				
			||||||
 | 
							LoginName          string
 | 
				
			||||||
 | 
							Type               int
 | 
				
			||||||
 | 
							Salt               string `xorm:"VARCHAR(10)"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// hashPassword hash password based on algo and salt
 | 
				
			||||||
 | 
						// state 461406070c
 | 
				
			||||||
 | 
						hashPassword := func(passwd, salt, algo string) string {
 | 
				
			||||||
 | 
							var tempPasswd []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch algo {
 | 
				
			||||||
 | 
							case algoBcrypt:
 | 
				
			||||||
 | 
								tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
 | 
				
			||||||
 | 
								return string(tempPasswd)
 | 
				
			||||||
 | 
							case algoScrypt:
 | 
				
			||||||
 | 
								tempPasswd, _ = scrypt.Key([]byte(passwd), []byte(salt), 65536, 16, 2, 50)
 | 
				
			||||||
 | 
							case algoArgon2:
 | 
				
			||||||
 | 
								tempPasswd = argon2.IDKey([]byte(passwd), []byte(salt), 2, 65536, 8, 50)
 | 
				
			||||||
 | 
							case algoPbkdf2:
 | 
				
			||||||
 | 
								fallthrough
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								tempPasswd = pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return fmt.Sprintf("%x", tempPasswd)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ValidatePassword checks if given password matches the one belongs to the user.
 | 
				
			||||||
 | 
						// state 461406070c, changed since it's not necessary to be time constant
 | 
				
			||||||
 | 
						ValidatePassword := func(u *User, passwd string) bool {
 | 
				
			||||||
 | 
							tempHash := hashPassword(passwd, u.Salt, u.PasswdHashAlgo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if u.PasswdHashAlgo != algoBcrypt && u.Passwd == tempHash {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const batchSize = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for start := 0; ; start += batchSize {
 | 
				
			||||||
 | 
							users := make([]*User, 0, batchSize)
 | 
				
			||||||
 | 
							if err = sess.Limit(batchSize, start).Where(builder.Neq{"passwd": ""}, 0).Find(&users); err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(users) == 0 {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err = sess.Begin(); err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, user := range users {
 | 
				
			||||||
 | 
								if ValidatePassword(user, "") {
 | 
				
			||||||
 | 
									user.Passwd = ""
 | 
				
			||||||
 | 
									user.Salt = ""
 | 
				
			||||||
 | 
									user.PasswdHashAlgo = ""
 | 
				
			||||||
 | 
									if _, err = sess.ID(user.ID).Cols("passwd", "salt", "passwd_hash_algo").Update(user); err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err = sess.Commit(); err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// delete salt and algo where password is empty
 | 
				
			||||||
 | 
						if _, err = sess.Where(builder.Eq{"passwd": ""}.And(builder.Neq{"salt": ""}.Or(builder.Neq{"passwd_hash_algo": ""}))).
 | 
				
			||||||
 | 
							Cols("salt", "passwd_hash_algo").Update(&User{}); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -395,10 +395,23 @@ func hashPassword(passwd, salt, algo string) string {
 | 
				
			|||||||
	return fmt.Sprintf("%x", tempPasswd)
 | 
						return fmt.Sprintf("%x", tempPasswd)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HashPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO.
 | 
					// SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO
 | 
				
			||||||
func (u *User) HashPassword(passwd string) {
 | 
					// change passwd, salt and passwd_hash_algo fields
 | 
				
			||||||
 | 
					func (u *User) SetPassword(passwd string) (err error) {
 | 
				
			||||||
 | 
						if len(passwd) == 0 {
 | 
				
			||||||
 | 
							u.Passwd = ""
 | 
				
			||||||
 | 
							u.Salt = ""
 | 
				
			||||||
 | 
							u.PasswdHashAlgo = ""
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if u.Salt, err = GetUserSalt(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	u.PasswdHashAlgo = setting.PasswordHashAlgo
 | 
						u.PasswdHashAlgo = setting.PasswordHashAlgo
 | 
				
			||||||
	u.Passwd = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo)
 | 
						u.Passwd = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidatePassword checks if given password matches the one belongs to the user.
 | 
					// ValidatePassword checks if given password matches the one belongs to the user.
 | 
				
			||||||
@@ -416,7 +429,7 @@ func (u *User) ValidatePassword(passwd string) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IsPasswordSet checks if the password is set or left empty
 | 
					// IsPasswordSet checks if the password is set or left empty
 | 
				
			||||||
func (u *User) IsPasswordSet() bool {
 | 
					func (u *User) IsPasswordSet() bool {
 | 
				
			||||||
	return !u.ValidatePassword("")
 | 
						return len(u.Passwd) != 0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsOrganization returns true if user is actually a organization.
 | 
					// IsOrganization returns true if user is actually a organization.
 | 
				
			||||||
@@ -826,10 +839,9 @@ func CreateUser(u *User) (err error) {
 | 
				
			|||||||
	if u.Rands, err = GetUserSalt(); err != nil {
 | 
						if u.Rands, err = GetUserSalt(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if u.Salt, err = GetUserSalt(); err != nil {
 | 
						if err = u.SetPassword(u.Passwd); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	u.HashPassword(u.Passwd)
 | 
					 | 
				
			||||||
	u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation
 | 
						u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation
 | 
				
			||||||
	u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
 | 
						u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
 | 
				
			||||||
	u.MaxRepoCreation = -1
 | 
						u.MaxRepoCreation = -1
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -220,8 +220,7 @@ func TestEmailNotificationPreferences(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestHashPasswordDeterministic(t *testing.T) {
 | 
					func TestHashPasswordDeterministic(t *testing.T) {
 | 
				
			||||||
	b := make([]byte, 16)
 | 
						b := make([]byte, 16)
 | 
				
			||||||
	rand.Read(b)
 | 
						u := &User{}
 | 
				
			||||||
	u := &User{Salt: string(b)}
 | 
					 | 
				
			||||||
	algos := []string{"argon2", "pbkdf2", "scrypt", "bcrypt"}
 | 
						algos := []string{"argon2", "pbkdf2", "scrypt", "bcrypt"}
 | 
				
			||||||
	for j := 0; j < len(algos); j++ {
 | 
						for j := 0; j < len(algos); j++ {
 | 
				
			||||||
		u.PasswdHashAlgo = algos[j]
 | 
							u.PasswdHashAlgo = algos[j]
 | 
				
			||||||
@@ -231,19 +230,15 @@ func TestHashPasswordDeterministic(t *testing.T) {
 | 
				
			|||||||
			pass := string(b)
 | 
								pass := string(b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// save the current password in the user - hash it and store the result
 | 
								// save the current password in the user - hash it and store the result
 | 
				
			||||||
			u.HashPassword(pass)
 | 
								u.SetPassword(pass)
 | 
				
			||||||
			r1 := u.Passwd
 | 
								r1 := u.Passwd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// run again
 | 
								// run again
 | 
				
			||||||
			u.HashPassword(pass)
 | 
								u.SetPassword(pass)
 | 
				
			||||||
			r2 := u.Passwd
 | 
								r2 := u.Passwd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// assert equal (given the same salt+pass, the same result is produced) except bcrypt
 | 
					 | 
				
			||||||
			if u.PasswdHashAlgo == "bcrypt" {
 | 
					 | 
				
			||||||
			assert.NotEqual(t, r1, r2)
 | 
								assert.NotEqual(t, r1, r2)
 | 
				
			||||||
			} else {
 | 
								assert.True(t, u.ValidatePassword(pass))
 | 
				
			||||||
				assert.Equal(t, r1, r2)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -252,12 +247,10 @@ func BenchmarkHashPassword(b *testing.B) {
 | 
				
			|||||||
	// BenchmarkHashPassword ensures that it takes a reasonable amount of time
 | 
						// BenchmarkHashPassword ensures that it takes a reasonable amount of time
 | 
				
			||||||
	// to hash a password - in order to protect from brute-force attacks.
 | 
						// to hash a password - in order to protect from brute-force attacks.
 | 
				
			||||||
	pass := "password1337"
 | 
						pass := "password1337"
 | 
				
			||||||
	bs := make([]byte, 16)
 | 
						u := &User{Passwd: pass}
 | 
				
			||||||
	rand.Read(bs)
 | 
					 | 
				
			||||||
	u := &User{Salt: string(bs), Passwd: pass}
 | 
					 | 
				
			||||||
	b.ResetTimer()
 | 
						b.ResetTimer()
 | 
				
			||||||
	for i := 0; i < b.N; i++ {
 | 
						for i := 0; i < b.N; i++ {
 | 
				
			||||||
		u.HashPassword(pass)
 | 
							u.SetPassword(pass)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -267,7 +267,10 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
 | 
				
			|||||||
			ctx.ServerError("UpdateUser", err)
 | 
								ctx.ServerError("UpdateUser", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		u.HashPassword(form.Password)
 | 
							if err = u.SetPassword(form.Password); err != nil {
 | 
				
			||||||
 | 
								ctx.InternalServerError(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(form.UserName) != 0 && u.Name != form.UserName {
 | 
						if len(form.UserName) != 0 && u.Name != form.UserName {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -174,7 +174,10 @@ func EditUser(ctx *context.APIContext, form api.EditUserOption) {
 | 
				
			|||||||
			ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
 | 
								ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		u.HashPassword(form.Password)
 | 
							if err = u.SetPassword(form.Password); err != nil {
 | 
				
			||||||
 | 
								ctx.InternalServerError(err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.MustChangePassword != nil {
 | 
						if form.MustChangePassword != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1517,11 +1517,10 @@ func ResetPasswdPost(ctx *context.Context) {
 | 
				
			|||||||
		ctx.ServerError("UpdateUser", err)
 | 
							ctx.ServerError("UpdateUser", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if u.Salt, err = models.GetUserSalt(); err != nil {
 | 
						if err = u.SetPassword(passwd); err != nil {
 | 
				
			||||||
		ctx.ServerError("UpdateUser", err)
 | 
							ctx.ServerError("UpdateUser", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	u.HashPassword(passwd)
 | 
					 | 
				
			||||||
	u.MustChangePassword = false
 | 
						u.MustChangePassword = false
 | 
				
			||||||
	if err := models.UpdateUserCols(u, "must_change_password", "passwd", "passwd_hash_algo", "rands", "salt"); err != nil {
 | 
						if err := models.UpdateUserCols(u, "must_change_password", "passwd", "passwd_hash_algo", "rands", "salt"); err != nil {
 | 
				
			||||||
		ctx.ServerError("UpdateUser", err)
 | 
							ctx.ServerError("UpdateUser", err)
 | 
				
			||||||
@@ -1591,12 +1590,11 @@ func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form aut
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if u.Salt, err = models.GetUserSalt(); err != nil {
 | 
						if err = u.SetPassword(form.Password); err != nil {
 | 
				
			||||||
		ctx.ServerError("UpdateUser", err)
 | 
							ctx.ServerError("UpdateUser", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u.HashPassword(form.Password)
 | 
					 | 
				
			||||||
	u.MustChangePassword = false
 | 
						u.MustChangePassword = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := models.UpdateUserCols(u, "must_change_password", "passwd", "passwd_hash_algo", "salt"); err != nil {
 | 
						if err := models.UpdateUserCols(u, "must_change_password", "passwd", "passwd_hash_algo", "salt"); err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,11 +63,10 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) {
 | 
				
			|||||||
		ctx.Flash.Error(errMsg)
 | 
							ctx.Flash.Error(errMsg)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
 | 
							if err = ctx.User.SetPassword(form.Password); err != nil {
 | 
				
			||||||
			ctx.ServerError("UpdateUser", err)
 | 
								ctx.ServerError("UpdateUser", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.User.HashPassword(form.Password)
 | 
					 | 
				
			||||||
		if err := models.UpdateUserCols(ctx.User, "salt", "passwd_hash_algo", "passwd"); err != nil {
 | 
							if err := models.UpdateUserCols(ctx.User, "salt", "passwd_hash_algo", "passwd"); err != nil {
 | 
				
			||||||
			ctx.ServerError("UpdateUser", err)
 | 
								ctx.ServerError("UpdateUser", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user