added stats page and password reset

This commit is contained in:
ston1th 2019-11-24 12:25:19 +01:00
commit cabc3e94b4
55 changed files with 8094 additions and 4362 deletions

View file

@ -0,0 +1,24 @@
Copyright (C) 2019 Marius Schellenberger
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* The names of the authors and/or contributors may not be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,59 @@
# authdav - a x/net/webdav wrapper
Wrappers:
* HTTP Basic Auth wrapper for `webdav.Handler`
* write only once FileSystem wrapper for `webdav.FileSystem`
## Usage
### HTTP Basic Auth
```
package main
import (
"git.giftfish.de/ston1th/authdav"
"golang.org/x/net/webdav"
"log"
"net/http"
)
type exampleAuthenticator struct {}
func (exampleAuthenticator) BasicAuth(user, password string) bool {
if user == "user" && password == "password" {
return true
}
return false
}
func main() {
auth := exampleAuthenticator{}
handler := authdav.NewWebdavBasicAuth("", webdav.Dir("."), nil, nil, auth, "webdav with basic auth")
log.Fatal(http.ListenAndServe(":8080", handler))
}
```
### Write Only
```
package main
import (
"git.giftfish.de/ston1th/authdav"
"golang.org/x/net/webdav"
"log"
"net/http"
)
func main() {
fs := authdav.NewWriteOnlyOnceFileSystem(webdav.Dir("."))
handler := &webdav.Handler{
Prefix: "",
FileSystem: fs,
LockSystem: webdav.NewMemLS(),
Logger: nil,
}
log.Fatal(http.ListenAndServe(":8080", handler))
}
```

View file

@ -0,0 +1,57 @@
// Copyright (C) 2019 Marius Schellenberger
package authdav
import (
"golang.org/x/net/webdav"
"net/http"
)
// DefaultRealm is the HTTP Basic Auth default realm
const DefaultRealm = "WebDav"
// Authenticator is the interface for Webdav HTTP Basic Auth
type Authenticator interface {
BasicAuth(username string, password string) bool
}
// WebdavBasicAuth is a WebDav wrapper for HTTP Basic Auth and implements the http.Handler interface
type WebdavBasicAuth struct {
dav *webdav.Handler
auth Authenticator
realm string
}
// NewWebdavBasicAuth returns a new WebdavBasicAuth wrapper
// prefix can be empty
// ls and log can be nil
// an empty authenticator disables HTTP Basic Auth
// realm can be empty and defaults to authdav.DefaultRealm
func NewWebdavBasicAuth(prefix string, fs webdav.FileSystem, ls webdav.LockSystem, log func(*http.Request, error), auth Authenticator, realm string) *WebdavBasicAuth {
if ls == nil {
ls = webdav.NewMemLS()
}
if realm == "" {
realm = DefaultRealm
}
dav := &webdav.Handler{
Prefix: prefix,
FileSystem: fs,
LockSystem: ls,
Logger: log,
}
return &WebdavBasicAuth{dav, auth, `Basic realm="` + realm + `"`}
}
func (h *WebdavBasicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if h.auth != nil {
u, p, ok := r.BasicAuth()
if !ok || !h.auth.BasicAuth(u, p) {
w.Header().Set("WWW-Authenticate", h.realm)
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Unauthorised\n"))
return
}
}
h.dav.ServeHTTP(w, r)
}

View file

@ -0,0 +1,46 @@
// Copyright (C) 2019 Marius Schellenberger
package authdav
import (
"golang.org/x/net/webdav"
"io"
"os"
)
// File is a wrapper for webdav.File providing write only functionality
type File struct {
f webdav.File
}
func (f *File) Close() error {
return f.f.Close()
}
func (f *File) Read(p []byte) (int, error) {
return 0, io.EOF
}
func (f *File) Seek(offset int64, whence int) (int64, error) {
return 0, nil
}
func (f *File) Readdir(count int) (fi []os.FileInfo, err error) {
ffi, err := f.f.Readdir(count)
for _, f := range ffi {
fi = append(fi, &FileInfo{f})
}
return
}
func (f *File) Stat() (os.FileInfo, error) {
fi, err := f.f.Stat()
if err != nil {
return nil, err
}
return &FileInfo{fi}, nil
}
func (f *File) Write(p []byte) (int, error) {
return f.f.Write(p)
}

View file

