docstore/pkg/db/user.go
2019-04-19 15:45:28 +02:00

169 lines
3.1 KiB
Go

// Copyright (C) 2019 Marius Schellenberger
package db
import (
"errors"
"git.giftfish.de/ston1th/docstore/pkg/core"
"git.giftfish.de/ston1th/docstore/pkg/otp"
"golang.org/x/crypto/bcrypt"
"regexp"
"strings"
)
const (
userPrefix = "user/"
userKey = "user"
)
var (
errWrongLogin = errors.New("db: wrong login")
errLocked = errors.New("db: user locked")
errUserExists = errors.New("db: user already exist")
userRe = regexp.MustCompile("[^a-zA-Z0-9]+")
)
func (db *DB) UserExists() (b bool) {
db.store.ForEach(func(k string, _ []byte) error {
if strings.HasPrefix(k, userPrefix) {
b = true
}
return nil
})
return
}
func (db *DB) GetUserWithoutPassword() (u core.User, err error) {
u, err = db.GetUser()
u.Password = ""
return
}
func (db *DB) GetUserSecret() (secret string, err error) {
var user core.User
user, err = db.GetUser()
secret = user.Secret
return
}
func (db *DB) GetUser() (u core.User, err error) {
err = db.store.Get(userPrefix+userKey, &u)
return
}
func (db *DB) CreateUser(username, password string) error {
username = userRe.ReplaceAllString(username, "")
if db.UserExists() {
return errUserExists
}
hash, err := bcrypt.GenerateFromPassword(validatePassword(password), bcryptCost)
if err != nil {
return err
}
return db.store.Set(userPrefix+userKey, &core.User{
Username: username,
Password: string(hash),
})
}
func (db *DB) Login(username, password string) (u core.User, err error) {
u, err = db.GetUser()
if err != nil {
return
}
if u.Username != username {
err = errWrongLogin
return
}
if u.Locked == 3 {
err = errLocked
return
}
if err = bcrypt.CompareHashAndPassword([]byte(u.Password), validatePassword(password)); err != nil {
u.Locked++
err = db.store.Set(userPrefix+username, u)
if err != nil {
return
}
err = errWrongLogin
return
}
if u.Locked > 0 {
u.Locked = 0
err = db.store.Set(userPrefix+userKey, u)
if err != nil {
return
}
}
return
}
func (db *DB) Totp(pin string) (u core.User, err error) {
u, err = db.GetUser()
if err != nil {
return
}
if u.Locked == 3 {
err = errLocked
return
}
if !otp.Validate(pin, u.Secret) {
u.Locked++
err = db.store.Set(userPrefix+userKey, u)
if err != nil {
return
}
err = errors.New("db: wrong pin")
return
}
if u.Locked > 0 {
u.Locked = 0
err = db.store.Set(userPrefix+userKey, u)
if err != nil {
return
}
}
return
}
func (db *DB) UnlockUser() (err error) {
var u core.User
u, err = db.GetUser()
if err != nil {
return
}
if u.Locked > 0 {
u.Locked = 0
err = db.store.Set(userPrefix+userKey, u)
if err != nil {
return
}
}
return
}
func (db *DB) UpdateUserSecret(secret string) error {
u, err := db.GetUser()
if err != nil {
return err
}
u.Secret = secret
return db.store.Set(userPrefix+userKey, u)
}
func (db *DB) UpdateUserPassword(password string) error {
if password == "" {
return errors.New("empty password")
}
u, err := db.GetUser()
if err != nil {
return err
}
hash, err := bcrypt.GenerateFromPassword(validatePassword(password), bcryptCost)
if err != nil {
return err
}
u.Password = string(hash)
return db.store.Set(userPrefix+userKey, u)
}