package user import ( "encoding/base64" "errors" "fmt" "golang.org/x/crypto/bcrypt" "log" "math/rand" "github.com/jmoiron/sqlx" ) type UserClient interface { register(username, password, email string) (bool, error) login(username, password string, requiredMailValidation bool) (bool, error) getMailValidationToken(email string, forceRecreate bool) (string, error) } type UserClientMemory struct { users map[string]string } func GetUserMemmoryClient() *UserClientMemory { uc := UserClientMemory{} uc.users = make(map[string]string) uc.users["admin"]="password" return &uc } func (uc *UserClientMemory) register(username, password, email string) (bool, error) { if _, ok := uc.users[username]; ok { return false, errors.New("Username already used") } uc.users[username] = password return true, nil } func (uc UserClientMemory) login(username, password string, requiredMailValidation bool) (bool, error) { if requiredMailValidation == true { return false, errors.New("In memmory User Client cant validate email addresses") } if val, ok := uc.users[username]; ok { fmt.Println("Login for valide user") if val == password { return true, nil } } else { fmt.Printf("User %v not found", username) } return false, nil } func (uc UserClientMemory) getMailValidationToken(email string, forceRecreate bool) (string, error) { token := randomString(35) token = fmt.Sprintf("%v/%v", base64.StdEncoding.EncodeToString([]byte(email)), token) return token, nil } type UserClientSql struct { db *sqlx.DB } type User struct { Id int `db:"id"` Mail string `db:"mail"` MailValidationCode *string `db:"mailValidationCode"` MailValidate bool `db:"mailValidate"` Username string `db:"username"` Password string `db:"password"` } func NewUserClientSql(DB *sqlx.DB) *UserClientSql { uc := UserClientSql{} uc.db = DB return &uc } func (uc *UserClientSql) register(username, password, email string) (bool, error) { _, err := uc.db.NamedExec("INSERT INTO `user` (`mail`, `username`, `password`) VALUE (:mail, :username, :password)", map[string]interface{}{ "mail": email, "username": username, "password": uc.hashAndSalt([]byte(password)), }) if err != nil { return false, err } return true, nil } func (uc UserClientSql) login(username, password string, requiredMailValidation bool) (bool, error) { user := User{} err := uc.db.Get(&user, "SELECT * FROM `user` WHERE `username` = ?", username) if err != nil { return false, err } if uc.comparePasswords(user.Password, []byte(password)) == false { return false, errors.New("Wrong password") } if requiredMailValidation && user.MailValidate == false { return false, errors.New("Mail not validated") } return true, nil } func (uc UserClientSql) getMailValidationToken(email string, forceRecreate bool) (string, error) { user := User{} err := uc.db.Get(&user, "SELECT * FROM `user` WHERE `mail` = ?", email) if err != nil { return "", err } if user.MailValidationCode != nil && forceRecreate == false { return *user.MailValidationCode, nil } token := randomString(25) _, err = uc.db.NamedExec("UPDATE `user` SET `mailValidationCode` = :code WHERE `mail` = :mail LIMIT 1", map[string]interface{}{ "code": token, "mail": email, }) if err != nil { return "", err } //token = fmt.Sprintf("%v/%v", base64.StdEncoding.EncodeToString([]byte(email)), token) return token, nil } func (uc UserClientSql)hashAndSalt(pwd []byte) string { // Use GenerateFromPassword to hash & salt pwd. // MinCost is just an integer constant provided by the bcrypt // package along with DefaultCost & MaxCost. // The cost can be any value you want provided it isn't lower // than the MinCost (4) hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost) if err != nil { log.Println(err) } // GenerateFromPassword returns a byte slice so we need to // convert the bytes to a string and return it return string(hash) } func (uc UserClientSql)comparePasswords(hashedPwd string, plainPwd []byte) bool { byteHash := []byte(hashedPwd) err := bcrypt.CompareHashAndPassword(byteHash, plainPwd) if err != nil { log.Println(err) return false } return true } func randomString(n int) string { var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") b := make([]byte, n) for i := range b { b[i] = letters[rand.Intn(len(letters))] } return string(b) }