123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- // Copyright (C) 2019-2023 Nicola Murino
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License as published
- // by the Free Software Foundation, version 3.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- package dataprovider
- import (
- "sync"
- "time"
- "github.com/drakkan/webdav"
- "github.com/drakkan/sftpgo/v2/internal/logger"
- "github.com/drakkan/sftpgo/v2/internal/util"
- )
- var (
- webDAVUsersCache *usersCache
- )
- func init() {
- webDAVUsersCache = &usersCache{
- users: map[string]CachedUser{},
- }
- }
- // InitializeWebDAVUserCache initializes the cache for webdav users
- func InitializeWebDAVUserCache(maxSize int) {
- webDAVUsersCache = &usersCache{
- users: map[string]CachedUser{},
- maxSize: maxSize,
- }
- }
- // CachedUser adds fields useful for caching to a SFTPGo user
- type CachedUser struct {
- User User
- Expiration time.Time
- Password string
- LockSystem webdav.LockSystem
- }
- // IsExpired returns true if the cached user is expired
- func (c *CachedUser) IsExpired() bool {
- if c.Expiration.IsZero() {
- return false
- }
- return c.Expiration.Before(time.Now())
- }
- type usersCache struct {
- sync.RWMutex
- users map[string]CachedUser
- maxSize int
- }
- func (cache *usersCache) updateLastLogin(username string) {
- cache.Lock()
- defer cache.Unlock()
- if cachedUser, ok := cache.users[username]; ok {
- cachedUser.User.LastLogin = util.GetTimeAsMsSinceEpoch(time.Now())
- cache.users[username] = cachedUser
- }
- }
- // swapWebDAVUser updates an existing cached user with the specified one
- // preserving the lock fs if possible
- // FIXME: this could be racy in rare cases
- func (cache *usersCache) swap(userRef *User) {
- user := userRef.getACopy()
- err := user.LoadAndApplyGroupSettings()
- cache.Lock()
- defer cache.Unlock()
- if cachedUser, ok := cache.users[user.Username]; ok {
- if cachedUser.User.Password != user.Password {
- providerLog(logger.LevelDebug, "current password different from the cached one for user %#v, removing from cache",
- user.Username)
- // the password changed, the cached user is no longer valid
- delete(cache.users, user.Username)
- return
- }
- if err != nil {
- providerLog(logger.LevelDebug, "unable to load group settings, for user %#v, removing from cache, err :%v",
- user.Username, err)
- delete(cache.users, user.Username)
- return
- }
- if cachedUser.User.isFsEqual(&user) {
- // the updated user has the same fs as the cached one, we can preserve the lock filesystem
- providerLog(logger.LevelDebug, "current password and fs unchanged for for user %#v, swap cached one",
- user.Username)
- cachedUser.User = user
- cache.users[user.Username] = cachedUser
- } else {
- // filesystem changed, the cached user is no longer valid
- providerLog(logger.LevelDebug, "current fs different from the cached one for user %#v, removing from cache",
- user.Username)
- delete(cache.users, user.Username)
- }
- }
- }
- func (cache *usersCache) add(cachedUser *CachedUser) {
- cache.Lock()
- defer cache.Unlock()
- if cache.maxSize > 0 && len(cache.users) >= cache.maxSize {
- var userToRemove string
- var expirationTime time.Time
- for k, v := range cache.users {
- if userToRemove == "" {
- userToRemove = k
- expirationTime = v.Expiration
- continue
- }
- expireTime := v.Expiration
- if !expireTime.IsZero() && expireTime.Before(expirationTime) {
- userToRemove = k
- expirationTime = expireTime
- }
- }
- delete(cache.users, userToRemove)
- }
- if cachedUser.User.Username != "" {
- cache.users[cachedUser.User.Username] = *cachedUser
- }
- }
- func (cache *usersCache) remove(username string) {
- cache.Lock()
- defer cache.Unlock()
- delete(cache.users, username)
- }
- func (cache *usersCache) get(username string) (*CachedUser, bool) {
- cache.RLock()
- defer cache.RUnlock()
- cachedUser, ok := cache.users[username]
- return &cachedUser, ok
- }
- // CacheWebDAVUser add a user to the WebDAV cache
- func CacheWebDAVUser(cachedUser *CachedUser) {
- webDAVUsersCache.add(cachedUser)
- }
- // GetCachedWebDAVUser returns a previously cached WebDAV user
- func GetCachedWebDAVUser(username string) (*CachedUser, bool) {
- return webDAVUsersCache.get(username)
- }
- // RemoveCachedWebDAVUser removes a cached WebDAV user
- func RemoveCachedWebDAVUser(username string) {
- webDAVUsersCache.remove(username)
- }
|