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