123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- // 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 command provides command configuration for SFTPGo hooks
- package command
- import (
- "fmt"
- "strings"
- "time"
- "github.com/drakkan/sftpgo/v2/internal/util"
- )
- const (
- minTimeout = 1
- maxTimeout = 300
- defaultTimeout = 30
- )
- // Supported hook names
- const (
- HookFsActions = "fs_actions"
- HookProviderActions = "provider_actions"
- HookStartup = "startup"
- HookPostConnect = "post_connect"
- HookPostDisconnect = "post_disconnect"
- HookDataRetention = "data_retention"
- HookCheckPassword = "check_password"
- HookPreLogin = "pre_login"
- HookPostLogin = "post_login"
- HookExternalAuth = "external_auth"
- HookKeyboardInteractive = "keyboard_interactive"
- )
- var (
- config Config
- supportedHooks = []string{HookFsActions, HookProviderActions, HookStartup, HookPostConnect, HookPostDisconnect,
- HookDataRetention, HookCheckPassword, HookPreLogin, HookPostLogin, HookExternalAuth, HookKeyboardInteractive}
- )
- // Command define the configuration for a specific commands
- type Command struct {
- // Path is the command path as defined in the hook configuration
- Path string `json:"path" mapstructure:"path"`
- // Timeout specifies a time limit, in seconds, for the command execution.
- // This value overrides the global timeout if set.
- // Do not use variables with the SFTPGO_ prefix to avoid conflicts with env
- // vars that SFTPGo sets
- Timeout int `json:"timeout" mapstructure:"timeout"`
- // Env defines environment variable for the command.
- // Each entry is of the form "key=value".
- // These values are added to the global environment variables if any
- Env []string `json:"env" mapstructure:"env"`
- // Args defines arguments to pass to the specified command
- Args []string `json:"args" mapstructure:"args"`
- // if not empty both command path and hook name must match
- Hook string `json:"hook" mapstructure:"hook"`
- }
- // Config defines the configuration for external commands such as
- // program based hooks
- type Config struct {
- // Timeout specifies a global time limit, in seconds, for the external commands execution
- Timeout int `json:"timeout" mapstructure:"timeout"`
- // Env defines environment variable for the commands.
- // Each entry is of the form "key=value".
- // Do not use variables with the SFTPGO_ prefix to avoid conflicts with env
- // vars that SFTPGo sets
- Env []string `json:"env" mapstructure:"env"`
- // Commands defines configuration for specific commands
- Commands []Command `json:"commands" mapstructure:"commands"`
- }
- func init() {
- config = Config{
- Timeout: defaultTimeout,
- }
- }
- // Initialize configures commands
- func (c Config) Initialize() error {
- if c.Timeout < minTimeout || c.Timeout > maxTimeout {
- return fmt.Errorf("invalid timeout %v", c.Timeout)
- }
- for _, env := range c.Env {
- if len(strings.SplitN(env, "=", 2)) != 2 {
- return fmt.Errorf("invalid env var %q", env)
- }
- }
- for idx, cmd := range c.Commands {
- if cmd.Path == "" {
- return fmt.Errorf("invalid path %q", cmd.Path)
- }
- if cmd.Timeout == 0 {
- c.Commands[idx].Timeout = c.Timeout
- } else {
- if cmd.Timeout < minTimeout || cmd.Timeout > maxTimeout {
- return fmt.Errorf("invalid timeout %v for command %q", cmd.Timeout, cmd.Path)
- }
- }
- for _, env := range cmd.Env {
- if len(strings.SplitN(env, "=", 2)) != 2 {
- return fmt.Errorf("invalid env var %q for command %q", env, cmd.Path)
- }
- }
- // don't validate args, we allow to pass empty arguments
- if cmd.Hook != "" {
- if !util.Contains(supportedHooks, cmd.Hook) {
- return fmt.Errorf("invalid hook name %q, supported values: %+v", cmd.Hook, supportedHooks)
- }
- }
- }
- config = c
- return nil
- }
- // GetConfig returns the configuration for the specified command
- func GetConfig(command, hook string) (time.Duration, []string, []string) {
- env := []string{}
- var args []string
- timeout := time.Duration(config.Timeout) * time.Second
- env = append(env, config.Env...)
- for _, cmd := range config.Commands {
- if cmd.Path == command {
- if cmd.Hook == "" || cmd.Hook == hook {
- timeout = time.Duration(cmd.Timeout) * time.Second
- env = append(env, cmd.Env...)
- args = cmd.Args
- break
- }
- }
- }
- return timeout, env, args
- }
|