webdav-server/webdav/webdav.go
Henrique Dias 8c66f0c585 feat: check basic auth user anyways
License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
2019-05-12 20:25:36 +01:00

120 lines
2.9 KiB
Go

package webdav
import (
"context"
"log"
"net/http"
)
// Config is the configuration of a WebDAV instance.
type Config struct {
*User
Auth bool
Users map[string]*User
}
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
u := c.User
if c.Auth {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
// Gets the correct user for this request.
username, password, ok := r.BasicAuth()
if !ok {
http.Error(w, "Not authorized", 401)
return
}
user, ok := c.Users[username]
if !ok {
http.Error(w, "Not authorized", 401)
return
}
if !checkPassword(user.Password, password) {
log.Println("Wrong Password for user", username)
http.Error(w, "Not authorized", 401)
return
}
u = user
} else {
// Even if Auth is disabled, we might want to get
// the user from the Basic Auth header. Useful for Caddy
// plugin implementation.
username, _, ok := r.BasicAuth()
if ok {
if user, ok := c.Users[username]; ok {
u = user
}
}
}
// Checks for user permissions relatively to this PATH.
if !u.Allowed(r.URL.Path) {
w.WriteHeader(http.StatusForbidden)
return
}
if r.Method == "HEAD" {
w = newResponseWriterNoBody(w)
}
// If this request modified the files and the user doesn't have permission
// to do so, return forbidden.
if (r.Method == "PUT" || r.Method == "POST" || r.Method == "MKCOL" ||
r.Method == "DELETE" || r.Method == "COPY" || r.Method == "MOVE") &&
!u.Modify {
w.WriteHeader(http.StatusForbidden)
return
}
// Excerpt from RFC4918, section 9.4:
//
// GET, when applied to a collection, may return the contents of an
// "index.html" resource, a human-readable view of the contents of
// the collection, or something else altogether.
//
// Get, when applied to collection, will return the same as PROPFIND method.
if r.Method == "GET" {
info, err := u.Handler.FileSystem.Stat(context.TODO(), r.URL.Path)
if err == nil && info.IsDir() {
r.Method = "PROPFIND"
if r.Header.Get("Depth") == "" {
r.Header.Add("Depth", "1")
}
}
}
// Runs the WebDAV.
u.Handler.ServeHTTP(w, r)
}
// responseWriterNoBody is a wrapper used to suprress the body of the response
// to a request. Mainly used for HEAD requests.
type responseWriterNoBody struct {
http.ResponseWriter
}
// newResponseWriterNoBody creates a new responseWriterNoBody.
func newResponseWriterNoBody(w http.ResponseWriter) *responseWriterNoBody {
return &responseWriterNoBody{w}
}
// Header executes the Header method from the http.ResponseWriter.
func (w responseWriterNoBody) Header() http.Header {
return w.ResponseWriter.Header()
}
// Write suprresses the body.
func (w responseWriterNoBody) Write(data []byte) (int, error) {
return 0, nil
}
// WriteHeader writes the header to the http.ResponseWriter.
func (w responseWriterNoBody) WriteHeader(statusCode int) {
w.ResponseWriter.WriteHeader(statusCode)
}