From 8143e484cd5332b396c2624fafea95ae2adc3122 Mon Sep 17 00:00:00 2001 From: ston1th Date: Sat, 6 Jul 2019 11:31:12 +0200 Subject: [PATCH] added prefix scan and fixed logout issue --- pkg/db/blacklist.go | 14 ++++++-------- pkg/db/db.go | 10 ++++------ pkg/db/helper.go | 8 -------- pkg/db/migration.go | 6 ++---- pkg/db/page.go | 44 +++++++++++++++++++----------------------- pkg/db/section.go | 18 +++++++---------- pkg/db/user.go | 23 +++++++++++----------- pkg/server/context.go | 10 ++++++++++ pkg/server/handler.go | 22 ++++++++++++++++----- pkg/store/boltstore.go | 9 +++++++++ pkg/store/helper.go | 11 +++++++++++ pkg/store/store.go | 1 + 12 files changed, 99 insertions(+), 77 deletions(-) create mode 100644 pkg/store/helper.go diff --git a/pkg/db/blacklist.go b/pkg/db/blacklist.go index e7160ae..ebc7653 100644 --- a/pkg/db/blacklist.go +++ b/pkg/db/blacklist.go @@ -21,15 +21,13 @@ func (db *DB) Check(sig string) (ok bool) { 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 + err = db.store.ForEachPrefix(blacklistPrefix, func(k string, v []byte) error { + var exp int64 + err := db.store.Unmarshal(v, &exp) + if err != nil { + return err } + list[k] = exp return nil }) return diff --git a/pkg/db/db.go b/pkg/db/db.go index d152fa3..190c6ec 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -96,12 +96,10 @@ func (db *DB) Restore(r io.Reader) (err error) { if err != nil { return } - err = db.store.ForEach(func(k string, _ []byte) error { - if trim, ok := hasTrimPrefix(k, pagePrefix); ok { - p := new(core.Page) - db.store.Get(pagePrefix+trim, p) - db.Index.Add(p.Title, p.StoreTitle, render.Render(p)) - } + err = db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error { + p := new(core.Page) + db.store.Get(pagePrefix+k, p) + db.Index.Add(p.Title, p.StoreTitle, render.Render(p)) return nil }) return diff --git a/pkg/db/helper.go b/pkg/db/helper.go index 820c792..860a4e5 100644 --- a/pkg/db/helper.go +++ b/pkg/db/helper.go @@ -20,14 +20,6 @@ var reservedNames = []string{ "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) { b = []byte(pw) if len(pw) <= 56 { diff --git a/pkg/db/migration.go b/pkg/db/migration.go index 441ada2..ef2fd90 100644 --- a/pkg/db/migration.go +++ b/pkg/db/migration.go @@ -21,10 +21,8 @@ var migrators = []migrator{ version = 1 // recreate all pages with new format var pages []string - db.store.ForEach(func(k string, _ []byte) error { - if trim, ok := hasTrimPrefix(k, pagePrefix); ok { - pages = append(pages, trim) - } + db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error { + pages = append(pages, k) return nil }) var p core.Page diff --git a/pkg/db/page.go b/pkg/db/page.go index 3d6396d..09d080d 100644 --- a/pkg/db/page.go +++ b/pkg/db/page.go @@ -24,19 +24,17 @@ var ( func (db *DB) GetAllSections(username string, admin bool) (sections core.SectionPages) { m := make(map[string]core.UpdatedPages) - 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 { - s := trim[:strings.Index(trim, "/")] - if _, ok := m[s]; !ok { - m[s] = core.UpdatedPages{} - } - m[s] = append(m[s], *p) + db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error { + if k == core.IndexURI { + return nil + } + p, err := db.getPage(k, username) + if err == nil { + s := k[:strings.Index(k, "/")] + if _, ok := m[s]; !ok { + m[s] = core.UpdatedPages{} } + m[s] = append(m[s], *p) } return nil }) @@ -67,18 +65,16 @@ func (db *DB) GetAllSectionPages(section, username string, admin bool) (pages co if err != nil { return } - db.store.ForEach(func(k string, _ []byte) error { - if trim, ok := hasTrimPrefix(k, pagePrefix); ok { - if !strings.HasPrefix(trim, section+"/") { - return nil - } - if trim == core.IndexURI { - return nil - } - p, err := db.getPage(trim, username) - if err == nil { - pages = append(pages, *p) - } + db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error { + if !strings.HasPrefix(k, section+"/") { + return nil + } + if k == core.IndexURI { + return nil + } + p, err := db.getPage(k, username) + if err == nil { + pages = append(pages, *p) } return nil }) diff --git a/pkg/db/section.go b/pkg/db/section.go index 39a108e..b700301 100644 --- a/pkg/db/section.go +++ b/pkg/db/section.go @@ -17,12 +17,10 @@ var ( ) func (db *DB) GetSections() (secs core.Sections, err error) { - err = db.store.ForEach(func(k string, _ []byte) error { - if trim, ok := hasTrimPrefix(k, sectionPrefix); ok { - s, err := db.GetSection(trim) - if err == nil { - secs = append(secs, s) - } + err = db.store.ForEachPrefix(sectionPrefix, func(k string, _ []byte) error { + s, err := db.GetSection(k) + if err == nil { + secs = append(secs, s) } return nil }) @@ -96,11 +94,9 @@ func (db *DB) DeleteSection(section string) error { } section += "/" var pages []string - db.store.ForEach(func(k string, _ []byte) error { - if trim, ok := hasTrimPrefix(k, pagePrefix); ok { - if strings.HasPrefix(trim, section) { - pages = append(pages, trim) - } + db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error { + if strings.HasPrefix(k, section) { + pages = append(pages, k) } return nil }) diff --git a/pkg/db/user.go b/pkg/db/user.go index 1954662..3bea219 100644 --- a/pkg/db/user.go +++ b/pkg/db/user.go @@ -16,6 +16,9 @@ import ( const userPrefix = "user/" 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") errUserExists = errors.New("db: user already exist") errNameReserved = errors.New("db: name is reserved") @@ -24,13 +27,11 @@ var ( ) 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) - } + err = db.store.ForEachPrefix(userPrefix, func(k string, _ []byte) error { + u, err := db.GetUser(k) + u.Password = "" + if err == nil { + users = append(users, u) } return nil }) @@ -94,7 +95,7 @@ func (db *DB) Login(username, password string) (u core.User, err error) { return } if u.Locked == 3 { - err = errors.New("db: user locked") + err = errUserLocked return } 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 { return } - err = errors.New("db: wrong login") + err = errUserLogin return } if u.Locked > 0 { @@ -122,7 +123,7 @@ func (db *DB) Totp(username, pin string) (u core.User, err error) { return } if u.Locked == 3 { - err = errors.New("db: user locked") + err = errUserLocked return } 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 { return } - err = errors.New("db: wrong pin") + err = errUserPin return } if u.Locked > 0 { diff --git a/pkg/server/context.go b/pkg/server/context.go index d178070..cc7491d 100644 --- a/pkg/server/context.go +++ b/pkg/server/context.go @@ -25,6 +25,8 @@ const ( totpClaim = "totp" rememberClaim = "remember" refererClaim = "referer" + + empty = "(empty)" ) func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) *Context { @@ -211,6 +213,14 @@ func (c *Context) Var(name string) (ret string) { 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 func (c *Context) CheckXsrf() (ok bool) { ok = checkXsrf(c.Form("token"), c.Token.RawSig()[:keySize]) diff --git a/pkg/server/handler.go b/pkg/server/handler.go index 4c82dd5..ee90602 100644 --- a/pkg/server/handler.go +++ b/pkg/server/handler.go @@ -26,10 +26,12 @@ func jwtHandler(h ctxHandler) ctxHandler { t, err := jwt.DecodeToken(c.Value) if err != nil { ctx.LogSetCookie("DecodeToken:", err) + ctx.Redirect(core.IndexURI, http.StatusFound) return } if err = ctx.Srv.JWT.Verify(t); err != nil { ctx.LogSetCookie("VerifyToken:", err) + ctx.Redirect(core.IndexURI, http.StatusFound) return } if t.Claims.GetString(totpClaim) != "" { @@ -156,7 +158,7 @@ func loginHandler(ctx *Context) { } u, err := ctx.Srv.DB.Login(data.User, password) 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") return } @@ -197,8 +199,10 @@ func loginTotpHandler(ctx *Context) { ctx.Error("empty pin") return } - u, err := ctx.Srv.DB.Totp(ctx.Totp(), pin) + user := ctx.Totp() + u, err := ctx.Srv.DB.Totp(user, pin) if err != nil { + log.Printf("totp: %s from: %s %s", user, ctx.Forwarded(), err) ctx.Error(err) return } @@ -875,12 +879,20 @@ func userDelHandler(ctx *Context) { } func logoutHandler(ctx *Context) { + var totp string user := ctx.User() if user == "" { - user = ctx.Totp() + " (totp)" + user = ctx.Totp() + if user != "" { + totp = " (totp)" + } } - log.Println("logout:", user) - ctx.Srv.JWT.Invalidate(&ctx.Token) + if user != "" { + ctx.Srv.JWT.Invalidate(&ctx.Token) + } else { + user = empty + } + log.Printf("logout: %s%s", user, totp) ctx.SetCookie(nil, 0) ctx.Redirect(core.IndexURI, http.StatusFound) } diff --git a/pkg/store/boltstore.go b/pkg/store/boltstore.go index edf1fcb..4d9c006 100644 --- a/pkg/store/boltstore.go +++ b/pkg/store/boltstore.go @@ -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 { return bs.db.Update(func(tx *bolt.Tx) error { return tx.Bucket([]byte(defaultBoltBucket)).Delete([]byte(key)) diff --git a/pkg/store/helper.go b/pkg/store/helper.go new file mode 100644 index 0000000..db17a72 --- /dev/null +++ b/pkg/store/helper.go @@ -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 +} diff --git a/pkg/store/store.go b/pkg/store/store.go index 88e8500..4a5f90b 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -17,6 +17,7 @@ type Store interface { Get(string, interface{}) error Set(string, interface{}) error ForEach(func(string, []byte) error) error + ForEachPrefix(string, func(string, []byte) error) error Delete(string) error Close() error }