diff --git a/main.go b/main.go
index df478d7..b27535a 100644
--- a/main.go
+++ b/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Marius Schellenberger
+// Copyright (C) 2018 Marius Schellenberger
//go:generate ./templates.sh
diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go
index a881164..5935e4c 100644
--- a/pkg/cache/cache.go
+++ b/pkg/cache/cache.go
@@ -34,11 +34,13 @@ func (c *Cache) Add(title string, p *core.Page) {
c.Cache[title] = &CachedPage{p, now}
}
-func (c *Cache) Get(title string) *core.Page {
+func (c *Cache) Get(title string) (p *core.Page) {
c.RLock()
defer c.RUnlock()
if cp, ok := c.Cache[title]; ok {
- return cp.Page
+ p = new(core.Page)
+ *p = *cp.Page
+ return
}
return nil
}
diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go
index b4bb16f..813c420 100644
--- a/pkg/cmd/cmd.go
+++ b/pkg/cmd/cmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Marius Schellenberger
+// Copyright (C) 2018 Marius Schellenberger
package cmd
diff --git a/pkg/db/db.go b/pkg/db/db.go
index 90cfd83..be73cbf 100644
--- a/pkg/db/db.go
+++ b/pkg/db/db.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Marius Schellenberger
+// Copyright (C) 2018 Marius Schellenberger
package db
diff --git a/pkg/db/dump.go b/pkg/db/dump.go
index 55f3c93..8fb7230 100644
--- a/pkg/db/dump.go
+++ b/pkg/db/dump.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Marius Schellenberger
+// Copyright (C) 2018 Marius Schellenberger
package db
diff --git a/pkg/db/helper.go b/pkg/db/helper.go
index e2dc880..ba527c8 100644
--- a/pkg/db/helper.go
+++ b/pkg/db/helper.go
@@ -7,6 +7,7 @@ import (
var reservedUsers = []string{
"all",
+ "blacklist",
"login",
"logout",
"new",
diff --git a/pkg/db/page.go b/pkg/db/page.go
index f8ce39b..eb46bde 100644
--- a/pkg/db/page.go
+++ b/pkg/db/page.go
@@ -4,7 +4,6 @@ import (
"git.giftfish.de/ston1th/gowiki/pkg/core"
"git.giftfish.de/ston1th/gowiki/pkg/render"
"git.giftfish.de/ston1th/gowiki/pkg/store"
- "git.giftfish.de/ston1th/gowiki/pkg/util"
"sort"
)
@@ -65,7 +64,7 @@ func (db *DB) CreatePage(title, section, markdown, username string, p core.Permi
err = errPrivateWikiPage
return
}
- st := util.StoreTitle(section, title)
+ st := render.StoreTitle(section, title)
if _, err = db.getPage(st, username); err != nil && err != store.ErrKeyNotFound {
return
}
@@ -104,7 +103,7 @@ func (db *DB) UpdatePage(title, section, markdown, username string, p core.Permi
err = errPrivateWikiPage
return
}
- st := util.StoreTitle(section, title)
+ st := render.StoreTitle(section, title)
page, err := db.getPage(st, username)
if err != nil {
return
@@ -140,7 +139,7 @@ func (db *DB) DeletePage(title, section, username string) (err error) {
err = errDeleteIndexPage
return
}
- st := util.StoreTitle(section, title)
+ st := render.StoreTitle(section, title)
if page, err = db.getPage(st, username); err != nil {
return
}
diff --git a/pkg/db/user.go b/pkg/db/user.go
index 28f3502..816f796 100644
--- a/pkg/db/user.go
+++ b/pkg/db/user.go
@@ -13,7 +13,7 @@ var (
errUserReserved = dbErr("username is reserved")
)
-func (db *DB) GetUsers() (users core.Users, err error) {
+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 {
@@ -21,6 +21,14 @@ func (db *DB) GetUsers() (users core.Users, err error) {
}
return nil
})
+ return
+}
+
+func (db *DB) GetUsers() (users core.Users, err error) {
+ users, err = db.DumpUsers()
+ for i := range users {
+ users[i].Password = ""
+ }
sort.Sort(users)
return
}
@@ -41,28 +49,7 @@ func (db *DB) UserExists(username string) error {
}
// TODO
-/*func (bs *BoltStore) DumpUsers() (users []User, err error) {
- var buf [][]byte
- err = bs.db.View(func(tx *bolt.Tx) error {
- return tx.Bucket([]byte(userTable)).ForEach(func(k, v []byte) error {
- buf = append(buf, copyBuf(v))
- return nil
- })
- })
- if err != nil {
- return
- }
- for _, b := range buf {
- u := new(User)
- if err = bs.Unmarshal(b, u); err != nil {
- return
- }
- users = append(users, *u)
- }
- return
-}
-
-func (bs *BoltStore) RestoreUsers(users []User) error {
+/*func (bs *BoltStore) RestoreUsers(users []User) error {
return bs.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(userTable))
err := b.ForEach(func(k, v []byte) error {
diff --git a/pkg/log/log.go b/pkg/log/log.go
index 0fb6fa7..4640c78 100644
--- a/pkg/log/log.go
+++ b/pkg/log/log.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Marius Schellenberger
+// Copyright (C) 2018 Marius Schellenberger
package log
diff --git a/pkg/render/render.go b/pkg/render/render.go
index d52a106..1c1b8ee 100644
--- a/pkg/render/render.go
+++ b/pkg/render/render.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Marius Schellenberger
+// Copyright (C) 2018 Marius Schellenberger
package render
@@ -22,8 +22,9 @@ func makeFilter() analysis.CharFilter {
}
var (
- reHeader = regexp.MustCompile("((.+)()")
- reLink = regexp.MustCompile(`id="(.+)">`)
+ //reHeader = regexp.MustCompile("((.+)()")
+ reHeader = regexp.MustCompile("()(.+)()")
+ reLink = regexp.MustCompile(`id="(.+?)"`)
whiteSpace = regexp.MustCompile(`\s+`)
reIndex = regexp.MustCompile("(.+)")
titleRe = regexp.MustCompile("[^a-zA-Z0-9 -]+")
@@ -42,13 +43,20 @@ func Render(p *core.Page) string {
func spaceReplace(s string) string {
return whiteSpace.ReplaceAllString(s, "+")
- //TODO remove
- //return strings.Replace(s, " ", "+", -1)
}
-func makeLinkTitle(t string) (newTitle string, title string) {
- title = titleRe.ReplaceAllString(t, "")
- newTitle = spaceReplace(title)
+func Title(title string) string {
+ return spaceReplace(titleRe.ReplaceAllString(title, ""))
+}
+
+func StoreTitle(section, title string) string {
+ return section + "/" + title
+}
+
+func UnstoreTitle(storeTitle string) (section, title string) {
+ if a := strings.Split(storeTitle, "/"); len(a) == 2 {
+ return a[0], a[1]
+ }
return
}
@@ -56,10 +64,11 @@ func buildIndex(html string) (string, string) {
var ret string
m := make(map[string]int)
hn := reIndex.FindAllStringSubmatch(html, -1)
- html = reHeader.ReplaceAllString(html, `${1} id="${2}">${2}${3}`)
+ //html = reHeader.ReplaceAllString(html, `${1} id="${2}">${2}${3}`)
+ html = reHeader.ReplaceAllString(html, `${1}${2}${3}`)
html = reLink.ReplaceAllStringFunc(html, spaceReplace)
for k, v := range hn {
- link, _ := makeLinkTitle(v[2])
+ link := Title(v[2])
i := m[link]
m[link] = i + 1
newLink := link + strconv.Itoa(i)
@@ -119,11 +128,9 @@ func closeTag(j int) (ret string) {
}
func renderResult(res []core.Result) (html string) {
- //TODO fmt.Sprintf ?
for _, r := range res {
html += `` + r.Title + "" +
`
` + r.Text + "
"
-
}
return
}
diff --git a/pkg/server/context.go b/pkg/server/context.go
index f6c3cae..a221f0c 100644
--- a/pkg/server/context.go
+++ b/pkg/server/context.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Marius Schellenberger
+// Copyright (C) 2018 Marius Schellenberger
package server
@@ -99,7 +99,7 @@ func (c *Context) Exec() {
c.Data.Admin = c.Admin()
c.Data.User = c.User()
c.Data.Login = c.LoggedOn()
- c.Data.Time = time.Since(c.Time).Nanoseconds() / 1e6
+ c.Data.Time = time.Since(c.Time).Nanoseconds() / 1e5
c.Err = c.T.Execute(c.Response, c.Data)
if c.Err != nil {
diff --git a/pkg/server/handler.go b/pkg/server/handler.go
index 9ec70a5..2f4c58b 100644
--- a/pkg/server/handler.go
+++ b/pkg/server/handler.go
@@ -1,11 +1,11 @@
-// Copyright (C) 2017 Marius Schellenberger
+// Copyright (C) 2018 Marius Schellenberger
package server
import (
"git.giftfish.de/ston1th/gowiki/pkg/core"
"git.giftfish.de/ston1th/gowiki/pkg/log"
- "git.giftfish.de/ston1th/gowiki/pkg/util"
+ "git.giftfish.de/ston1th/gowiki/pkg/render"
"git.giftfish.de/ston1th/jwt"
"net/http"
"time"
@@ -122,14 +122,15 @@ func searchHandler(ctx *Context) {
return
}
- res, err := ctx.Srv.DB.Index.Search(search)
- if err != nil {
- ctx.Data.Msg = err.Error()
- ctx.Exec()
- return
- }
+ //TODO
+ //res, err := ctx.Srv.DB.Index.Search(search)
+ //if err != nil {
+ // ctx.Data.Msg = err.Error()
+ // ctx.Exec()
+ // return
+ //}
+ //ctx.Data.Data = res
ctx.Data.Search = search
- ctx.Data.Data = res
ctx.Exec()
}
}
@@ -150,15 +151,17 @@ func pageNewHandler(ctx *Context) {
ctx.Data = webData{Title: "New Page"}
switch ctx.Method() {
case "GET":
+ ctx.Data.Data = core.Page{Perm: core.Public}
ctx.Exec()
case "POST":
section := ctx.Form("section")
- title := ctx.Form("title")
+ title := render.Title(ctx.Form("title"))
markdown := ctx.Form("markdown")
- perm := ctx.Form("perm")
+ perm := core.ParsePermString(ctx.Form("perm"))
p := core.Page{
Title: title,
Markdown: markdown,
+ Perm: perm,
}
ctx.Data.Data = p
if !ctx.CheckXsrf(nil) {
@@ -167,7 +170,7 @@ func pageNewHandler(ctx *Context) {
if section != core.WikiSection {
section = ctx.User()
}
- page, err := ctx.Srv.DB.CreatePage(title, section, markdown, ctx.User(), core.ParsePermString(perm))
+ page, err := ctx.Srv.DB.CreatePage(title, section, markdown, ctx.User(), perm)
if err != nil {
ctx.Data.Msg = err.Error()
ctx.Exec()
@@ -208,7 +211,7 @@ func pageHandler(ctx *Context) {
ctx.Template("pageHandler")
}
}
- page, err := ctx.Srv.DB.GetPage(util.StoreTitle(section, title), user)
+ page, err := ctx.Srv.DB.GetPage(render.StoreTitle(section, title), user)
if err != nil {
log.Println(err)
ctx.NotFound()
@@ -227,91 +230,135 @@ func pageMDHandler(f ctxHandler) ctxHandler {
}
}
-// TODO add share token blacklisting
func pageShareHandler(ctx *Context) {
ctx.Template("pageShareHandler")
ctx.Data = webData{}
section := ctx.Var("section")
- title := ctx.Var("title")
+ title := render.Title(ctx.Var("title"))
user := ctx.User()
- st := util.StoreTitle(section, title)
+ st := render.StoreTitle(section, title)
page, err := ctx.Srv.DB.GetPage(st, user)
if err != nil {
ctx.NotFound()
return
}
- t := jwt.NewToken(map[string]interface{}{
- sharedClaim: user,
- sectionClaim: section,
- titleClaim: title,
- jwt.ExpClaim: jwt.NewExp(time.Hour * 24),
- }, nil)
-
- if err = ctx.Srv.JWT.Sign(t); err != nil {
- log.Println("SignToken:", err)
- ctx.NotFound()
- return
- }
- /* TODO
- host := ctx.Request.Header.Get("X-Forwarded-For")
- if host == "" {
- ctx.Request.Host
- }
- ctx.Data.Data = "https://" + host + "/view/" + t.String()
- */
- ctx.Data.Key = t.String()
ctx.Data.Data = st
ctx.Data.Title = page.Title
ctx.Data.BodyTitle = page.Title
- ctx.Exec()
+ switch ctx.Method() {
+ case "GET":
+ ctx.Exec()
+ case "POST":
+ duration := ctx.Form("duration")
+ if !ctx.CheckXsrf(nil) {
+ return
+ }
+ d, err := time.ParseDuration(duration)
+ if err != nil {
+ ctx.Data.Msg = err.Error()
+ ctx.Exec()
+ return
+ }
+ t := jwt.NewToken(map[string]interface{}{
+ sharedClaim: user,
+ sectionClaim: section,
+ titleClaim: title,
+ jwt.ExpClaim: jwt.NewExp(d),
+ }, nil)
+
+ if err = ctx.Srv.JWT.Sign(t); err != nil {
+ log.Println("SignToken:", err)
+ ctx.NotFound()
+ return
+ }
+ ctx.Data.Key = t.String()
+ ctx.Exec()
+ }
+}
+
+func pageBlacklistHandler(ctx *Context) {
+ ctx.Template("pageBlacklistHandler")
+ ctx.Data = webData{
+ Title: "Blacklist",
+ BodyTitle: "Blacklist",
+ }
+ switch ctx.Method() {
+ case "GET":
+ ctx.Exec()
+ case "POST":
+ if !ctx.CheckXsrf(nil) {
+ return
+ }
+ t, err := jwt.DecodeToken(ctx.Form("share"))
+ if err != nil {
+ ctx.Data.Msg = err.Error()
+ ctx.Exec()
+ return
+ }
+ err = ctx.Srv.JWT.Verify(t)
+ if err != nil {
+ ctx.Data.Msg = err.Error()
+ ctx.Exec()
+ return
+ }
+ user := t.Claims.GetString(sharedClaim)
+ if user == "" {
+ ctx.Data.Msg = "blacklisting denied: no share token"
+ ctx.Exec()
+ return
+ }
+ if ctx.User() != user {
+ ctx.Data.Msg = "blacklisting denied: token was issued by " + user
+ ctx.Exec()
+ return
+ }
+ err = ctx.Srv.JWT.Invalidate(t)
+ if err != nil {
+ ctx.Data.Msg = err.Error()
+ ctx.Exec()
+ return
+ }
+ ctx.Exec()
+ }
}
func pageEditHandler(ctx *Context) {
ctx.Template("pageEditHandler")
ctx.Data = webData{}
section := ctx.Var("section")
- title := ctx.Var("title")
- page, err := ctx.Srv.DB.GetPage(util.StoreTitle(section, title), ctx.User())
+ title := render.Title(ctx.Var("title"))
+ page, err := ctx.Srv.DB.GetPage(render.StoreTitle(section, title), ctx.User())
if err != nil {
ctx.NotFound()
return
}
+ ctx.Data.Title = page.Title
+ ctx.Data.BodyTitle = page.Title
switch ctx.Method() {
case "GET":
ctx.Data.Data = page
- ctx.Data.Title = page.Title
- ctx.Data.BodyTitle = page.Title
ctx.Exec()
case "POST":
- storeTitle := ctx.Form("store_title")
- section, title = util.UnstoreTitle(storeTitle)
- markdown := ctx.Form("markdown")
- perm := core.ParsePermString(ctx.Form("perm"))
- ctx.Data.Data = core.Page{
- Title: page.Title,
- StoreTitle: storeTitle,
- Markdown: markdown,
- Perm: perm,
- }
- ctx.Data.Title = page.Title
- ctx.Data.BodyTitle = page.Title
+ page.Markdown = ctx.Form("markdown")
+ page.Perm = core.ParsePermString(ctx.Form("perm"))
+ ctx.Data.Data = page
if !ctx.CheckXsrf(nil) {
return
}
- err = ctx.Srv.DB.UpdatePage(title, section, markdown, ctx.User(), perm)
+ err = ctx.Srv.DB.UpdatePage(title, section, page.Markdown, ctx.User(), page.Perm)
if err != nil {
ctx.Data.Msg = err.Error()
ctx.Exec()
return
}
- ctx.Redirect("/"+page.StoreTitle, 302)
+ ctx.Redirect(page.StoreTitle, 302)
}
}
func pageDelHandler(ctx *Context) {
section := ctx.Var("section")
- title := ctx.Var("title")
- st := util.StoreTitle(section, title)
+ title := render.Title(ctx.Var("title"))
+ st := render.StoreTitle(section, title)
ctx.Template("pageDelHandler")
ctx.Data.Data = st
ctx.Data.Title = title
@@ -473,6 +520,7 @@ func userDelHandler(ctx *Context) {
}
func logoutHandler(ctx *Context) {
+ ctx.Srv.JWT.Invalidate(&ctx.Token)
ctx.SetCookie(nil)
ctx.Redirect(core.IndexURI, 302)
}
diff --git a/pkg/server/routes.go b/pkg/server/routes.go
index fdb8c0e..bd1e9c8 100644
--- a/pkg/server/routes.go
+++ b/pkg/server/routes.go
@@ -45,6 +45,12 @@ var routes = []route{
allHandler,
[]string{"GET"},
},
+ {
+ "/blacklist",
+ authHandler(
+ pageBlacklistHandler),
+ []string{"GET", "POST"},
+ },
{
"/user",
adminAuthHandler(
@@ -105,7 +111,7 @@ var routes = []route{
{
"/{section}/{title}/share",
pageShareHandler,
- []string{"GET"},
+ []string{"GET", "POST"},
},
{
"/{section}/{title}/edit",
diff --git a/pkg/server/templates.go b/pkg/server/templates.go
index 60bac8b..66b8f3a 100644
--- a/pkg/server/templates.go
+++ b/pkg/server/templates.go
@@ -18,13 +18,11 @@ const (
{{.BodyTitle}}
-
+
{{end}}`
@@ -32,80 +30,65 @@ const (
GoWiki | {{.Title}}
-
-
+
+
-
-
+
+
-
-
+
GoWiki
+
{{template "menu" .}}
-
-
-
- {{if .Msg}}
-
- {{end}}
- {{template "body" .}}
-
-
-
-
`
login = `{{define "body"}}