added prefix scan and fixed logout issue

This commit is contained in:
ston1th 2019-07-06 11:31:12 +02:00
commit 8143e484cd
12 changed files with 99 additions and 77 deletions

View file

@ -21,15 +21,13 @@ func (db *DB) Check(sig string) (ok bool) {
func (db *DB) Map() (list jwt.BlacklistMap, err error) { func (db *DB) Map() (list jwt.BlacklistMap, err error) {
list = make(jwt.BlacklistMap) list = make(jwt.BlacklistMap)
err = db.store.ForEach(func(k string, v []byte) error { err = db.store.ForEachPrefix(blacklistPrefix, func(k string, v []byte) error {
if trim, ok := hasTrimPrefix(k, blacklistPrefix); ok {
var exp int64 var exp int64
err := db.store.Unmarshal(v, &exp) err := db.store.Unmarshal(v, &exp)
if err != nil { if err != nil {
return err return err
} }
list[trim] = exp list[k] = exp
}
return nil return nil
}) })
return return

View file

@ -96,12 +96,10 @@ func (db *DB) Restore(r io.Reader) (err error) {
if err != nil { if err != nil {
return return
} }
err = db.store.ForEach(func(k string, _ []byte) error { err = db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error {
if trim, ok := hasTrimPrefix(k, pagePrefix); ok {
p := new(core.Page) p := new(core.Page)
db.store.Get(pagePrefix+trim, p) db.store.Get(pagePrefix+k, p)
db.Index.Add(p.Title, p.StoreTitle, render.Render(p)) db.Index.Add(p.Title, p.StoreTitle, render.Render(p))
}
return nil return nil
}) })
return return

View file

@ -20,14 +20,6 @@ var reservedNames = []string{
"view", "view",
} }
// 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 validatePassword(pw string) (b []byte) { func validatePassword(pw string) (b []byte) {
b = []byte(pw) b = []byte(pw)
if len(pw) <= 56 { if len(pw) <= 56 {

View file

@ -21,10 +21,8 @@ var migrators = []migrator{
version = 1 version = 1
// recreate all pages with new format // recreate all pages with new format
var pages []string var pages []string
db.store.ForEach(func(k string, _ []byte) error { db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error {
if trim, ok := hasTrimPrefix(k, pagePrefix); ok { pages = append(pages, k)
pages = append(pages, trim)
}
return nil return nil
}) })
var p core.Page var p core.Page

View file

@ -24,20 +24,18 @@ var (
func (db *DB) GetAllSections(username string, admin bool) (sections core.SectionPages) { func (db *DB) GetAllSections(username string, admin bool) (sections core.SectionPages) {
m := make(map[string]core.UpdatedPages) m := make(map[string]core.UpdatedPages)
db.store.ForEach(func(k string, _ []byte) error { db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error {
if trim, ok := hasTrimPrefix(k, pagePrefix); ok { if k == core.IndexURI {
if trim == core.IndexURI {
return nil return nil
} }
p, err := db.getPage(trim, username) p, err := db.getPage(k, username)
if err == nil { if err == nil {
s := trim[:strings.Index(trim, "/")] s := k[:strings.Index(k, "/")]
if _, ok := m[s]; !ok { if _, ok := m[s]; !ok {
m[s] = core.UpdatedPages{} m[s] = core.UpdatedPages{}
} }
m[s] = append(m[s], *p) m[s] = append(m[s], *p)
} }
}
return nil return nil
}) })
if admin { if admin {
@ -67,19 +65,17 @@ func (db *DB) GetAllSectionPages(section, username string, admin bool) (pages co
if err != nil { if err != nil {
return return
} }
db.store.ForEach(func(k string, _ []byte) error { db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error {
if trim, ok := hasTrimPrefix(k, pagePrefix); ok { if !strings.HasPrefix(k, section+"/") {
if !strings.HasPrefix(trim, section+"/") {
return nil return nil
} }
if trim == core.IndexURI { if k == core.IndexURI {
return nil return nil
} }
p, err := db.getPage(trim, username) p, err := db.getPage(k, username)
if err == nil { if err == nil {
pages = append(pages, *p) pages = append(pages, *p)
} }
}
return nil return nil
}) })
if len(pages) == 0 && !admin { if len(pages) == 0 && !admin {

View file

@ -17,13 +17,11 @@ var (
) )
func (db *DB) GetSections() (secs core.Sections, err error) { func (db *DB) GetSections() (secs core.Sections, err error) {
err = db.store.ForEach(func(k string, _ []byte) error { err = db.store.ForEachPrefix(sectionPrefix, func(k string, _ []byte) error {
if trim, ok := hasTrimPrefix(k, sectionPrefix); ok { s, err := db.GetSection(k)
s, err := db.GetSection(trim)
if err == nil { if err == nil {
secs = append(secs, s) secs = append(secs, s)
} }
}
return nil return nil
}) })
sort.Sort(secs) sort.Sort(secs)
@ -96,11 +94,9 @@ func (db *DB) DeleteSection(section string) error {
} }
section += "/" section += "/"
var pages []string var pages []string
db.store.ForEach(func(k string, _ []byte) error { db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error {
if trim, ok := hasTrimPrefix(k, pagePrefix); ok { if strings.HasPrefix(k, section) {
if strings.HasPrefix(trim, section) { pages = append(pages, k)
pages = append(pages, trim)
}
} }
return nil return nil
}) })

View file

@ -16,6 +16,9 @@ import (
const userPrefix = "user/" const userPrefix = "user/"
var ( var (
errUserLocked = errors.New("db: user locked")
errUserLogin = errors.New("db: wrong login")
errUserPin = errors.New("db: wrong pin")
errUserNotFound = errors.New("db: user not found") errUserNotFound = errors.New("db: user not found")
errUserExists = errors.New("db: user already exist") errUserExists = errors.New("db: user already exist")
errNameReserved = errors.New("db: name is reserved") errNameReserved = errors.New("db: name is reserved")
@ -24,14 +27,12 @@ var (
) )
func (db *DB) GetUsers() (users core.Users, err error) { func (db *DB) GetUsers() (users core.Users, err error) {
err = db.store.ForEach(func(k string, _ []byte) error { err = db.store.ForEachPrefix(userPrefix, func(k string, _ []byte) error {
if trim, ok := hasTrimPrefix(k, userPrefix); ok { u, err := db.GetUser(k)
u, err := db.GetUser(trim)
u.Password = "" u.Password = ""
if err == nil { if err == nil {
users = append(users, u) users = append(users, u)
} }
}
return nil return nil
}) })
sort.Sort(users) sort.Sort(users)
@ -94,7 +95,7 @@ func (db *DB) Login(username, password string) (u core.User, err error) {
return return
} }
if u.Locked == 3 { if u.Locked == 3 {
err = errors.New("db: user locked") err = errUserLocked
return return
} }
if err = bcrypt.CompareHashAndPassword([]byte(u.Password), validatePassword(password)); err != nil { if err = bcrypt.CompareHashAndPassword([]byte(u.Password), validatePassword(password)); err != nil {
@ -103,7 +104,7 @@ func (db *DB) Login(username, password string) (u core.User, err error) {
if err != nil { if err != nil {
return return
} }
err = errors.New("db: wrong login") err = errUserLogin
return return
} }
if u.Locked > 0 { if u.Locked > 0 {
@ -122,7 +123,7 @@ func (db *DB) Totp(username, pin string) (u core.User, err error) {
return return
} }
if u.Locked == 3 { if u.Locked == 3 {
err = errors.New("db: user locked") err = errUserLocked
return return
} }
if !otp.Validate(pin, u.Secret) { if !otp.Validate(pin, u.Secret) {
@ -131,7 +132,7 @@ func (db *DB) Totp(username, pin string) (u core.User, err error) {
if err != nil { if err != nil {
return return
} }
err = errors.New("db: wrong pin") err = errUserPin
return return
} }
if u.Locked > 0 { if u.Locked > 0 {

View file

@ -25,6 +25,8 @@ const (
totpClaim = "totp" totpClaim = "totp"
rememberClaim = "remember" rememberClaim = "remember"
refererClaim = "referer" refererClaim = "referer"
empty = "(empty)"
) )
func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) *Context { func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) *Context {
@ -211,6 +213,14 @@ func (c *Context) Var(name string) (ret string) {
return return
} }
func (c *Context) Forwarded() (ret string) {
ret = c.Request.Header.Get("X-Forwarded-For")
if ret == "" {
ret = empty
}
return
}
// CheckXsrf validates the xsrf token // CheckXsrf validates the xsrf token
func (c *Context) CheckXsrf() (ok bool) { func (c *Context) CheckXsrf() (ok bool) {
ok = checkXsrf(c.Form("token"), c.Token.RawSig()[:keySize]) ok = checkXsrf(c.Form("token"), c.Token.RawSig()[:keySize])

View file

@ -26,10 +26,12 @@ func jwtHandler(h ctxHandler) ctxHandler {
t, err := jwt.DecodeToken(c.Value) t, err := jwt.DecodeToken(c.Value)
if err != nil { if err != nil {
ctx.LogSetCookie("DecodeToken:", err) ctx.LogSetCookie("DecodeToken:", err)
ctx.Redirect(core.IndexURI, http.StatusFound)
return return
} }
if err = ctx.Srv.JWT.Verify(t); err != nil { if err = ctx.Srv.JWT.Verify(t); err != nil {
ctx.LogSetCookie("VerifyToken:", err) ctx.LogSetCookie("VerifyToken:", err)
ctx.Redirect(core.IndexURI, http.StatusFound)
return return
} }
if t.Claims.GetString(totpClaim) != "" { if t.Claims.GetString(totpClaim) != "" {
@ -156,7 +158,7 @@ func loginHandler(ctx *Context) {
} }
u, err := ctx.Srv.DB.Login(data.User, password) u, err := ctx.Srv.DB.Login(data.User, password)
if err != nil { if err != nil {
log.Println("login:", data.User, err) log.Printf("login: %s from: %s %s", data.User, ctx.Forwarded(), err)
ctx.Error("bad username or password") ctx.Error("bad username or password")
return return
} }
@ -197,8 +199,10 @@ func loginTotpHandler(ctx *Context) {
ctx.Error("empty pin") ctx.Error("empty pin")
return return
} }
u, err := ctx.Srv.DB.Totp(ctx.Totp(), pin) user := ctx.Totp()
u, err := ctx.Srv.DB.Totp(user, pin)
if err != nil { if err != nil {
log.Printf("totp: %s from: %s %s", user, ctx.Forwarded(), err)
ctx.Error(err) ctx.Error(err)
return return
} }
@ -875,12 +879,20 @@ func userDelHandler(ctx *Context) {
} }
func logoutHandler(ctx *Context) { func logoutHandler(ctx *Context) {
var totp string
user := ctx.User() user := ctx.User()
if user == "" { if user == "" {
user = ctx.Totp() + " (totp)" user = ctx.Totp()
if user != "" {
totp = " (totp)"
} }
log.Println("logout:", user) }
if user != "" {
ctx.Srv.JWT.Invalidate(&ctx.Token) ctx.Srv.JWT.Invalidate(&ctx.Token)
} else {
user = empty
}
log.Printf("logout: %s%s", user, totp)
ctx.SetCookie(nil, 0) ctx.SetCookie(nil, 0)
ctx.Redirect(core.IndexURI, http.StatusFound) ctx.Redirect(core.IndexURI, http.StatusFound)
} }

View file

@ -119,6 +119,15 @@ func (bs *BoltStore) ForEach(f func(string, []byte) error) error {
}) })
} }
func (bs *BoltStore) ForEachPrefix(prefix string, f func(string, []byte) error) error {
return bs.ForEach(func(k string, v []byte) error {
if trim, ok := hasTrimPrefix(k, prefix); ok {
return f(trim, v)
}
return nil
})
}
func (bs *BoltStore) Delete(key string) error { func (bs *BoltStore) Delete(key string) error {
return bs.db.Update(func(tx *bolt.Tx) error { return bs.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket([]byte(defaultBoltBucket)).Delete([]byte(key)) return tx.Bucket([]byte(defaultBoltBucket)).Delete([]byte(key))

11
pkg/store/helper.go Normal file
View file

@ -0,0 +1,11 @@
// Copyright (C) 2019 Marius Schellenberger
package store
// 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
}

View file

@ -17,6 +17,7 @@ type Store interface {
Get(string, interface{}) error Get(string, interface{}) error
Set(string, interface{}) error Set(string, interface{}) error
ForEach(func(string, []byte) error) error ForEach(func(string, []byte) error) error
ForEachPrefix(string, func(string, []byte) error) error
Delete(string) error Delete(string) error
Close() error Close() error
} }