connection_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. // Copyright (C) 2019-2023 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package common
  15. import (
  16. "errors"
  17. "os"
  18. "path"
  19. "path/filepath"
  20. "runtime"
  21. "testing"
  22. "time"
  23. "github.com/pkg/sftp"
  24. "github.com/rs/xid"
  25. "github.com/sftpgo/sdk"
  26. "github.com/stretchr/testify/assert"
  27. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  28. "github.com/drakkan/sftpgo/v2/internal/kms"
  29. "github.com/drakkan/sftpgo/v2/internal/util"
  30. "github.com/drakkan/sftpgo/v2/internal/vfs"
  31. )
  32. var (
  33. errWalkDir = errors.New("err walk dir")
  34. )
  35. // MockOsFs mockable OsFs
  36. type MockOsFs struct {
  37. vfs.Fs
  38. hasVirtualFolders bool
  39. name string
  40. err error
  41. }
  42. // Name returns the name for the Fs implementation
  43. func (fs *MockOsFs) Name() string {
  44. if fs.name != "" {
  45. return fs.name
  46. }
  47. return "mockOsFs"
  48. }
  49. // HasVirtualFolders returns true if folders are emulated
  50. func (fs *MockOsFs) HasVirtualFolders() bool {
  51. return fs.hasVirtualFolders
  52. }
  53. func (fs *MockOsFs) IsUploadResumeSupported() bool {
  54. return !fs.hasVirtualFolders
  55. }
  56. func (fs *MockOsFs) Chtimes(_ string, _, _ time.Time, _ bool) error {
  57. return vfs.ErrVfsUnsupported
  58. }
  59. func (fs *MockOsFs) Lstat(name string) (os.FileInfo, error) {
  60. if fs.err != nil {
  61. return nil, fs.err
  62. }
  63. return fs.Fs.Lstat(name)
  64. }
  65. // Walk returns a duplicate path for testing
  66. func (fs *MockOsFs) Walk(_ string, walkFn filepath.WalkFunc) error {
  67. if fs.err == errWalkDir {
  68. walkFn("fsdpath", vfs.NewFileInfo("dpath", true, 0, time.Now(), false), nil) //nolint:errcheck
  69. return walkFn("fsdpath", vfs.NewFileInfo("dpath", true, 0, time.Now(), false), nil) //nolint:errcheck
  70. }
  71. walkFn("fsfpath", vfs.NewFileInfo("fpath", false, 0, time.Now(), false), nil) //nolint:errcheck
  72. return fs.err
  73. }
  74. func newMockOsFs(hasVirtualFolders bool, connectionID, rootDir, name string, err error) vfs.Fs {
  75. return &MockOsFs{
  76. Fs: vfs.NewOsFs(connectionID, rootDir, "", nil),
  77. name: name,
  78. hasVirtualFolders: hasVirtualFolders,
  79. err: err,
  80. }
  81. }
  82. func TestRemoveErrors(t *testing.T) {
  83. mappedPath := filepath.Join(os.TempDir(), "map")
  84. homePath := filepath.Join(os.TempDir(), "home")
  85. user := dataprovider.User{
  86. BaseUser: sdk.BaseUser{
  87. Username: "remove_errors_user",
  88. HomeDir: homePath,
  89. },
  90. VirtualFolders: []vfs.VirtualFolder{
  91. {
  92. BaseVirtualFolder: vfs.BaseVirtualFolder{
  93. Name: filepath.Base(mappedPath),
  94. MappedPath: mappedPath,
  95. },
  96. VirtualPath: "/virtualpath",
  97. },
  98. },
  99. }
  100. user.Permissions = make(map[string][]string)
  101. user.Permissions["/"] = []string{dataprovider.PermAny}
  102. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  103. conn := NewBaseConnection("", ProtocolFTP, "", "", user)
  104. err := conn.IsRemoveDirAllowed(fs, mappedPath, "/virtualpath1")
  105. if assert.Error(t, err) {
  106. assert.Contains(t, err.Error(), "permission denied")
  107. }
  108. err = conn.RemoveFile(fs, filepath.Join(homePath, "missing_file"), "/missing_file",
  109. vfs.NewFileInfo("info", false, 100, time.Now(), false))
  110. assert.Error(t, err)
  111. }
  112. func TestSetStatMode(t *testing.T) {
  113. oldSetStatMode := Config.SetstatMode
  114. Config.SetstatMode = 1
  115. fakePath := "fake path"
  116. user := dataprovider.User{
  117. BaseUser: sdk.BaseUser{
  118. HomeDir: os.TempDir(),
  119. },
  120. }
  121. user.Permissions = make(map[string][]string)
  122. user.Permissions["/"] = []string{dataprovider.PermAny}
  123. fs := newMockOsFs(true, "", user.GetHomeDir(), "", nil)
  124. conn := NewBaseConnection("", ProtocolWebDAV, "", "", user)
  125. err := conn.handleChmod(fs, fakePath, fakePath, nil)
  126. assert.NoError(t, err)
  127. err = conn.handleChown(fs, fakePath, fakePath, nil)
  128. assert.NoError(t, err)
  129. err = conn.handleChtimes(fs, fakePath, fakePath, nil)
  130. assert.NoError(t, err)
  131. Config.SetstatMode = 2
  132. err = conn.handleChmod(fs, fakePath, fakePath, nil)
  133. assert.NoError(t, err)
  134. err = conn.handleChtimes(fs, fakePath, fakePath, &StatAttributes{
  135. Atime: time.Now(),
  136. Mtime: time.Now(),
  137. })
  138. assert.NoError(t, err)
  139. Config.SetstatMode = oldSetStatMode
  140. }
  141. func TestRecursiveRenameWalkError(t *testing.T) {
  142. fs := vfs.NewOsFs("", filepath.Clean(os.TempDir()), "", nil)
  143. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{
  144. BaseUser: sdk.BaseUser{
  145. Permissions: map[string][]string{
  146. "/": {dataprovider.PermListItems, dataprovider.PermUpload,
  147. dataprovider.PermDownload, dataprovider.PermRenameDirs},
  148. },
  149. },
  150. })
  151. err := conn.checkRecursiveRenameDirPermissions(fs, fs, filepath.Join(os.TempDir(), "/source"),
  152. filepath.Join(os.TempDir(), "/target"), "/source", "/target",
  153. vfs.NewFileInfo("source", true, 0, time.Now(), false))
  154. assert.ErrorIs(t, err, os.ErrNotExist)
  155. fs = newMockOsFs(false, "mockID", filepath.Clean(os.TempDir()), "S3Fs", errWalkDir)
  156. err = conn.checkRecursiveRenameDirPermissions(fs, fs, filepath.Join(os.TempDir(), "/source"),
  157. filepath.Join(os.TempDir(), "/target"), "/source", "/target",
  158. vfs.NewFileInfo("source", true, 0, time.Now(), false))
  159. if assert.Error(t, err) {
  160. assert.Equal(t, err.Error(), conn.GetOpUnsupportedError().Error())
  161. }
  162. conn.User.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
  163. dataprovider.PermDownload, dataprovider.PermRenameFiles}
  164. // no dir rename permission, the quick check path returns permission error without walking
  165. err = conn.checkRecursiveRenameDirPermissions(fs, fs, filepath.Join(os.TempDir(), "/source"),
  166. filepath.Join(os.TempDir(), "/target"), "/source", "/target",
  167. vfs.NewFileInfo("source", true, 0, time.Now(), false))
  168. if assert.Error(t, err) {
  169. assert.EqualError(t, err, conn.GetPermissionDeniedError().Error())
  170. }
  171. }
  172. func TestCrossRenameFsErrors(t *testing.T) {
  173. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  174. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{})
  175. res := conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, "missingsource")
  176. assert.False(t, res)
  177. if runtime.GOOS != osWindows {
  178. dirPath := filepath.Join(os.TempDir(), "d")
  179. err := os.Mkdir(dirPath, os.ModePerm)
  180. assert.NoError(t, err)
  181. err = os.Chmod(dirPath, 0001)
  182. assert.NoError(t, err)
  183. res = conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, dirPath)
  184. assert.False(t, res)
  185. err = os.Chmod(dirPath, os.ModePerm)
  186. assert.NoError(t, err)
  187. err = os.Remove(dirPath)
  188. assert.NoError(t, err)
  189. }
  190. }
  191. func TestRenameVirtualFolders(t *testing.T) {
  192. vdir := "/avdir"
  193. u := dataprovider.User{}
  194. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  195. BaseVirtualFolder: vfs.BaseVirtualFolder{
  196. Name: "name",
  197. MappedPath: "mappedPath",
  198. },
  199. VirtualPath: vdir,
  200. })
  201. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  202. conn := NewBaseConnection("", ProtocolFTP, "", "", u)
  203. res := conn.isRenamePermitted(fs, fs, "source", "target", vdir, "vdirtarget", nil)
  204. assert.False(t, res)
  205. }
  206. func TestRenamePerms(t *testing.T) {
  207. src := "source"
  208. target := "target"
  209. sub := "/sub"
  210. subTarget := sub + "/target"
  211. u := dataprovider.User{}
  212. u.Permissions = map[string][]string{}
  213. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermCreateSymlinks,
  214. dataprovider.PermDeleteFiles}
  215. conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
  216. assert.False(t, conn.hasRenamePerms(src, target, nil))
  217. u.Permissions["/"] = []string{dataprovider.PermRename}
  218. assert.True(t, conn.hasRenamePerms(src, target, nil))
  219. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteFiles,
  220. dataprovider.PermDeleteDirs}
  221. assert.False(t, conn.hasRenamePerms(src, target, nil))
  222. info := vfs.NewFileInfo(src, true, 0, time.Now(), false)
  223. u.Permissions["/"] = []string{dataprovider.PermRenameFiles}
  224. assert.False(t, conn.hasRenamePerms(src, target, info))
  225. u.Permissions["/"] = []string{dataprovider.PermRenameDirs}
  226. assert.True(t, conn.hasRenamePerms(src, target, info))
  227. u.Permissions["/"] = []string{dataprovider.PermRename}
  228. assert.True(t, conn.hasRenamePerms(src, target, info))
  229. u.Permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDeleteDirs}
  230. assert.False(t, conn.hasRenamePerms(src, target, info))
  231. // test with different permissions between source and target
  232. u.Permissions["/"] = []string{dataprovider.PermRename}
  233. u.Permissions[sub] = []string{dataprovider.PermRenameFiles}
  234. assert.False(t, conn.hasRenamePerms(src, subTarget, info))
  235. u.Permissions[sub] = []string{dataprovider.PermRenameDirs}
  236. assert.True(t, conn.hasRenamePerms(src, subTarget, info))
  237. // test files
  238. info = vfs.NewFileInfo(src, false, 0, time.Now(), false)
  239. u.Permissions["/"] = []string{dataprovider.PermRenameDirs}
  240. assert.False(t, conn.hasRenamePerms(src, target, info))
  241. u.Permissions["/"] = []string{dataprovider.PermRenameFiles}
  242. assert.True(t, conn.hasRenamePerms(src, target, info))
  243. u.Permissions["/"] = []string{dataprovider.PermRename}
  244. assert.True(t, conn.hasRenamePerms(src, target, info))
  245. // test with different permissions between source and target
  246. u.Permissions["/"] = []string{dataprovider.PermRename}
  247. u.Permissions[sub] = []string{dataprovider.PermRenameDirs}
  248. assert.False(t, conn.hasRenamePerms(src, subTarget, info))
  249. u.Permissions[sub] = []string{dataprovider.PermRenameFiles}
  250. assert.True(t, conn.hasRenamePerms(src, subTarget, info))
  251. }
  252. func TestRenameNestedFolders(t *testing.T) {
  253. u := dataprovider.User{}
  254. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  255. BaseVirtualFolder: vfs.BaseVirtualFolder{
  256. Name: "vfolder",
  257. MappedPath: filepath.Join(os.TempDir(), "f"),
  258. },
  259. VirtualPath: "/vdirs/f",
  260. })
  261. conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
  262. err := conn.checkFolderRename(nil, nil, filepath.Clean(os.TempDir()), filepath.Join(os.TempDir(), "subdir"), "/src", "/dst", nil)
  263. assert.Error(t, err)
  264. err = conn.checkFolderRename(nil, nil, filepath.Join(os.TempDir(), "subdir"), filepath.Clean(os.TempDir()), "/src", "/dst", nil)
  265. assert.Error(t, err)
  266. err = conn.checkFolderRename(nil, nil, "", "", "/src/sub", "/src", nil)
  267. assert.Error(t, err)
  268. err = conn.checkFolderRename(nil, nil, filepath.Join(os.TempDir(), "src"), filepath.Join(os.TempDir(), "vdirs"), "/src", "/vdirs", nil)
  269. assert.Error(t, err)
  270. }
  271. func TestUpdateQuotaAfterRename(t *testing.T) {
  272. user := dataprovider.User{
  273. BaseUser: sdk.BaseUser{
  274. Username: userTestUsername,
  275. HomeDir: filepath.Join(os.TempDir(), "home"),
  276. },
  277. }
  278. mappedPath := filepath.Join(os.TempDir(), "vdir")
  279. user.Permissions = make(map[string][]string)
  280. user.Permissions["/"] = []string{dataprovider.PermAny}
  281. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  282. BaseVirtualFolder: vfs.BaseVirtualFolder{
  283. MappedPath: mappedPath,
  284. },
  285. VirtualPath: "/vdir",
  286. QuotaFiles: -1,
  287. QuotaSize: -1,
  288. })
  289. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  290. BaseVirtualFolder: vfs.BaseVirtualFolder{
  291. MappedPath: mappedPath,
  292. },
  293. VirtualPath: "/vdir1",
  294. QuotaFiles: -1,
  295. QuotaSize: -1,
  296. })
  297. err := os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  298. assert.NoError(t, err)
  299. err = os.MkdirAll(mappedPath, os.ModePerm)
  300. assert.NoError(t, err)
  301. fs, err := user.GetFilesystem("id")
  302. assert.NoError(t, err)
  303. c := NewBaseConnection("", ProtocolSFTP, "", "", user)
  304. request := sftp.NewRequest("Rename", "/testfile")
  305. if runtime.GOOS != osWindows {
  306. request.Filepath = "/dir"
  307. request.Target = path.Join("/vdir", "dir")
  308. testDirPath := filepath.Join(mappedPath, "dir")
  309. err := os.MkdirAll(testDirPath, os.ModePerm)
  310. assert.NoError(t, err)
  311. err = os.Chmod(testDirPath, 0001)
  312. assert.NoError(t, err)
  313. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, testDirPath, 0, -1, -1)
  314. assert.Error(t, err)
  315. err = os.Chmod(testDirPath, os.ModePerm)
  316. assert.NoError(t, err)
  317. }
  318. testFile1 := "/testfile1"
  319. request.Target = testFile1
  320. request.Filepath = path.Join("/vdir", "file")
  321. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 0, -1, -1)
  322. assert.Error(t, err)
  323. err = os.WriteFile(filepath.Join(mappedPath, "file"), []byte("test content"), os.ModePerm)
  324. assert.NoError(t, err)
  325. request.Filepath = testFile1
  326. request.Target = path.Join("/vdir", "file")
  327. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12, -1, -1)
  328. assert.NoError(t, err)
  329. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "testfile1"), []byte("test content"), os.ModePerm)
  330. assert.NoError(t, err)
  331. request.Target = testFile1
  332. request.Filepath = path.Join("/vdir", "file")
  333. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12, -1, -1)
  334. assert.NoError(t, err)
  335. request.Target = path.Join("/vdir1", "file")
  336. request.Filepath = path.Join("/vdir", "file")
  337. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12, -1, -1)
  338. assert.NoError(t, err)
  339. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12, 1, 100)
  340. assert.NoError(t, err)
  341. err = os.RemoveAll(mappedPath)
  342. assert.NoError(t, err)
  343. err = os.RemoveAll(user.GetHomeDir())
  344. assert.NoError(t, err)
  345. }
  346. func TestErrorsMapping(t *testing.T) {
  347. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  348. conn := NewBaseConnection("", ProtocolSFTP, "", "", dataprovider.User{BaseUser: sdk.BaseUser{HomeDir: os.TempDir()}})
  349. osErrorsProtocols := []string{ProtocolWebDAV, ProtocolFTP, ProtocolHTTP, ProtocolHTTPShare,
  350. ProtocolDataRetention, ProtocolOIDC, protocolEventAction}
  351. for _, protocol := range supportedProtocols {
  352. conn.SetProtocol(protocol)
  353. err := conn.GetFsError(fs, os.ErrNotExist)
  354. if protocol == ProtocolSFTP {
  355. assert.ErrorIs(t, err, sftp.ErrSSHFxNoSuchFile)
  356. } else if util.Contains(osErrorsProtocols, protocol) {
  357. assert.EqualError(t, err, os.ErrNotExist.Error())
  358. } else {
  359. assert.EqualError(t, err, ErrNotExist.Error())
  360. }
  361. err = conn.GetFsError(fs, os.ErrPermission)
  362. if protocol == ProtocolSFTP {
  363. assert.EqualError(t, err, sftp.ErrSSHFxPermissionDenied.Error())
  364. } else {
  365. assert.EqualError(t, err, ErrPermissionDenied.Error())
  366. }
  367. err = conn.GetFsError(fs, os.ErrClosed)
  368. if protocol == ProtocolSFTP {
  369. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  370. } else {
  371. assert.EqualError(t, err, ErrGenericFailure.Error())
  372. }
  373. err = conn.GetFsError(fs, ErrPermissionDenied)
  374. if protocol == ProtocolSFTP {
  375. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  376. } else {
  377. assert.EqualError(t, err, ErrPermissionDenied.Error())
  378. }
  379. err = conn.GetFsError(fs, vfs.ErrVfsUnsupported)
  380. if protocol == ProtocolSFTP {
  381. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  382. } else {
  383. assert.EqualError(t, err, ErrOpUnsupported.Error())
  384. }
  385. err = conn.GetFsError(fs, vfs.ErrStorageSizeUnavailable)
  386. if protocol == ProtocolSFTP {
  387. assert.ErrorIs(t, err, sftp.ErrSSHFxOpUnsupported)
  388. assert.Contains(t, err.Error(), vfs.ErrStorageSizeUnavailable.Error())
  389. } else {
  390. assert.EqualError(t, err, vfs.ErrStorageSizeUnavailable.Error())
  391. }
  392. err = conn.GetQuotaExceededError()
  393. assert.True(t, conn.IsQuotaExceededError(err))
  394. err = conn.GetReadQuotaExceededError()
  395. if protocol == ProtocolSFTP {
  396. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  397. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  398. } else {
  399. assert.ErrorIs(t, err, ErrReadQuotaExceeded)
  400. }
  401. err = conn.GetNotExistError()
  402. assert.True(t, conn.IsNotExistError(err))
  403. err = conn.GetFsError(fs, nil)
  404. assert.NoError(t, err)
  405. err = conn.GetOpUnsupportedError()
  406. if protocol == ProtocolSFTP {
  407. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  408. } else {
  409. assert.EqualError(t, err, ErrOpUnsupported.Error())
  410. }
  411. err = conn.GetFsError(fs, ErrShuttingDown)
  412. if protocol == ProtocolSFTP {
  413. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  414. assert.Contains(t, err.Error(), ErrShuttingDown.Error())
  415. } else {
  416. assert.EqualError(t, err, ErrShuttingDown.Error())
  417. }
  418. }
  419. }
  420. func TestMaxWriteSize(t *testing.T) {
  421. permissions := make(map[string][]string)
  422. permissions["/"] = []string{dataprovider.PermAny}
  423. user := dataprovider.User{
  424. BaseUser: sdk.BaseUser{
  425. Username: userTestUsername,
  426. Permissions: permissions,
  427. HomeDir: filepath.Clean(os.TempDir()),
  428. },
  429. }
  430. fs, err := user.GetFilesystem("123")
  431. assert.NoError(t, err)
  432. conn := NewBaseConnection("", ProtocolFTP, "", "", user)
  433. quotaResult := vfs.QuotaCheckResult{
  434. HasSpace: true,
  435. }
  436. size, err := conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  437. assert.NoError(t, err)
  438. assert.Equal(t, int64(0), size)
  439. conn.User.Filters.MaxUploadFileSize = 100
  440. size, err = conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  441. assert.NoError(t, err)
  442. assert.Equal(t, int64(100), size)
  443. quotaResult.QuotaSize = 1000
  444. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  445. assert.NoError(t, err)
  446. assert.Equal(t, int64(100), size)
  447. quotaResult.QuotaSize = 1000
  448. quotaResult.UsedSize = 990
  449. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  450. assert.NoError(t, err)
  451. assert.Equal(t, int64(60), size)
  452. quotaResult.QuotaSize = 0
  453. quotaResult.UsedSize = 0
  454. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  455. assert.True(t, conn.IsQuotaExceededError(err))
  456. assert.Equal(t, int64(0), size)
  457. size, err = conn.GetMaxWriteSize(quotaResult, true, 10, fs.IsUploadResumeSupported())
  458. assert.NoError(t, err)
  459. assert.Equal(t, int64(90), size)
  460. fs = newMockOsFs(true, fs.ConnectionID(), user.GetHomeDir(), "", nil)
  461. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  462. assert.EqualError(t, err, ErrOpUnsupported.Error())
  463. assert.Equal(t, int64(0), size)
  464. }
  465. func TestCheckParentDirsErrors(t *testing.T) {
  466. permissions := make(map[string][]string)
  467. permissions["/"] = []string{dataprovider.PermAny}
  468. user := dataprovider.User{
  469. BaseUser: sdk.BaseUser{
  470. Username: userTestUsername,
  471. Permissions: permissions,
  472. HomeDir: filepath.Clean(os.TempDir()),
  473. },
  474. FsConfig: vfs.Filesystem{
  475. Provider: sdk.CryptedFilesystemProvider,
  476. },
  477. }
  478. c := NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  479. err := c.CheckParentDirs("/a/dir")
  480. assert.Error(t, err)
  481. user.FsConfig.Provider = sdk.LocalFilesystemProvider
  482. user.VirtualFolders = nil
  483. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  484. BaseVirtualFolder: vfs.BaseVirtualFolder{
  485. FsConfig: vfs.Filesystem{
  486. Provider: sdk.CryptedFilesystemProvider,
  487. },
  488. },
  489. VirtualPath: "/vdir",
  490. })
  491. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  492. BaseVirtualFolder: vfs.BaseVirtualFolder{
  493. MappedPath: filepath.Clean(os.TempDir()),
  494. },
  495. VirtualPath: "/vdir/sub",
  496. })
  497. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  498. err = c.CheckParentDirs("/vdir/sub/dir")
  499. assert.Error(t, err)
  500. user = dataprovider.User{
  501. BaseUser: sdk.BaseUser{
  502. Username: userTestUsername,
  503. Permissions: permissions,
  504. HomeDir: filepath.Clean(os.TempDir()),
  505. },
  506. FsConfig: vfs.Filesystem{
  507. Provider: sdk.S3FilesystemProvider,
  508. S3Config: vfs.S3FsConfig{
  509. BaseS3FsConfig: sdk.BaseS3FsConfig{
  510. Bucket: "buck",
  511. Region: "us-east-1",
  512. AccessKey: "key",
  513. },
  514. AccessSecret: kms.NewPlainSecret("s3secret"),
  515. },
  516. },
  517. }
  518. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  519. err = c.CheckParentDirs("/a/dir")
  520. assert.NoError(t, err)
  521. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  522. BaseVirtualFolder: vfs.BaseVirtualFolder{
  523. MappedPath: filepath.Clean(os.TempDir()),
  524. },
  525. VirtualPath: "/local/dir",
  526. })
  527. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  528. err = c.CheckParentDirs("/local/dir/sub-dir")
  529. assert.NoError(t, err)
  530. err = os.RemoveAll(filepath.Join(os.TempDir(), "sub-dir"))
  531. assert.NoError(t, err)
  532. }
  533. func TestErrorResolvePath(t *testing.T) {
  534. u := dataprovider.User{
  535. BaseUser: sdk.BaseUser{
  536. HomeDir: filepath.Join(os.TempDir(), "u"),
  537. Status: 1,
  538. Permissions: map[string][]string{
  539. "/": {dataprovider.PermAny},
  540. },
  541. },
  542. }
  543. u.FsConfig.Provider = sdk.GCSFilesystemProvider
  544. u.FsConfig.GCSConfig.Bucket = "test"
  545. u.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("invalid JSON for credentials")
  546. u.VirtualFolders = []vfs.VirtualFolder{
  547. {
  548. BaseVirtualFolder: vfs.BaseVirtualFolder{
  549. Name: "f",
  550. MappedPath: filepath.Join(os.TempDir(), "f"),
  551. },
  552. VirtualPath: "/f",
  553. },
  554. }
  555. conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
  556. err := conn.doRecursiveRemoveDirEntry("/vpath", nil)
  557. assert.Error(t, err)
  558. err = conn.checkCopy(vfs.NewFileInfo("name", true, 0, time.Unix(0, 0), false), nil, "/source", "/target")
  559. assert.Error(t, err)
  560. sourceFile := filepath.Join(os.TempDir(), "f", "source")
  561. err = os.MkdirAll(filepath.Dir(sourceFile), os.ModePerm)
  562. assert.NoError(t, err)
  563. err = os.WriteFile(sourceFile, []byte(""), 0666)
  564. assert.NoError(t, err)
  565. err = conn.checkCopy(vfs.NewFileInfo("name", true, 0, time.Unix(0, 0), false), nil, "/f/source", "/target")
  566. assert.Error(t, err)
  567. err = conn.checkCopy(vfs.NewFileInfo("source", false, 0, time.Unix(0, 0), false), vfs.NewFileInfo("target", true, 0, time.Unix(0, 0), false), "/f/source", "/f/target")
  568. assert.Error(t, err)
  569. err = os.RemoveAll(filepath.Dir(sourceFile))
  570. assert.NoError(t, err)
  571. }
  572. func TestConnectionKeepAlive(t *testing.T) {
  573. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{})
  574. lastActivity := conn.GetLastActivity()
  575. done := make(chan bool)
  576. go func() {
  577. time.Sleep(200 * time.Millisecond)
  578. close(done)
  579. }()
  580. keepConnectionAlive(conn, done, 50*time.Millisecond)
  581. assert.Greater(t, conn.GetLastActivity(), lastActivity)
  582. }
  583. func TestFsFileCopier(t *testing.T) {
  584. fs := vfs.Fs(&vfs.AzureBlobFs{})
  585. _, ok := fs.(vfs.FsFileCopier)
  586. assert.True(t, ok)
  587. fs = vfs.Fs(&vfs.OsFs{})
  588. _, ok = fs.(vfs.FsFileCopier)
  589. assert.False(t, ok)
  590. fs = vfs.Fs(&vfs.SFTPFs{})
  591. _, ok = fs.(vfs.FsFileCopier)
  592. assert.False(t, ok)
  593. fs = vfs.Fs(&vfs.GCSFs{})
  594. _, ok = fs.(vfs.FsFileCopier)
  595. assert.True(t, ok)
  596. fs = vfs.Fs(&vfs.S3Fs{})
  597. _, ok = fs.(vfs.FsFileCopier)
  598. assert.True(t, ok)
  599. }