@ -0,0 +1,44 @@
// Copyright (C) 2019 Marius Schellenberger
package authdav
import (
"os"
"time"
)
// FileInfo is a wrapper for os.FileInfo which always raturns a zero length for regular files
type FileInfo struct {
fi os.FileInfo
}
func (fi *FileInfo) Name() string {
return fi.fi.Name()
}
func (fi *FileInfo) RealSize() int64 {
return fi.fi.Size()
}
func (fi *FileInfo) Size() int64 {
if fi.Mode().IsRegular() {
return 0
}
return fi.fi.Size()
}
func (fi *FileInfo) Mode() os.FileMode {
return fi.fi.Mode()
}
func (fi *FileInfo) ModTime() time.Time {
return fi.fi.ModTime()
}
func (fi *FileInfo) IsDir() bool {
return fi.fi.IsDir()
}
func (fi *FileInfo) Sys() interface{} {
return fi.fi.Sys()
}

View file

@ -0,0 +1,79 @@
// Copyright (C) 2019 Marius Schellenberger
package authdav
import (
"context"
"errors"
"golang.org/x/net/webdav"
"os"
)
// ErrWonly indicates forbidden operation
var ErrWonly = errors.New("authdav: only file write once operations permitted")
const writeFlag = os.O_RDWR | os.O_CREATE | os.O_TRUNC
// WriteOnlyOnceFileSystem is a file write only wrapper for a webdav.FileSystem
type WriteOnlyOnceFileSystem struct {
Filters []Filter
fs webdav.FileSystem
}
// NewWriteOnlyOnceFileSystem returns a new WriteOnlyOnceFileSystem wrapping a webdav.FileSystem
func NewWriteOnlyOnceFileSystem(fs webdav.FileSystem) *WriteOnlyOnceFileSystem {
return &WriteOnlyOnceFileSystem{fs: fs}
}
// Mkdir is not permitted
func (fs *WriteOnlyOnceFileSystem) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
return ErrWonly
}
// OpenFile allows write only once operations
func (fs *WriteOnlyOnceFileSystem) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
fi, err := fs.Stat(ctx, name)
if err == nil && fi.Mode().IsRegular() {
f, ok := fi.(*FileInfo)
if ok && flag == writeFlag && f.RealSize() > 0 {
return nil, ErrWonly
}
}
for _, filter := range fs.Filters {
err = filter.Filter(name)
if err != nil {
return nil, err
}
}
f, err := fs.fs.OpenFile(ctx, name, flag, perm)
if err != nil {
return nil, err
}
return &File{f}, nil
}
// RemoveAll only permits deleting empty files
func (fs *WriteOnlyOnceFileSystem) RemoveAll(ctx context.Context, name string) error {
fi, err := fs.fs.Stat(ctx, name)
if err != nil {
return err
}
if fi.Mode().IsRegular() && fi.Size() == 0 {
return fs.fs.RemoveAll(ctx, name)
}
return ErrWonly
}
// Rename is not permitted
func (fs *WriteOnlyOnceFileSystem) Rename(ctx context.Context, oldName, newName string) error {
return ErrWonly
}
// Stat returns os.FileInfo wrapped in FileInfo
func (fs *WriteOnlyOnceFileSystem) Stat(ctx context.Context, name string) (os.FileInfo, error) {
fi, err := fs.fs.Stat(ctx, name)
if err != nil {
return nil, err
}
return &FileInfo{fi}, nil
}

View file

@ -0,0 +1,50 @@
// Copyright (C) 2019 Marius Schellenberger
package authdav
import (
"errors"
"path/filepath"
"strings"
)
// Matcher is the matcher function type
type Matcher func(path string) bool
// Filter defines an interface to filter paths or files
type Filter interface {
Filter(path string) error
}
// ErrMacOSFilter error returned by the MacOSFilter
var ErrMacOSFilter = errors.New("authdav: MacOS filter: access denied")
// MacOSFilter implements a filter for MacOS specific files
type MacOSFilter struct {
match []Matcher
}
// NewMacOSFilter returns a new MacOSFilter
func NewMacOSFilter() *MacOSFilter {
return &MacOSFilter{
match: []Matcher{
func(p string) bool {
return p == ".DS_Store"
},
func(p string) bool {
return strings.HasPrefix(p, "._")
},
},
}
}
// Filter implements the Filter interface
func (f *MacOSFilter) Filter(path string) error {
_, file := filepath.Split(path)
for _, m := range f.match {
if m(file) {
return ErrMacOSFilter
}
}
return nil
}

View file

@ -0,0 +1,5 @@
module git.giftfish.de/ston1th/authdav
go 1.12
require golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297

View file

@ -0,0 +1,5 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=