single db with prefixes, dump/restore, blacklisting on user deletion
This commit is contained in:
parent
62ff59957a
commit
d4971cda89
20 changed files with 264 additions and 163 deletions
36
pkg/db/blacklist.go
Normal file
36
pkg/db/blacklist.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package db
|
||||
|
||||
import "git.giftfish.de/ston1th/jwt/v3"
|
||||
|
||||
const blacklistPrefix = "blacklist/"
|
||||
|
||||
func (db *DB) Add(sig string, exp int64) error {
|
||||
return db.store.Set(blacklistPrefix+sig, exp)
|
||||
}
|
||||
|
||||
func (db *DB) Remove(sig string) error {
|
||||
return db.store.Delete(blacklistPrefix + sig)
|
||||
}
|
||||
|
||||
func (db *DB) Check(sig string) (ok bool) {
|
||||
if db.store.Get(blacklistPrefix+sig, nil) == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (db *DB) Map() (list jwt.BlacklistMap, err error) {
|
||||
list = make(jwt.BlacklistMap)
|
||||
err = db.store.ForEach(func(k string, v []byte) error {
|
||||
if trim, ok := hasTrimPrefix(k, blacklistPrefix); ok {
|
||||
var exp int64
|
||||
err := db.store.Unmarshal(v, &exp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
list[trim] = exp
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
18
pkg/db/db.go
18
pkg/db/db.go
|
|
@ -13,8 +13,7 @@ import (
|
|||
const (
|
||||
defUser = "admin"
|
||||
defPassword = "gowiki"
|
||||
usersFile = "users.db"
|
||||
pagesFile = "pages.db"
|
||||
storeFile = "store.db"
|
||||
bcryptCost = 13
|
||||
welcome = `# Welcome to GoWiki
|
||||
This is the [Index](/wiki/Index) page.
|
||||
|
|
@ -28,8 +27,7 @@ func dbErr(i interface{}) error {
|
|||
}
|
||||
|
||||
type DB struct {
|
||||
pages store.Store
|
||||
users store.Store
|
||||
store store.Store
|
||||
cache *cache.Cache
|
||||
Index *index.Index
|
||||
}
|
||||
|
|
@ -41,7 +39,7 @@ func New(datadir string) (db *DB, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
db.users, err = store.NewBoltStore(usersFile, nil)
|
||||
db.store, err = store.NewBoltStore(storeFile, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -56,10 +54,6 @@ func New(datadir string) (db *DB, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
db.pages, err = store.NewBoltStore(pagesFile, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = db.CreatePage(core.IndexPage, core.WikiSection, welcome, defUser, core.Public)
|
||||
if err != nil && err != store.ErrKeyNotFound {
|
||||
return
|
||||
|
|
@ -68,11 +62,7 @@ func New(datadir string) (db *DB, err error) {
|
|||
}
|
||||
|
||||
func (db *DB) Close() error {
|
||||
err := db.pages.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = db.users.Close()
|
||||
err := db.store.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@ var reservedUsers = []string{
|
|||
"wiki",
|
||||
}
|
||||
|
||||
// Parts taken from strings.TrimPrefix
|
||||
func hasTrimPrefix(s, prefix string) (string, bool) {
|
||||
if len(s) >= len(prefix) && s[0:len(prefix)] == prefix {
|
||||
return s[len(prefix):], true
|
||||
}
|
||||
return s, false
|
||||
}
|
||||
|
||||
func reservedUser(username string) bool {
|
||||
for _, v := range reservedUsers {
|
||||
if username == v {
|
||||
|
|
|
|||
11
pkg/db/lockout.go
Normal file
11
pkg/db/lockout.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package db
|
||||
|
||||
const lockoutPrefix = "lockout/"
|
||||
|
||||
func (db *DB) lockout(username, created string) error {
|
||||
return db.store.Set(lockoutPrefix+username+created, nil)
|
||||
}
|
||||
|
||||
func (db *DB) LockedOut(username, created string) bool {
|
||||
return db.store.Get(lockoutPrefix+username+created, nil) == nil
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"sort"
|
||||
)
|
||||
|
||||
const pagePrefix = "page/"
|
||||
|
||||
var (
|
||||
errPageNotFound = store.ErrKeyNotFound
|
||||
errPermissionDenied = dbErr("permission denied")
|
||||
|
|
@ -18,13 +20,15 @@ var (
|
|||
)
|
||||
|
||||
func (db *DB) GetAllPages(username string) (pages core.Pages) {
|
||||
db.pages.ForEach(func(title string, _ []byte) error {
|
||||
if title == core.IndexURI {
|
||||
return nil
|
||||
}
|
||||
p, err := db.GetPage(title, username)
|
||||
if err == nil {
|
||||
pages = append(pages, *p)
|
||||
db.store.ForEach(func(k string, _ []byte) error {
|
||||
if trim, ok := hasTrimPrefix(k, pagePrefix); ok {
|
||||
if trim == core.IndexURI {
|
||||
return nil
|
||||
}
|
||||
p, err := db.GetPage(trim, username)
|
||||
if err == nil {
|
||||
pages = append(pages, *p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
@ -47,7 +51,7 @@ func (db *DB) GetPage(title, username string) (p *core.Page, err error) {
|
|||
|
||||
func (db *DB) getPage(title, username string) (p *core.Page, err error) {
|
||||
p = new(core.Page)
|
||||
err = db.pages.Get(title, p)
|
||||
err = db.store.Get(pagePrefix+title, p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -85,7 +89,7 @@ func (db *DB) CreatePage(title, section, markdown, username string, p core.Permi
|
|||
}
|
||||
search := render.Render(page)
|
||||
|
||||
err = db.pages.Set(st, page)
|
||||
err = db.store.Set(pagePrefix+st, page)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -121,7 +125,7 @@ func (db *DB) UpdatePage(title, section, markdown, username string, p core.Permi
|
|||
page.Perm = p
|
||||
search := render.Render(page)
|
||||
|
||||
err = db.pages.Set(st, page)
|
||||
err = db.store.Set(pagePrefix+st, page)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -134,6 +138,14 @@ func (db *DB) UpdatePage(title, section, markdown, username string, p core.Permi
|
|||
return
|
||||
}
|
||||
|
||||
func (db *DB) deletePage(st string) (err error) {
|
||||
if err = db.store.Delete(pagePrefix + st); err != nil {
|
||||
return
|
||||
}
|
||||
db.cache.Delete(st)
|
||||
return db.Index.Delete(st)
|
||||
}
|
||||
|
||||
func (db *DB) DeletePage(title, section, username string) (err error) {
|
||||
var page *core.Page
|
||||
if section == core.WikiSection && title == core.IndexPage {
|
||||
|
|
@ -147,10 +159,5 @@ func (db *DB) DeletePage(title, section, username string) (err error) {
|
|||
if !core.WritePerm(username, section, page) {
|
||||
return errPermissionDenied
|
||||
}
|
||||
|
||||
if err = db.pages.Delete(st); err != nil {
|
||||
return
|
||||
}
|
||||
db.cache.Delete(st)
|
||||
return db.Index.Delete(st)
|
||||
return db.deletePage(st)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,16 @@ package db
|
|||
import (
|
||||
"errors"
|
||||
"git.giftfish.de/ston1th/gowiki/pkg/core"
|
||||
"git.giftfish.de/ston1th/gowiki/pkg/log"
|
||||
"git.giftfish.de/ston1th/jwt/v3"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const userPrefix = "user/"
|
||||
|
||||
var (
|
||||
errUserNotFound = dbErr("user not found")
|
||||
errUserExists = dbErr("user already exist")
|
||||
|
|
@ -16,10 +22,12 @@ var (
|
|||
)
|
||||
|
||||
func (db *DB) DumpUsers() (users core.Users, err error) {
|
||||
db.users.ForEach(func(username string, _ []byte) error {
|
||||
u, err := db.GetUser(username)
|
||||
if err == nil {
|
||||
users = append(users, u)
|
||||
db.store.ForEach(func(k string, _ []byte) error {
|
||||
if trim, ok := hasTrimPrefix(k, userPrefix); ok {
|
||||
u, err := db.GetUser(trim)
|
||||
if err == nil {
|
||||
users = append(users, u)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
@ -42,12 +50,12 @@ func (db *DB) GetUserWithoutPassword(username string) (u core.User, err error) {
|
|||
}
|
||||
|
||||
func (db *DB) GetUser(username string) (u core.User, err error) {
|
||||
err = db.users.Get(username, &u)
|
||||
err = db.store.Get(userPrefix+username, &u)
|
||||
return
|
||||
}
|
||||
|
||||
func (db *DB) UserExists(username string) error {
|
||||
return db.users.Get(username, nil)
|
||||
return db.store.Get(userPrefix+username, nil)
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
|
@ -85,9 +93,10 @@ func (db *DB) CreateUser(username, password string, admin bool) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.users.Set(username, &core.User{
|
||||
return db.store.Set(userPrefix+username, &core.User{
|
||||
Username: username,
|
||||
Password: string(hash),
|
||||
Created: strconv.Itoa(int(jwt.Now())),
|
||||
Admin: admin,
|
||||
})
|
||||
}
|
||||
|
|
@ -103,7 +112,7 @@ func (db *DB) Login(username, password string) (u core.User, err error) {
|
|||
}
|
||||
if err = bcrypt.CompareHashAndPassword([]byte(u.Password), validatePassword(password)); err != nil {
|
||||
u.Locked++
|
||||
err = db.users.Set(username, u)
|
||||
err = db.store.Set(userPrefix+username, u)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -112,7 +121,7 @@ func (db *DB) Login(username, password string) (u core.User, err error) {
|
|||
}
|
||||
if u.Locked > 0 {
|
||||
u.Locked = 0
|
||||
err = db.users.Set(username, u)
|
||||
err = db.store.Set(userPrefix+username, u)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -128,7 +137,7 @@ func (db *DB) UnlockUser(username string) (err error) {
|
|||
}
|
||||
if u.Locked > 0 {
|
||||
u.Locked = 0
|
||||
err = db.users.Set(username, u)
|
||||
err = db.store.Set(userPrefix+username, u)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -146,7 +155,7 @@ func (db *DB) UpdateUserPassword(username, password string) error {
|
|||
return err
|
||||
}
|
||||
u.Password = string(hash)
|
||||
return db.users.Set(username, u)
|
||||
return db.store.Set(userPrefix+username, u)
|
||||
}
|
||||
|
||||
func (db *DB) AdminUpdateUser(username, password string, admin bool) error {
|
||||
|
|
@ -165,13 +174,40 @@ func (db *DB) AdminUpdateUser(username, password string, admin bool) error {
|
|||
}
|
||||
u.Password = string(hash)
|
||||
}
|
||||
return db.users.Set(username, u)
|
||||
return db.store.Set(userPrefix+username, u)
|
||||
}
|
||||
|
||||
//TODO delete all user owned pages
|
||||
func (db *DB) DeleteUser(username string) error {
|
||||
if username == defUser {
|
||||
return errors.New("user '" + defUser + "' cannot be deleted")
|
||||
}
|
||||
return db.users.Delete(username)
|
||||
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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue