Browse Source

httpd service: add CORS support

Nicola Murino 4 years ago
parent
commit
0833b4698e
10 changed files with 78 additions and 18 deletions
  1. 17 1
      config/config.go
  2. 8 0
      docs/full-configuration.md
  3. 2 2
      go.mod
  4. 4 4
      go.sum
  5. 15 2
      httpd/httpd.go
  6. 3 3
      httpd/internal_test.go
  7. 15 1
      httpd/server.go
  8. 10 1
      sftpgo.json
  9. 3 3
      webdavd/webdavd.go
  10. 1 1
      webdavd/webdavd_test.go

+ 17 - 1
config/config.go

@@ -195,7 +195,7 @@ func Init() {
 			CertificateKeyFile: "",
 			CertificateKeyFile: "",
 			CACertificates:     []string{},
 			CACertificates:     []string{},
 			CARevocationLists:  []string{},
 			CARevocationLists:  []string{},
-			Cors: webdavd.Cors{
+			Cors: webdavd.CorsConfig{
 				Enabled:          false,
 				Enabled:          false,
 				AllowedOrigins:   []string{},
 				AllowedOrigins:   []string{},
 				AllowedMethods:   []string{},
 				AllowedMethods:   []string{},
@@ -280,6 +280,15 @@ func Init() {
 			CARevocationLists:  nil,
 			CARevocationLists:  nil,
 			SigningPassphrase:  "",
 			SigningPassphrase:  "",
 			MaxUploadFileSize:  1048576000,
 			MaxUploadFileSize:  1048576000,
+			Cors: httpd.CorsConfig{
+				Enabled:          false,
+				AllowedOrigins:   []string{},
+				AllowedMethods:   []string{},
+				AllowedHeaders:   []string{},
+				ExposedHeaders:   []string{},
+				AllowCredentials: false,
+				MaxAge:           0,
+			},
 		},
 		},
 		HTTPConfig: httpclient.Config{
 		HTTPConfig: httpclient.Config{
 			Timeout:        20,
 			Timeout:        20,
@@ -1217,6 +1226,13 @@ func setViperDefaults() {
 	viper.SetDefault("httpd.ca_revocation_lists", globalConf.HTTPDConfig.CARevocationLists)
 	viper.SetDefault("httpd.ca_revocation_lists", globalConf.HTTPDConfig.CARevocationLists)
 	viper.SetDefault("httpd.signing_passphrase", globalConf.HTTPDConfig.SigningPassphrase)
 	viper.SetDefault("httpd.signing_passphrase", globalConf.HTTPDConfig.SigningPassphrase)
 	viper.SetDefault("httpd.max_upload_file_size", globalConf.HTTPDConfig.MaxUploadFileSize)
 	viper.SetDefault("httpd.max_upload_file_size", globalConf.HTTPDConfig.MaxUploadFileSize)
+	viper.SetDefault("httpd.cors.enabled", globalConf.HTTPDConfig.Cors.Enabled)
+	viper.SetDefault("httpd.cors.allowed_origins", globalConf.HTTPDConfig.Cors.AllowedOrigins)
+	viper.SetDefault("httpd.cors.allowed_methods", globalConf.HTTPDConfig.Cors.AllowedMethods)
+	viper.SetDefault("httpd.cors.allowed_headers", globalConf.HTTPDConfig.Cors.AllowedHeaders)
+	viper.SetDefault("httpd.cors.exposed_headers", globalConf.HTTPDConfig.Cors.ExposedHeaders)
+	viper.SetDefault("httpd.cors.allow_credentials", globalConf.HTTPDConfig.Cors.AllowCredentials)
+	viper.SetDefault("httpd.cors.max_age", globalConf.HTTPDConfig.Cors.MaxAge)
 	viper.SetDefault("http.timeout", globalConf.HTTPConfig.Timeout)
 	viper.SetDefault("http.timeout", globalConf.HTTPConfig.Timeout)
 	viper.SetDefault("http.retry_wait_min", globalConf.HTTPConfig.RetryWaitMin)
 	viper.SetDefault("http.retry_wait_min", globalConf.HTTPConfig.RetryWaitMin)
 	viper.SetDefault("http.retry_wait_max", globalConf.HTTPConfig.RetryWaitMax)
 	viper.SetDefault("http.retry_wait_max", globalConf.HTTPConfig.RetryWaitMax)

+ 8 - 0
docs/full-configuration.md

@@ -232,6 +232,14 @@ The configuration file contains the following sections:
   - `ca_revocation_lists`, list of strings. Set a revocation lists, one for each root CA, to be used to check if a client certificate has been revoked. The revocation lists can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
   - `ca_revocation_lists`, list of strings. Set a revocation lists, one for each root CA, to be used to check if a client certificate has been revoked. The revocation lists can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
   - `signing_passphrase`, string. Passphrase to use to derive the signing key for JWT and CSRF tokens. If empty a random signing key will be generated each time SFTPGo starts. If you set a signing passphrase you should consider rotating it periodically for added security.
   - `signing_passphrase`, string. Passphrase to use to derive the signing key for JWT and CSRF tokens. If empty a random signing key will be generated each time SFTPGo starts. If you set a signing passphrase you should consider rotating it periodically for added security.
   - `max_upload_file_size`, integer. Defines the maximum request body size, in bytes, for Web Client/API HTTP upload requests. 0 means no limit. Default: 1048576000.
   - `max_upload_file_size`, integer. Defines the maximum request body size, in bytes, for Web Client/API HTTP upload requests. 0 means no limit. Default: 1048576000.
+  - `cors` struct containing CORS configuration. SFTPGo uses [Go CORS handler](https://github.com/rs/cors), please refer to upstream documentation for fields meaning and their default values.
+    - `enabled`, boolean, set to true to enable CORS.
+    - `allowed_origins`, list of strings.
+    - `allowed_methods`, list of strings.
+    - `allowed_headers`, list of strings.
+    - `exposed_headers`, list of strings.
+    - `allow_credentials` boolean.
+    - `max_age`, integer.
 - **"telemetry"**, the configuration for the telemetry server, more details [below](#telemetry-server)
 - **"telemetry"**, the configuration for the telemetry server, more details [below](#telemetry-server)
   - `bind_port`, integer. The port used for serving HTTP requests. Set to 0 to disable HTTP server. Default: 10000
   - `bind_port`, integer. The port used for serving HTTP requests. Set to 0 to disable HTTP server. Default: 10000
   - `bind_address`, string. Leave blank to listen on all available network interfaces. On \*NIX you can specify an absolute path to listen on a Unix-domain socket. Default: "127.0.0.1"
   - `bind_address`, string. Leave blank to listen on all available network interfaces. On \*NIX you can specify an absolute path to listen on a Unix-domain socket. Default: "127.0.0.1"

+ 2 - 2
go.mod

@@ -8,7 +8,7 @@ require (
 	github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
 	github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
 	github.com/alexedwards/argon2id v0.0.0-20210511081203-7d35d68092b8
 	github.com/alexedwards/argon2id v0.0.0-20210511081203-7d35d68092b8
 	github.com/aws/aws-sdk-go v1.42.4
 	github.com/aws/aws-sdk-go v1.42.4
-	github.com/cockroachdb/cockroach-go/v2 v2.2.1
+	github.com/cockroachdb/cockroach-go/v2 v2.2.3
 	github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b
 	github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b
 	github.com/fclairamb/ftpserverlib v0.16.0
 	github.com/fclairamb/ftpserverlib v0.16.0
 	github.com/fclairamb/go-log v0.1.0
 	github.com/fclairamb/go-log v0.1.0
@@ -25,7 +25,7 @@ require (
 	github.com/hashicorp/go-retryablehttp v0.7.0
 	github.com/hashicorp/go-retryablehttp v0.7.0
 	github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
 	github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
 	github.com/klauspost/compress v1.13.6
 	github.com/klauspost/compress v1.13.6
-	github.com/lestrrat-go/jwx v1.2.10
+	github.com/lestrrat-go/jwx v1.2.11
 	github.com/lib/pq v1.10.4
 	github.com/lib/pq v1.10.4
 	github.com/lithammer/shortuuid/v3 v3.0.7
 	github.com/lithammer/shortuuid/v3 v3.0.7
 	github.com/mattn/go-sqlite3 v1.14.9
 	github.com/mattn/go-sqlite3 v1.14.9

+ 4 - 4
go.sum

@@ -192,8 +192,8 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 h1:zH8ljVhhq7yC0MIeUL/IviMtY8hx2mK8cN9wEYb8ggw=
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 h1:zH8ljVhhq7yC0MIeUL/IviMtY8hx2mK8cN9wEYb8ggw=
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
-github.com/cockroachdb/cockroach-go/v2 v2.2.1 h1:nZte1DDdL9iu8IV0YPmX8l9Lg2+HRJ3CMvkT3iG52rc=
-github.com/cockroachdb/cockroach-go/v2 v2.2.1/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI=
+github.com/cockroachdb/cockroach-go/v2 v2.2.3 h1:2881elKwTMrAWuSP2N/4PtU6XyqoyI55Fv3TSTD+Efo=
+github.com/cockroachdb/cockroach-go/v2 v2.2.3/go.mod h1:u3MiKYGupPPjkn3ozknpMUpxPaNLTFWAya419/zv6eI=
 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@@ -566,8 +566,8 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++
 github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
 github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
 github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
 github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
 github.com/lestrrat-go/jwx v1.2.6/go.mod h1:tJuGuAI3LC71IicTx82Mz1n3w9woAs2bYJZpkjJQ5aU=
 github.com/lestrrat-go/jwx v1.2.6/go.mod h1:tJuGuAI3LC71IicTx82Mz1n3w9woAs2bYJZpkjJQ5aU=
-github.com/lestrrat-go/jwx v1.2.10 h1:rz6Ywm3wCRWsy2lyRZ7uHzE4E09m7X9eINaoAEVXCKw=
-github.com/lestrrat-go/jwx v1.2.10/go.mod h1:25DcLbNWArPA/Ew5CcBmewl32cJKxOk5cbepBsIJFzw=
+github.com/lestrrat-go/jwx v1.2.11 h1:e9BS5NQ003hxXogNsgf5fEWf01ZJvj4Aj1qy7Dykqm8=
+github.com/lestrrat-go/jwx v1.2.11/go.mod h1:25DcLbNWArPA/Ew5CcBmewl32cJKxOk5cbepBsIJFzw=
 github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
 github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=

+ 15 - 2
httpd/httpd.go

@@ -319,6 +319,17 @@ type ServicesStatus struct {
 	MFA          mfa.ServiceStatus           `json:"mfa"`
 	MFA          mfa.ServiceStatus           `json:"mfa"`
 }
 }
 
 
+// CorsConfig defines the CORS configuration
+type CorsConfig struct {
+	AllowedOrigins   []string `json:"allowed_origins" mapstructure:"allowed_origins"`
+	AllowedMethods   []string `json:"allowed_methods" mapstructure:"allowed_methods"`
+	AllowedHeaders   []string `json:"allowed_headers" mapstructure:"allowed_headers"`
+	ExposedHeaders   []string `json:"exposed_headers" mapstructure:"exposed_headers"`
+	AllowCredentials bool     `json:"allow_credentials" mapstructure:"allow_credentials"`
+	Enabled          bool     `json:"enabled" mapstructure:"enabled"`
+	MaxAge           int      `json:"max_age" mapstructure:"max_age"`
+}
+
 // Conf httpd daemon configuration
 // Conf httpd daemon configuration
 type Conf struct {
 type Conf struct {
 	// Addresses and ports to bind to
 	// Addresses and ports to bind to
@@ -351,6 +362,8 @@ type Conf struct {
 	// MaxUploadFileSize Defines the maximum request body size, in bytes, for Web Client/API HTTP upload requests.
 	// MaxUploadFileSize Defines the maximum request body size, in bytes, for Web Client/API HTTP upload requests.
 	// 0 means no limit
 	// 0 means no limit
 	MaxUploadFileSize int64 `json:"max_upload_file_size" mapstructure:"max_upload_file_size"`
 	MaxUploadFileSize int64 `json:"max_upload_file_size" mapstructure:"max_upload_file_size"`
+	// CORS configuration
+	Cors CorsConfig `json:"cors" mapstructure:"cors"`
 }
 }
 
 
 type apiResponse struct {
 type apiResponse struct {
@@ -456,7 +469,7 @@ func (c *Conf) Initialize(configDir string) error {
 		}
 		}
 
 
 		go func(b Binding) {
 		go func(b Binding) {
-			server := newHttpdServer(b, staticFilesPath, c.SigningPassphrase)
+			server := newHttpdServer(b, staticFilesPath, c.SigningPassphrase, c.Cors)
 
 
 			exitChannel <- server.listenAndServe()
 			exitChannel <- server.listenAndServe()
 		}(binding)
 		}(binding)
@@ -600,7 +613,7 @@ func GetHTTPRouter() http.Handler {
 		EnableWebAdmin:  true,
 		EnableWebAdmin:  true,
 		EnableWebClient: true,
 		EnableWebClient: true,
 	}
 	}
-	server := newHttpdServer(b, "../static", "")
+	server := newHttpdServer(b, "../static", "", CorsConfig{})
 	server.initializeRouter()
 	server.initializeRouter()
 	return server.router
 	return server.router
 }
 }

+ 3 - 3
httpd/internal_test.go

@@ -1406,7 +1406,7 @@ func TestProxyHeaders(t *testing.T) {
 	}
 	}
 	err = b.parseAllowedProxy()
 	err = b.parseAllowedProxy()
 	assert.NoError(t, err)
 	assert.NoError(t, err)
-	server := newHttpdServer(b, "", "")
+	server := newHttpdServer(b, "", "", CorsConfig{Enabled: true})
 	server.initializeRouter()
 	server.initializeRouter()
 	testServer := httptest.NewServer(server.router)
 	testServer := httptest.NewServer(server.router)
 	defer testServer.Close()
 	defer testServer.Close()
@@ -1492,7 +1492,7 @@ func TestRecoverer(t *testing.T) {
 		EnableWebAdmin:  true,
 		EnableWebAdmin:  true,
 		EnableWebClient: false,
 		EnableWebClient: false,
 	}
 	}
-	server := newHttpdServer(b, "../static", "")
+	server := newHttpdServer(b, "../static", "", CorsConfig{})
 	server.initializeRouter()
 	server.initializeRouter()
 	server.router.Get(recoveryPath, func(w http.ResponseWriter, r *http.Request) {
 	server.router.Get(recoveryPath, func(w http.ResponseWriter, r *http.Request) {
 		panic("panic")
 		panic("panic")
@@ -1607,7 +1607,7 @@ func TestWebAdminRedirect(t *testing.T) {
 		EnableWebAdmin:  true,
 		EnableWebAdmin:  true,
 		EnableWebClient: false,
 		EnableWebClient: false,
 	}
 	}
-	server := newHttpdServer(b, "../static", "")
+	server := newHttpdServer(b, "../static", "", CorsConfig{})
 	server.initializeRouter()
 	server.initializeRouter()
 	testServer := httptest.NewServer(server.router)
 	testServer := httptest.NewServer(server.router)
 	defer testServer.Close()
 	defer testServer.Close()

+ 15 - 1
httpd/server.go

@@ -16,6 +16,7 @@ import (
 	"github.com/go-chi/jwtauth/v5"
 	"github.com/go-chi/jwtauth/v5"
 	"github.com/go-chi/render"
 	"github.com/go-chi/render"
 	"github.com/lestrrat-go/jwx/jwa"
 	"github.com/lestrrat-go/jwx/jwa"
+	"github.com/rs/cors"
 	"github.com/rs/xid"
 	"github.com/rs/xid"
 
 
 	"github.com/drakkan/sftpgo/v2/common"
 	"github.com/drakkan/sftpgo/v2/common"
@@ -41,15 +42,17 @@ type httpdServer struct {
 	router            *chi.Mux
 	router            *chi.Mux
 	tokenAuth         *jwtauth.JWTAuth
 	tokenAuth         *jwtauth.JWTAuth
 	signingPassphrase string
 	signingPassphrase string
+	cors              CorsConfig
 }
 }
 
 
-func newHttpdServer(b Binding, staticFilesPath, signingPassphrase string) *httpdServer {
+func newHttpdServer(b Binding, staticFilesPath, signingPassphrase string, cors CorsConfig) *httpdServer {
 	return &httpdServer{
 	return &httpdServer{
 		binding:           b,
 		binding:           b,
 		staticFilesPath:   staticFilesPath,
 		staticFilesPath:   staticFilesPath,
 		enableWebAdmin:    b.EnableWebAdmin,
 		enableWebAdmin:    b.EnableWebAdmin,
 		enableWebClient:   b.EnableWebClient,
 		enableWebClient:   b.EnableWebClient,
 		signingPassphrase: signingPassphrase,
 		signingPassphrase: signingPassphrase,
+		cors:              cors,
 	}
 	}
 }
 }
 
 
@@ -943,6 +946,17 @@ func (s *httpdServer) initializeRouter() {
 	s.router.Use(s.checkConnection)
 	s.router.Use(s.checkConnection)
 	s.router.Use(logger.NewStructuredLogger(logger.GetLogger()))
 	s.router.Use(logger.NewStructuredLogger(logger.GetLogger()))
 	s.router.Use(recoverer)
 	s.router.Use(recoverer)
+	if s.cors.Enabled {
+		c := cors.New(cors.Options{
+			AllowedOrigins:   s.cors.AllowedOrigins,
+			AllowedMethods:   s.cors.AllowedMethods,
+			AllowedHeaders:   s.cors.AllowedHeaders,
+			ExposedHeaders:   s.cors.ExposedHeaders,
+			MaxAge:           s.cors.MaxAge,
+			AllowCredentials: s.cors.AllowCredentials,
+		})
+		s.router.Use(c.Handler)
+	}
 	s.router.Use(middleware.GetHead)
 	s.router.Use(middleware.GetHead)
 	s.router.Use(middleware.StripSlashes)
 	s.router.Use(middleware.StripSlashes)
 
 

+ 10 - 1
sftpgo.json

@@ -221,7 +221,16 @@
     "ca_certificates": [],
     "ca_certificates": [],
     "ca_revocation_lists": [],
     "ca_revocation_lists": [],
     "signing_passphrase": "",
     "signing_passphrase": "",
-    "max_upload_file_size": 1048576000
+    "max_upload_file_size": 1048576000,
+    "cors": {
+      "enabled": false,
+      "allowed_origins": [],
+      "allowed_methods": [],
+      "allowed_headers": [],
+      "exposed_headers": [],
+      "allow_credentials": false,
+      "max_age": 0
+    }
   },
   },
   "telemetry": {
   "telemetry": {
     "bind_port": 10000,
     "bind_port": 10000,

+ 3 - 3
webdavd/webdavd.go

@@ -37,8 +37,8 @@ type ServiceStatus struct {
 	Bindings []Binding `json:"bindings"`
 	Bindings []Binding `json:"bindings"`
 }
 }
 
 
-// Cors configuration
-type Cors struct {
+// CorsConfig defines the CORS configuration
+type CorsConfig struct {
 	AllowedOrigins   []string `json:"allowed_origins" mapstructure:"allowed_origins"`
 	AllowedOrigins   []string `json:"allowed_origins" mapstructure:"allowed_origins"`
 	AllowedMethods   []string `json:"allowed_methods" mapstructure:"allowed_methods"`
 	AllowedMethods   []string `json:"allowed_methods" mapstructure:"allowed_methods"`
 	AllowedHeaders   []string `json:"allowed_headers" mapstructure:"allowed_headers"`
 	AllowedHeaders   []string `json:"allowed_headers" mapstructure:"allowed_headers"`
@@ -135,7 +135,7 @@ type Configuration struct {
 	// if a client certificate has been revoked
 	// if a client certificate has been revoked
 	CARevocationLists []string `json:"ca_revocation_lists" mapstructure:"ca_revocation_lists"`
 	CARevocationLists []string `json:"ca_revocation_lists" mapstructure:"ca_revocation_lists"`
 	// CORS configuration
 	// CORS configuration
-	Cors Cors `json:"cors" mapstructure:"cors"`
+	Cors CorsConfig `json:"cors" mapstructure:"cors"`
 	// Cache configuration
 	// Cache configuration
 	Cache Cache `json:"cache" mapstructure:"cache"`
 	Cache Cache `json:"cache" mapstructure:"cache"`
 }
 }

+ 1 - 1
webdavd/webdavd_test.go

@@ -347,7 +347,7 @@ func TestMain(m *testing.M) {
 			ClientAuthType: 2,
 			ClientAuthType: 2,
 		},
 		},
 	}
 	}
-	webDavConf.Cors = webdavd.Cors{
+	webDavConf.Cors = webdavd.CorsConfig{
 		Enabled:        true,
 		Enabled:        true,
 		AllowedOrigins: []string{"*"},
 		AllowedOrigins: []string{"*"},
 		AllowedMethods: []string{
 		AllowedMethods: []string{