actions_test.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package common
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "os/exec"
  7. "path/filepath"
  8. "runtime"
  9. "testing"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/drakkan/sftpgo/v2/dataprovider"
  12. "github.com/drakkan/sftpgo/v2/sdk"
  13. "github.com/drakkan/sftpgo/v2/vfs"
  14. )
  15. func TestNewActionNotification(t *testing.T) {
  16. user := &dataprovider.User{
  17. BaseUser: sdk.BaseUser{
  18. Username: "username",
  19. },
  20. }
  21. user.FsConfig.Provider = sdk.LocalFilesystemProvider
  22. user.FsConfig.S3Config = vfs.S3FsConfig{
  23. S3FsConfig: sdk.S3FsConfig{
  24. Bucket: "s3bucket",
  25. Endpoint: "endpoint",
  26. },
  27. }
  28. user.FsConfig.GCSConfig = vfs.GCSFsConfig{
  29. GCSFsConfig: sdk.GCSFsConfig{
  30. Bucket: "gcsbucket",
  31. },
  32. }
  33. user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{
  34. AzBlobFsConfig: sdk.AzBlobFsConfig{
  35. Container: "azcontainer",
  36. Endpoint: "azendpoint",
  37. },
  38. }
  39. user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{
  40. SFTPFsConfig: sdk.SFTPFsConfig{
  41. Endpoint: "sftpendpoint",
  42. },
  43. }
  44. a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, 0, errors.New("fake error"))
  45. assert.Equal(t, user.Username, a.Username)
  46. assert.Equal(t, 0, len(a.Bucket))
  47. assert.Equal(t, 0, len(a.Endpoint))
  48. assert.Equal(t, 0, a.Status)
  49. user.FsConfig.Provider = sdk.S3FilesystemProvider
  50. a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSSH, 123, 0, nil)
  51. assert.Equal(t, "s3bucket", a.Bucket)
  52. assert.Equal(t, "endpoint", a.Endpoint)
  53. assert.Equal(t, 1, a.Status)
  54. user.FsConfig.Provider = sdk.GCSFilesystemProvider
  55. a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, 0, ErrQuotaExceeded)
  56. assert.Equal(t, "gcsbucket", a.Bucket)
  57. assert.Equal(t, 0, len(a.Endpoint))
  58. assert.Equal(t, 2, a.Status)
  59. user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
  60. a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, 0, nil)
  61. assert.Equal(t, "azcontainer", a.Bucket)
  62. assert.Equal(t, "azendpoint", a.Endpoint)
  63. assert.Equal(t, 1, a.Status)
  64. a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSCP, 123, os.O_APPEND, nil)
  65. assert.Equal(t, "azcontainer", a.Bucket)
  66. assert.Equal(t, "azendpoint", a.Endpoint)
  67. assert.Equal(t, 1, a.Status)
  68. assert.Equal(t, os.O_APPEND, a.OpenFlags)
  69. user.FsConfig.Provider = sdk.SFTPFilesystemProvider
  70. a = newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, 0, nil)
  71. assert.Equal(t, "sftpendpoint", a.Endpoint)
  72. }
  73. func TestActionHTTP(t *testing.T) {
  74. actionsCopy := Config.Actions
  75. Config.Actions = ProtocolActions{
  76. ExecuteOn: []string{operationDownload},
  77. Hook: fmt.Sprintf("http://%v", httpAddr),
  78. }
  79. user := &dataprovider.User{
  80. BaseUser: sdk.BaseUser{
  81. Username: "username",
  82. },
  83. }
  84. a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, 0, nil)
  85. err := actionHandler.Handle(a)
  86. assert.NoError(t, err)
  87. Config.Actions.Hook = "http://invalid:1234"
  88. err = actionHandler.Handle(a)
  89. assert.Error(t, err)
  90. Config.Actions.Hook = fmt.Sprintf("http://%v/404", httpAddr)
  91. err = actionHandler.Handle(a)
  92. if assert.Error(t, err) {
  93. assert.EqualError(t, err, errUnexpectedHTTResponse.Error())
  94. }
  95. Config.Actions = actionsCopy
  96. }
  97. func TestActionCMD(t *testing.T) {
  98. if runtime.GOOS == osWindows {
  99. t.Skip("this test is not available on Windows")
  100. }
  101. actionsCopy := Config.Actions
  102. hookCmd, err := exec.LookPath("true")
  103. assert.NoError(t, err)
  104. Config.Actions = ProtocolActions{
  105. ExecuteOn: []string{operationDownload},
  106. Hook: hookCmd,
  107. }
  108. user := &dataprovider.User{
  109. BaseUser: sdk.BaseUser{
  110. Username: "username",
  111. },
  112. }
  113. a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", ProtocolSFTP, 123, 0, nil)
  114. err = actionHandler.Handle(a)
  115. assert.NoError(t, err)
  116. ExecuteActionNotification(user, OperationSSHCmd, "path", "vpath", "target", "sha1sum", ProtocolSSH, 0, nil)
  117. Config.Actions = actionsCopy
  118. }
  119. func TestWrongActions(t *testing.T) {
  120. actionsCopy := Config.Actions
  121. badCommand := "/bad/command"
  122. if runtime.GOOS == osWindows {
  123. badCommand = "C:\\bad\\command"
  124. }
  125. Config.Actions = ProtocolActions{
  126. ExecuteOn: []string{operationUpload},
  127. Hook: badCommand,
  128. }
  129. user := &dataprovider.User{
  130. BaseUser: sdk.BaseUser{
  131. Username: "username",
  132. },
  133. }
  134. a := newActionNotification(user, operationUpload, "", "", "", "", ProtocolSFTP, 123, 0, nil)
  135. err := actionHandler.Handle(a)
  136. assert.Error(t, err, "action with bad command must fail")
  137. a.Action = operationDelete
  138. err = actionHandler.Handle(a)
  139. assert.EqualError(t, err, errUnconfiguredAction.Error())
  140. Config.Actions.Hook = "http://foo\x7f.com/"
  141. a.Action = operationUpload
  142. err = actionHandler.Handle(a)
  143. assert.Error(t, err, "action with bad url must fail")
  144. Config.Actions.Hook = ""
  145. err = actionHandler.Handle(a)
  146. if assert.Error(t, err) {
  147. assert.EqualError(t, err, errNoHook.Error())
  148. }
  149. Config.Actions.Hook = "relative path"
  150. err = actionHandler.Handle(a)
  151. if assert.Error(t, err) {
  152. assert.EqualError(t, err, fmt.Sprintf("invalid notification command %#v", Config.Actions.Hook))
  153. }
  154. Config.Actions = actionsCopy
  155. }
  156. func TestPreDeleteAction(t *testing.T) {
  157. if runtime.GOOS == osWindows {
  158. t.Skip("this test is not available on Windows")
  159. }
  160. actionsCopy := Config.Actions
  161. hookCmd, err := exec.LookPath("true")
  162. assert.NoError(t, err)
  163. Config.Actions = ProtocolActions{
  164. ExecuteOn: []string{operationPreDelete},
  165. Hook: hookCmd,
  166. }
  167. homeDir := filepath.Join(os.TempDir(), "test_user")
  168. err = os.MkdirAll(homeDir, os.ModePerm)
  169. assert.NoError(t, err)
  170. user := dataprovider.User{
  171. BaseUser: sdk.BaseUser{
  172. Username: "username",
  173. HomeDir: homeDir,
  174. },
  175. }
  176. user.Permissions = make(map[string][]string)
  177. user.Permissions["/"] = []string{dataprovider.PermAny}
  178. fs := vfs.NewOsFs("id", homeDir, "")
  179. c := NewBaseConnection("id", ProtocolSFTP, "", user)
  180. testfile := filepath.Join(user.HomeDir, "testfile")
  181. err = os.WriteFile(testfile, []byte("test"), os.ModePerm)
  182. assert.NoError(t, err)
  183. info, err := os.Stat(testfile)
  184. assert.NoError(t, err)
  185. err = c.RemoveFile(fs, testfile, "testfile", info)
  186. assert.NoError(t, err)
  187. assert.FileExists(t, testfile)
  188. os.RemoveAll(homeDir)
  189. Config.Actions = actionsCopy
  190. }
  191. type actionHandlerStub struct {
  192. called bool
  193. }
  194. func (h *actionHandlerStub) Handle(notification *ActionNotification) error {
  195. h.called = true
  196. return nil
  197. }
  198. func TestInitializeActionHandler(t *testing.T) {
  199. handler := &actionHandlerStub{}
  200. InitializeActionHandler(handler)
  201. t.Cleanup(func() {
  202. InitializeActionHandler(&defaultActionHandler{})
  203. })
  204. err := actionHandler.Handle(&ActionNotification{})
  205. assert.NoError(t, err)
  206. assert.True(t, handler.called)
  207. }