mirror of
https://github.com/hacdias/webdav.git
synced 2026-09-22 03:20:41 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd472b26be | ||
|
|
7358553e69 | ||
|
|
764a69cd33 | ||
|
|
d266f1150e |
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=
|
||||||
|
|||||||
@@ -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
@@ -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"`)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user