mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-06 06:10:54 +03:00
pre-login hook: require either a full user object or no user modification
The previous behavior was a leftover from an old refactor. This change aligns the pre-login hook with the behavior of other hooks, although it may break some edge cases that relied on the previous inconsistent behavior. Fixes #2107 Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
@@ -4205,6 +4205,8 @@ func getPreLoginHookResponse(loginMethod, ip, protocol string, userAsJSON []byte
|
||||
}
|
||||
|
||||
func executePreLoginHook(username, loginMethod, ip, protocol string, oidcTokenFields *map[string]any) (User, error) {
|
||||
var user User
|
||||
|
||||
u, mergedUser, userAsJSON, err := getUserAndJSONForHook(username, oidcTokenFields)
|
||||
if err != nil {
|
||||
return u, err
|
||||
@@ -4227,53 +4229,38 @@ func executePreLoginHook(username, loginMethod, ip, protocol string, oidcTokenFi
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
userID := u.ID
|
||||
userUsedQuotaSize := u.UsedQuotaSize
|
||||
userUsedQuotaFiles := u.UsedQuotaFiles
|
||||
userUsedDownloadTransfer := u.UsedDownloadDataTransfer
|
||||
userUsedUploadTransfer := u.UsedUploadDataTransfer
|
||||
userLastQuotaUpdate := u.LastQuotaUpdate
|
||||
userLastLogin := u.LastLogin
|
||||
userFirstDownload := u.FirstDownload
|
||||
userFirstUpload := u.FirstUpload
|
||||
userLastPwdChange := u.LastPasswordChange
|
||||
userCreatedAt := u.CreatedAt
|
||||
totpConfig := u.Filters.TOTPConfig
|
||||
recoveryCodes := u.Filters.RecoveryCodes
|
||||
err = json.Unmarshal(out, &u)
|
||||
err = json.Unmarshal(out, &user)
|
||||
if err != nil {
|
||||
return u, fmt.Errorf("invalid pre-login hook response %q, error: %v", out, err)
|
||||
}
|
||||
u.ID = userID
|
||||
u.UsedQuotaSize = userUsedQuotaSize
|
||||
u.UsedQuotaFiles = userUsedQuotaFiles
|
||||
u.UsedUploadDataTransfer = userUsedUploadTransfer
|
||||
u.UsedDownloadDataTransfer = userUsedDownloadTransfer
|
||||
u.LastQuotaUpdate = userLastQuotaUpdate
|
||||
u.LastLogin = userLastLogin
|
||||
u.LastPasswordChange = userLastPwdChange
|
||||
u.FirstDownload = userFirstDownload
|
||||
u.FirstUpload = userFirstUpload
|
||||
u.CreatedAt = userCreatedAt
|
||||
if userID == 0 {
|
||||
err = provider.addUser(&u)
|
||||
} else {
|
||||
u.UpdatedAt = util.GetTimeAsMsSinceEpoch(time.Now())
|
||||
if u.ID > 0 {
|
||||
user.ID = u.ID
|
||||
user.UsedQuotaSize = u.UsedQuotaSize
|
||||
user.UsedQuotaFiles = u.UsedQuotaFiles
|
||||
user.UsedUploadDataTransfer = u.UsedUploadDataTransfer
|
||||
user.UsedDownloadDataTransfer = u.UsedDownloadDataTransfer
|
||||
user.LastQuotaUpdate = u.LastQuotaUpdate
|
||||
user.LastLogin = u.LastLogin
|
||||
user.LastPasswordChange = u.LastPasswordChange
|
||||
user.FirstDownload = u.FirstDownload
|
||||
user.FirstUpload = u.FirstUpload
|
||||
// preserve TOTP config and recovery codes
|
||||
u.Filters.TOTPConfig = totpConfig
|
||||
u.Filters.RecoveryCodes = recoveryCodes
|
||||
err = provider.updateUser(&u)
|
||||
user.Filters.TOTPConfig = u.Filters.TOTPConfig
|
||||
user.Filters.RecoveryCodes = u.Filters.RecoveryCodes
|
||||
if err := provider.updateUser(&user); err != nil {
|
||||
return u, err
|
||||
}
|
||||
} else {
|
||||
if err := provider.addUser(&user); err != nil {
|
||||
return u, err
|
||||
}
|
||||
}
|
||||
user, err = provider.userExists(user.Username, "")
|
||||
if err != nil {
|
||||
return u, err
|
||||
}
|
||||
user, err := provider.userExists(username, "")
|
||||
if err != nil {
|
||||
return u, err
|
||||
}
|
||||
providerLog(logger.LevelDebug, "user %q added/updated from pre-login hook response, id: %d", username, userID)
|
||||
if userID > 0 {
|
||||
providerLog(logger.LevelDebug, "user %q added/updated from pre-login hook response, id: %d", username, u.ID)
|
||||
if u.ID > 0 {
|
||||
webDAVUsersCache.swap(&user, "")
|
||||
}
|
||||
return user, nil
|
||||
|
||||
@@ -3000,6 +3000,15 @@ func TestPreLoginScript(t *testing.T) {
|
||||
}
|
||||
usePubKey := true
|
||||
u := getTestUser(usePubKey)
|
||||
mappedPath := filepath.Join(os.TempDir(), "vdir")
|
||||
folderName := filepath.Base(mappedPath)
|
||||
folderMountPath := "/vpath"
|
||||
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
|
||||
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
||||
Name: folderName,
|
||||
},
|
||||
VirtualPath: folderMountPath,
|
||||
})
|
||||
err := dataprovider.Close()
|
||||
assert.NoError(t, err)
|
||||
err = config.LoadConfig(configDir, "")
|
||||
@@ -3011,8 +3020,6 @@ func TestPreLoginScript(t *testing.T) {
|
||||
err = dataprovider.Initialize(providerConf, configDir, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
mappedPath := filepath.Join(os.TempDir(), "vdir")
|
||||
folderName := filepath.Base(mappedPath)
|
||||
f := vfs.BaseVirtualFolder{
|
||||
Name: folderName,
|
||||
MappedPath: mappedPath,
|
||||
@@ -3026,13 +3033,6 @@ func TestPreLoginScript(t *testing.T) {
|
||||
_, _, err = httpdtest.AddFolder(f, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
folderMountPath := "/vpath"
|
||||
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
|
||||
BaseVirtualFolder: vfs.BaseVirtualFolder{
|
||||
Name: folderName,
|
||||
},
|
||||
VirtualPath: folderMountPath,
|
||||
})
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
conn, client, err := getSftpClient(u, usePubKey)
|
||||
@@ -3108,6 +3108,7 @@ func TestPreLoginUserCreation(t *testing.T) {
|
||||
}
|
||||
usePubKey := false
|
||||
u := getTestUser(usePubKey)
|
||||
u.Permissions["/list"] = []string{"list", "download"}
|
||||
err := dataprovider.Close()
|
||||
assert.NoError(t, err)
|
||||
err = config.LoadConfig(configDir, "")
|
||||
@@ -3129,6 +3130,23 @@ func TestPreLoginUserCreation(t *testing.T) {
|
||||
}
|
||||
user, _, err := httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, user.Permissions, 2)
|
||||
assert.Empty(t, user.Description)
|
||||
u.Description = "some desc"
|
||||
delete(u.Permissions, "/list")
|
||||
err = os.WriteFile(preLoginPath, getPreLoginScriptContent(u, false), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
// The user should be updated and list permission removed
|
||||
conn, client, err = getSftpClient(u, usePubKey)
|
||||
if assert.NoError(t, err) {
|
||||
defer conn.Close()
|
||||
defer client.Close()
|
||||
assert.NoError(t, checkBasicSFTP(client))
|
||||
}
|
||||
user, _, err = httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, user.Permissions, 1)
|
||||
assert.NotEmpty(t, user.Description)
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
|
||||
Reference in New Issue
Block a user