some tests and major cleanup

This commit is contained in:
ston1th 2018-10-19 23:08:32 +02:00
commit 16671b3406
30 changed files with 192057 additions and 202431 deletions

View file

@ -10,13 +10,19 @@ all: $(PROGRAM)
setup: setup:
$(CC) get github.com/client9/misspell $(CC) get github.com/client9/misspell
vendor: clean codeqa
CGO_ENABLED=0 $(CC) $(BUILD) -mod=vendor $(LDFLAGS)
$(PROGRAM): clean codeqa $(PROGRAM): clean codeqa
CGO_ENABLED=0 $(CC) $(BUILD) $(LDFLAGS) CGO_ENABLED=0 $(CC) $(BUILD) $(LDFLAGS)
clean: clean:
$(CC) clean -x $(CC) clean -x
codeqa: templates gofmt misspell golint codeqa: generate gofmt misspell golint test
generate:
$(CC) generate
gofmt: gofmt:
gofmt -w . gofmt -w .
@ -25,9 +31,14 @@ golint:
$(GOPATH)/bin/golint . $(GOPATH)/bin/golint .
misspell: misspell:
# ignore misspellings from vendor/
$(GOPATH)/bin/misspell -i functionallity,avaliable,HALP,halp,agains,becuase,Christiaan,compatiblity,conjuction,frequence,inital,inot,occurance,overriden,pervious,preceeded,preceeds,precendence,PROPOGATE,reamining,represenation,soley,substract,supression,whcih,preceed * $(GOPATH)/bin/misspell -i functionallity,avaliable,HALP,halp,agains,becuase,Christiaan,compatiblity,conjuction,frequence,inital,inot,occurance,overriden,pervious,preceeded,preceeds,precendence,PROPOGATE,reamining,represenation,soley,substract,supression,whcih,preceed *
templates: test:
$(CC) generate ifeq ($(shell go env GOARCH), $(shell go env GOHOSTARCH))
$(CC) test ./...
else
$(info skipping tests of other platforms)
endif
.PHONY: setup build clean templates gofmt misspell golint .PHONY: setup vendor build clean codeqa generate gofmt golint misspell test

1
go.mod
View file

@ -42,4 +42,5 @@ require (
github.com/urfave/cli v1.19.1 github.com/urfave/cli v1.19.1
github.com/willf/bitset v1.1.9 // indirect github.com/willf/bitset v1.1.9 // indirect
golang.org/x/crypto v0.0.0-20161031180806-9477e0b78b9a golang.org/x/crypto v0.0.0-20161031180806-9477e0b78b9a
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e
) )

50
pkg/cache/cache_test.go vendored Normal file
View file

@ -0,0 +1,50 @@
// Copyright (C) 2018 Marius Schellenberger
package cache
import (
"git.giftfish.de/ston1th/gowiki/pkg/core"
"testing"
)
func TestCache(t *testing.T) {
title := "test/page"
pages := []*core.Page{
&core.Page{
Title: title,
Markdown: "page v1",
},
&core.Page{
Title: title,
Markdown: "page v2",
},
}
c := NewCache()
t.Run("Add", func(t *testing.T) {
c.Add(title, pages[0])
})
t.Run("Get", func(t *testing.T) {
p := c.Get(title)
if p.Markdown != pages[0].Markdown {
t.Fail()
}
})
t.Run("Add", func(t *testing.T) {
c.Add(title, pages[1])
})
t.Run("Get", func(t *testing.T) {
p := c.Get(title)
if p.Markdown == pages[0].Markdown {
t.Fail()
}
})
t.Run("Delete", func(t *testing.T) {
c.Delete(title)
})
t.Run("Get", func(t *testing.T) {
p := c.Get(title)
if p != nil {
t.Fail()
}
})
}

View file

