initial commit
This commit is contained in:
commit
f6e1813d15
7 changed files with 296 additions and 0 deletions
24
LICENSE
Normal file
24
LICENSE
Normal 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.
|
||||||
59
README.md
Normal file
59
README.md
Normal 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))
|
||||||
|
}
|
||||||
|
```
|
||||||
57
authdav.go
Normal file
57
authdav.go
Normal 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)
|
||||||
|
}
|
||||||
46
file.go
Normal file
46
file.go
Normal 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)
|
||||||
|
}
|
||||||
44
fileinfo.go
Normal file
44
fileinfo.go
Normal 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()
|
||||||
|
}
|
||||||
65
filesystem.go
Normal file
65
filesystem.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
// 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 {
|
||||||
|
fs webdav.FileSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriteOnlyOnceFileSystem returns a new WriteOnlyOnceFileSystem wrapping a webdav.FileSystem
|
||||||
|
func NewWriteOnlyOnceFileSystem(fs webdav.FileSystem) *WriteOnlyOnceFileSystem {
|
||||||
|
return &WriteOnlyOnceFileSystem{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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f, err := fs.fs.OpenFile(ctx, name, flag, perm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &File{f}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAll is not permitted
|
||||||
|
func (fs *WriteOnlyOnceFileSystem) RemoveAll(ctx context.Context, name string) error {
|
||||||
|
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
|
||||||
|
}
|
||||||
1
go.mod
Normal file
1
go.mod
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module git.giftfish.de/ston1th/authdav
|
||||||
Loading…
Add table
Add a link
Reference in a new issue