code.gitea.io/gitea@v1.21.7/models/migrations/v1_14/v166.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package v1_14 //nolint 5 6 import ( 7 "encoding/hex" 8 9 "github.com/minio/sha256-simd" 10 "golang.org/x/crypto/argon2" 11 "golang.org/x/crypto/bcrypt" 12 "golang.org/x/crypto/pbkdf2" 13 "golang.org/x/crypto/scrypt" 14 "xorm.io/builder" 15 "xorm.io/xorm" 16 ) 17 18 func RecalculateUserEmptyPWD(x *xorm.Engine) (err error) { 19 const ( 20 algoBcrypt = "bcrypt" 21 algoScrypt = "scrypt" 22 algoArgon2 = "argon2" 23 algoPbkdf2 = "pbkdf2" 24 ) 25 26 type User struct { 27 ID int64 `xorm:"pk autoincr"` 28 Passwd string `xorm:"NOT NULL"` 29 PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"` 30 MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` 31 LoginType int 32 LoginName string 33 Type int 34 Salt string `xorm:"VARCHAR(10)"` 35 } 36 37 // hashPassword hash password based on algo and salt 38 // state 461406070c 39 hashPassword := func(passwd, salt, algo string) string { 40 var tempPasswd []byte 41 42 switch algo { 43 case algoBcrypt: 44 tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost) 45 return string(tempPasswd) 46 case algoScrypt: 47 tempPasswd, _ = scrypt.Key([]byte(passwd), []byte(salt), 65536, 16, 2, 50) 48 case algoArgon2: 49 tempPasswd = argon2.IDKey([]byte(passwd), []byte(salt), 2, 65536, 8, 50) 50 case algoPbkdf2: 51 fallthrough 52 default: 53 tempPasswd = pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New) 54 } 55 56 return hex.EncodeToString(tempPasswd) 57 } 58 59 // ValidatePassword checks if given password matches the one belongs to the user. 60 // state 461406070c, changed since it's not necessary to be time constant 61 ValidatePassword := func(u *User, passwd string) bool { 62 tempHash := hashPassword(passwd, u.Salt, u.PasswdHashAlgo) 63 64 if u.PasswdHashAlgo != algoBcrypt && u.Passwd == tempHash { 65 return true 66 } 67 if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil { 68 return true 69 } 70 return false 71 } 72 73 sess := x.NewSession() 74 defer sess.Close() 75 76 const batchSize = 100 77 78 for start := 0; ; start += batchSize { 79 users := make([]*User, 0, batchSize) 80 if err = sess.Limit(batchSize, start).Where(builder.Neq{"passwd": ""}, 0).Find(&users); err != nil { 81 return err 82 } 83 if len(users) == 0 { 84 break 85 } 86 87 if err = sess.Begin(); err != nil { 88 return err 89 } 90 91 for _, user := range users { 92 if ValidatePassword(user, "") { 93 user.Passwd = "" 94 user.Salt = "" 95 user.PasswdHashAlgo = "" 96 if _, err = sess.ID(user.ID).Cols("passwd", "salt", "passwd_hash_algo").Update(user); err != nil { 97 return err 98 } 99 } 100 } 101 102 if err = sess.Commit(); err != nil { 103 return err 104 } 105 } 106 107 // delete salt and algo where password is empty 108 _, err = sess.Where(builder.Eq{"passwd": ""}.And(builder.Neq{"salt": ""}.Or(builder.Neq{"passwd_hash_algo": ""}))). 109 Cols("salt", "passwd_hash_algo").Update(&User{}) 110 111 return err 112 }