@ -32,6 +32,9 @@ var (
) )
func initServer(conf core.Config) (err error) { func initServer(conf core.Config) (err error) {
if err = core.Pledge("stdio rpath wpath cpath inet fattr flock proc exec id", ""); err != nil {
return
}
dropCfg := godrop.Config{ dropCfg := godrop.Config{
User: conf.User, User: conf.User,
Group: conf.Group, Group: conf.Group,
@ -47,7 +50,11 @@ func initServer(conf core.Config) (err error) {
return return
} }
log.NewLogger(conf.LogFile) if err = core.Pledge("stdio rpath wpath cpath inet fattr flock", ""); err != nil {
return
}
log.InitLogger(conf.DataDir, conf.LogFile, conf.Debug)
l, err := godrop.GetListener() l, err := godrop.GetListener()
if err != nil { if err != nil {
@ -89,7 +96,7 @@ func Run(version string) {
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
conf.Version = app.Version conf.Version = app.Version
if err := initServer(conf); err != nil { if err := initServer(conf); err != nil {
stdlog.Fatal("server: ", err) stdlog.Fatal(err)
} }
return nil return nil
}, },
@ -187,7 +194,7 @@ func serverFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "log", Name: "log",
Value: defLogFile, Value: defLogFile,
Usage: "log file (leave empty for stdout, this only works in combination with -f)", Usage: "log file (use - for stdout, this only works in combination with -f)",
Destination: &conf.LogFile, Destination: &conf.LogFile,
}, },
cli.StringFlag{ cli.StringFlag{
@ -208,7 +215,7 @@ func serverFlags() []cli.Flag {
cli.BoolFlag{ cli.BoolFlag{
Name: "debug", Name: "debug",
Usage: "enable debugging", Usage: "enable debugging",
Destination: &log.DebugEnabled, Destination: &conf.Debug,
}, },
} }
} }

View file

@ -11,18 +11,6 @@ const (
Private Private
) )
func ParsePerm(p int) Permission {
switch p {
case 1:
return Public
case 2:
return Internal
case 3:
return Private
}
return Invalid
}
func ParsePermString(p string) Permission { func ParsePermString(p string) Permission {
switch p { switch p {
case "1": case "1":
@ -51,10 +39,6 @@ func WritePerm(username, section string, p *Page) bool {
if section != WikiSection && section != username { if section != WikiSection && section != username {
return false return false
} }
return writePerm(username, p)
}
func writePerm(username string, p *Page) bool {
switch p.Perm { switch p.Perm {
case Public, Internal: case Public, Internal:
if p.Owner == WikiSection { if p.Owner == WikiSection {

View file

@ -0,0 +1,81 @@
// Copyright (C) 2018 Marius Schellenberger
package core
import (
"fmt"
"testing"
)
func TestParsePermString(t *testing.T) {
data := map[string]Permission{
"": Invalid,
"1": Public,
"2": Internal,
"3": Private,
"123": Invalid,
"test": Invalid,
}
for k, v := range data {
t.Run(k, func(t *testing.T) {
if ParsePermString(k) != v {
t.Fail()
}
})
}
}
func TestReadPerm(t *testing.T) {
data := []struct {
Username string
Page *Page
OK bool
}{
{"", &Page{Perm: Invalid, Owner: "admin"}, false},
{"", &Page{Perm: Public, Owner: "admin"}, true},
{"user", &Page{Perm: Public, Owner: "admin"}, true},
{"", &Page{Perm: Internal, Owner: "admin"}, false},
{"user", &Page{Perm: Internal, Owner: "admin"}, true},
{"", &Page{Perm: Private, Owner: "admin"}, false},
{"user", &Page{Perm: Private, Owner: "admin"}, false},
{"admin", &Page{Perm: Private, Owner: "admin"}, true},
}
for i, v := range data {
t.Run(fmt.Sprintf("Read:%d", i), func(t *testing.T) {
if ReadPerm(v.Username, v.Page) != v.OK {
t.Fail()
}
})
}
}
func TestWritePerm(t *testing.T) {
data := []struct {
Username string
Section string
Page *Page
OK bool
}{
{"user", "admin", &Page{Perm: Invalid, Owner: "admin"}, false},
{"", "user", &Page{Perm: Invalid, Owner: "admin"}, false},
{"user", "user", &Page{Perm: Invalid, Owner: "user"}, false},
{"user", "user", &Page{Perm: Public, Owner: "user"}, true},
{"user", "user", &Page{Perm: Internal, Owner: "user"}, true},
{"user", "user", &Page{Perm: Private, Owner: "user"}, true},
{"", WikiSection, &Page{Perm: Invalid, Owner: "admin"}, false},
{"", WikiSection, &Page{Perm: Public, Owner: "admin"}, false},
{"", WikiSection, &Page{Perm: Internal, Owner: "admin"}, false},
{"user", WikiSection, &Page{Perm: Public, Owner: WikiSection}, true},
{"user", WikiSection, &Page{Perm: Internal, Owner: WikiSection}, true},
{"", "admin", &Page{Perm: Private, Owner: "admin"}, false},
{"user", "admin", &Page{Perm: Private, Owner: "admin"}, false},
{"admin", "admin", &Page{Perm: Private, Owner: "admin"}, true},
}
for i, v := range data {
t.Run(fmt.Sprintf("Write:%d", i), func(t *testing.T) {
if WritePerm(v.Username, v.Section, v.Page) != v.OK {
t.Fail()
}
})
}
}

7
pkg/core/pledge.go Normal file
View file

@ -0,0 +1,7 @@
// Copyright (C) 2018 Marius Schellenberger
// +build !openbsd
package core
func Pledge(promises, execpromises string) error { return nil }

View file

@ -0,0 +1,11 @@
// Copyright (C) 2018 Marius Schellenberger
// +build openbsd
package core
import "golang.org/x/sys/unix"
func Pledge(promises, execpromises string) error {
return unix.Pledge(promises, execpromises)
}

View file

@ -8,7 +8,7 @@ type Page struct {
Title string `json:"title"` Title string `json:"title"`
StoreTitle string `json:"store_title"` StoreTitle string `json:"store_title"`
Markdown string `json:"markdown"` Markdown string `json:"markdown"`
PageIndex template.HTML `json:"page_index"` TOC template.HTML `json:"toc"`
HTML template.HTML `json:"html"` HTML template.HTML `json:"html"`
Created string `json:"created"` Created string `json:"created"`
Updated string `json:"updated"` Updated string `json:"updated"`
@ -52,4 +52,5 @@ type Config struct {
Secret string Secret string
Foreground bool Foreground bool
SecureCookie bool SecureCookie bool
Debug bool
} }

View file

@ -3,13 +3,13 @@
package db package db
import ( import (
"fmt"
"git.giftfish.de/ston1th/gowiki/pkg/cache" "git.giftfish.de/ston1th/gowiki/pkg/cache"
"git.giftfish.de/ston1th/gowiki/pkg/core" "git.giftfish.de/ston1th/gowiki/pkg/core"
"git.giftfish.de/ston1th/gowiki/pkg/index" "git.giftfish.de/ston1th/gowiki/pkg/index"
"git.giftfish.de/ston1th/gowiki/pkg/render" "git.giftfish.de/ston1th/gowiki/pkg/render"
"git.giftfish.de/ston1th/gowiki/pkg/store" "git.giftfish.de/ston1th/gowiki/pkg/store"
"io" "io"
"path/filepath"
) )
const ( const (
@ -24,24 +24,19 @@ You can customize it how you like.`
const blevePath = "bleve" const blevePath = "bleve"
func dbErr(i interface{}) error {
return fmt.Errorf("db: %s", i)
}
type DB struct { type DB struct {
store store.Store store store.Store
cache *cache.Cache cache *cache.Cache
Index *index.Index Index *index.Index
} }
func New(datadir string) (db *DB, err error) { func New(dir string) (db *DB, err error) {
db = &DB{cache: cache.NewCache()} db = &DB{cache: cache.NewCache()}
db.Index, err = index.NewIndex(datadir + "/" + blevePath) db.store, err = store.NewBoltStore(filepath.Join(dir, storeFile), nil)
if err != nil { if err != nil {
return return
} }
db.Index, err = index.NewIndex(filepath.Join(dir, blevePath))
db.store, err = store.NewBoltStore(storeFile, nil)
if err != nil { if err != nil {
return return
} }

View file

@ -3,6 +3,7 @@
package db package db
import ( import (
"errors"
"git.giftfish.de/ston1th/gowiki/pkg/core" "git.giftfish.de/ston1th/gowiki/pkg/core"
"git.giftfish.de/ston1th/gowiki/pkg/render" "git.giftfish.de/ston1th/gowiki/pkg/render"
"git.giftfish.de/ston1th/gowiki/pkg/store" "git.giftfish.de/ston1th/gowiki/pkg/store"
@ -13,10 +14,10 @@ const pagePrefix = "page/"
var ( var (
errPageNotFound = store.ErrKeyNotFound errPageNotFound = store.ErrKeyNotFound
errPermissionDenied = dbErr("permission denied") errPermissionDenied = errors.New("db: permission denied")
errInvalidPermission = dbErr("invalid permission") errInvalidPermission = errors.New("db: invalid permission")
errPrivateWikiPage = dbErr("private wiki pages are not allowed") errPrivateWikiPage = errors.New("db: private wiki pages are not allowed")
errDeleteIndexPage = dbErr("index page can not be deleted") errDeleteIndexPage = errors.New("db: index page can not be deleted")
) )
func (db *DB) GetAllPages(username string) (pages core.Pages) { func (db *DB) GetAllPages(username string) (pages core.Pages) {

View file

@ -17,9 +17,9 @@ import (
const userPrefix = "user/" const userPrefix = "user/"
var ( var (
errUserNotFound = dbErr("user not found") errUserNotFound = errors.New("db: user not found")
errUserExists = dbErr("user already exist") errUserExists = errors.New("db: user already exist")
errUserReserved = dbErr("username is reserved") errUserReserved = errors.New("db: username is reserved")
userRe = regexp.MustCompile("[^a-zA-Z0-9]+") userRe = regexp.MustCompile("[^a-zA-Z0-9]+")
) )

View file

@ -3,7 +3,7 @@
package index package index
import ( import (
"fmt" "errors"
"git.giftfish.de/ston1th/gowiki/pkg/core" "git.giftfish.de/ston1th/gowiki/pkg/core"
"github.com/blevesearch/bleve" "github.com/blevesearch/bleve"
"html/template" "html/template"
@ -18,10 +18,6 @@ type indexPage struct {
Search string `json:"search"` Search string `json:"search"`
} }
func indexErr(i interface{}) error {
return fmt.Errorf("index: %s", i)
}
type Index struct { type Index struct {
i bleve.Index i bleve.Index
} }
@ -32,13 +28,13 @@ func NewIndex(path string) (i *Index, err error) {
mapping := bleve.NewIndexMapping() mapping := bleve.NewIndexMapping()
bi, err = bleve.New(path, mapping) bi, err = bleve.New(path, mapping)
if err != nil { if err != nil {
err = indexErr(err) err = errors.New("index: " + err.Error())
return return
} }
} else { } else {
bi, err = bleve.Open(path) bi, err = bleve.Open(path)
if err != nil { if err != nil {
err = indexErr(err) err = errors.New("index: " + err.Error())
return return
} }
} }

View file

@ -5,21 +5,20 @@ package log
import ( import (
stdlog "log" stdlog "log"
"os" "os"
"path/filepath"
) )
const debug = "debug: "
var ( var (
log *stdlog.Logger log = stdlog.New(os.Stdout, "", stdlog.LstdFlags)
DebugEnabled = false debug = false
) )
func NewLogger(file string) { func InitLogger(dir, file string, debug bool) {
if file == "" { debug = debug
log = stdlog.New(os.Stdout, "", stdlog.LstdFlags) if file == "-" {
return return
} }
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0640) f, err := os.OpenFile(filepath.Join(dir, file), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0640)
if err != nil { if err != nil {
stdlog.Fatal(err) stdlog.Fatal(err)
} }
@ -40,13 +39,13 @@ func Fatal(v ...interface{}) {
} }
func Debug(v ...interface{}) { func Debug(v ...interface{}) {
if DebugEnabled { if debug {
log.Println(append([]interface{}{debug}, v...)) log.Println(append([]interface{}{"debug:"}, v...))
} }
} }
func Debugf(fmt string, v ...interface{}) { func Debugf(fmt string, v ...interface{}) {
if DebugEnabled { if debug {
log.Printf(debug+fmt, v...) log.Printf("debug: "+fmt, v...)
} }
} }

View file

@ -30,21 +30,17 @@ var (
func Render(p *core.Page) string { func Render(p *core.Page) string {
rend := blackfriday.MarkdownCommon([]byte(p.Markdown)) rend := blackfriday.MarkdownCommon([]byte(p.Markdown))
index, html := buildTOC(string(rend)) toc, html := buildTOC(string(rend))
html = strings.Replace(html, "<table>", `<table class="table">`, -1) html = strings.Replace(html, "<table>", `<table class="table">`, -1)
html = strings.Replace(html, "</h1>", `</h1><hr>`, -1) html = strings.Replace(html, "</h1>", `</h1><hr>`, -1)
html = strings.Replace(html, "<a", `<a target="_blank"`, -1) html = strings.Replace(html, "<a", `<a target="_blank"`, -1)
p.PageIndex = template.HTML(index) p.TOC = template.HTML(toc)
p.HTML = template.HTML(html) p.HTML = template.HTML(html)
return whiteSpace.ReplaceAllString(string(filter.Filter(rend)), " ") return whiteSpace.ReplaceAllString(string(filter.Filter(rend)), " ")
} }
func spaceReplace(s string) string {
return whiteSpace.ReplaceAllString(s, "+")
}
func Title(title string) string { func Title(title string) string {
return spaceReplace(titleRe.ReplaceAllString(title, "")) return whiteSpace.ReplaceAllString(titleRe.ReplaceAllString(title, ""), "+")
} }
func StoreTitle(section, title string) string { func StoreTitle(section, title string) string {
@ -59,7 +55,7 @@ func UnstoreTitle(storeTitle string) (section, title string) {
} }
func buildTOC(html string) (string, string) { func buildTOC(html string) (string, string) {
var ret string var toc string
m := make(map[string]int) m := make(map[string]int)
hn := reIndex.FindAllStringSubmatch(html, -1) hn := reIndex.FindAllStringSubmatch(html, -1)
html = reHeader.ReplaceAllString(html, `<a id="${2}" class="anchor"></a>${1}${2}${3}`) html = reHeader.ReplaceAllString(html, `<a id="${2}" class="anchor"></a>${1}${2}${3}`)
@ -75,17 +71,17 @@ func buildTOC(html string) (string, string) {
m[link] = i + 1 m[link] = i + 1
newLink := link + strconv.Itoa(i) newLink := link + strconv.Itoa(i)
html = strings.Replace(html, link+`"`, newLink+`"`, 1) html = strings.Replace(html, link+`"`, newLink+`"`, 1)
ret += before(hn, k) toc += before(hn, k)
ret += `<li><a href="#` + newLink + `">` + v[2] + "</a>" toc += `<li><a href="#` + newLink + `">` + v[2] + "</a>"
ret += after(hn, k) toc += after(hn, k)
} }
return ret, html return toc, html
} }
func before(arr [][]string, i int) string { func before(arr [][]string, i int) string {
c, _ := strconv.Atoi(arr[i][1]) c, _ := strconv.Atoi(arr[i][1])
if c == 1 { if c == 1 {
return `<ul type="circle">` return `<ul class="toc">`
} }
return "" return ""
} }
@ -96,13 +92,14 @@ func after(arr [][]string, i int) string {
return closeTag(c-1) + "</ul>" return closeTag(c-1) + "</ul>"
} }
n, _ := strconv.Atoi(arr[i+1][1]) n, _ := strconv.Atoi(arr[i+1][1])
if n == 1 { switch {
case n == 1:
return closeTag(c-n) + "</ul>" return closeTag(c-n) + "</ul>"
} else if n < c { case n < c:
return closeTag(c - n) return closeTag(c - n)
} else if n > c { case n > c:
return openTag(n - c) return openTag(n - c)
} else if n == c { case n == c:
return "</li>" return "</li>"
} }
return "" return ""
@ -110,11 +107,11 @@ func after(arr [][]string, i int) string {
func openTag(j int) (ret string) { func openTag(j int) (ret string) {
for i := 0; i < j; i++ { for i := 0; i < j; i++ {
ret += `<ul type="circle"><li>` ret += `<ul class="toc"><li>`
}
if j > 1 {
ret = strings.Replace(ret, "circle", "none", j-1)
} }
//if j > 1 {
// ret = strings.Replace(ret, "circle", "none", j-1)
//}
ret = ret[0 : len(ret)-4] ret = ret[0 : len(ret)-4]
return return
} }

29
pkg/render/render_test.go Normal file
View file

@ -0,0 +1,29 @@
// Copyright (C) 2018 Marius Schellenberger
package render
import (
"testing"
)
func TestTitleFunctions(t *testing.T) {
section := "section"
title := "title"
combined := "section/title"
t.Run("StoreTitle", func(t *testing.T) {
if StoreTitle(section, title) != combined {
t.Fail()
}
})
t.Run("UnstoreTitle", func(t *testing.T) {
se, ti := UnstoreTitle(combined)
if se != section || ti != title {
t.Fail()
}
})
t.Run("Title", func(t *testing.T) {
if Title("9ßw$%4h4t-8v74 287(G /SV&3[45v}=94#+?)") != "9w4h4t-8v74+287G+SV345v94" {
t.Fail()
}
})
}

View file

@ -5,6 +5,7 @@ package server
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"git.giftfish.de/ston1th/gowiki/pkg/core" "git.giftfish.de/ston1th/gowiki/pkg/core"
"git.giftfish.de/ston1th/gowiki/pkg/db" "git.giftfish.de/ston1th/gowiki/pkg/db"
"git.giftfish.de/ston1th/gowiki/pkg/log" "git.giftfish.de/ston1th/gowiki/pkg/log"
@ -54,7 +55,7 @@ func NewHTTPServer(l net.Listener, conf core.Config) (srv *HTTPServer) {
func (s *HTTPServer) Start() (err error) { func (s *HTTPServer) Start() (err error) {
s.DB, err = db.New(s.DataDir) s.DB, err = db.New(s.DataDir)
if err != nil { if err != nil {
return return errors.New("db: " + err.Error())
} }
if s.Secret != "" { if s.Secret != "" {
s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, bytes.NewBufferString(s.Secret)) s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, bytes.NewBufferString(s.Secret))
@ -62,7 +63,7 @@ func (s *HTTPServer) Start() (err error) {
s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, nil) s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, nil)
} }
if err != nil { if err != nil {
return return errors.New("server: " + err.Error())
} }
go func() { go func() {
log.Println(s.srv.Serve(s.listener)) log.Println(s.srv.Serve(s.listener))

View file

@ -48,7 +48,7 @@ const (
<head> <head>
<title>GoWiki | {{.Title}}</title> <title>GoWiki | {{.Title}}</title>
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link> <link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-1LNAeuiM/rtsY1jIgLovUZ7HbYnOCkj3ZyailNA3rFQ="></link> <link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-/SZfW8eVqFuioMg+QXacvK1Zc3XLtT5LGu9tMle65Jc="></link>
</head> </head>
<body id="back-to-top"> <body id="back-to-top">
<div class="navbar navbar-expand fixed-top navbar-dark bg-primary"> <div class="navbar navbar-expand fixed-top navbar-dark bg-primary">
@ -259,7 +259,7 @@ const (
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
{{.Data.PageIndex}} {{.Data.TOC}}
</div> </div>
<div class="col"> <div class="col">
<div class="btn-group float-md-right"> <div class="btn-group float-md-right">
@ -388,7 +388,7 @@ const (
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
{{.Data.PageIndex}} {{.Data.TOC}}
</div> </div>
<div class="col"> <div class="col">
<div class="btn-group float-md-right"> <div class="btn-group float-md-right">
@ -680,6 +680,10 @@ h6 {
font-family: 'Courier New'; font-family: 'Courier New';
font-size: 14; font-size: 14;
} }
ul.toc {
list-style-type: disc;
margin-bottom: 0 !important;
}
pre { pre {
display: block; display: block;
padding: 10px; padding: 10px;

View file

@ -30,6 +30,9 @@ func checkXsrf(xsrf string, secret []byte) bool {
if err != nil { if err != nil {
return false return false
} }
if len(t) != jwt.KeySize {
return false
}
return subtle.ConstantTimeCompare(secret, xor(t[keySize:], t[:keySize])) == 1 return subtle.ConstantTimeCompare(secret, xor(t[keySize:], t[:keySize])) == 1
} }

31
pkg/server/xsrf_test.go Normal file
View file

@ -0,0 +1,31 @@
// Copyright (C) 2018 Marius Schellenberger
package server
import (
"testing"
)
func TestXsrf(t *testing.T) {
sec := genKey(keySize)
if len(sec) != keySize {
t.Fatal("len(sec) != keySize")
}
xsrf := newXsrf(sec)
if xsrf == "" {
t.Fatal("xsrf is empty")
}
if !checkXsrf(xsrf, sec) {
t.Fatal("valid xsrf check failed")
}
if checkXsrf("test", sec) {
t.Fatal("invalid xsrf check succeeded")
}
rnd := genKey(keySize)
if checkXsrf(xsrf, rnd) {
t.Fatal("invalid xsrf check succeeded")
}
}

View file

@ -8,7 +8,10 @@ import (
"io" "io"
) )
const defaultBoltBucket = "default" const (
defaultBoltBucket = "default"
fileMode = 0640
)
type BoltStore struct { type BoltStore struct {
Marshaler Marshaler
@ -19,7 +22,7 @@ func NewBoltStore(file string, m Marshaler) (bs *BoltStore, err error) {
if m == nil { if m == nil {
m = NewGOB() m = NewGOB()
} }
db, err := bolt.Open(file, 0640, nil) db, err := bolt.Open(file, fileMode, nil)
if err != nil { if err != nil {
return return
} }

View file

@ -0,0 +1,99 @@
// Copyright (C) 2018 Marius Schellenberger
package store
import (
"bytes"
"errors"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
type testData struct {
Data string
}
func TestBoltStore(t *testing.T) {
data := []testData{
{"123"},
{"hello"},
}
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
bs, err := NewBoltStore(filepath.Join(dir, "bolt.db"), nil)
if err != nil {
t.Fatal(err)
}
err = bs.Set("key0", data[0])
if err != nil {
t.Fatal(err)
}
err = bs.Set("key1", data[1])
if err != nil {
t.Fatal(err)
}
t.Run("GetKey0", func(t *testing.T) {
var d testData
err := bs.Get("key0", &d)
if err != nil {
t.Fatal(err)
}
if d != data[0] {
t.Error("d is not data[0]")
}
})
t.Run("ForEach", func(t *testing.T) {
err := bs.ForEach(func(k string, v []byte) error {
if k == "key0" || k == "key1" {
return nil
}
return errors.New("key0 or key1 not found")
})
if err != nil {
t.Fatal(err)
}
})
t.Run("DumpRestore", func(t *testing.T) {
buf := new(bytes.Buffer)
err := bs.Dump(buf)
if err != nil {
t.Fatal(err)
}
if buf.Len() == 0 {
t.Error("no dump written")
}
err = bs.Restore(buf)
if err != nil {
t.Fatal(err)
}
})
t.Run("DeleteKey1", func(t *testing.T) {
err := bs.Delete("key1")
if err != nil {
t.Fatal(err)
}
})
t.Run("GetKey1", func(t *testing.T) {
var d testData
err := bs.Get("key1", &d)
if err == nil {
t.Error("key1 should be deleted")
}
if d == data[1] {
t.Error("d is not data[1]")
}
})
t.Run("Close", func(t *testing.T) {
err := bs.Close()
if err != nil {
t.Fatal(err)
}
})
}

View file

@ -2,16 +2,12 @@
package store package store
import "fmt" import "errors"
func storeErr(i interface{}) error {
return fmt.Errorf("store: %s", i)
}
var ( var (
ErrKeyNotFound = storeErr("key not found") ErrKeyNotFound = errors.New("store: key not found")
ErrWriterIsNil = storeErr("writer is nil") ErrWriterIsNil = errors.New("store: writer is nil")
ErrReaderIsNil = storeErr("reader is nil") ErrReaderIsNil = errors.New("store: reader is nil")
) )
type Store interface { type Store interface {

View file

@ -6,7 +6,7 @@
# of the templates directory # of the templates directory
templates_go="pkg/server/templates.go" templates_go="pkg/server/templates.go"
echo "Generating templates.go" echo "Generating ${templates_go}"
if [ ! -d templates ]; then if [ ! -d templates ]; then
echo "error: templates directory not found" 1>&2 echo "error: templates directory not found" 1>&2

View file

@ -75,6 +75,10 @@ h6 {
font-family: 'Courier New'; font-family: 'Courier New';
font-size: 14; font-size: 14;
} }
ul.toc {
list-style-type: disc;
margin-bottom: 0 !important;
}
pre { pre {
display: block; display: block;
padding: 10px; padding: 10px;

View file

@ -3,7 +3,7 @@
<head> <head>
<title>GoWiki | {{.Title}}</title> <title>GoWiki | {{.Title}}</title>
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link> <link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-NeLmQ7cX66J4MdtgGlG3O/TgvsSyP1b9WbaQtxYZUbQ="></link>
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-1LNAeuiM/rtsY1jIgLovUZ7HbYnOCkj3ZyailNA3rFQ="></link> <link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-/SZfW8eVqFuioMg+QXacvK1Zc3XLtT5LGu9tMle65Jc="></link>
</head> </head>
<body id="back-to-top"> <body id="back-to-top">
<div class="navbar navbar-expand fixed-top navbar-dark bg-primary"> <div class="navbar navbar-expand fixed-top navbar-dark bg-primary">

View file

@ -25,7 +25,7 @@
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
{{.Data.PageIndex}} {{.Data.TOC}}
</div> </div>
<div class="col"> <div class="col">
<div class="btn-group float-md-right"> <div class="btn-group float-md-right">

View file

@ -25,7 +25,7 @@
</div> </div>
<div class="row mt-5"> <div class="row mt-5">
<div class="col"> <div class="col">
{{.Data.PageIndex}} {{.Data.TOC}}
</div> </div>
<div class="col"> <div class="col">
<div class="btn-group float-md-right"> <div class="btn-group float-md-right">

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff