added base path filter

This commit is contained in:
ston1th 2019-05-03 10:44:17 +02:00
commit ec5a376733
15 changed files with 104 additions and 44 deletions

View file

@ -2,7 +2,7 @@
package core package core
var ( const (
IndexURI = "/" IndexURI = "/"
TotpURI = "/totp" TotpURI = "/totp"
LoginURI = "/login" LoginURI = "/login"

View file

@ -1,15 +1,12 @@
package core package core
import "time" import (
"time"
const (
timeFmt = "2006-01-02 15:04:05"
fileFmt = "20060102_150405"
) )
func Now() string { const (
return time.Now().Format(timeFmt) fileFmt = "20060102_150405"
} )
func FileName() string { func FileName() string {
return time.Now().Format(fileFmt) return time.Now().Format(fileFmt)

View file

@ -18,6 +18,8 @@ type Result struct {
HTML template.HTML HTML template.HTML
} }
type Results []Result
type Path struct { type Path struct {
Name string Name string
Abs string Abs string

View file

@ -54,6 +54,10 @@ func (fs *Filesystem) NewFile(r io.Reader, path, orig, name string) (newfile str
name = orig name = orig
} }
newfile = filepath.Join(path, name) newfile = filepath.Join(path, name)
err = checkPath(newfile)
if err != nil {
return
}
f, err := os.OpenFile(filepath.Join(fs.Base, newfile), os.O_RDWR|os.O_CREATE, FileMode) f, err := os.OpenFile(filepath.Join(fs.Base, newfile), os.O_RDWR|os.O_CREATE, FileMode)
if err != nil { if err != nil {
return return
@ -64,7 +68,12 @@ func (fs *Filesystem) NewFile(r io.Reader, path, orig, name string) (newfile str
} }
func (fs *Filesystem) Mkdir(path, name string) error { func (fs *Filesystem) Mkdir(path, name string) error {
return os.Mkdir(filepath.Join(fs.Base, Clean(path), Clean(name)), DirMode) dirpath := filepath.Join(Clean(path), Clean(name))
err := checkPath(dirpath)
if err != nil {
return err
}
return os.Mkdir(filepath.Join(fs.Base, dirpath), DirMode)
} }
func (fs *Filesystem) Move(path, dest, name string) (string, error) { func (fs *Filesystem) Move(path, dest, name string) (string, error) {
@ -75,7 +84,11 @@ func (fs *Filesystem) Move(path, dest, name string) (string, error) {
if fullpath == fs.Base { if fullpath == fs.Base {
return "", errors.New("the root path can not be moved") return "", errors.New("the root path can not be moved")
} }
newfile := Clean(filepath.Join(dest, name)) newfile := filepath.Join(Clean(dest), Clean(name))
err := checkPath(newfile)
if err != nil {
return "", err
}
return newfile, os.Rename(fullpath, filepath.Join(fs.Base, newfile)) return newfile, os.Rename(fullpath, filepath.Join(fs.Base, newfile))
} }

View file

@ -1,6 +1,11 @@
package fs package fs
import "path/filepath" import (
"errors"
"git.giftfish.de/ston1th/docstore/pkg/core"
"path/filepath"
"strings"
)
const sep = "/" const sep = "/"
@ -12,3 +17,27 @@ func File(path string) string {
_, f := filepath.Split(Clean(path)) _, f := filepath.Split(Clean(path))
return f return f
} }
var reserved = []string{
core.TotpURI,
core.LoginURI,
core.LogoutURI,
core.RawPrefix,
core.IndexPrefix,
core.UploadPrefix,
core.NewPrefix,
core.MovePrefix,
core.DeletePrefix,
core.Favicon,
core.BootstrapCSS,
core.CustomCSS,
}
func checkPath(path string) error {
for _, p := range reserved {
if strings.HasPrefix(path, p) {
return errors.New("base path '" + p + "' is reserved")
}
}
return nil
}

View file

@ -80,7 +80,7 @@ func (i *Index) Delete(id string) error {
return i.i.Delete(id) return i.i.Delete(id)
} }
func (i *Index) Search(search string) (results []core.Result, err error) { func (i *Index) Search(search string) (results core.Results, err error) {
req := bleve.NewSearchRequest(bleve.NewQueryStringQuery(search)) req := bleve.NewSearchRequest(bleve.NewQueryStringQuery(search))
req.Highlight = bleve.NewHighlightWithStyle("html") req.Highlight = bleve.NewHighlightWithStyle("html")
req.Size = maxSearchResult req.Size = maxSearchResult
@ -98,18 +98,17 @@ func (i *Index) Search(search string) (results []core.Result, err error) {
continue continue
} }
for fragField, frags := range hit.Fragments { for fragField, frags := range hit.Fragments {
if fragField == "text" || fragField == "created" {
for _, f := range frags {
r.HTML += template.HTML(f)
}
break
}
if fragField == "keyword" { if fragField == "keyword" {
var k string var k string
for _, f := range frags { for _, f := range frags {
k += f k += f
} }
r.Keyword = strings.ReplaceAll(strings.ReplaceAll(k, "<mark>", ""), "</mark>", "") r.Keyword = strings.ReplaceAll(strings.ReplaceAll(k, "<mark>", ""), "</mark>", "")
}
if fragField == "text" || fragField == "created" {
for _, f := range frags {
r.HTML += template.HTML(f)
}
break break
} }
} }

View file

@ -1,6 +0,0 @@
package scan
type PDF struct {
cmd string
args []string
}

View file

@ -17,6 +17,19 @@ import (
const pdfExt = ".pdf" const pdfExt = ".pdf"
type PDF struct {
cmd string
args []string
}
var defaultLangs = []string{"eng", "deu"}
type Tesseract struct {
cmd string
args []string
env []string
}
var ( var (
wsr = regexp.MustCompile(`[\t\f ]+`) wsr = regexp.MustCompile(`[\t\f ]+`)
nlr = regexp.MustCompile(`[\n]{3,}`) nlr = regexp.MustCompile(`[\n]{3,}`)

View file

@ -1,9 +0,0 @@
package scan
var defaultLangs = []string{"eng", "deu"}
type Tesseract struct {
cmd string
args []string
env []string
}

View file

@ -15,8 +15,6 @@ import (
"time" "time"
) )
//TODO add missing CSRF checks
const noSuchFile = "no such file or directory" const noSuchFile = "no such file or directory"
type notFoundHandler struct { type notFoundHandler struct {
@ -95,7 +93,6 @@ func authHandler(h ctxHandler) ctxHandler {
h(ctx) h(ctx)
return return
} }
//ctx.Forbidden()
ctx.Redirect(core.LoginURI, http.StatusFound) ctx.Redirect(core.LoginURI, http.StatusFound)
} }
} }
@ -229,6 +226,9 @@ func uploadHandler(ctx *Context) {
case "GET": case "GET":
ctx.Exec() ctx.Exec()
case "POST": case "POST":
if !ctx.CheckXsrf() {
return
}
f, orig, err := ctx.File("file") f, orig, err := ctx.File("file")
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
@ -255,6 +255,9 @@ func newDirHandler(ctx *Context) {
case "GET": case "GET":
ctx.Exec() ctx.Exec()
case "POST": case "POST":
if !ctx.CheckXsrf() {
return
}
err := ctx.Srv.FS.Mkdir(path, ctx.Form("name")) err := ctx.Srv.FS.Mkdir(path, ctx.Form("name"))
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
@ -298,6 +301,9 @@ func moveHandler(ctx *Context) {
case "GET": case "GET":
ctx.Exec() ctx.Exec()
case "POST": case "POST":
if !ctx.CheckXsrf() {
return
}
name := ctx.Form("name") name := ctx.Form("name")
dest := ctx.Form("destination") dest := ctx.Form("destination")
newfile, err := ctx.Srv.FS.Move(path, dest, name) newfile, err := ctx.Srv.FS.Move(path, dest, name)
@ -347,6 +353,9 @@ func deleteHandler(ctx *Context) {
case "GET": case "GET":
ctx.Exec() ctx.Exec()
case "POST": case "POST":
if !ctx.CheckXsrf() {
return
}
files, err := ctx.Srv.FS.Delete(path) files, err := ctx.Srv.FS.Delete(path)
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
@ -405,6 +414,11 @@ func dirHandler(ctx *Context) {
} }
ctx.Data.Paths = fs.Paths(path) ctx.Data.Paths = fs.Paths(path)
ctx.Exec() ctx.Exec()
default:
ctx.Data = webData{
Title: "Viewer",
}
ctx.Error(noSuchFile)
} }
} }
} }
@ -638,5 +652,5 @@ func logoutHandler(ctx *Context) {
log.Println("logout:", user) log.Println("logout:", user)
ctx.Srv.JWT.Invalidate(&ctx.Token) ctx.Srv.JWT.Invalidate(&ctx.Token)
ctx.SetCookie(nil, 0) ctx.SetCookie(nil, 0)
ctx.Redirect(core.IndexURI, http.StatusFound) ctx.Redirect(core.LoginURI, http.StatusFound)
} }

