connection_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. package common
  2. import (
  3. "os"
  4. "path"
  5. "path/filepath"
  6. "runtime"
  7. "testing"
  8. "time"
  9. "github.com/pkg/sftp"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/drakkan/sftpgo/v2/dataprovider"
  12. "github.com/drakkan/sftpgo/v2/vfs"
  13. )
  14. // MockOsFs mockable OsFs
  15. type MockOsFs struct {
  16. vfs.Fs
  17. hasVirtualFolders bool
  18. }
  19. // Name returns the name for the Fs implementation
  20. func (fs MockOsFs) Name() string {
  21. return "mockOsFs"
  22. }
  23. // HasVirtualFolders returns true if folders are emulated
  24. func (fs MockOsFs) HasVirtualFolders() bool {
  25. return fs.hasVirtualFolders
  26. }
  27. func (fs MockOsFs) IsUploadResumeSupported() bool {
  28. return !fs.hasVirtualFolders
  29. }
  30. func newMockOsFs(hasVirtualFolders bool, connectionID, rootDir string) vfs.Fs {
  31. return &MockOsFs{
  32. Fs: vfs.NewOsFs(connectionID, rootDir, ""),
  33. hasVirtualFolders: hasVirtualFolders,
  34. }
  35. }
  36. func TestRemoveErrors(t *testing.T) {
  37. mappedPath := filepath.Join(os.TempDir(), "map")
  38. homePath := filepath.Join(os.TempDir(), "home")
  39. user := dataprovider.User{
  40. Username: "remove_errors_user",
  41. HomeDir: homePath,
  42. VirtualFolders: []vfs.VirtualFolder{
  43. {
  44. BaseVirtualFolder: vfs.BaseVirtualFolder{
  45. Name: filepath.Base(mappedPath),
  46. MappedPath: mappedPath,
  47. },
  48. VirtualPath: "/virtualpath",
  49. },
  50. },
  51. }
  52. user.Permissions = make(map[string][]string)
  53. user.Permissions["/"] = []string{dataprovider.PermAny}
  54. fs := vfs.NewOsFs("", os.TempDir(), "")
  55. conn := NewBaseConnection("", ProtocolFTP, "", user)
  56. err := conn.IsRemoveDirAllowed(fs, mappedPath, "/virtualpath1")
  57. if assert.Error(t, err) {
  58. assert.Contains(t, err.Error(), "permission denied")
  59. }
  60. err = conn.RemoveFile(fs, filepath.Join(homePath, "missing_file"), "/missing_file",
  61. vfs.NewFileInfo("info", false, 100, time.Now(), false))
  62. assert.Error(t, err)
  63. }
  64. func TestSetStatMode(t *testing.T) {
  65. oldSetStatMode := Config.SetstatMode
  66. Config.SetstatMode = 1
  67. fakePath := "fake path"
  68. user := dataprovider.User{
  69. HomeDir: os.TempDir(),
  70. }
  71. user.Permissions = make(map[string][]string)
  72. user.Permissions["/"] = []string{dataprovider.PermAny}
  73. fs := newMockOsFs(true, "", user.GetHomeDir())
  74. conn := NewBaseConnection("", ProtocolWebDAV, "", user)
  75. err := conn.handleChmod(fs, fakePath, fakePath, nil)
  76. assert.NoError(t, err)
  77. err = conn.handleChown(fs, fakePath, fakePath, nil)
  78. assert.NoError(t, err)
  79. err = conn.handleChtimes(fs, fakePath, fakePath, nil)
  80. assert.NoError(t, err)
  81. Config.SetstatMode = 2
  82. err = conn.handleChmod(fs, fakePath, fakePath, nil)
  83. assert.NoError(t, err)
  84. Config.SetstatMode = oldSetStatMode
  85. }
  86. func TestRecursiveRenameWalkError(t *testing.T) {
  87. fs := vfs.NewOsFs("", os.TempDir(), "")
  88. conn := NewBaseConnection("", ProtocolWebDAV, "", dataprovider.User{})
  89. err := conn.checkRecursiveRenameDirPermissions(fs, fs, "/source", "/target")
  90. assert.ErrorIs(t, err, os.ErrNotExist)
  91. }
  92. func TestCrossRenameFsErrors(t *testing.T) {
  93. fs := vfs.NewOsFs("", os.TempDir(), "")
  94. conn := NewBaseConnection("", ProtocolWebDAV, "", dataprovider.User{})
  95. res := conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, "missingsource")
  96. assert.False(t, res)
  97. if runtime.GOOS != osWindows {
  98. dirPath := filepath.Join(os.TempDir(), "d")
  99. err := os.Mkdir(dirPath, os.ModePerm)
  100. assert.NoError(t, err)
  101. err = os.Chmod(dirPath, 0001)
  102. assert.NoError(t, err)
  103. res = conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, dirPath)
  104. assert.False(t, res)
  105. err = os.Chmod(dirPath, os.ModePerm)
  106. assert.NoError(t, err)
  107. err = os.Remove(dirPath)
  108. assert.NoError(t, err)
  109. }
  110. }
  111. func TestRenameVirtualFolders(t *testing.T) {
  112. vdir := "/avdir"
  113. u := dataprovider.User{}
  114. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  115. BaseVirtualFolder: vfs.BaseVirtualFolder{
  116. Name: "name",
  117. MappedPath: "mappedPath",
  118. },
  119. VirtualPath: vdir,
  120. })
  121. fs := vfs.NewOsFs("", os.TempDir(), "")
  122. conn := NewBaseConnection("", ProtocolFTP, "", u)
  123. res := conn.isRenamePermitted(fs, fs, "source", "target", vdir, "vdirtarget", nil)
  124. assert.False(t, res)
  125. }
  126. func TestUpdateQuotaAfterRename(t *testing.T) {
  127. user := dataprovider.User{
  128. Username: userTestUsername,
  129. HomeDir: filepath.Join(os.TempDir(), "home"),
  130. }
  131. mappedPath := filepath.Join(os.TempDir(), "vdir")
  132. user.Permissions = make(map[string][]string)
  133. user.Permissions["/"] = []string{dataprovider.PermAny}
  134. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  135. BaseVirtualFolder: vfs.BaseVirtualFolder{
  136. MappedPath: mappedPath,
  137. },
  138. VirtualPath: "/vdir",
  139. QuotaFiles: -1,
  140. QuotaSize: -1,
  141. })
  142. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  143. BaseVirtualFolder: vfs.BaseVirtualFolder{
  144. MappedPath: mappedPath,
  145. },
  146. VirtualPath: "/vdir1",
  147. QuotaFiles: -1,
  148. QuotaSize: -1,
  149. })
  150. err := os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  151. assert.NoError(t, err)
  152. err = os.MkdirAll(mappedPath, os.ModePerm)
  153. assert.NoError(t, err)
  154. fs, err := user.GetFilesystem("id")
  155. assert.NoError(t, err)
  156. c := NewBaseConnection("", ProtocolSFTP, "", user)
  157. request := sftp.NewRequest("Rename", "/testfile")
  158. if runtime.GOOS != osWindows {
  159. request.Filepath = "/dir"
  160. request.Target = path.Join("/vdir", "dir")
  161. testDirPath := filepath.Join(mappedPath, "dir")
  162. err := os.MkdirAll(testDirPath, os.ModePerm)
  163. assert.NoError(t, err)
  164. err = os.Chmod(testDirPath, 0001)
  165. assert.NoError(t, err)
  166. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, testDirPath, 0)
  167. assert.Error(t, err)
  168. err = os.Chmod(testDirPath, os.ModePerm)
  169. assert.NoError(t, err)
  170. }
  171. testFile1 := "/testfile1"
  172. request.Target = testFile1
  173. request.Filepath = path.Join("/vdir", "file")
  174. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 0)
  175. assert.Error(t, err)
  176. err = os.WriteFile(filepath.Join(mappedPath, "file"), []byte("test content"), os.ModePerm)
  177. assert.NoError(t, err)
  178. request.Filepath = testFile1
  179. request.Target = path.Join("/vdir", "file")
  180. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  181. assert.NoError(t, err)
  182. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "testfile1"), []byte("test content"), os.ModePerm)
  183. assert.NoError(t, err)
  184. request.Target = testFile1
  185. request.Filepath = path.Join("/vdir", "file")
  186. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  187. assert.NoError(t, err)
  188. request.Target = path.Join("/vdir1", "file")
  189. request.Filepath = path.Join("/vdir", "file")
  190. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  191. assert.NoError(t, err)
  192. err = os.RemoveAll(mappedPath)
  193. assert.NoError(t, err)
  194. err = os.RemoveAll(user.GetHomeDir())
  195. assert.NoError(t, err)
  196. }
  197. func TestErrorsMapping(t *testing.T) {
  198. fs := vfs.NewOsFs("", os.TempDir(), "")
  199. conn := NewBaseConnection("", ProtocolSFTP, "", dataprovider.User{HomeDir: os.TempDir()})
  200. for _, protocol := range supportedProtocols {
  201. conn.SetProtocol(protocol)
  202. err := conn.GetFsError(fs, os.ErrNotExist)
  203. if protocol == ProtocolSFTP {
  204. assert.EqualError(t, err, sftp.ErrSSHFxNoSuchFile.Error())
  205. } else if protocol == ProtocolWebDAV || protocol == ProtocolFTP || protocol == ProtocolHTTP {
  206. assert.EqualError(t, err, os.ErrNotExist.Error())
  207. } else {
  208. assert.EqualError(t, err, ErrNotExist.Error())
  209. }
  210. err = conn.GetFsError(fs, os.ErrPermission)
  211. if protocol == ProtocolSFTP {
  212. assert.EqualError(t, err, sftp.ErrSSHFxPermissionDenied.Error())
  213. } else {
  214. assert.EqualError(t, err, ErrPermissionDenied.Error())
  215. }
  216. err = conn.GetFsError(fs, os.ErrClosed)
  217. if protocol == ProtocolSFTP {
  218. assert.EqualError(t, err, sftp.ErrSSHFxFailure.Error())
  219. } else {
  220. assert.EqualError(t, err, ErrGenericFailure.Error())
  221. }
  222. err = conn.GetFsError(fs, ErrPermissionDenied)
  223. if protocol == ProtocolSFTP {
  224. assert.EqualError(t, err, sftp.ErrSSHFxFailure.Error())
  225. } else {
  226. assert.EqualError(t, err, ErrPermissionDenied.Error())
  227. }
  228. err = conn.GetFsError(fs, vfs.ErrVfsUnsupported)
  229. if protocol == ProtocolSFTP {
  230. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  231. } else {
  232. assert.EqualError(t, err, ErrOpUnsupported.Error())
  233. }
  234. err = conn.GetFsError(fs, vfs.ErrStorageSizeUnavailable)
  235. if protocol == ProtocolSFTP {
  236. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  237. } else {
  238. assert.EqualError(t, err, vfs.ErrStorageSizeUnavailable.Error())
  239. }
  240. err = conn.GetFsError(fs, nil)
  241. assert.NoError(t, err)
  242. err = conn.GetOpUnsupportedError()
  243. if protocol == ProtocolSFTP {
  244. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  245. } else {
  246. assert.EqualError(t, err, ErrOpUnsupported.Error())
  247. }
  248. }
  249. }
  250. func TestMaxWriteSize(t *testing.T) {
  251. permissions := make(map[string][]string)
  252. permissions["/"] = []string{dataprovider.PermAny}
  253. user := dataprovider.User{
  254. Username: userTestUsername,
  255. Permissions: permissions,
  256. HomeDir: filepath.Clean(os.TempDir()),
  257. }
  258. fs, err := user.GetFilesystem("123")
  259. assert.NoError(t, err)
  260. conn := NewBaseConnection("", ProtocolFTP, "", user)
  261. quotaResult := vfs.QuotaCheckResult{
  262. HasSpace: true,
  263. }
  264. size, err := conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  265. assert.NoError(t, err)
  266. assert.Equal(t, int64(0), size)
  267. conn.User.Filters.MaxUploadFileSize = 100
  268. size, err = conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  269. assert.NoError(t, err)
  270. assert.Equal(t, int64(100), size)
  271. quotaResult.QuotaSize = 1000
  272. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  273. assert.NoError(t, err)
  274. assert.Equal(t, int64(100), size)
  275. quotaResult.QuotaSize = 1000
  276. quotaResult.UsedSize = 990
  277. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  278. assert.NoError(t, err)
  279. assert.Equal(t, int64(60), size)
  280. quotaResult.QuotaSize = 0
  281. quotaResult.UsedSize = 0
  282. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  283. assert.EqualError(t, err, ErrQuotaExceeded.Error())
  284. assert.Equal(t, int64(0), size)
  285. size, err = conn.GetMaxWriteSize(quotaResult, true, 10, fs.IsUploadResumeSupported())
  286. assert.NoError(t, err)
  287. assert.Equal(t, int64(90), size)
  288. fs = newMockOsFs(true, fs.ConnectionID(), user.GetHomeDir())
  289. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  290. assert.EqualError(t, err, ErrOpUnsupported.Error())
  291. assert.Equal(t, int64(0), size)
  292. }