mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-06 22:30:56 +03:00
WebDAV: add caching for authenticated users
In this way we get a big performance boost
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
"github.com/drakkan/sftpgo/common"
|
||||
"github.com/drakkan/sftpgo/dataprovider"
|
||||
"github.com/drakkan/sftpgo/httpd"
|
||||
"github.com/drakkan/sftpgo/vfs"
|
||||
)
|
||||
|
||||
@@ -611,3 +612,263 @@ func TestTransferSeek(t *testing.T) {
|
||||
err = os.Remove(testFilePath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestBasicUsersCache(t *testing.T) {
|
||||
username := "webdav_internal_test"
|
||||
password := "pwd"
|
||||
u := dataprovider.User{
|
||||
Username: username,
|
||||
Password: password,
|
||||
HomeDir: filepath.Join(os.TempDir(), username),
|
||||
Status: 1,
|
||||
ExpirationDate: 0,
|
||||
}
|
||||
u.Permissions = make(map[string][]string)
|
||||
u.Permissions["/"] = []string{dataprovider.PermAny}
|
||||
user, _, err := httpd.AddUser(u, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
c := &Configuration{
|
||||
BindPort: 9000,
|
||||
Cache: Cache{
|
||||
Enabled: true,
|
||||
MaxSize: 50,
|
||||
ExpirationTime: 1,
|
||||
},
|
||||
}
|
||||
server, err := newServer(c, configDir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, _, err = server.authenticate(req)
|
||||
assert.Error(t, err)
|
||||
|
||||
now := time.Now()
|
||||
req.SetBasicAuth(username, password)
|
||||
_, isCached, err := server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
// now the user should be cached
|
||||
var cachedUser dataprovider.CachedUser
|
||||
result, ok := dataprovider.GetCachedWebDAVUser(username)
|
||||
if assert.True(t, ok) {
|
||||
cachedUser = result.(dataprovider.CachedUser)
|
||||
assert.False(t, cachedUser.IsExpired())
|
||||
assert.True(t, cachedUser.Expiration.After(now.Add(time.Duration(c.Cache.ExpirationTime)*time.Minute)))
|
||||
// authenticate must return the cached user now
|
||||
authUser, isCached, err := server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, isCached)
|
||||
assert.Equal(t, cachedUser.User, authUser)
|
||||
}
|
||||
// a wrong password must fail
|
||||
req.SetBasicAuth(username, "wrong")
|
||||
_, _, err = server.authenticate(req)
|
||||
assert.EqualError(t, err, dataprovider.ErrInvalidCredentials.Error())
|
||||
req.SetBasicAuth(username, password)
|
||||
|
||||
// force cached user expiration
|
||||
cachedUser.Expiration = now
|
||||
dataprovider.CacheWebDAVUser(cachedUser, c.Cache.MaxSize)
|
||||
result, ok = dataprovider.GetCachedWebDAVUser(username)
|
||||
if assert.True(t, ok) {
|
||||
cachedUser = result.(dataprovider.CachedUser)
|
||||
assert.True(t, cachedUser.IsExpired())
|
||||
}
|
||||
// now authenticate should get the user from the data provider and update the cache
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
result, ok = dataprovider.GetCachedWebDAVUser(username)
|
||||
if assert.True(t, ok) {
|
||||
cachedUser = result.(dataprovider.CachedUser)
|
||||
assert.False(t, cachedUser.IsExpired())
|
||||
}
|
||||
// cache is invalidated after a user modification
|
||||
user, _, err = httpd.UpdateUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(username)
|
||||
assert.False(t, ok)
|
||||
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(username)
|
||||
assert.True(t, ok)
|
||||
// cache is invalidated after user deletion
|
||||
_, err = httpd.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(username)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestUsersCacheSizeAndExpiration(t *testing.T) {
|
||||
username := "webdav_internal_test"
|
||||
password := "pwd"
|
||||
u := dataprovider.User{
|
||||
HomeDir: filepath.Join(os.TempDir(), username),
|
||||
Status: 1,
|
||||
ExpirationDate: 0,
|
||||
}
|
||||
u.Username = username + "1"
|
||||
u.Password = password + "1"
|
||||
u.Permissions = make(map[string][]string)
|
||||
u.Permissions["/"] = []string{dataprovider.PermAny}
|
||||
user1, _, err := httpd.AddUser(u, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
u.Username = username + "2"
|
||||
u.Password = password + "2"
|
||||
user2, _, err := httpd.AddUser(u, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
u.Username = username + "3"
|
||||
u.Password = password + "3"
|
||||
user3, _, err := httpd.AddUser(u, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
u.Username = username + "4"
|
||||
u.Password = password + "4"
|
||||
user4, _, err := httpd.AddUser(u, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
c := &Configuration{
|
||||
BindPort: 9000,
|
||||
Cache: Cache{
|
||||
Enabled: true,
|
||||
MaxSize: 3,
|
||||
ExpirationTime: 1,
|
||||
},
|
||||
}
|
||||
server, err := newServer(c, configDir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user1.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user1.Username, password+"1")
|
||||
_, isCached, err := server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user2.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user2.Username, password+"2")
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user3.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user3.Username, password+"3")
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
|
||||
// the first 3 users are now cached
|
||||
_, ok := dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user2.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user3.Username)
|
||||
assert.True(t, ok)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user4.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user4.Username, password+"4")
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
// user1, the first cached, should be removed now
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user2.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user3.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user4.Username)
|
||||
assert.True(t, ok)
|
||||
|
||||
// user1 logins, user2 should be removed
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user1.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user1.Username, password+"1")
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user2.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user3.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user4.Username)
|
||||
assert.True(t, ok)
|
||||
|
||||
// user2 logins, user3 should be removed
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user2.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user2.Username, password+"2")
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user3.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user2.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user4.Username)
|
||||
assert.True(t, ok)
|
||||
|
||||
// user3 logins, user4 should be removed
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user3.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user3.Username, password+"3")
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user4.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user2.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user3.Username)
|
||||
assert.True(t, ok)
|
||||
|
||||
// now remove user1 after an update
|
||||
user1, _, err = httpd.UpdateUser(user1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
assert.False(t, ok)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user4.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user4.Username, password+"4")
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user1.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user1.Username, password+"1")
|
||||
_, isCached, err = server.authenticate(req)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user2.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user3.Username)
|
||||
assert.True(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user4.Username)
|
||||
assert.True(t, ok)
|
||||
|
||||
_, err = httpd.RemoveUser(user1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpd.RemoveUser(user2, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpd.RemoveUser(user3, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpd.RemoveUser(user4, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user