diff --git a/cmd/webdav/main.go b/cmd/webdav/main.go index adc8cd0..360ca22 100644 --- a/cmd/webdav/main.go +++ b/cmd/webdav/main.go @@ -14,7 +14,6 @@ import ( "strings" "github.com/hacdias/webdav" - "golang.org/x/crypto/bcrypt" wd "golang.org/x/net/webdav" yaml "gopkg.in/yaml.v2" ) @@ -106,12 +105,12 @@ func parseUsers(raw []map[string]interface{}, c *cfg) { } } - c.auth[username] = password - user := &webdav.User{ - Scope: c.webdav.User.Scope, - Modify: c.webdav.User.Modify, - Rules: c.webdav.User.Rules, + Username: username, + Password: password, + Scope: c.webdav.User.Scope, + Modify: c.webdav.User.Modify, + Rules: c.webdav.User.Rules, } if scope, ok := r["scope"].(string); ok { @@ -163,10 +162,8 @@ type cfg struct { address string port string tls bool - noAuth bool cert string key string - auth map[string]string } func parseConfig() *cfg { @@ -177,7 +174,7 @@ func parseConfig() *cfg { Port string `json:"port" yaml:"port"` TLS bool `json:"tls" yaml:"tls"` Cert string `json:"cert" yaml:"cert"` - NoAuth bool `json:"noauth" yaml:"noauth"` + Auth bool `json:"auth" yaml:"auth"` Key string `json:"key" yaml:"key"` Scope string `json:"scope" yaml:"scope"` Modify bool `json:"modify" yaml:"modify"` @@ -190,7 +187,7 @@ func parseConfig() *cfg { Cert: "cert.pem", Key: "key.pem", Scope: "./", - NoAuth: false, + Auth: true, Modify: true, } @@ -211,8 +208,6 @@ func parseConfig() *cfg { tls: data.TLS, cert: data.Cert, key: data.Key, - noAuth: data.NoAuth, - auth: map[string]string{}, webdav: &webdav.Config{ User: &webdav.User{ Scope: data.Scope, @@ -223,6 +218,7 @@ func parseConfig() *cfg { LockSystem: wd.NewMemLS(), }, }, + Auth: data.Auth, Users: map[string]*webdav.User{}, }, } @@ -235,53 +231,10 @@ func parseConfig() *cfg { return config } -func basicAuth(c *cfg) http.Handler { - if c.noAuth { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - c.webdav.ServeHTTP(w, r) - }) - } - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) - - username, password, authOK := r.BasicAuth() - if !authOK { - http.Error(w, "Not authorized", 401) - return - } - - p, ok := c.auth[username] - if !ok { - http.Error(w, "Not authorized", 401) - return - } - - if !checkPassword(p, password) { - log.Println("Wrong Password for user", username) - http.Error(w, "Not authorized", 401) - return - } - - c.webdav.ServeHTTP(w, r) - }) -} - -func checkPassword(saved, input string) bool { - if strings.HasPrefix(saved, "{bcrypt}") { - savedPassword := strings.TrimPrefix(saved, "{bcrypt}") - return bcrypt.CompareHashAndPassword([]byte(savedPassword), []byte(input)) == nil - } - - return saved == input -} - func main() { flag.Parse() cfg := parseConfig() - handler := basicAuth(cfg) - // Builds the address and a listener. laddr := cfg.address + ":" + cfg.port listener, err := net.Listen("tcp", laddr) @@ -294,11 +247,11 @@ func main() { // Starts the server. if cfg.tls { - if err := http.ServeTLS(listener, handler, cfg.cert, cfg.key); err != nil { + if err := http.ServeTLS(listener, cfg.webdav, cfg.cert, cfg.key); err != nil { log.Fatal(err) } } else { - if err := http.Serve(listener, handler); err != nil { + if err := http.Serve(listener, cfg.webdav); err != nil { log.Fatal(err) } diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..167af5b --- /dev/null +++ b/config.yml @@ -0,0 +1,9 @@ +scope: . +address: 0.0.0.0 +port: 8080 +auth: false +users: + - username: admin + password: admin + - username: test + modify: false \ No newline at end of file diff --git a/user.go b/user.go new file mode 100644 index 0000000..73d9c91 --- /dev/null +++ b/user.go @@ -0,0 +1,39 @@ +package webdav + +import ( + "strings" + + "golang.org/x/net/webdav" +) + +// User contains the settings of each user. +type User struct { + Username string + Password string + Scope string + Modify bool + Rules []*Rule + Handler *webdav.Handler +} + +// Allowed checks if the user has permission to access a directory/file +func (u User) Allowed(url string) bool { + var rule *Rule + i := len(u.Rules) - 1 + + for i >= 0 { + rule = u.Rules[i] + + if rule.Regex { + if rule.Regexp.MatchString(url) { + return rule.Allow + } + } else if strings.HasPrefix(url, rule.Path) { + return rule.Allow + } + + i-- + } + + return true +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..f237e4f --- /dev/null +++ b/utils.go @@ -0,0 +1,16 @@ +package webdav + +import ( + "strings" + + "golang.org/x/crypto/bcrypt" +) + +func checkPassword(saved, input string) bool { + if strings.HasPrefix(saved, "{bcrypt}") { + savedPassword := strings.TrimPrefix(saved, "{bcrypt}") + return bcrypt.CompareHashAndPassword([]byte(savedPassword), []byte(input)) == nil + } + + return saved == input +} diff --git a/webdav.go b/webdav.go index 62bf67c..e9a2dbe 100644 --- a/webdav.go +++ b/webdav.go @@ -2,16 +2,15 @@ package webdav import ( "context" + "log" "net/http" "regexp" - "strings" - - "golang.org/x/net/webdav" ) // Config is the configuration of a WebDAV instance. type Config struct { *User + Auth bool Users map[string]*User } @@ -19,12 +18,29 @@ type Config struct { func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) { u := c.User - // Gets the correct user for this request. - username, _, ok := r.BasicAuth() - if ok { - if user, ok := c.Users[username]; ok { - u = 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 } // Checks for user permissions relatively to this PATH. @@ -76,36 +92,6 @@ type Rule struct { Regexp *regexp.Regexp } -// User contains the settings of each user. -type User struct { - Scope string - Modify bool - Rules []*Rule - Handler *webdav.Handler -} - -// Allowed checks if the user has permission to access a directory/file -func (u User) Allowed(url string) bool { - var rule *Rule - i := len(u.Rules) - 1 - - for i >= 0 { - rule = u.Rules[i] - - if rule.Regex { - if rule.Regexp.MatchString(url) { - return rule.Allow - } - } else if strings.HasPrefix(url, rule.Path) { - return rule.Allow - } - - i-- - } - - return true -} - // responseWriterNoBody is a wrapper used to suprress the body of the response // to a request. Mainly used for HEAD requests. type responseWriterNoBody struct {