mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 23:00:55 +03:00
webdav: fix caching with external auth/plugins
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
@@ -77,7 +77,7 @@ func (cache *usersCache) updateLastLogin(username string) {
|
||||
// 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) {
|
||||
func (cache *usersCache) swap(userRef *User, plainPassword string) {
|
||||
user := userRef.getACopy()
|
||||
err := user.LoadAndApplyGroupSettings()
|
||||
|
||||
@@ -85,19 +85,23 @@ func (cache *usersCache) swap(userRef *User) {
|
||||
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 %q, 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 %q, removing from cache, err :%v",
|
||||
user.Username, err)
|
||||
delete(cache.users, user.Username)
|
||||
return
|
||||
}
|
||||
if plainPassword != "" {
|
||||
cachedUser.Password = plainPassword
|
||||
} else {
|
||||
if cachedUser.User.Password != user.Password {
|
||||
providerLog(logger.LevelDebug, "current password different from the cached one for user %q, removing from cache",
|
||||
user.Username)
|
||||
// the password changed, the cached user is no longer valid
|
||||
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 %q, swap cached one",
|
||||
@@ -154,7 +158,10 @@ func (cache *usersCache) get(username string) (*CachedUser, bool) {
|
||||
defer cache.RUnlock()
|
||||
|
||||
cachedUser, ok := cache.users[username]
|
||||
return &cachedUser, ok
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return &cachedUser, true
|
||||
}
|
||||
|
||||
// CacheWebDAVUser add a user to the WebDAV cache
|
||||
|
||||
@@ -1103,38 +1103,47 @@ func CheckAdminAndPass(username, password, ip string) (Admin, error) {
|
||||
}
|
||||
|
||||
// CheckCachedUserCredentials checks the credentials for a cached user
|
||||
func CheckCachedUserCredentials(user *CachedUser, password, loginMethod, protocol string, tlsCert *x509.Certificate) error {
|
||||
func CheckCachedUserCredentials(user *CachedUser, password, ip, loginMethod, protocol string, tlsCert *x509.Certificate) (*CachedUser, *User, error) {
|
||||
if !user.User.skipExternalAuth() && isExternalAuthConfigured(loginMethod) {
|
||||
u, _, err := CheckCompositeCredentials(user.User.Username, password, ip, loginMethod, protocol, tlsCert)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
webDAVUsersCache.swap(&u, password)
|
||||
cu, _ := webDAVUsersCache.get(u.Username)
|
||||
return cu, &u, nil
|
||||
}
|
||||
if err := user.User.CheckLoginConditions(); err != nil {
|
||||
return err
|
||||
return user, nil, err
|
||||
}
|
||||
if loginMethod == LoginMethodPassword && user.User.Filters.IsAnonymous {
|
||||
return nil
|
||||
return user, nil, nil
|
||||
}
|
||||
if loginMethod != LoginMethodPassword {
|
||||
_, err := checkUserAndTLSCertificate(&user.User, protocol, tlsCert)
|
||||
if err != nil {
|
||||
return err
|
||||
return user, nil, err
|
||||
}
|
||||
if loginMethod == LoginMethodTLSCertificate {
|
||||
if !user.User.IsLoginMethodAllowed(LoginMethodTLSCertificate, protocol, nil) {
|
||||
return fmt.Errorf("certificate login method is not allowed for user %q", user.User.Username)
|
||||
return user, nil, fmt.Errorf("certificate login method is not allowed for user %q", user.User.Username)
|
||||
}
|
||||
return nil
|
||||
return user, nil, nil
|
||||
}
|
||||
}
|
||||
if password == "" {
|
||||
return ErrInvalidCredentials
|
||||
return user, nil, ErrInvalidCredentials
|
||||
}
|
||||
if user.Password != "" {
|
||||
if password == user.Password {
|
||||
return nil
|
||||
return user, nil, nil
|
||||
}
|
||||
} else {
|
||||
if ok, _ := isPasswordOK(&user.User, password); ok {
|
||||
return nil
|
||||
return user, nil, nil
|
||||
}
|
||||
}
|
||||
return ErrInvalidCredentials
|
||||
return user, nil, ErrInvalidCredentials
|
||||
}
|
||||
|
||||
// CheckCompositeCredentials checks multiple credentials.
|
||||
@@ -1689,7 +1698,7 @@ func DeleteRole(name string, executor, ipAddress, executorRole string) error {
|
||||
provider.setUpdatedAt(user)
|
||||
u, err := provider.userExists(user, "")
|
||||
if err == nil {
|
||||
webDAVUsersCache.swap(&u)
|
||||
webDAVUsersCache.swap(&u, "")
|
||||
executeAction(operationUpdate, executor, ipAddress, actionObjectUser, u.Username, u.Role, &u)
|
||||
}
|
||||
}
|
||||
@@ -1721,7 +1730,7 @@ func UpdateGroup(group *Group, users []string, executor, ipAddress, role string)
|
||||
provider.setUpdatedAt(user)
|
||||
u, err := provider.userExists(user, "")
|
||||
if err == nil {
|
||||
webDAVUsersCache.swap(&u)
|
||||
webDAVUsersCache.swap(&u, "")
|
||||
} else {
|
||||
RemoveCachedWebDAVUser(user)
|
||||
}
|
||||
@@ -2073,7 +2082,7 @@ func UpdateUserPassword(username, plainPwd, executor, ipAddress, role string) er
|
||||
if err := provider.updateUser(&user); err != nil {
|
||||
return err
|
||||
}
|
||||
webDAVUsersCache.swap(&user)
|
||||
webDAVUsersCache.swap(&user, plainPwd)
|
||||
executeAction(operationUpdate, executor, ipAddress, actionObjectUser, username, role, &user)
|
||||
return nil
|
||||
}
|
||||
@@ -2085,7 +2094,7 @@ func UpdateUser(user *User, executor, ipAddress, role string) error {
|
||||
}
|
||||
err := provider.updateUser(user)
|
||||
if err == nil {
|
||||
webDAVUsersCache.swap(user)
|
||||
webDAVUsersCache.swap(user, "")
|
||||
executeAction(operationUpdate, executor, ipAddress, actionObjectUser, user.Username, role, user)
|
||||
}
|
||||
return err
|
||||
@@ -2252,7 +2261,7 @@ func UpdateFolder(folder *vfs.BaseVirtualFolder, users []string, groups []string
|
||||
provider.setUpdatedAt(user)
|
||||
u, err := provider.userExists(user, "")
|
||||
if err == nil {
|
||||
webDAVUsersCache.swap(&u)
|
||||
webDAVUsersCache.swap(&u, "")
|
||||
executeAction(operationUpdate, executor, ipAddress, actionObjectUser, u.Username, u.Role, &u)
|
||||
} else {
|
||||
RemoveCachedWebDAVUser(user)
|
||||
@@ -3950,7 +3959,7 @@ func executePreLoginHook(username, loginMethod, ip, protocol string, oidcTokenFi
|
||||
u.Filters.RecoveryCodes = recoveryCodes
|
||||
err = provider.updateUser(&u)
|
||||
if err == nil {
|
||||
webDAVUsersCache.swap(&u)
|
||||
webDAVUsersCache.swap(&u, "")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -4123,11 +4132,7 @@ func doExternalAuth(username, password string, pubKey []byte, keyboardInteractiv
|
||||
return user, err
|
||||
}
|
||||
|
||||
if mergedUser.Filters.Hooks.ExternalAuthDisabled {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
if mergedUser.isExternalAuthCached() {
|
||||
if mergedUser.skipExternalAuth() {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
@@ -4184,7 +4189,9 @@ func doExternalAuth(username, password string, pubKey []byte, keyboardInteractiv
|
||||
user.Filters.RecoveryCodes = u.Filters.RecoveryCodes
|
||||
err = provider.updateUser(&user)
|
||||
if err == nil {
|
||||
webDAVUsersCache.swap(&user)
|
||||
if protocol != protocolWebDAV {
|
||||
webDAVUsersCache.swap(&user, password)
|
||||
}
|
||||
cachedUserPasswords.Add(user.Username, password, user.Password)
|
||||
}
|
||||
return user, err
|
||||
@@ -4206,11 +4213,7 @@ func doPluginAuth(username, password string, pubKey []byte, ip, protocol string,
|
||||
return user, err
|
||||
}
|
||||
|
||||
if mergedUser.Filters.Hooks.ExternalAuthDisabled {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
if mergedUser.isExternalAuthCached() {
|
||||
if mergedUser.skipExternalAuth() {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
@@ -4257,7 +4260,9 @@ func doPluginAuth(username, password string, pubKey []byte, ip, protocol string,
|
||||
user.Filters.RecoveryCodes = u.Filters.RecoveryCodes
|
||||
err = provider.updateUser(&user)
|
||||
if err == nil {
|
||||
webDAVUsersCache.swap(&user)
|
||||
if protocol != protocolWebDAV {
|
||||
webDAVUsersCache.swap(&user, password)
|
||||
}
|
||||
cachedUserPasswords.Add(user.Username, password, user.Password)
|
||||
}
|
||||
return user, err
|
||||
@@ -4313,6 +4318,33 @@ func isLastActivityRecent(lastActivity int64, minDelay time.Duration) bool {
|
||||
return diff < minDelay
|
||||
}
|
||||
|
||||
func isExternalAuthConfigured(loginMethod string) bool {
|
||||
if config.ExternalAuthHook != "" {
|
||||
if config.ExternalAuthScope == 0 {
|
||||
return true
|
||||
}
|
||||
switch loginMethod {
|
||||
case LoginMethodPassword:
|
||||
return config.ExternalAuthScope&1 != 0
|
||||
case LoginMethodTLSCertificate:
|
||||
return config.ExternalAuthScope&8 != 0
|
||||
case LoginMethodTLSCertificateAndPwd:
|
||||
return config.ExternalAuthScope&1 != 0 || config.ExternalAuthScope&8 != 0
|
||||
}
|
||||
}
|
||||
switch loginMethod {
|
||||
case LoginMethodPassword:
|
||||
return plugin.Handler.HasAuthScope(plugin.AuthScopePassword)
|
||||
case LoginMethodTLSCertificate:
|
||||
return plugin.Handler.HasAuthScope(plugin.AuthScopeTLSCertificate)
|
||||
case LoginMethodTLSCertificateAndPwd:
|
||||
return plugin.Handler.HasAuthScope(plugin.AuthScopePassword) ||
|
||||
plugin.Handler.HasAuthScope(plugin.AuthScopeTLSCertificate)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func getConfigPath(name, configDir string) string {
|
||||
if !util.IsFileInputValid(name) {
|
||||
return ""
|
||||
|
||||
@@ -130,7 +130,7 @@ func checkUserCache() {
|
||||
cachedUserPasswords.Remove(user.Username)
|
||||
delayedQuotaUpdater.resetUserQuota(user.Username)
|
||||
} else {
|
||||
webDAVUsersCache.swap(&user)
|
||||
webDAVUsersCache.swap(&user, "")
|
||||
}
|
||||
}
|
||||
lastUserCacheUpdate.Store(checkTime)
|
||||
|
||||
@@ -1039,14 +1039,16 @@ func (u *User) CanManageMFA() bool {
|
||||
return len(mfa.GetAvailableTOTPConfigs()) > 0
|
||||
}
|
||||
|
||||
func (u *User) isExternalAuthCached() bool {
|
||||
func (u *User) skipExternalAuth() bool {
|
||||
if u.Filters.Hooks.ExternalAuthDisabled {
|
||||
return true
|
||||
}
|
||||
if u.ID <= 0 {
|
||||
return false
|
||||
}
|
||||
if u.Filters.ExternalAuthCacheTime <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return isLastActivityRecent(u.LastLogin, time.Duration(u.Filters.ExternalAuthCacheTime)*time.Second)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user