// Copyright (C) 2018 Marius Schellenberger package db import ( "errors" "git.giftfish.de/ston1th/gowiki/pkg/core" "git.giftfish.de/ston1th/gowiki/pkg/log" "git.giftfish.de/ston1th/gowiki/pkg/otp" "git.giftfish.de/ston1th/jwt/v3" "golang.org/x/crypto/bcrypt" "regexp" "sort" "strconv" "strings" ) const userPrefix = "user/" var ( errUserNotFound = errors.New("db: user not found") errUserExists = errors.New("db: user already exist") errUserReserved = errors.New("db: username is reserved") userRe = regexp.MustCompile("[^a-zA-Z0-9]+") ) func (db *DB) GetUsers() (users core.Users, err error) { err = db.store.ForEach(func(k string, _ []byte) error { if trim, ok := hasTrimPrefix(k, userPrefix); ok { u, err := db.GetUser(trim) u.Password = "" if err == nil { users = append(users, u) } } return nil }) sort.Sort(users) return } func (db *DB) GetUserWithoutPassword(username string) (u core.User, err error) { u, err = db.GetUser(username) u.Password = "" return } func (db *DB) GetUserSecret(username string) (secret string, err error) { var user core.User user, err = db.GetUser(username) secret = user.Secret return } func (db *DB) GetUser(username string) (u core.User, err error) { err = db.store.Get(userPrefix+username, &u) return } func (db *DB) UserExists(username string) error { return db.store.Get(userPrefix+username, nil) } func (db *DB) CreateUser(username, password string, admin bool) error { username = userRe.ReplaceAllString(username, "") if reservedUser(username) { return errUserReserved } if err := db.UserExists(username); err == nil { return errUserExists } hash, err := bcrypt.GenerateFromPassword(validatePassword(password), bcryptCost) if err != nil { return err } return db.store.Set(userPrefix+username, &core.User{ Username: username, Password: string(hash), Created: strconv.Itoa(int(jwt.Now())), Admin: admin, }) } func (db *DB) Login(username, password string) (u core.User, err error) { u, err = db.GetUser(username) if err != nil { return } if u.Locked == 3 { err = errors.New("db: user locked") 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 = errors.New("db: wrong login") return } if u.Locked > 0 { u.Locked = 0 err = db.store.Set(userPrefix+username, u) if err != nil { return } } return } func (db *DB) Totp(username, pin string) (u core.User, err error) { u, err = db.GetUser(username) if err != nil { return } if u.Locked == 3 { err = errors.New("db: user locked") return } if !otp.Validate(pin, u.Secret) { u.Locked++ err = db.store.Set(userPrefix+username, u) if err != nil { return } err = errors.New("db: wrong pin") return } if u.Locked > 0 { u.Locked = 0 err = db.store.Set(userPrefix+username, u) if err != nil { return } } return } func (db *DB) UnlockUser(username string) (err error) { var u core.User u, err = db.GetUser(username) if err != nil { return } if u.Locked > 0 { u.Locked = 0 err = db.store.Set(userPrefix+username, u) if err != nil { return } } return } func (db *DB) UpdateUserSecret(username, secret string) error { u, err := db.GetUser(username) if err != nil { return err } u.Secret = secret return db.store.Set(userPrefix+username, u) } func (db *DB) UpdateUserPassword(username, password string) error { u, err := db.GetUser(username) 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+username, u) } func (db *DB) AdminUpdateUser(username, password string, admin bool) error { if username == defUser && !admin { return errors.New("user '" + defUser + "' cannot lose admin privileges") } u, err := db.GetUser(username) if err != nil { return err } u.Admin = admin if password != "" { hash, err := bcrypt.GenerateFromPassword(validatePassword(password), bcryptCost) if err != nil { return err } u.Password = string(hash) } return db.store.Set(userPrefix+username, u) } func (db *DB) DeleteUser(username string) error { if username == defUser { return errors.New("user '" + defUser + "' cannot be deleted") } u, err := db.GetUser(username) if err != nil { return err } err = db.store.Delete(userPrefix + username) if err != nil { return err } err = db.lockout(username, u.Created) if err != nil { return err } username += "/" var pages []string db.store.ForEach(func(k string, _ []byte) error { if trim, ok := hasTrimPrefix(k, pagePrefix); ok { if strings.HasPrefix(trim, username) { pages = append(pages, trim) } } return nil }) for _, p := range pages { err := db.deletePage(p) if err != nil { log.Debugf("DeleteUser %s, %s", username, err) } } return nil }