mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-07 12:00:44 +08:00
Go refactor: merge similar functions (#16098)
### What problem does this PR solve? Merge password related functions ### Type of change - [x] Refactoring Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
@@ -250,12 +250,12 @@ func (s *Service) CreateUser(username, password, role string) (map[string]interf
|
||||
return nil, fmt.Errorf("User '%s' already exists", username)
|
||||
}
|
||||
|
||||
decryptedPassword, err := DecryptPassword(password)
|
||||
decryptedPassword, err := common.DecryptPassword(password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt password: %w", err)
|
||||
}
|
||||
|
||||
hashedPassword, err := GenerateWerkzeugPasswordHash(decryptedPassword, 150000)
|
||||
hashedPassword, err := common.GenerateWerkzeugPasswordHash(decryptedPassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
@@ -770,16 +770,16 @@ func (s *Service) ChangePassword(username, newPassword string) error {
|
||||
|
||||
user := userList[0]
|
||||
|
||||
decryptedPassword, err := DecryptPassword(newPassword)
|
||||
decryptedPassword, err := common.DecryptPassword(newPassword)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decrypt password: %w", err)
|
||||
}
|
||||
|
||||
if user.Password != nil && CheckWerkzeugPassword(decryptedPassword, *user.Password) {
|
||||
if user.Password != nil && common.CheckWerkzeugPassword(decryptedPassword, *user.Password) {
|
||||
return nil
|
||||
}
|
||||
|
||||
hashedPassword, err := GenerateWerkzeugPasswordHash(decryptedPassword, 150000)
|
||||
hashedPassword, err := common.GenerateWerkzeugPasswordHash(decryptedPassword)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
@@ -1784,7 +1784,7 @@ func (s *Service) InitDefaultAdmin() error {
|
||||
// Python: password = encode_to_base64(password) = base64.b64encode(password)
|
||||
// Then: generate_password_hash(base64_password) creates werkzeug hash
|
||||
password := base64.StdEncoding.EncodeToString([]byte(defaultPassword))
|
||||
hashedPassword, err := GenerateWerkzeugPasswordHash(password, 150000)
|
||||
hashedPassword, err := common.GenerateWerkzeugPasswordHash(password)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package admin
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@@ -148,7 +148,7 @@ func IsWerkzeugHash(hashStr string) bool {
|
||||
|
||||
// GenerateWerkzeugPasswordHash generates a werkzeug-compatible password hash using scrypt
|
||||
// This matches Python werkzeug's default behavior
|
||||
func GenerateWerkzeugPasswordHash(password string, iterations int) (string, error) {
|
||||
func GenerateWerkzeugPasswordHash(password string) (string, error) {
|
||||
// Generate random bytes (12 bytes will produce 16-char base64 string)
|
||||
randomBytes := make([]byte, 12)
|
||||
if _, err := rand.Read(randomBytes); err != nil {
|
||||
@@ -181,7 +181,7 @@ func DecryptPassword(encryptedPassword string) (string, error) {
|
||||
}
|
||||
|
||||
// Load private key
|
||||
privateKey, err := loadPrivateKey()
|
||||
privateKey, err := LoadPrivateKey()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -196,8 +196,8 @@ func DecryptPassword(encryptedPassword string) (string, error) {
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
// loadPrivateKey loads and decrypts the RSA private key from conf/private.pem
|
||||
func loadPrivateKey() (*rsa.PrivateKey, error) {
|
||||
// LoadPrivateKey loads and decrypts the RSA private key from conf/private.pem
|
||||
func LoadPrivateKey() (*rsa.PrivateKey, error) {
|
||||
// Read private key file
|
||||
keyData, err := os.ReadFile("conf/private.pem")
|
||||
if err != nil {
|
||||
@@ -18,14 +18,11 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
@@ -125,13 +122,13 @@ func (s *UserService) Register(req *RegisterRequest) (*entity.User, common.Error
|
||||
return nil, common.CodeServerError, fmt.Errorf("failed to check existing user: %w", err)
|
||||
}
|
||||
|
||||
decryptedPassword, err := s.decryptPasswordForRegister(req.Password)
|
||||
decryptedPassword, err := common.DecryptPassword(req.Password)
|
||||
if err != nil {
|
||||
return nil, common.CodeExceptionError, err
|
||||
}
|
||||
|
||||
var hashedPassword string
|
||||
hashedPassword, err = s.HashPassword(decryptedPassword)
|
||||
hashedPassword, err = common.GenerateWerkzeugPasswordHash(decryptedPassword)
|
||||
if err != nil {
|
||||
return nil, common.CodeServerError, fmt.Errorf("failed to hash password: %w", err)
|
||||
}
|
||||
@@ -362,7 +359,7 @@ func (s *UserService) Login(req *LoginRequest) (*entity.User, common.ErrorCode,
|
||||
}
|
||||
|
||||
// Decrypt password using RSA
|
||||
decryptedPassword, err := s.decryptPassword(req.Password)
|
||||
decryptedPassword, err := common.DecryptPassword(req.Password)
|
||||
if err != nil {
|
||||
return nil, common.CodeServerError, fmt.Errorf("failed to decrypt password: %w", err)
|
||||
}
|
||||
@@ -399,7 +396,7 @@ func (s *UserService) LoginByEmail(req *EmailLoginRequest) (*entity.User, common
|
||||
return nil, common.CodeAuthenticationError, fmt.Errorf("email: %s is not registered!", req.Email)
|
||||
}
|
||||
|
||||
decryptedPassword, err := s.decryptPassword(req.Password)
|
||||
decryptedPassword, err := common.DecryptPassword(req.Password)
|
||||
if err != nil {
|
||||
return nil, common.CodeServerError, fmt.Errorf("fail to crypt password")
|
||||
}
|
||||
@@ -473,31 +470,6 @@ func (s *UserService) ListUsers(page, pageSize int) ([]*UserResponse, int64, com
|
||||
return responses, total, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
// HashPassword generate password hash using scrypt (werkzeug compatible)
|
||||
// The password should already be base64 encoded (from decrypt process)
|
||||
// Werkzeug default format: scrypt:32768:8:1$base64(salt)$hex(hash)
|
||||
// IMPORTANT: werkzeug uses the base64-encoded salt string as UTF-8 bytes, NOT the decoded bytes
|
||||
func (s *UserService) HashPassword(password string) (string, error) {
|
||||
// Generate random bytes (12 bytes will produce 16-char base64 string)
|
||||
randomBytes, err := s.generateSalt()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate salt: %w", err)
|
||||
}
|
||||
|
||||
// Encode to base64 string (this will be 16 characters)
|
||||
saltB64 := base64.StdEncoding.EncodeToString(randomBytes)
|
||||
|
||||
// Use scrypt with werkzeug default parameters: N=32768, r=8, p=1, keyLen=64
|
||||
// IMPORTANT: werkzeug uses the base64 string as UTF-8 bytes, NOT the decoded bytes
|
||||
hash, err := scrypt.Key([]byte(password), []byte(saltB64), 32768, 8, 1, 64)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to compute scrypt hash: %w", err)
|
||||
}
|
||||
|
||||
// Format: scrypt:n:r:p$base64(salt)$hex(hash)
|
||||
return fmt.Sprintf("scrypt:32768:8:1$%s$%x", saltB64, hash), nil
|
||||
}
|
||||
|
||||
// VerifyPassword verify password
|
||||
// Supports both werkzeug pbkdf2 format (pbkdf2:sha256:iterations$salt$hash) and scrypt format
|
||||
func (s *UserService) VerifyPassword(hashedPassword, password string) bool {
|
||||
@@ -618,15 +590,6 @@ func (s *UserService) verifyScryptPassword(hashedPassword, password string) bool
|
||||
return s.constantTimeCompare(expectedHash, computed)
|
||||
}
|
||||
|
||||
// generateSalt generates a random 12-byte salt (werkzeug default)
|
||||
func (s *UserService) generateSalt() ([]byte, error) {
|
||||
salt := make([]byte, 12)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate random salt: %w", err)
|
||||
}
|
||||
return salt, nil
|
||||
}
|
||||
|
||||
// constantTimeCompare constant time comparison
|
||||
func (s *UserService) constantTimeCompare(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
@@ -641,95 +604,6 @@ func (s *UserService) constantTimeCompare(a, b []byte) bool {
|
||||
return result == 0
|
||||
}
|
||||
|
||||
// loadPrivateKey loads and decrypts the RSA private key from conf/private.pem
|
||||
// nolint:static check // DecryptPEMBlock is deprecated but still works for traditional PEM encryption
|
||||
func (s *UserService) loadPrivateKey() (*rsa.PrivateKey, error) {
|
||||
// Read private key file
|
||||
keyData, err := os.ReadFile("conf/private.pem")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read private key file: %w", err)
|
||||
}
|
||||
|
||||
// Parse PEM block
|
||||
block, _ := pem.Decode(keyData)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode PEM block")
|
||||
}
|
||||
|
||||
// Decrypt the PEM block if it's encrypted
|
||||
var privateKey interface{}
|
||||
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
|
||||
// Decrypt using password "Welcome"
|
||||
// Note: DecryptPEMBlock is deprecated but still functional for traditional PEM encryption
|
||||
decryptedData, err := x509.DecryptPEMBlock(block, []byte("Welcome"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt private key: %w", err)
|
||||
}
|
||||
|
||||
// Parse the decrypted key
|
||||
privateKey, err = x509.ParsePKCS1PrivateKey(decryptedData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Not encrypted, parse directly
|
||||
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("not an RSA private key")
|
||||
}
|
||||
|
||||
return rsaPrivateKey, nil
|
||||
}
|
||||
|
||||
// decryptPassword decrypts the password using RSA private key
|
||||
func (s *UserService) decryptPassword(encryptedPassword string) (string, error) {
|
||||
// Try to decode base64
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(encryptedPassword)
|
||||
if err != nil {
|
||||
// If base64 decoding fails, assume it's already a plain password
|
||||
return encryptedPassword, nil
|
||||
}
|
||||
|
||||
// Load private key
|
||||
privateKey, err := s.loadPrivateKey()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Decrypt using PKCS#1 v1.5
|
||||
plaintext, err := rsa.DecryptPKCS1v15(nil, privateKey, ciphertext)
|
||||
if err != nil {
|
||||
// If decryption fails, assume it's already a plain password
|
||||
return encryptedPassword, nil
|
||||
}
|
||||
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
func (s *UserService) decryptPasswordForRegister(encryptedPassword string) (string, error) {
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(encryptedPassword)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error('Incorrect padding')")
|
||||
}
|
||||
|
||||
privateKey, err := s.loadPrivateKey()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
plaintext, err := rsa.DecryptPKCS1v15(nil, privateKey, ciphertext)
|
||||
if err != nil {
|
||||
return "Fail to decrypt password!", nil
|
||||
}
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
func defaultUserLanguage() string {
|
||||
if strings.Contains(os.Getenv("LANG"), "zh_CN") {
|
||||
return "Chinese"
|
||||
@@ -898,7 +772,7 @@ func (s *UserService) UpdateUserSettings(user *entity.User, req *UpdateSettingsR
|
||||
if err != nil {
|
||||
return common.CodeExceptionError, fmt.Errorf("Error('Incorrect padding')")
|
||||
}
|
||||
privateKey, err := s.loadPrivateKey()
|
||||
privateKey, err := common.LoadPrivateKey()
|
||||
if err != nil {
|
||||
return common.CodeExceptionError, err
|
||||
}
|
||||
@@ -921,7 +795,7 @@ func (s *UserService) UpdateUserSettings(user *entity.User, req *UpdateSettingsR
|
||||
return common.CodeExceptionError, err
|
||||
}
|
||||
|
||||
hashedPassword, err := s.HashPassword(string(newPasswordBytes))
|
||||
hashedPassword, err := common.GenerateWerkzeugPasswordHash(string(newPasswordBytes))
|
||||
if err != nil {
|
||||
return common.CodeExceptionError, err
|
||||
}
|
||||
@@ -967,7 +841,7 @@ func (s *UserService) ChangePassword(user *entity.User, req *ChangePasswordReque
|
||||
|
||||
// If new password is provided, update password
|
||||
if req.NewPassword != nil {
|
||||
hashedPassword, err := s.HashPassword(*req.NewPassword)
|
||||
hashedPassword, err := common.GenerateWerkzeugPasswordHash(*req.NewPassword)
|
||||
if err != nil {
|
||||
return common.CodeServerError, fmt.Errorf("failed to hash new password: %w", err)
|
||||
}
|
||||
@@ -1411,11 +1285,11 @@ func (s *UserService) ForgotResetPassword(req *ForgotResetPasswordRequest) (*ent
|
||||
return nil, common.CodeAuthenticationError, fmt.Errorf("email not verified")
|
||||
}
|
||||
|
||||
plain, err := s.decryptPassword(req.NewPassword)
|
||||
plain, err := common.DecryptPassword(req.NewPassword)
|
||||
if err != nil {
|
||||
return nil, common.CodeServerError, fmt.Errorf("fail to decrypt password")
|
||||
}
|
||||
confirm, err := s.decryptPassword(req.ConfirmNewPassword)
|
||||
confirm, err := common.DecryptPassword(req.ConfirmNewPassword)
|
||||
if err != nil {
|
||||
return nil, common.CodeServerError, fmt.Errorf("fail to decrypt password")
|
||||
}
|
||||
@@ -1428,7 +1302,7 @@ func (s *UserService) ForgotResetPassword(req *ForgotResetPasswordRequest) (*ent
|
||||
return nil, common.CodeDataError, fmt.Errorf("invalid email")
|
||||
}
|
||||
|
||||
hashed, err := s.HashPassword(plain)
|
||||
hashed, err := common.GenerateWerkzeugPasswordHash(plain)
|
||||
if err != nil {
|
||||
return nil, common.CodeServerError, fmt.Errorf("failed to hash new password: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user