github.com/grafviktor/keep-my-secret@v0.9.10-0.20230908165355-19f35cce90e5/internal/model/user.go (about)

     1  package model
     2  
     3  import (
     4  	"errors"
     5  
     6  	"golang.org/x/crypto/bcrypt"
     7  
     8  	"github.com/grafviktor/keep-my-secret/internal/api/utils"
     9  )
    10  
    11  func hashString(s string) (string, error) {
    12  	bytes, err := bcrypt.GenerateFromPassword([]byte(s), bcrypt.DefaultCost)
    13  
    14  	return string(bytes), err
    15  }
    16  
    17  type User struct {
    18  	ID             int64  `json:"id"`
    19  	Login          string `json:"login,omitempty"`
    20  	HashedPassword string `json:"-"`
    21  	// RestorePassword was not implemented and not used anywhere
    22  	RestorePassword string `json:"-"`
    23  	DataKey         string `json:"-"`
    24  }
    25  
    26  // NewUser creates a new New User model with a random data key. The key should never be given to a user.
    27  // They will be automatically restored from the database when user logs in.
    28  func NewUser(login, password string) (*User, error) {
    29  	hashedPassword, err := hashString(password)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	// When we create a new user, we generate a new random password
    35  	// this password is used to encrypt user's data internally. For security
    36  	// reasons, the user never knows his own 'data' password.
    37  	// 'data' password is stored in the database and encrypted by the user's
    38  	// original password. When user logs in, we decrypt 'data' password and save
    39  	// it into RAM. This process is transparent for the users.
    40  	key := utils.GenerateRandomPassword()
    41  	encryptedKey, err := utils.Encrypt([]byte(key), password)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	u := User{
    47  		Login:           login,
    48  		HashedPassword:  hashedPassword,
    49  		RestorePassword: hashedPassword,
    50  		DataKey:         string(encryptedKey),
    51  	}
    52  
    53  	return &u, nil
    54  }
    55  
    56  // PasswordMatches check if password which was provided by the user during login process is correct
    57  func (u *User) PasswordMatches(plainText string) (bool, error) {
    58  	err := bcrypt.CompareHashAndPassword([]byte(u.HashedPassword), []byte(plainText))
    59  	if err != nil {
    60  		switch {
    61  		case errors.Is(err, bcrypt.ErrMismatchedHashAndPassword):
    62  			// invalid password
    63  			return false, nil
    64  		default:
    65  			return false, err
    66  		}
    67  	}
    68  
    69  	return true, nil
    70  }
    71  
    72  func (u *User) GetDataKey(password string) (string, error) {
    73  	if len(password) == 0 {
    74  		return "", errors.New("cannot decrypt data key - no password set")
    75  	}
    76  
    77  	key, err := utils.Decrypt([]byte(u.DataKey), password)
    78  	if err != nil {
    79  		return "", err
    80  	}
    81  
    82  	return string(key), nil
    83  }