Rename before_login_program to pre_login_program
and some documentation update
This commit is contained in:
parent
e69536f540
commit
637463a068
5 changed files with 36 additions and 36 deletions
14
README.md
14
README.md
|
@ -12,7 +12,7 @@ Full featured and highly configurable SFTP server
|
|||
- Keyboard interactive authentication. You can easily setup a customizable multi-factor authentication.
|
||||
- Per user authentication methods. You can, for example, deny one or more authentication methods to one or more users.
|
||||
- Custom authentication via external programs is supported.
|
||||
- Dynamic users modifications before login via external programs are supported.
|
||||
- Dynamic user modification before login via external programs is supported.
|
||||
- Quota support: accounts can have individual quota expressed as max total size and/or max number of files.
|
||||
- Bandwidth throttling is supported, with distinct settings for upload and download.
|
||||
- Per user maximum concurrent sessions.
|
||||
|
@ -183,7 +183,7 @@ The `sftpgo` configuration file contains the following sections:
|
|||
- `external_auth_program`, string. Absolute path to an external program to use for users authentication. See the "External Authentication" paragraph for more details. Leave empty to disable.
|
||||
- `external_auth_scope`, integer. 0 means all supported authetication scopes (passwords, public keys and keyboard interactive). 1 means passwords only. 2 means public keys only. 4 means key keyboard interactive only. The flags can be combined, for example 6 means public keys and keyboard interactive
|
||||
- `credentials_path`, string. It defines the directory for storing user provided credential files such as Google Cloud Storage credentials. This can be an absolute path or a path relative to the config dir
|
||||
- `before_login_program`, string. Absolute path to an external program to use to modify user details just before the login. See the "Dynamic users modifications" paragraph for more details. Leave empty to disable.
|
||||
- `pre_login_program`, string. Absolute path to an external program to use to modify user details just before the login. See the "Dynamic user modification" paragraph for more details. Leave empty to disable.
|
||||
- **"httpd"**, the configuration for the HTTP server used to serve REST API
|
||||
- `bind_port`, integer. The port used for serving HTTP requests. Set to 0 to disable HTTP server. Default: 8080
|
||||
- `bind_address`, string. Leave blank to listen on all available network interfaces. Default: "127.0.0.1"
|
||||
|
@ -243,7 +243,7 @@ Here is a full example showing the default config in JSON format:
|
|||
"external_auth_program": "",
|
||||
"external_auth_scope": 0,
|
||||
"credentials_path": "credentials",
|
||||
"before_login_program": ""
|
||||
"pre_login_program": ""
|
||||
},
|
||||
"httpd": {
|
||||
"bind_port": 8080,
|
||||
|
@ -400,10 +400,10 @@ fi
|
|||
|
||||
If you have an external authentication program that could be useful for others too, please let us know and/or send a pull request.
|
||||
|
||||
## Dynamic users modifications
|
||||
## Dynamic user modification
|
||||
|
||||
Dynamic users modifications are supported via an external program that can be executed just before the user login.
|
||||
To enable dynamic users modifications you must set the absolute path of your program using the `before_login_program` key in your configuration file.
|
||||
Dynamic user modification is supported via an external program that can be executed just before the user login.
|
||||
To enable dynamic user modification you must set the absolute path of your program using the `pre_login_program` key in your configuration file.
|
||||
|
||||
The external program can read the following environment variables to get info about the user trying to login:
|
||||
|
||||
|
@ -418,7 +418,7 @@ The JSON response can include only the fields that need to the updated instead o
|
|||
```
|
||||
The external program must finish within 60 seconds.
|
||||
|
||||
If an error happens while executing your program then login will be denied. "Dynamic users modifications" and "External Authentication" are mutally exclusive.
|
||||
If an error happens while executing your program then login will be denied. "Dynamic user modification" and "External Authentication" are mutally exclusive.
|
||||
|
||||
Let's see a very basic example. Our sample program will grant access to the user `test_user` only in the time range 10:00-18:00. Other users will not be modified since the program will terminate with no output.
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ func init() {
|
|||
ExternalAuthProgram: "",
|
||||
ExternalAuthScope: 0,
|
||||
CredentialsPath: "credentials",
|
||||
BeforeLoginProgram: "",
|
||||
PreLoginProgram: "",
|
||||
},
|
||||
HTTPDConfig: httpd.Conf{
|
||||
BindPort: 8080,
|
||||
|
|
|
@ -214,10 +214,10 @@ type Config struct {
|
|||
//
|
||||
// The external program must finish within 60 seconds.
|
||||
//
|
||||
// If an error happen while executing the "BeforeLoginProgram" then login will be denied.
|
||||
// BeforeLoginProgram and ExternalAuthProgram are mutally exclusive.
|
||||
// If an error happens while executing the "PreLoginProgram" then login will be denied.
|
||||
// PreLoginProgram and ExternalAuthProgram are mutally exclusive.
|
||||
// Leave empty to disable.
|
||||
BeforeLoginProgram string `json:"before_login_program" mapstructure:"before_login_program"`
|
||||
PreLoginProgram string `json:"pre_login_program" mapstructure:"pre_login_program"`
|
||||
}
|
||||
|
||||
// BackupData defines the structure for the backup/restore files
|
||||
|
@ -316,11 +316,11 @@ func Initialize(cnf Config, basePath string) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
if len(config.BeforeLoginProgram) > 0 {
|
||||
if !filepath.IsAbs(config.BeforeLoginProgram) {
|
||||
return fmt.Errorf("invalid pre login program: %#v must be an absolute path", config.BeforeLoginProgram)
|
||||
if len(config.PreLoginProgram) > 0 {
|
||||
if !filepath.IsAbs(config.PreLoginProgram) {
|
||||
return fmt.Errorf("invalid pre login program: %#v must be an absolute path", config.PreLoginProgram)
|
||||
}
|
||||
_, err := os.Stat(config.BeforeLoginProgram)
|
||||
_, err := os.Stat(config.PreLoginProgram)
|
||||
if err != nil {
|
||||
providerLog(logger.LevelWarn, "invalid pre login program: %v", err)
|
||||
return err
|
||||
|
@ -366,8 +366,8 @@ func CheckUserAndPass(p Provider, username string, password string) (User, error
|
|||
}
|
||||
return checkUserAndPass(user, password)
|
||||
}
|
||||
if len(config.BeforeLoginProgram) > 0 {
|
||||
user, err := executeBeforeLoginProgram(username, SSHLoginMethodPassword)
|
||||
if len(config.PreLoginProgram) > 0 {
|
||||
user, err := executePreLoginProgram(username, SSHLoginMethodPassword)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
@ -385,8 +385,8 @@ func CheckUserAndPubKey(p Provider, username string, pubKey string) (User, strin
|
|||
}
|
||||
return checkUserAndPubKey(user, pubKey)
|
||||
}
|
||||
if len(config.BeforeLoginProgram) > 0 {
|
||||
user, err := executeBeforeLoginProgram(username, SSHLoginMethodPublicKey)
|
||||
if len(config.PreLoginProgram) > 0 {
|
||||
user, err := executePreLoginProgram(username, SSHLoginMethodPublicKey)
|
||||
if err != nil {
|
||||
return user, "", err
|
||||
}
|
||||
|
@ -402,8 +402,8 @@ func CheckKeyboardInteractiveAuth(p Provider, username, authProgram string, clie
|
|||
var err error
|
||||
if len(config.ExternalAuthProgram) > 0 && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&4 != 0) {
|
||||
user, err = doExternalAuth(username, "", "", "1")
|
||||
} else if len(config.BeforeLoginProgram) > 0 {
|
||||
user, err = executeBeforeLoginProgram(username, SSHLoginMethodKeyboardInteractive)
|
||||
} else if len(config.PreLoginProgram) > 0 {
|
||||
user, err = executePreLoginProgram(username, SSHLoginMethodKeyboardInteractive)
|
||||
} else {
|
||||
user, err = p.userExists(username)
|
||||
}
|
||||
|
@ -1143,7 +1143,7 @@ func doKeyboardInteractiveAuth(user User, authProgram string, client ssh.Keyboar
|
|||
return user, nil
|
||||
}
|
||||
|
||||
func executeBeforeLoginProgram(username, loginMethod string) (User, error) {
|
||||
func executePreLoginProgram(username, loginMethod string) (User, error) {
|
||||
u, err := provider.userExists(username)
|
||||
if err != nil {
|
||||
return u, err
|
||||
|
@ -1155,7 +1155,7 @@ func executeBeforeLoginProgram(username, loginMethod string) (User, error) {
|
|||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, config.BeforeLoginProgram)
|
||||
cmd := exec.CommandContext(ctx, config.PreLoginProgram)
|
||||
cmd.Env = append(os.Environ(),
|
||||
fmt.Sprintf("SFTPGO_LOGIND_USER=%v", string(userAsJSON)),
|
||||
fmt.Sprintf("SFTPGO_LOGIND_METHOD=%v", loginMethod))
|
||||
|
|
|
@ -99,7 +99,7 @@ var (
|
|||
gitWrapPath string
|
||||
extAuthPath string
|
||||
keyIntAuthPath string
|
||||
beforeLoginPath string
|
||||
preLoginPath string
|
||||
logFilePath string
|
||||
)
|
||||
|
||||
|
@ -172,7 +172,7 @@ func TestMain(m *testing.M) {
|
|||
privateKeyPath = filepath.Join(homeBasePath, "ssh_key")
|
||||
gitWrapPath = filepath.Join(homeBasePath, "gitwrap.sh")
|
||||
extAuthPath = filepath.Join(homeBasePath, "extauth.sh")
|
||||
beforeLoginPath = filepath.Join(homeBasePath, "beforelogin.sh")
|
||||
preLoginPath = filepath.Join(homeBasePath, "prelogin.sh")
|
||||
err = ioutil.WriteFile(pubKeyPath, []byte(testPubKey+"\n"), 0600)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("unable to save public key to file: %v", err)
|
||||
|
@ -212,7 +212,7 @@ func TestMain(m *testing.M) {
|
|||
os.Remove(privateKeyPath)
|
||||
os.Remove(gitWrapPath)
|
||||
os.Remove(extAuthPath)
|
||||
os.Remove(beforeLoginPath)
|
||||
os.Remove(preLoginPath)
|
||||
os.Remove(keyIntAuthPath)
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
@ -1305,7 +1305,7 @@ func TestLoginKeyboardInteractiveAuth(t *testing.T) {
|
|||
os.RemoveAll(user.GetHomeDir())
|
||||
}
|
||||
|
||||
func TestBeforeLoginScript(t *testing.T) {
|
||||
func TestPreLoginScript(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("this test is not available on Windows")
|
||||
}
|
||||
|
@ -1315,8 +1315,8 @@ func TestBeforeLoginScript(t *testing.T) {
|
|||
dataprovider.Close(dataProvider)
|
||||
config.LoadConfig(configDir, "")
|
||||
providerConf := config.GetProviderConf()
|
||||
ioutil.WriteFile(beforeLoginPath, getBeforeLoginScriptContent(u, false), 0755)
|
||||
providerConf.BeforeLoginProgram = beforeLoginPath
|
||||
ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(u, false), 0755)
|
||||
providerConf.PreLoginProgram = preLoginPath
|
||||
err := dataprovider.Initialize(providerConf, configDir)
|
||||
if err != nil {
|
||||
t.Errorf("error initializing data provider")
|
||||
|
@ -1338,16 +1338,16 @@ func TestBeforeLoginScript(t *testing.T) {
|
|||
t.Errorf("unable to get working dir: %v", err)
|
||||
}
|
||||
}
|
||||
ioutil.WriteFile(beforeLoginPath, getBeforeLoginScriptContent(user, true), 0755)
|
||||
ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(user, true), 0755)
|
||||
_, err = getSftpClient(u, usePubKey)
|
||||
if err == nil {
|
||||
t.Error("before login script returned a non json response, login must fail")
|
||||
t.Error("pre login script returned a non json response, login must fail")
|
||||
}
|
||||
user.Status = 0
|
||||
ioutil.WriteFile(beforeLoginPath, getBeforeLoginScriptContent(user, false), 0755)
|
||||
ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(user, false), 0755)
|
||||
_, err = getSftpClient(u, usePubKey)
|
||||
if err == nil {
|
||||
t.Error("before login script returned a disabled user, login must fail")
|
||||
t.Error("pre login script returned a disabled user, login must fail")
|
||||
}
|
||||
_, err = httpd.RemoveUser(user, http.StatusOK)
|
||||
if err != nil {
|
||||
|
@ -1364,7 +1364,7 @@ func TestBeforeLoginScript(t *testing.T) {
|
|||
}
|
||||
httpd.SetDataProvider(dataprovider.GetProvider())
|
||||
sftpd.SetDataProvider(dataprovider.GetProvider())
|
||||
os.Remove(beforeLoginPath)
|
||||
os.Remove(preLoginPath)
|
||||
}
|
||||
|
||||
func TestLoginExternalAuthPwdAndPubKey(t *testing.T) {
|
||||
|
@ -5042,7 +5042,7 @@ func getExtAuthScriptContent(user dataprovider.User, sleepTime int, nonJsonRespo
|
|||
return extAuthContent
|
||||
}
|
||||
|
||||
func getBeforeLoginScriptContent(user dataprovider.User, nonJsonResponse bool) []byte {
|
||||
func getPreLoginScriptContent(user dataprovider.User, nonJsonResponse bool) []byte {
|
||||
content := []byte("#!/bin/sh\n\n")
|
||||
if nonJsonResponse {
|
||||
content = append(content, []byte("echo 'text response'\n")...)
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
"external_auth_program": "",
|
||||
"external_auth_scope": 0,
|
||||
"credentials_path": "credentials",
|
||||
"before_login_program": ""
|
||||
"pre_login_program": ""
|
||||
},
|
||||
"httpd": {
|
||||
"bind_port": 8080,
|
||||
|
|
Loading…
Reference in a new issue