Compare commits

...
4 Commits
Author SHA1 Message Date
Henrique Dias cd472b26be chore: bump to v2.0.0
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-06-11 13:09:51 +01:00
Steven Vandevelde 7358553e69 feat(BREAKING): extend cors functionality (#25) 2019-06-11 13:02:10 +01:00
Henrique Dias 764a69cd33 fix: numeric passwords (#24)
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-06-09 13:51:53 +01:00
Henrique Dias d266f1150e fix: auth enabled by default
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-06-09 13:47:28 +01:00
7 changed files with 94 additions and 36 deletions
+18 -2
View File
@@ -28,8 +28,17 @@ rules: []
# CORS configuration # CORS configuration
cors: cors:
- enabled: false enabled: true
allowed_hosts: [] credentials: true
allowed_headers:
- Depth
allowed_hosts:
- http://localhost:8080
allowed_methods:
- GET
exposed_headers:
- Content-Length
- Content-Range
users: users:
- username: admin - username: admin
@@ -54,6 +63,13 @@ There are more ways to customize how you run WebDAV through flags and environmen
An example of how to use this with `systemd` is on [webdav.service.example](/webdav.service.example). An example of how to use this with `systemd` is on [webdav.service.example](/webdav.service.example).
### CORS
The `allowed_*` properties are optional, the default value for each of them will be `*`. `exposed_headers` is optional as well, but is not set if not defined. Setting `credentials` to `true` will allow you to:
1. Use `withCredentials = true` in javascript.
2. Use the `username:password@host` syntax.
## License ## License
MIT © [Henrique Dias](https://hacdias.com) MIT © [Henrique Dias](https://hacdias.com)
+39 -20
View File
@@ -5,9 +5,10 @@ import (
"log" "log"
"os" "os"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/hacdias/webdav/webdav" "github.com/hacdias/webdav/v2/webdav"
"github.com/spf13/pflag" "github.com/spf13/pflag"
v "github.com/spf13/viper" v "github.com/spf13/viper"
wd "golang.org/x/net/webdav" wd "golang.org/x/net/webdav"
@@ -81,6 +82,10 @@ func parseUsers(raw []interface{}, c *webdav.Config) {
password, ok := u["password"].(string) password, ok := u["password"].(string)
if !ok { if !ok {
password = "" password = ""
if numPwd, ok := u["password"].(int); ok {
password = strconv.Itoa(numPwd)
}
} }
if strings.HasPrefix(password, "{env}") { if strings.HasPrefix(password, "{env}") {
@@ -118,30 +123,44 @@ func parseUsers(raw []interface{}, c *webdav.Config) {
} }
} }
func parseCors(raw []interface{}, c *webdav.Config) { func parseCors(cfg map[string]interface{}, c *webdav.Config) {
hosts := []string{} cors := webdav.CorsCfg{
Enabled: cfg["enabled"].(bool),
Credentials: cfg["credentials"].(bool),
}
for _, v := range raw { cors.AllowedHeaders = corsProperty("allowed_headers", cfg)
cors.AllowedHosts = corsProperty("allowed_hosts", cfg)
cors.AllowedMethods = corsProperty("allowed_methods", cfg)
cors.ExposedHeaders = corsProperty("exposed_headers", cfg)
if cfg, ok := v.(map[interface{}]interface{}); ok { c.Cors = cors
}
cors := webdav.CorsCfg{ func corsProperty(property string, cfg map[string]interface{}) []string {
Enabled: cfg["enabled"].(bool), var def []string
AllowedHosts: []string{},
}
if allowedHosts, ok := cfg["allowed_hosts"]; ok { if property == "exposed_headers" {
hosts = append(hosts, strings.Split(allowedHosts.(string), ",")...) def = []string{}
} } else {
def = []string{"*"}
}
if len(hosts) == 0 { if allowed, ok := cfg[property].([]interface{}); ok {
hosts = append(hosts, "*") items := make([]string, len(allowed))
}
cors.AllowedHosts = hosts for idx, a := range allowed {
c.Cors = cors items[idx] = a.(string)
}
if len(items) == 0 {
return def
} else {
return items
} }
} }
return def
} }
func readConfig(flags *pflag.FlagSet) *webdav.Config { func readConfig(flags *pflag.FlagSet) *webdav.Config {
@@ -157,8 +176,8 @@ func readConfig(flags *pflag.FlagSet) *webdav.Config {
}, },
Auth: getOptB(flags, "auth"), Auth: getOptB(flags, "auth"),
Cors: webdav.CorsCfg{ Cors: webdav.CorsCfg{
Enabled: false, Enabled: false,
AllowedHosts: []string{}, Credentials: false,
}, },
Users: map[string]*webdav.User{}, Users: map[string]*webdav.User{},
} }
@@ -174,7 +193,7 @@ func readConfig(flags *pflag.FlagSet) *webdav.Config {
} }
rawCors := v.Get("cors") rawCors := v.Get("cors")
if cors, ok := rawCors.([]interface{}); ok { if cors, ok := rawCors.(map[string]interface{}); ok {
parseCors(cors, cfg) parseCors(cors, cfg)
} }
+1
View File
@@ -21,6 +21,7 @@ func init() {
flags := rootCmd.Flags() flags := rootCmd.Flags()
flags.StringVarP(&cfgFile, "config", "c", "", "config file path") flags.StringVarP(&cfgFile, "config", "c", "", "config file path")
flags.BoolP("tls", "t", false, "enable tls") flags.BoolP("tls", "t", false, "enable tls")
flags.Bool("auth", true, "enable auth")
flags.String("cert", "cert.pem", "TLS certificate") flags.String("cert", "cert.pem", "TLS certificate")
flags.String("key", "key.pem", "TLS key") flags.String("key", "key.pem", "TLS key")
flags.StringP("address", "a", "0.0.0.0", "address to listen to") flags.StringP("address", "a", "0.0.0.0", "address to listen to")
+2 -3
View File
@@ -1,10 +1,9 @@
module github.com/hacdias/webdav module github.com/hacdias/webdav/v2
go 1.12 go 1.12
require ( require (
github.com/BurntSushi/toml v0.3.1 // indirect github.com/hacdias/webdav v1.4.1 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/spf13/cobra v0.0.3 github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.3.2 github.com/spf13/viper v1.3.2
+2
View File
@@ -8,6 +8,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/hacdias/webdav v1.4.1 h1:yhR4dgKr3JUfgDhFO2nWTq3HiYA37Sk/kaud/bRx1io=
github.com/hacdias/webdav v1.4.1/go.mod h1:1qXJOCQfiAXgYonkWHIMgWSfbKqUW8F4DlzXxSJCYIg=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+1 -1
View File
@@ -3,7 +3,7 @@ package main
import ( import (
"runtime" "runtime"
"github.com/hacdias/webdav/cmd" "github.com/hacdias/webdav/v2/cmd"
) )
func main() { func main() {
+31 -10
View File
@@ -4,12 +4,17 @@ import (
"context" "context"
"log" "log"
"net/http" "net/http"
"strings"
) )
// CorsCfg is the CORS config. // CorsCfg is the CORS config.
type CorsCfg struct { type CorsCfg struct {
Enabled bool Enabled bool
AllowedHosts []string Credentials bool
AllowedHeaders []string
AllowedHosts []string
AllowedMethods []string
ExposedHeaders []string
} }
// Config is the configuration of a WebDAV instance. // Config is the configuration of a WebDAV instance.
@@ -25,19 +30,34 @@ func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
u := c.User u := c.User
requestOrigin := r.Header.Get("Origin") 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 // Add CORS headers before any operation so even on a 401 unauthorized status, CORS will work.
if c.Cors.Enabled && requestOrigin != "" { if c.Cors.Enabled && requestOrigin != "" {
headers := w.Header() headers := w.Header()
if len(c.Cors.AllowedHosts) == 1 && c.Cors.AllowedHosts[0] == "*" { allowedHeaders := strings.Join(c.Cors.AllowedHeaders, ", ")
headers.Set("Access-Control-Allow-Methods", "*") allowedMethods := strings.Join(c.Cors.AllowedMethods, ", ")
headers.Set("Access-Control-Allow-Headers", "*") exposedHeaders := strings.Join(c.Cors.ExposedHeaders, ", ")
allowAllHosts := len(c.Cors.AllowedHosts) == 1 && c.Cors.AllowedHosts[0] == "*"
allowedHost := isAllowedHost(c.Cors.AllowedHosts, requestOrigin)
if allowAllHosts {
headers.Set("Access-Control-Allow-Origin", "*") headers.Set("Access-Control-Allow-Origin", "*")
} else if isAllowedHost(c.Cors.AllowedHosts, requestOrigin) { } else if allowedHost {
headers.Set("Access-Control-Allow-Origin", requestOrigin) headers.Set("Access-Control-Allow-Origin", requestOrigin)
headers.Set("Access-Control-Allow-Headers", "*") }
headers.Set("Access-Control-Allow-Methods", "*")
if allowAllHosts || allowedHost {
headers.Set("Access-Control-Allow-Headers", allowedHeaders)
headers.Set("Access-Control-Allow-Methods", allowedMethods)
if c.Cors.Credentials {
headers.Set("Access-Control-Allow-Credentials", "true")
}
if len(c.Cors.ExposedHeaders) > 0 {
headers.Set("Access-Control-Expose-Headers", exposedHeaders)
}
} }
} }
@@ -45,6 +65,7 @@ func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
// Authentication
if c.Auth { if c.Auth {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)