git.sophuwu.com > authuwu   
              95
            
             package userpass

import (
	"crypto/rand"
	"crypto/subtle"
	"git.sophuwu.com/authuwu/db"
	"git.sophuwu.com/authuwu/standard"
)

type Password struct {
	Hash []byte
	Salt []byte
}

func (p *Password) Encode() string {
	e := standard.NewEncoder()
	s := e.EncodeToString(p.Hash)
	s += ":"
	s += e.EncodeToString(p.Salt)
	return s
}

func (p *Password) CheckPassword(password string) bool {
	h := standard.NewHash()
	h.Write(p.Salt)
	h.Write([]byte(password))
	b := h.Sum(nil)
	return 1 == subtle.ConstantTimeCompare(b, p.Hash)
}

func Salt() ([]byte, error) {
	salt := make([]byte, standard.SaltLength)
	_, err := rand.Read(salt)
	return salt, err
}

func (p *Password) SetPassword(password string) error {
	salt, err := Salt()
	if err != nil {
		return err
	}
	p.Salt = salt
	h := standard.NewHash()
	h.Write(salt)
	h.Write([]byte(password))
	p.Hash = h.Sum(nil)
	return nil
}

func (u *User) SetPassword(password string) error {
	if err := u.Password.SetPassword(password); err != nil {
		return err
	}
	return nil
}

func (u *User) Authenticate(password string) bool {
	return u.Password.CheckPassword(password)
}

type User struct {
	Username string   `storm:"id"`
	Password Password `storm:"inline"`
}

func (u *User) String() string {
	return u.Username + ":" + u.Password.Encode()
}

func NewUser(username string, password string) error {
	u := &User{Username: username}
	err := u.SetPassword(password)
	if err != nil {
		return err
	}
	err = db.AuthUwu.Save(u)
	return err
}

func GetUser(username string) (*User, error) {
	u := &User{Username: username}
	err := db.AuthUwu.One("Username", username, u)
	if err != nil {
		return nil, err
	}
	return u, nil
}

func UserAuth(username string, password string) (bool, error) {
	u, err := GetUser(username)
	if err != nil {
		return false, err
	}
	return u.Authenticate(password), nil
}