Skip to content

Commit 867ee87

Browse files
committed
Migration: Where Password is Valid with Empty String delete it
1 parent f8f8109 commit 867ee87

File tree

3 files changed

+118
-1
lines changed

3 files changed

+118
-1
lines changed

models/login_source.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ func UserSignIn(username, password string) (*User, error) {
774774
if err = user.SetPassword(password); err != nil {
775775
return nil, err
776776
}
777-
if err := UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
777+
if err = UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
778778
return nil, err
779779
}
780780
}

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ var migrations = []Migration{
277277
NewMigration("Add scope and nonce columns to oauth2_grant table", addScopeAndNonceColumnsToOAuth2Grant),
278278
// v165 -> v166
279279
NewMigration("Convert hook task type from char(16) to varchar(16) and trim the column", convertHookTaskTypeToVarcharAndTrim),
280+
// v166 -> v167
281+
NewMigration("Where Password is Valid with Empty String delete it", recalculateUserEmptyPWD),
280282
}
281283

282284
// GetCurrentDBVersion returns the current db version

models/migrations/v166.go

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

0 commit comments

Comments
 (0)