// Copyright (C) 2021 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" "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") ) 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 = nameRe.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) BasicAuth(username, password string) bool { _, err := db.Login(username, password) return err == nil } 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) }