mirror of https://github.com/Xhofe/alist
feat: rehash password with a unique salt for each user
parent
ff5cf3f4fa
commit
1c00d64952
|
@ -5,7 +5,6 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/alist-org/alist/v3/internal/conf"
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
"github.com/alist-org/alist/v3/internal/setting"
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
@ -70,7 +69,7 @@ func setAdminPassword(pwd string) {
|
||||||
utils.Log.Errorf("failed get admin user: %+v", err)
|
utils.Log.Errorf("failed get admin user: %+v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
admin.PwdHash = model.HashPwd(pwd)
|
admin.SetPassword(pwd)
|
||||||
if err := op.UpdateUser(admin); err != nil {
|
if err := op.UpdateUser(admin); err != nil {
|
||||||
utils.Log.Errorf("failed update admin user: %+v", err)
|
utils.Log.Errorf("failed update admin user: %+v", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -24,9 +24,11 @@ func initUser() {
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
salt := random.String(16)
|
||||||
admin = &model.User{
|
admin = &model.User{
|
||||||
Username: "admin",
|
Username: "admin",
|
||||||
PwdHash: model.HashPwd(adminPassword),
|
Salt: salt,
|
||||||
|
PwdHash: model.TwoHashPwd(adminPassword, salt),
|
||||||
Role: model.ADMIN,
|
Role: model.ADMIN,
|
||||||
BasePath: "/",
|
BasePath: "/",
|
||||||
}
|
}
|
||||||
|
@ -42,9 +44,10 @@ func initUser() {
|
||||||
guest, err := op.GetGuest()
|
guest, err := op.GetGuest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
salt := random.String(16)
|
||||||
guest = &model.User{
|
guest = &model.User{
|
||||||
Username: "guest",
|
Username: "guest",
|
||||||
PwdHash: model.HashPwd("guest"),
|
PwdHash: model.TwoHashPwd("guest", salt),
|
||||||
Role: model.GUEST,
|
Role: model.GUEST,
|
||||||
BasePath: "/",
|
BasePath: "/",
|
||||||
Permission: 0,
|
Permission: 0,
|
||||||
|
@ -68,7 +71,8 @@ func hashPwdForOldVersion() {
|
||||||
for i := range users {
|
for i := range users {
|
||||||
user := users[i]
|
user := users[i]
|
||||||
if user.PwdHash == "" {
|
if user.PwdHash == "" {
|
||||||
user.PwdHash = model.HashPwd(user.Password)
|
user.SetPassword(user.Password)
|
||||||
|
user.Password = ""
|
||||||
if err := db.UpdateUser(&user); err != nil {
|
if err := db.UpdateUser(&user); err != nil {
|
||||||
utils.Log.Fatalf("[hash pwd for old version] failed update user: %v", err)
|
utils.Log.Fatalf("[hash pwd for old version] failed update user: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils/random"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,15 +15,16 @@ const (
|
||||||
ADMIN
|
ADMIN
|
||||||
)
|
)
|
||||||
|
|
||||||
const HashSalt = "https://github.com/alist-org/alist"
|
const StaticHashSalt = "https://github.com/alist-org/alist"
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID uint `json:"id" gorm:"primaryKey"` // unique key
|
ID uint `json:"id" gorm:"primaryKey"` // unique key
|
||||||
Username string `json:"username" gorm:"unique" binding:"required"` // username
|
Username string `json:"username" gorm:"unique" binding:"required"` // username
|
||||||
PwdHash string `json:"-"` // password hash
|
PwdHash string `json:"-"` // password hash
|
||||||
Password string `json:"-"` // password
|
Salt string // unique salt
|
||||||
BasePath string `json:"base_path"` // base path
|
Password string `json:"-"` // Deprecated password
|
||||||
Role int `json:"role"` // user's role
|
BasePath string `json:"base_path"` // base path
|
||||||
|
Role int `json:"role"` // user's role
|
||||||
Disabled bool `json:"disabled"`
|
Disabled bool `json:"disabled"`
|
||||||
// Determine permissions by bit
|
// Determine permissions by bit
|
||||||
// 0: can see hidden files
|
// 0: can see hidden files
|
||||||
|
@ -41,76 +43,90 @@ type User struct {
|
||||||
SsoID string `json:"sso_id"` // unique by sso platform
|
SsoID string `json:"sso_id"` // unique by sso platform
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) IsGuest() bool {
|
func (u *User) IsGuest() bool {
|
||||||
return u.Role == GUEST
|
return u.Role == GUEST
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) IsAdmin() bool {
|
func (u *User) IsAdmin() bool {
|
||||||
return u.Role == ADMIN
|
return u.Role == ADMIN
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) ValidatePassword(password string) error {
|
func (u *User) ValidateRawPassword(password string) error {
|
||||||
return u.ValidatePwdHash(HashPwd(password))
|
return u.ValidatePwdStaticHash(StaticHash(password))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) ValidatePwdHash(pwdHash string) error {
|
func (u *User) ValidatePwdStaticHash(pwdStaticHash string) error {
|
||||||
if pwdHash == "" {
|
if pwdStaticHash == "" {
|
||||||
return errors.WithStack(errs.EmptyPassword)
|
return errors.WithStack(errs.EmptyPassword)
|
||||||
}
|
}
|
||||||
if u.PwdHash != pwdHash {
|
if u.PwdHash != HashPwd(pwdStaticHash, u.Salt) {
|
||||||
return errors.WithStack(errs.WrongPassword)
|
return errors.WithStack(errs.WrongPassword)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanSeeHides() bool {
|
func (u *User) SetPassword(pwd string) *User {
|
||||||
|
u.Salt = random.String(16)
|
||||||
|
u.PwdHash = TwoHashPwd(pwd, u.Salt)
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) CanSeeHides() bool {
|
||||||
return u.IsAdmin() || u.Permission&1 == 1
|
return u.IsAdmin() || u.Permission&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanAccessWithoutPassword() bool {
|
func (u *User) CanAccessWithoutPassword() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>1)&1 == 1
|
return u.IsAdmin() || (u.Permission>>1)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanAddAria2Tasks() bool {
|
func (u *User) CanAddAria2Tasks() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>2)&1 == 1
|
return u.IsAdmin() || (u.Permission>>2)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanWrite() bool {
|
func (u *User) CanWrite() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>3)&1 == 1
|
return u.IsAdmin() || (u.Permission>>3)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanRename() bool {
|
func (u *User) CanRename() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>4)&1 == 1
|
return u.IsAdmin() || (u.Permission>>4)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanMove() bool {
|
func (u *User) CanMove() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>5)&1 == 1
|
return u.IsAdmin() || (u.Permission>>5)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanCopy() bool {
|
func (u *User) CanCopy() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>6)&1 == 1
|
return u.IsAdmin() || (u.Permission>>6)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanRemove() bool {
|
func (u *User) CanRemove() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>7)&1 == 1
|
return u.IsAdmin() || (u.Permission>>7)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanWebdavRead() bool {
|
func (u *User) CanWebdavRead() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>8)&1 == 1
|
return u.IsAdmin() || (u.Permission>>8)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanWebdavManage() bool {
|
func (u *User) CanWebdavManage() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>9)&1 == 1
|
return u.IsAdmin() || (u.Permission>>9)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) CanAddQbittorrentTasks() bool {
|
func (u *User) CanAddQbittorrentTasks() bool {
|
||||||
return u.IsAdmin() || (u.Permission>>10)&1 == 1
|
return u.IsAdmin() || (u.Permission>>10)&1 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) JoinPath(reqPath string) (string, error) {
|
func (u *User) JoinPath(reqPath string) (string, error) {
|
||||||
return utils.JoinBasePath(u.BasePath, reqPath)
|
return utils.JoinBasePath(u.BasePath, reqPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HashPwd(password string) string {
|
func StaticHash(password string) string {
|
||||||
return utils.GetSHA256Encode([]byte(fmt.Sprintf("%s-%s", password, HashSalt)))
|
return utils.GetSHA256Encode([]byte(fmt.Sprintf("%s-%s", password, StaticHashSalt)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func HashPwd(static string, salt string) string {
|
||||||
|
return utils.GetSHA256Encode([]byte(fmt.Sprintf("%s-%s", static, salt)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TwoHashPwd(password string, salt string) string {
|
||||||
|
return HashPwd(StaticHash(password), salt)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ func Login(c *gin.Context) {
|
||||||
common.ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Password = model.HashPwd(req.Password)
|
req.Password = model.StaticHash(req.Password)
|
||||||
loginHash(c, &req)
|
loginHash(c, &req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ func loginHash(c *gin.Context, req *LoginReq) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// validate password hash
|
// validate password hash
|
||||||
if err := user.ValidatePwdHash(req.Password); err != nil {
|
if err := user.ValidatePwdStaticHash(req.Password); err != nil {
|
||||||
common.ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
loginCache.Set(ip, count+1)
|
loginCache.Set(ip, count+1)
|
||||||
return
|
return
|
||||||
|
|
|
@ -78,7 +78,7 @@ func WebDAVAuth(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user, err := op.GetUserByName(username)
|
user, err := op.GetUserByName(username)
|
||||||
if err != nil || user.ValidatePassword(password) != nil {
|
if err != nil || user.ValidateRawPassword(password) != nil {
|
||||||
if c.Request.Method == "OPTIONS" {
|
if c.Request.Method == "OPTIONS" {
|
||||||
c.Set("user", guest)
|
c.Set("user", guest)
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|
Loading…
Reference in New Issue