diff --git a/cmd/config.go b/cmd/config.go index 8d0bb75..4566b45 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -118,6 +118,34 @@ func parseUsers(raw []interface{}, c *webdav.Config) { } } +func parseCors(raw []interface{}, c *webdav.Config) { + hosts := []string{} + + for _, v := range raw { + + if cfg, ok := v.(map[interface{}]interface{}); ok { + + cors := webdav.CorsCfg{ + Enabled: cfg["enabled"].(bool), + AllowedHosts: []string{}, + } + + if allowed_hosts, ok := cfg["allowed_hosts"]; ok { + for _, host := range strings.Split(allowed_hosts.(string), ",") { + hosts = append(hosts, host) + } + } + + if len(hosts) == 0 { + hosts = append(hosts, "*") + } + + cors.AllowedHosts = hosts + c.Cors = cors + } + } +} + func readConfig(flags *pflag.FlagSet) *webdav.Config { cfg := &webdav.Config{ User: &webdav.User{ @@ -130,6 +158,10 @@ func readConfig(flags *pflag.FlagSet) *webdav.Config { }, }, Auth: getOptB(flags, "auth"), + Cors: webdav.CorsCfg{ + Enabled: false, + AllowedHosts: []string{}, + }, Users: map[string]*webdav.User{}, } @@ -143,6 +175,11 @@ func readConfig(flags *pflag.FlagSet) *webdav.Config { parseUsers(users, cfg) } + rawCors := v.Get("cors") + if cors, ok := rawCors.([]interface{}); ok { + parseCors(cors, cfg) + } + if len(cfg.Users) != 0 && !cfg.Auth { log.Print("Users will be ignored due to auth=false") } diff --git a/webdav/user.go b/webdav/user.go old mode 100644 new mode 100755 diff --git a/webdav/utils.go b/webdav/utils.go old mode 100644 new mode 100755 index f237e4f..9acd415 --- a/webdav/utils.go +++ b/webdav/utils.go @@ -14,3 +14,12 @@ func checkPassword(saved, input string) bool { return saved == input } + +func isAllowedHost(allowedHosts []string, origin string) bool { + for _, host := range allowedHosts { + if host == origin { + return true + } + } + return false +} diff --git a/webdav/webdav.go b/webdav/webdav.go old mode 100644 new mode 100755 index 84455ef..b818538 --- a/webdav/webdav.go +++ b/webdav/webdav.go @@ -7,15 +7,43 @@ import ( ) // Config is the configuration of a WebDAV instance. + +type CorsCfg struct { + Enabled bool + AllowedHosts []string +} + type Config struct { *User Auth bool + Cors CorsCfg 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 + requestOrigin := r.Header.Get("Origin") + + // add cors headers before any operation so even on 401 unauthorized cors will working only when Origin header is present so request came from browser + if c.Cors.Enabled && requestOrigin != "" { + + headers := w.Header() + + if(len(c.Cors.AllowedHosts) == 1 && c.Cors.AllowedHosts[0] == "*") { + headers.Set("Access-Control-Allow-Methods", "*") + headers.Set("Access-Control-Allow-Headers", "*") + headers.Set("Access-Control-Allow-Origin", "*") + } else if(isAllowedHost(c.Cors.AllowedHosts, requestOrigin)) { + headers.Set("Access-Control-Allow-Origin", requestOrigin) + headers.Set("Access-Control-Allow-Headers", "*") + headers.Set("Access-Control-Allow-Methods", "*") + } + } + + if r.Method == "OPTIONS" && c.Cors.Enabled && requestOrigin != "" { + return + } if c.Auth { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)