added prefix scan and fixed logout issue
This commit is contained in:
parent
4812a60367
commit
8143e484cd
12 changed files with 99 additions and 77 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
10
pkg/db/db.go
10
pkg/db/db.go
|
|
@ -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+k, p)
|
||||||
db.store.Get(pagePrefix+trim, 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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,17 @@ 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(k, username)
|
||||||
p, err := db.getPage(trim, username)
|
if err == nil {
|
||||||
if err == nil {
|
s := k[:strings.Index(k, "/")]
|
||||||
s := trim[:strings.Index(trim, "/")]
|
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
|
||||||
})
|
})
|
||||||
|
|
@ -67,18 +65,16 @@ 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 k == core.IndexURI {
|
||||||
if trim == core.IndexURI {
|
return nil
|
||||||
return nil
|
}
|
||||||
}
|
p, err := db.getPage(k, username)
|
||||||
p, err := db.getPage(trim, username)
|
if err == nil {
|
||||||
if err == nil {
|
pages = append(pages, *p)
|
||||||
pages = append(pages, *p)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,10 @@ 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
|
||||||
})
|
})
|
||||||
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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,13 +27,11 @@ 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
|
||||||
})
|
})
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
11
pkg/store/helper.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue