From 4c658bb6f0d01c9f7f981ef2a55bc9ddd65669fa Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Sun, 7 Mar 2021 17:10:45 +0100 Subject: [PATCH] webdav: add prefix support --- README.md | 2 ++ config/config.go | 7 +++++++ config/config_test.go | 5 +++++ docs/full-configuration.md | 1 + docs/rest-api.md | 2 +- docs/webdav.md | 4 +++- sftpgo.json | 3 ++- webdavd/server.go | 1 + webdavd/webdavd.go | 3 +++ 9 files changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d36e1101..4e7d8f60 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,8 @@ After starting SFTPGo you can manage users and folders using: To support embedded data providers like `bolt` and `SQLite` we can't have a CLI that directly write users and folders to the data provider, we always have to use the REST API. +Full details for users, folders, admins and other resources are documented in the [OpenAPI](/httpd/schema/openapi.yaml) schema. If you want to render the schema without importing it manually, you can explore it on [Stoplight](https://sftpgo.stoplight.io/docs/sftpgo/openapi.yaml). + ## Tutorials Some step-to-step tutorials can be found inside the source tree [howto](./docs/howto "How-to") directory. diff --git a/config/config.go b/config/config.go index 8dc015fd..2c37b218 100644 --- a/config/config.go +++ b/config/config.go @@ -59,6 +59,7 @@ var ( EnableHTTPS: false, ClientAuthType: 0, TLSCipherSuites: nil, + Prefix: "", } defaultHTTPDBinding = httpd.Binding{ Address: "127.0.0.1", @@ -675,6 +676,12 @@ func getWebDAVDBindingFromEnv(idx int) { isSet = true } + prefix, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PREFIX", idx)) + if ok { + binding.Prefix = prefix + isSet = true + } + if isSet { if len(globalConf.WebDAVD.Bindings) > idx { globalConf.WebDAVD.Bindings[idx] = binding diff --git a/config/config_test.go b/config/config_test.go index e9fe5a04..167ad32e 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -523,6 +523,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) { os.Setenv("SFTPGO_WEBDAVD__BINDINGS__2__PORT", "9000") os.Setenv("SFTPGO_WEBDAVD__BINDINGS__2__ENABLE_HTTPS", "1") os.Setenv("SFTPGO_WEBDAVD__BINDINGS__2__CLIENT_AUTH_TYPE", "1") + os.Setenv("SFTPGO_WEBDAVD__BINDINGS__2__PREFIX", "/dav2") t.Cleanup(func() { os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__ADDRESS") os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__PORT") @@ -532,6 +533,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) { os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__2__PORT") os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__2__ENABLE_HTTPS") os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__2__CLIENT_AUTH_TYPE") + os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__2__PREFIX") }) configDir := ".." @@ -543,17 +545,20 @@ func TestWebDAVBindingsFromEnv(t *testing.T) { require.Empty(t, bindings[0].Address) require.False(t, bindings[0].EnableHTTPS) require.Len(t, bindings[0].TLSCipherSuites, 0) + require.Empty(t, bindings[0].Prefix) require.Equal(t, 8000, bindings[1].Port) require.Equal(t, "127.0.0.1", bindings[1].Address) require.False(t, bindings[1].EnableHTTPS) require.Equal(t, 0, bindings[1].ClientAuthType) require.Len(t, bindings[1].TLSCipherSuites, 1) require.Equal(t, "TLS_RSA_WITH_AES_128_CBC_SHA", bindings[1].TLSCipherSuites[0]) + require.Empty(t, bindings[1].Prefix) require.Equal(t, 9000, bindings[2].Port) require.Equal(t, "127.0.1.1", bindings[2].Address) require.True(t, bindings[2].EnableHTTPS) require.Equal(t, 1, bindings[2].ClientAuthType) require.Nil(t, bindings[2].TLSCipherSuites) + require.Equal(t, "/dav2", bindings[2].Prefix) } func TestHTTPDBindingsFromEnv(t *testing.T) { diff --git a/docs/full-configuration.md b/docs/full-configuration.md index 6690974b..385f3107 100644 --- a/docs/full-configuration.md +++ b/docs/full-configuration.md @@ -134,6 +134,7 @@ The configuration file contains the following sections: - `enable_https`, boolean. Set to `true` and provide both a certificate and a key file to enable HTTPS connection for this binding. Default `false`. - `client_auth_type`, integer. Set to `1` to require a client certificate and verify it. Set to `2` to request a client certificate during the TLS handshake and verify it if given, in this mode the client is allowed not to send a certificate. At least one certification authority must be defined in order to verify client certificates. If no certification authority is defined, this setting is ignored. Default: 0. - `tls_cipher_suites`, list of strings. List of supported cipher suites for TLS version 1.2. If empty, a default list of secure cipher suites is used, with a preference order based on hardware performance. Note that TLS 1.3 ciphersuites are not configurable. The supported ciphersuites names are defined [here](https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52). Any invalid name will be silently ignored. The order matters, the ciphers listed first will be the preferred ones. Default: empty. + - `prefix`, string. Prefix for WebDAV resources, if empty WebDAV resources will be available at the `/` URI. If defined it must be an absolute URI, for example `/dav`. Default: "". - `bind_port`, integer. Deprecated, please use `bindings`. - `bind_address`, string. Deprecated, please use `bindings`. - `certificate_file`, string. Certificate for WebDAV over HTTPS. This can be an absolute path or a path relative to the config dir. diff --git a/docs/rest-api.md b/docs/rest-api.md index a1e2dc26..8182bc18 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -40,7 +40,7 @@ You can create other administrator and assign them the following permissions: You can also restrict administrator access based on the source IP address. If you are running SFTPGo behind a reverse proxy you need to allow both the proxy IP address and the real client IP. -The OpenAPI 3 schema for the exposed API can be found inside the source tree: [openapi.yaml](../httpd/schema/openapi.yaml "OpenAPI 3 specs"). +The OpenAPI 3 schema for the exposed API can be found inside the source tree: [openapi.yaml](../httpd/schema/openapi.yaml "OpenAPI 3 specs"). If you want to render the schema without importing it manually, you can explore it on [Stoplight](https://sftpgo.stoplight.io/docs/sftpgo/openapi.yaml). You can generate your own REST client in your preferred programming language, or even bash scripts, using an OpenAPI generator such as [swagger-codegen](https://github.com/swagger-api/swagger-codegen) or [OpenAPI Generator](https://openapi-generator.tech/). diff --git a/docs/webdav.md b/docs/webdav.md index 118db880..49880678 100644 --- a/docs/webdav.md +++ b/docs/webdav.md @@ -2,7 +2,7 @@ The `WebDAV` support can be enabled by configuring one or more `bindings` inside the `webdavd` configuration section. -Each user can access their home directory using the path `http/s://:/`. +Each user can access their home directory using the path `http/s://:/`. By default `prefix` is empty. If you define a prefix it must be an abosulte URI, for example `/dav`. WebDAV is quite a different protocol than SCP/FTP, there is no session concept, each command is a separate HTTP request and must be authenticated, to improve performance SFTPGo caches authenticated users. This way SFTPGo don't need to do a dataprovider query and a password check for each request. @@ -19,6 +19,8 @@ The MIME types caching configurations allows to set the maximum number of MIME t WebDAV should work as expected for most use cases but there are some minor issues and some missing features. +If you use WebDAV behind a reverse proxy ensure to preserve the `Host` header or `COPY`/`MOVE` operations will fail. For example for apache you have to set `ProxyPreserveHost On`. + Know issues: - removing a directory tree on Cloud Storage backends could generate a `not found` error when removing the last (virtual) directory. This happens if the client cycles the directories tree itself and removes files and directories one by one instead of issuing a single remove command diff --git a/sftpgo.json b/sftpgo.json index 0d041c5c..1ee63557 100644 --- a/sftpgo.json +++ b/sftpgo.json @@ -86,7 +86,8 @@ "address": "", "enable_https": false, "client_auth_type": 0, - "tls_cipher_suites": [] + "tls_cipher_suites": [], + "prefix": "" } ], "certificate_file": "", diff --git a/webdavd/server.go b/webdavd/server.go index 197e6973..933baba5 100644 --- a/webdavd/server.go +++ b/webdavd/server.go @@ -203,6 +203,7 @@ func (s *webDavServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } handler := webdav.Handler{ + Prefix: s.binding.Prefix, FileSystem: connection, LockSystem: lockSystem, Logger: writeLog, diff --git a/webdavd/webdavd.go b/webdavd/webdavd.go index 6f03cb81..e7d5ce1f 100644 --- a/webdavd/webdavd.go +++ b/webdavd/webdavd.go @@ -86,6 +86,9 @@ type Binding struct { // any invalid name will be silently ignored. // The order matters, the ciphers listed first will be the preferred ones. TLSCipherSuites []string `json:"tls_cipher_suites" mapstructure:"tls_cipher_suites"` + // Prefix for WebDAV resources, if empty WebDAV resources will be available at the + // root ("/") URI. If defined it must be an absolute URI. + Prefix string `json:"prefix" mapstructure:"prefix"` } func (b *Binding) isMutualTLSEnabled() bool {