mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-06 22:30:56 +03:00
webdav: user caching is now mandatory
we cache the lock system with the user, without user caching we cannot support locks for resource
This commit is contained in:
@@ -104,7 +104,6 @@ func init() {
|
|||||||
},
|
},
|
||||||
Cache: webdavd.Cache{
|
Cache: webdavd.Cache{
|
||||||
Users: webdavd.UsersCacheConfig{
|
Users: webdavd.UsersCacheConfig{
|
||||||
Enabled: true,
|
|
||||||
ExpirationTime: 0,
|
ExpirationTime: 0,
|
||||||
MaxSize: 50,
|
MaxSize: 50,
|
||||||
},
|
},
|
||||||
@@ -399,7 +398,6 @@ func setViperDefaults() {
|
|||||||
viper.SetDefault("webdavd.cors.exposed_headers", globalConf.WebDAVD.Cors.ExposedHeaders)
|
viper.SetDefault("webdavd.cors.exposed_headers", globalConf.WebDAVD.Cors.ExposedHeaders)
|
||||||
viper.SetDefault("webdavd.cors.allow_credentials", globalConf.WebDAVD.Cors.AllowCredentials)
|
viper.SetDefault("webdavd.cors.allow_credentials", globalConf.WebDAVD.Cors.AllowCredentials)
|
||||||
viper.SetDefault("webdavd.cors.max_age", globalConf.WebDAVD.Cors.MaxAge)
|
viper.SetDefault("webdavd.cors.max_age", globalConf.WebDAVD.Cors.MaxAge)
|
||||||
viper.SetDefault("webdavd.cache.users.enabled", globalConf.WebDAVD.Cache.Users.Enabled)
|
|
||||||
viper.SetDefault("webdavd.cache.users.expiration_time", globalConf.WebDAVD.Cache.Users.ExpirationTime)
|
viper.SetDefault("webdavd.cache.users.expiration_time", globalConf.WebDAVD.Cache.Users.ExpirationTime)
|
||||||
viper.SetDefault("webdavd.cache.users.max_size", globalConf.WebDAVD.Cache.Users.MaxSize)
|
viper.SetDefault("webdavd.cache.users.max_size", globalConf.WebDAVD.Cache.Users.MaxSize)
|
||||||
viper.SetDefault("webdavd.cache.mime_types.enabled", globalConf.WebDAVD.Cache.MimeTypes.Enabled)
|
viper.SetDefault("webdavd.cache.mime_types.enabled", globalConf.WebDAVD.Cache.MimeTypes.Enabled)
|
||||||
|
|||||||
@@ -4,13 +4,12 @@ The experimental `WebDAV` support can be enabled by setting a `bind_port` inside
|
|||||||
|
|
||||||
Each user has his own path like `http/s://<SFTPGo ip>:<WevDAVPORT>/<username>` and it must authenticate using password credentials.
|
Each user has his own path like `http/s://<SFTPGo ip>:<WevDAVPORT>/<username>` and it must authenticate using password credentials.
|
||||||
|
|
||||||
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, performance can be greatly improved enabling caching for the authenticated users (it is enabled by default). This way SFTPGo don't need to do a dataprovider query and a password check for each request.
|
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.
|
||||||
If you enable quota support a dataprovider query is required, to update the user quota, after each file upload.
|
|
||||||
|
|
||||||
The user caching configuration allows to set:
|
The user caching configuration allows to set:
|
||||||
|
|
||||||
- `expiration_time` in minutes. If a user is cached for more than the specified minutes it will be removed from the cache and a new dataprovider query will be performed. Please note that the `last_login` field will not be updated and `external_auth_hook`, `pre_login_hook` and `check_password_hook` will not be executed if the user is obtained from the cache.
|
- `expiration_time` in minutes. If a user is cached for more than the specified minutes it will be removed from the cache and a new dataprovider query will be performed. Please note that the `last_login` field will not be updated and `external_auth_hook`, `pre_login_hook` and `check_password_hook` will not be executed if the user is obtained from the cache.
|
||||||
- `max_size`. Maximum number of users to cache. When this limit is reached the user with the oldest expiration date will be removed from the cache. 0 means no limit however the cache size cannot exceed the number of users so if you have a small number of users you can leave this setting to 0.
|
- `max_size`. Maximum number of users to cache. When this limit is reached the user with the oldest expiration date will be removed from the cache. 0 means no limit however the cache size cannot exceed the number of users so if you have a small number of users you can set this value to 0.
|
||||||
|
|
||||||
Users are automatically removed from the cache after an update/delete.
|
Users are automatically removed from the cache after an update/delete.
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,6 @@
|
|||||||
},
|
},
|
||||||
"cache": {
|
"cache": {
|
||||||
"users": {
|
"users": {
|
||||||
"enabled": true,
|
|
||||||
"expiration_time": 0,
|
"expiration_time": 0,
|
||||||
"max_size": 50
|
"max_size": 50
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ func (fi *webDavFileInfo) ContentType(ctx context.Context) (string, error) {
|
|||||||
return contentType, nil
|
return contentType, nil
|
||||||
}
|
}
|
||||||
contentType, err := fi.Fs.GetMimeType(fi.fsPath)
|
contentType, err := fi.Fs.GetMimeType(fi.fsPath)
|
||||||
mimeTypeCache.addMimeToCache(extension, contentType)
|
|
||||||
if contentType != "" {
|
if contentType != "" {
|
||||||
|
mimeTypeCache.addMimeToCache(extension, contentType)
|
||||||
return contentType, err
|
return contentType, err
|
||||||
}
|
}
|
||||||
return "", webdav.ErrNotImplemented
|
return "", webdav.ErrNotImplemented
|
||||||
|
|||||||
@@ -647,7 +647,6 @@ func TestBasicUsersCache(t *testing.T) {
|
|||||||
BindPort: 9000,
|
BindPort: 9000,
|
||||||
Cache: Cache{
|
Cache: Cache{
|
||||||
Users: UsersCacheConfig{
|
Users: UsersCacheConfig{
|
||||||
Enabled: true,
|
|
||||||
MaxSize: 50,
|
MaxSize: 50,
|
||||||
ExpirationTime: 1,
|
ExpirationTime: 1,
|
||||||
},
|
},
|
||||||
@@ -752,7 +751,6 @@ func TestUsersCacheSizeAndExpiration(t *testing.T) {
|
|||||||
BindPort: 9000,
|
BindPort: 9000,
|
||||||
Cache: Cache{
|
Cache: Cache{
|
||||||
Users: UsersCacheConfig{
|
Users: UsersCacheConfig{
|
||||||
Enabled: true,
|
|
||||||
MaxSize: 3,
|
MaxSize: 3,
|
||||||
ExpirationTime: 1,
|
ExpirationTime: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (s *webDavServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if path.Clean(r.URL.Path) == "/" && (r.Method == "GET" || r.Method == "PROPFIND" || r.Method == "OPTIONS") {
|
if path.Clean(r.URL.Path) == "/" && (r.Method == http.MethodGet || r.Method == "PROPFIND" || r.Method == http.MethodOptions) {
|
||||||
http.Redirect(w, r, path.Join("/", user.Username), http.StatusMovedPermanently)
|
http.Redirect(w, r, path.Join("/", user.Username), http.StatusMovedPermanently)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ func (s *webDavServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
prefix := path.Join("/", user.Username)
|
prefix := path.Join("/", user.Username)
|
||||||
// see RFC4918, section 9.4
|
// see RFC4918, section 9.4
|
||||||
if r.Method == "GET" {
|
if r.Method == http.MethodGet {
|
||||||
p := strings.TrimPrefix(path.Clean(r.URL.Path), prefix)
|
p := strings.TrimPrefix(path.Clean(r.URL.Path), prefix)
|
||||||
info, err := connection.Stat(ctx, p)
|
info, err := connection.Stat(ctx, p)
|
||||||
if err == nil && info.IsDir() {
|
if err == nil && info.IsDir() {
|
||||||
@@ -170,7 +170,6 @@ func (s *webDavServer) authenticate(r *http.Request) (dataprovider.User, bool, w
|
|||||||
if !ok {
|
if !ok {
|
||||||
return user, false, nil, err401
|
return user, false, nil, err401
|
||||||
}
|
}
|
||||||
if s.config.Cache.Users.Enabled {
|
|
||||||
result, ok := dataprovider.GetCachedWebDAVUser(username)
|
result, ok := dataprovider.GetCachedWebDAVUser(username)
|
||||||
if ok {
|
if ok {
|
||||||
cachedUser := result.(*dataprovider.CachedUser)
|
cachedUser := result.(*dataprovider.CachedUser)
|
||||||
@@ -184,14 +183,13 @@ func (s *webDavServer) authenticate(r *http.Request) (dataprovider.User, bool, w
|
|||||||
return user, false, nil, dataprovider.ErrInvalidCredentials
|
return user, false, nil, dataprovider.ErrInvalidCredentials
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
user, err = dataprovider.CheckUserAndPass(username, password, utils.GetIPFromRemoteAddress(r.RemoteAddr), common.ProtocolWebDAV)
|
user, err = dataprovider.CheckUserAndPass(username, password, utils.GetIPFromRemoteAddress(r.RemoteAddr), common.ProtocolWebDAV)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
updateLoginMetrics(username, r.RemoteAddr, err)
|
updateLoginMetrics(username, r.RemoteAddr, err)
|
||||||
return user, false, nil, err
|
return user, false, nil, err
|
||||||
}
|
}
|
||||||
lockSystem := webdav.NewMemLS()
|
lockSystem := webdav.NewMemLS()
|
||||||
if s.config.Cache.Users.Enabled && len(password) > 0 {
|
if password != "" {
|
||||||
cachedUser := &dataprovider.CachedUser{
|
cachedUser := &dataprovider.CachedUser{
|
||||||
User: user,
|
User: user,
|
||||||
Password: password,
|
Password: password,
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ type Cors struct {
|
|||||||
|
|
||||||
// UsersCacheConfig defines the cache configuration for users
|
// UsersCacheConfig defines the cache configuration for users
|
||||||
type UsersCacheConfig struct {
|
type UsersCacheConfig struct {
|
||||||
Enabled bool `json:"enabled" mapstructure:"enabled"`
|
|
||||||
ExpirationTime int `json:"expiration_time" mapstructure:"expiration_time"`
|
ExpirationTime int `json:"expiration_time" mapstructure:"expiration_time"`
|
||||||
MaxSize int `json:"max_size" mapstructure:"max_size"`
|
MaxSize int `json:"max_size" mapstructure:"max_size"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user