bugfixes and updated go.mod

This commit is contained in:
ston1th 2019-05-15 22:52:27 +02:00
commit 6e317eb08b
300 changed files with 53136 additions and 9719 deletions

View file

@ -135,6 +135,7 @@ func Run(version string) {
if err = loadConfig(); err != nil {
stdlog.Fatal("dump: ", err)
}
log.Stderr()
if dumpFile != "-" {
file, err = os.OpenFile(dumpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
if err != nil {

View file

@ -12,4 +12,8 @@ const (
LogoutURI = "/logout"
TotpURI = "/totp"
SectionsURI = "/sections"
BootstrapCSS = "/bootstrap.css"
CustomCSS = "/custom.css"
Favicon = "/favicon.ico"
)

View file

@ -187,7 +187,7 @@ func (db *DB) UpdatePage(title, section, markdown, username string, p core.Permi
if err != nil {
return
}
if !db.WritePerm(title, username, page.Perm) {
if !db.WritePerm(section, username, page.Perm) {
return errPermissionDenied
}

View file

@ -7,11 +7,16 @@ import (
"git.giftfish.de/ston1th/godrop/v2"
"git.giftfish.de/ston1th/gowiki/pkg/core"
"github.com/blevesearch/bleve"
"github.com/blevesearch/bleve/mapping"
"html/template"
"os"
)
const maxSearchResult = 101
const (
maxSearchResult = 101
docType = "page"
)
type indexPage struct {
Title string `json:"title"`
@ -19,6 +24,10 @@ type indexPage struct {
Search string `json:"search"`
}
func (*indexPage) Type() string {
return docType
}
type Index struct {
i bleve.Index
}
@ -30,8 +39,22 @@ func NewIndex(path string) (i *Index, err error) {
return
}
if _, err = os.Stat(path); os.IsNotExist(err) {
mapping := bleve.NewIndexMapping()
bi, err = bleve.New(path, mapping)
err = os.Mkdir(path, 0700)
if err != nil {
err = errors.New("index: " + err.Error())
return
}
err = godrop.Unveil(path, "rwc")
if err != nil {
return
}
var m *mapping.IndexMappingImpl
m, err = createMapping()
if err != nil {
err = errors.New("mapping: " + err.Error())
return
}
bi, err = bleve.New(path, m)
if err != nil {
err = errors.New("index: " + err.Error())
return
@ -59,7 +82,7 @@ func (i *Index) Add(title, storeTitle, search string) error {
return err
}
}
return i.i.Index(storeTitle, indexPage{title, storeTitle, search})
return i.i.Index(storeTitle, &indexPage{title, storeTitle, search})
}
func (i *Index) Delete(storeTitle string) error {
@ -67,7 +90,7 @@ func (i *Index) Delete(storeTitle string) error {
}
func (i *Index) Search(search string) (results []core.Result, err error) {
req := bleve.NewSearchRequest(bleve.NewQueryStringQuery("title:" + search + " search:" + search))
req := bleve.NewSearchRequest(bleve.NewQueryStringQuery(search))
req.Highlight = bleve.NewHighlightWithStyle("html")
req.Size = maxSearchResult
res, err := i.i.Search(req)

51
pkg/index/mapping.go Normal file
View file

@ -0,0 +1,51 @@
// Copyright (C) 2019 Marius Schellenberger
package index
import (
"github.com/blevesearch/bleve"
"github.com/blevesearch/bleve/analysis/analyzer/custom"
"github.com/blevesearch/bleve/analysis/token/edgengram"
"github.com/blevesearch/bleve/analysis/token/lowercase"
"github.com/blevesearch/bleve/analysis/tokenizer/unicode"
"github.com/blevesearch/bleve/mapping"
)
func createMapping() (m *mapping.IndexMappingImpl, err error) {
m = bleve.NewIndexMapping()
err = m.AddCustomTokenFilter("bigram_tokenfilter", map[string]interface{}{
"type": edgengram.Name,
"side": edgengram.FRONT,
"min": 3.0,
"max": 25.0,
})
if err != nil {
return
}
err = m.AddCustomAnalyzer("fulltext_ngram", map[string]interface{}{
"type": custom.Name,
"tokenizer": unicode.Name,
"token_filters": []string{
lowercase.Name,
"bigram_tokenfilter",
},
})
if err != nil {
return
}
doc := bleve.NewDocumentMapping()
ti := bleve.NewTextFieldMapping()
ti.Analyzer = "fulltext_ngram"
doc.AddFieldMappingsAt("title", ti)
st := bleve.NewTextFieldMapping()
st.Analyzer = "fulltext_ngram"
doc.AddFieldMappingsAt("store_title", st)
s := bleve.NewTextFieldMapping()
s.Analyzer = "fulltext_ngram"
doc.AddFieldMappingsAt("search", s)
m.AddDocumentMapping(docType, doc)
return
}

View file

@ -15,6 +15,10 @@ var (
debug = false
)
func Stderr() {
log = stdlog.New(os.Stderr, "", stdlog.LstdFlags)
}
func InitLogger(cfg core.Config) {
debug = cfg.Debug
if cfg.LogFile == "-" {

View file

@ -27,53 +27,19 @@ const (
refererClaim = "referer"
)
func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) (ctx *Context) {
func newContext(w http.ResponseWriter, r *http.Request, s *HTTPServer) *Context {
h := w.Header()
h.Set("X-Frame-Options", "DENY")
h.Set("X-Content-Type-Options", "nosniff")
h.Set("X-XSS-Protection", "1; mode=block")
h.Set("Content-Security-Policy", "default-src 'none';style-src 'self';img-src 'self' https: data:;connect-src 'self';frame-ancestors 'none'")
h.Set("Referrer-Policy", "same-origin")
ctx = &Context{
return &Context{
Request: r,
Response: w,
Srv: s,
Time: time.Now(),
}
path := ctx.Path()
if path == "/bootstrap.css" || path == "/custom.css" {
return
}
if c, err := r.Cookie(cookieName); err == nil {
t, err := jwt.DecodeToken(c.Value)
if err != nil {
ctx.LogSetCookie("DecodeToken:", err)
return
}
if err = s.JWT.Verify(t); err != nil {
ctx.LogSetCookie("VerifyToken:", err)
return
}
if t.Claims.GetString(totpClaim) != "" {
if path == core.TotpURI || path == core.LoginURI || path == core.LogoutURI {
ctx.Token = *t
return
}
ctx.Redirect(core.TotpURI, http.StatusFound)
return nil
}
if !s.DB.LockedOut(t.Claims.GetString(userClaim), t.Claims.GetString(userCreatedClaim)) {
ctx.Token = *t
return
}
if err = s.JWT.Invalidate(t); err != nil {
log.Println("Invalidate:", err)
}
}
ctx.SetCookie(nil, 0)
return
}
type Context struct {
@ -132,7 +98,10 @@ func (c *Context) Exec() {
c.Err = errors.New("template is nil")
return
}
c.Data.Token = newXsrf(c.Token.RawSig()[:keySize])
sig := c.Token.RawSig()
if len(sig) >= keySize {
c.Data.Token = newXsrf(sig[:keySize])
}
c.Data.Version = c.Srv.Config.Version
if c.Data.BodyTitle == "" {

View file

@ -20,6 +20,43 @@ func (nf *notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
newContext(w, r, nf.s).NotFound()
}
func jwtHandler(h ctxHandler) ctxHandler {
return func(ctx *Context) {
if c, err := ctx.Request.Cookie(cookieName); err == nil {
t, err := jwt.DecodeToken(c.Value)
if err != nil {
ctx.LogSetCookie("DecodeToken:", err)
return
}
if err = ctx.Srv.JWT.Verify(t); err != nil {
ctx.LogSetCookie("VerifyToken:", err)
return
}
if t.Claims.GetString(totpClaim) != "" {
path := ctx.Path()
if path == core.TotpURI || path == core.LoginURI || path == core.LogoutURI {
ctx.Token = *t
h(ctx)
return
}
ctx.Redirect(core.TotpURI, http.StatusFound)
return
}
if !ctx.Srv.DB.LockedOut(t.Claims.GetString(userClaim), t.Claims.GetString(userCreatedClaim)) {
ctx.Token = *t
h(ctx)
return
}
if err = ctx.Srv.JWT.Invalidate(t); err != nil {
log.Println("Invalidate:", err)
}
}
ctx.SetCookie(nil, 0)
h(ctx)
}
}
func totpAuthHandler(h ctxHandler) ctxHandler {
return func(ctx *Context) {
if ctx.Totp() != "" {
@ -69,11 +106,11 @@ func staticHandler(ctx *Context) {
ctx.SetHeader("Content-Type", "text/css; charset=utf-8")
ctx.SetHeader("Expires", time.Now().UTC().Add(max).Format(http.TimeFormat))
switch ctx.Path() {
case "/bootstrap.css":
case core.BootstrapCSS:
err = ctx.Write(ctx.Srv.res["bootstrap.css"])
case "/custom.css":
case core.CustomCSS:
err = ctx.Write(ctx.Srv.res["custom.css"])
case "/favicon.ico":
case core.Favicon:
ctx.SetHeader("Content-Type", "image/x-icon")
err = ctx.Write(ctx.Srv.res["favicon.ico"])
}
@ -458,8 +495,8 @@ func pageHandler(ctx *Context) {
return
}
ctx.Data.Data = page
ctx.Data.Title = page.Title
ctx.Data.BodyTitle = page.Title
ctx.Data.Title = section + " | " + page.Title
ctx.Data.BodyTitle = section
ctx.Exec()
}

View file

@ -2,167 +2,197 @@
package server
import "git.giftfish.de/ston1th/gowiki/pkg/core"
type route struct {
Path string
Handler ctxHandler
Methods []string
}
var static = []route{
{
core.Favicon,
staticHandler,
[]string{"GET"},
},
{
core.BootstrapCSS,
staticHandler,
[]string{"GET"},
},
{
core.CustomCSS,
staticHandler,
[]string{"GET"},
},
}
var routes = []route{
{
"/",
indexHandler,
[]string{"GET"},
},
{
"/favicon.ico",
staticHandler,
[]string{"GET"},
},
{
"/bootstrap.css",
staticHandler,
[]string{"GET"},
},
{
"/custom.css",
staticHandler,
jwtHandler(
indexHandler),
[]string{"GET"},
},
{
"/sections",
sectionsHandler,
jwtHandler(
sectionsHandler),
[]string{"GET"},
},
{
"/section/new",
adminAuthHandler(
sectionNewHandler),
jwtHandler(
adminAuthHandler(
sectionNewHandler)),
[]string{"GET", "POST"},
},
{
"/section/edit/{section:[a-zA-Z0-9]+$}",
adminAuthHandler(
sectionEditHandler),
jwtHandler(
adminAuthHandler(
sectionEditHandler)),
[]string{"GET", "POST"},
},
{
"/section/del/{section:[a-zA-Z0-9]+$}",
adminAuthHandler(
sectionDelHandler),
jwtHandler(
adminAuthHandler(
sectionDelHandler)),
[]string{"GET", "POST"},
},
{
"/search",
searchHandler,
jwtHandler(
searchHandler),
[]string{"GET", "POST"},
},
{
"/new",
authHandler(
pageNewHandler),
jwtHandler(
authHandler(
pageNewHandler)),
[]string{"GET", "POST"},
},
{
"/login",
loginHandler,
jwtHandler(
loginHandler),
[]string{"GET", "POST"},
},
{
"/totp",
totpAuthHandler(
loginTotpHandler),
jwtHandler(
totpAuthHandler(
loginTotpHandler)),
[]string{"GET", "POST"},
},
{
"/blacklist",
authHandler(
pageBlacklistHandler),
jwtHandler(
authHandler(
pageBlacklistHandler)),
[]string{"GET", "POST"},
},
{
"/users",
adminAuthHandler(
usersHandler),
jwtHandler(
adminAuthHandler(
usersHandler)),
[]string{"GET"},
},
{
"/user/new",
adminAuthHandler(
userNewHandler),
jwtHandler(
adminAuthHandler(
userNewHandler)),
[]string{"GET", "POST"},
},
{
"/user/edit/{user:[a-zA-Z0-9]+$}",
userAuthHandler(
userEditHandler),
jwtHandler(
userAuthHandler(
userEditHandler)),
[]string{"GET", "POST"},
},
{
"/user/del/{user:[a-zA-Z0-9]+$}",
userAuthHandler(
userDelHandler),
jwtHandler(
userAuthHandler(
userDelHandler)),
[]string{"GET", "POST"},
},
{
"/user/totp/{user:[a-zA-Z0-9]+$}",
userAuthHandler(
userTotpHandler),
jwtHandler(
userAuthHandler(
userTotpHandler)),
[]string{"GET", "POST"},
},
{
"/user/unlock/{user:[a-zA-Z0-9]+$}",
adminAuthHandler(
userUnlockHandler),
jwtHandler(
adminAuthHandler(
userUnlockHandler)),
[]string{"POST"},
},
{
"/logout",
logoutHandler,
jwtHandler(
logoutHandler),
[]string{"GET", "POST"},
},
{
"/view/{key}",
pageHandler,
jwtHandler(
pageHandler),
[]string{"GET"},
},
{
"/view/{key}/md",
pageMDHandler(
pageHandler),
jwtHandler(
pageMDHandler(
pageHandler)),
[]string{"GET"},
},
{
"/{section:[a-zA-Z0-9]+}",
sectionHandler,
jwtHandler(
sectionHandler),
[]string{"GET"},
},
{
"/{section:[a-zA-Z0-9]+}/{title:[a-zA-Z0-9+-]+}",
pageHandler,
[]string{"GET"},
},
{
"/{section:[a-zA-Z0-9]+}/{title:[a-zA-Z0-9+-]+}/md",
pageMDHandler(
jwtHandler(
pageHandler),
[]string{"GET"},
},
{
"/{section:[a-zA-Z0-9]+}/{title:[a-zA-Z0-9+-]+}/md",
jwtHandler(
pageMDHandler(
pageHandler)),
[]string{"GET"},
},
{
"/{section:[a-zA-Z0-9]+}/{title:[a-zA-Z0-9+-]+}/share",
pageShareHandler,
jwtHandler(
pageShareHandler),
[]string{"GET", "POST"},
},
{
"/{section:[a-zA-Z0-9]+}/{title:[a-zA-Z0-9+-]+}/edit",
authHandler(
pageEditHandler),
jwtHandler(
authHandler(
pageEditHandler)),
[]string{"GET", "POST"},
},
{
"/{section:[a-zA-Z0-9]+}/{title:[a-zA-Z0-9+-]+}/del",
authHandler(
pageDelHandler),
jwtHandler(
authHandler(
pageDelHandler)),
[]string{"GET", "POST"},
},
}

View file

@ -19,7 +19,7 @@ import (
)
const (
cookieName = "gosession"
cookieName = "gowiki_session"
)
type HTTPServer struct {
@ -81,6 +81,9 @@ func (s *HTTPServer) Stop() error {
func (s *HTTPServer) buildRoutes() http.Handler {
m := mux.NewRouter()
m.NotFoundHandler = &notFoundHandler{s}
for _, v := range static {
m.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...)
}
for _, v := range routes {
m.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...)
}
@ -88,10 +91,6 @@ func (s *HTTPServer) buildRoutes() http.Handler {
}
func (s *HTTPServer) contextWrapper(h ctxHandler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := newContext(w, r, s)
if ctx == nil {
return
}
h(ctx)
h(newContext(w, r, s))
}
}

File diff suppressed because one or more lines are too long