View file

@ -48,7 +48,7 @@ const (
</ol> </ol>
<div class="btn-group btn-breadcrumb"> <div class="btn-group btn-breadcrumb">
<a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a> <a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a>
<a href="/new{{.Path}}" class="btn btn-sm btn-primary">New Directory</a> <a href="/new{{.Path}}" class="btn btn-sm btn-primary">New</a>
<a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a> <a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a>
<a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a> <a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a>
</div> </div>
@ -139,7 +139,7 @@ const (
<title>DocStore | {{.Title}}</title> <title>DocStore | {{.Title}}</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link> <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link> <link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-L50A9TBaaysKaYOQWW5+H5nHTNLFLH4+F7Au3o4enAM="></link> <link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-VMvzVve7KY7ia4G8uBrASNEZ/Qc/5jCqVV3WWU+Y9ao="></link>
<meta name="theme-color" content="#375a7f"> <meta name="theme-color" content="#375a7f">
<meta name="description" content="{{.Title}}"> <meta name="description" content="{{.Title}}">
</head> </head>
@ -365,7 +365,7 @@ const (
</div> </div>
</div> </div>
<div class="row mt-3"> <div class="row mt-3">
<div class="col"> <div class="col col-nopad">
<form class="form-horizontal" action="/search" method="post"> <form class="form-horizontal" action="/search" method="post">
<input type="hidden" name="token" value="{{.Token}}"> <input type="hidden" name="token" value="{{.Token}}">
<div class="form-group"> <div class="form-group">
@ -397,7 +397,7 @@ const (
<br><pre class="wrap">{{$item.HTML}}</pre> <br><pre class="wrap">{{$item.HTML}}</pre>
{{end}} {{end}}
</td> </td>
<td>{{if $item.Keyword}}<span class="badge badge-primary">{{$item.Keyword}}</span>{{end}}</td> <td>{{$item.Keyword}}</td>
</tr> </tr>
{{end}} {{end}}
{{else}} {{else}}
@ -661,6 +661,10 @@ html, body, .container, .content {
.full-width { .full-width {
width: 100%; width: 100%;
} }
.col-nopad {
padding-left: 0;
padding-right: 0;
}
.relative { .relative {
position: relative; position: relative;
} }

View file

@ -143,6 +143,10 @@ html, body, .container, .content {
.full-width { .full-width {
width: 100%; width: 100%;
} }
.col-nopad {
padding-left: 0;
padding-right: 0;
}
.relative { .relative {
position: relative; position: relative;
} }

View file

@ -10,7 +10,7 @@
</ol> </ol>
<div class="btn-group btn-breadcrumb"> <div class="btn-group btn-breadcrumb">
<a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a> <a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a>
<a href="/new{{.Path}}" class="btn btn-sm btn-primary">New Directory</a> <a href="/new{{.Path}}" class="btn btn-sm btn-primary">New</a>
<a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a> <a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a>
<a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a> <a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a>
</div> </div>

View file

@ -4,7 +4,7 @@
<title>DocStore | {{.Title}}</title> <title>DocStore | {{.Title}}</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link> <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link> <link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-L50A9TBaaysKaYOQWW5+H5nHTNLFLH4+F7Au3o4enAM="></link> <link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-VMvzVve7KY7ia4G8uBrASNEZ/Qc/5jCqVV3WWU+Y9ao="></link>
<meta name="theme-color" content="#375a7f"> <meta name="theme-color" content="#375a7f">
<meta name="description" content="{{.Title}}"> <meta name="description" content="{{.Title}}">
</head> </head>

View file

@ -5,7 +5,7 @@
</div> </div>
</div> </div>
<div class="row mt-3"> <div class="row mt-3">
<div class="col"> <div class="col col-nopad">
<form class="form-horizontal" action="/search" method="post"> <form class="form-horizontal" action="/search" method="post">
<input type="hidden" name="token" value="{{.Token}}"> <input type="hidden" name="token" value="{{.Token}}">
<div class="form-group"> <div class="form-group">
@ -37,7 +37,7 @@
<br><pre class="wrap">{{$item.HTML}}</pre> <br><pre class="wrap">{{$item.HTML}}</pre>
{{end}} {{end}}
</td> </td>
<td>{{if $item.Keyword}}<span class="badge badge-primary">{{$item.Keyword}}</span>{{end}}</td> <td>{{$item.Keyword}}</td>
</tr> </tr>
{{end}} {{end}}
{{else}} {{else}}