external auth: allow to map multiple login username to a single account

some external auth users want to map multiple login usernames with a single
SGTPGo account.
For example an SFTP user logins using "user1" or "user2" and the external auth
returns "user" in both cases, so we use the username returned from external auth
and not the one used to login

Fixes #125
This commit is contained in:
Nicola Murino 2020-06-08 13:06:02 +02:00
parent c231b663a3
commit 01d681faa3
2 changed files with 103 additions and 19 deletions

View file

@ -1634,7 +1634,11 @@ func doExternalAuth(username, password string, pubKey []byte, keyboardInteractiv
if len(pkey) > 0 && !utils.IsStringPrefixInSlice(pkey, user.PublicKeys) {
user.PublicKeys = append(user.PublicKeys, pkey)
}
u, err := provider.userExists(username)
// some users want to map multiple login usernames with a single SGTPGo account
// for example an SFTP user logins using "user1" or "user2" and the external auth
// returns "user" in both cases, so we use the username returned from
// external auth and not the one used to login
u, err := provider.userExists(user.Username)
if err == nil {
user.ID = u.ID
user.UsedQuotaSize = u.UsedQuotaSize
@ -1648,7 +1652,7 @@ func doExternalAuth(username, password string, pubKey []byte, keyboardInteractiv
if err != nil {
return user, err
}
return provider.userExists(username)
return provider.userExists(user.Username)
}
func providerLog(level logger.LogLevel, format string, v ...interface{}) {

View file

@ -21,7 +21,6 @@ import (
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
"time"
@ -1348,7 +1347,7 @@ func TestLoginExternalAuthPwdAndPubKey(t *testing.T) {
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf := config.GetProviderConf()
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false), 0755)
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), 0755)
assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 0
@ -1378,7 +1377,7 @@ func TestLoginExternalAuthPwdAndPubKey(t *testing.T) {
usePubKey = false
u = getTestUser(usePubKey)
u.PublicKeys = []string{}
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false), 0755)
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), 0755)
assert.NoError(t, err)
client, err = getSftpClient(u, usePubKey)
if assert.NoError(t, err) {
@ -1387,16 +1386,95 @@ func TestLoginExternalAuthPwdAndPubKey(t *testing.T) {
}
users, _, err := httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, 1, len(users))
user := users[0]
assert.Equal(t, 0, len(user.PublicKeys))
assert.Equal(t, testFileSize, user.UsedQuotaSize)
assert.Equal(t, 1, user.UsedQuotaFiles)
if assert.Equal(t, 1, len(users)) {
user := users[0]
assert.Equal(t, 0, len(user.PublicKeys))
assert.Equal(t, testFileSize, user.UsedQuotaSize)
assert.Equal(t, 1, user.UsedQuotaFiles)
_, err = httpd.RemoveUser(user, http.StatusOK)
_, err = httpd.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
}
dataProvider = dataprovider.GetProvider()
err = dataprovider.Close(dataProvider)
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf = config.GetProviderConf()
err = dataprovider.Initialize(providerConf, configDir)
assert.NoError(t, err)
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
err = os.Remove(extAuthPath)
assert.NoError(t, err)
}
func TestExternalAuthDifferentUsername(t *testing.T) {
if runtime.GOOS == osWindows {
t.Skip("this test is not available on Windows")
}
usePubKey := false
extAuthUsername := "common_user"
u := getTestUser(usePubKey)
u.QuotaFiles = 1000
dataProvider := dataprovider.GetProvider()
err := dataprovider.Close(dataProvider)
assert.NoError(t, err)
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf := config.GetProviderConf()
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, extAuthUsername), 0755)
assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 0
err = dataprovider.Initialize(providerConf, configDir)
assert.NoError(t, err)
httpd.SetDataProvider(dataprovider.GetProvider())
sftpd.SetDataProvider(dataprovider.GetProvider())
// the user logins using "defaultUsername" and the external auth returns "extAuthUsername"
testFileSize := int64(65535)
client, err := getSftpClient(u, usePubKey)
if assert.NoError(t, err) {
defer client.Close()
testFileName := "test_file.dat"
testFilePath := filepath.Join(homeBasePath, testFileName)
err = createTestFile(testFilePath, testFileSize)
assert.NoError(t, err)
err = sftpUploadFile(testFilePath, testFileName, testFileSize, client)
assert.NoError(t, err)
err = os.Remove(testFilePath)
assert.NoError(t, err)
}
// logins again to test that used quota is preserved
client, err = getSftpClient(u, usePubKey)
if assert.NoError(t, err) {
defer client.Close()
err = checkBasicSFTP(client)
assert.NoError(t, err)
}
users, _, err := httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, 0, len(users))
users, _, err = httpd.GetUsers(0, 0, extAuthUsername, http.StatusOK)
assert.NoError(t, err)
if assert.Equal(t, 1, len(users)) {
user := users[0]
assert.Equal(t, 0, len(user.PublicKeys))
assert.Equal(t, testFileSize, user.UsedQuotaSize)
assert.Equal(t, 1, user.UsedQuotaFiles)
_, err = httpd.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
}
dataProvider = dataprovider.GetProvider()
err = dataprovider.Close(dataProvider)
@ -1440,7 +1518,7 @@ func TestLoginExternalAuth(t *testing.T) {
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf := config.GetProviderConf()
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false), 0755)
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), 0755)
assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = authScope
@ -1510,7 +1588,7 @@ func TestLoginExternalAuthInteractive(t *testing.T) {
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf := config.GetProviderConf()
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false), 0755)
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), 0755)
assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 4
@ -1572,7 +1650,7 @@ func TestLoginExternalAuthErrors(t *testing.T) {
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf := config.GetProviderConf()
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, true), 0755)
err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, true, ""), 0755)
assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 0
@ -6320,10 +6398,13 @@ func getKeyboardInteractiveScriptContent(questions []string, sleepTime int, nonJ
return content
}
func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool) []byte {
func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool, username string) []byte {
extAuthContent := []byte("#!/bin/sh\n\n")
u, _ := json.Marshal(user)
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...)
if len(username) > 0 {
user.Username = username
}
u, _ := json.Marshal(user)
if nonJSONResponse {
extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
} else {
@ -6333,8 +6414,7 @@ func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool) []byt
if nonJSONResponse {
extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
} else {
json, _ := json.Marshal(user)
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("echo '%v'\n", strconv.Quote(string(json))))...)
extAuthContent = append(extAuthContent, []byte("echo '{\"username\":\"\"}'\n")...)
}
extAuthContent = append(extAuthContent, []byte("fi\n")...)
return extAuthContent