connection_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. // Copyright (C) 2019-2022 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. "os"
  17. "path"
  18. "path/filepath"
  19. "runtime"
  20. "testing"
  21. "time"
  22. "github.com/pkg/sftp"
  23. "github.com/rs/xid"
  24. "github.com/sftpgo/sdk"
  25. "github.com/stretchr/testify/assert"
  26. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  27. "github.com/drakkan/sftpgo/v2/internal/kms"
  28. "github.com/drakkan/sftpgo/v2/internal/util"
  29. "github.com/drakkan/sftpgo/v2/internal/vfs"
  30. )
  31. // MockOsFs mockable OsFs
  32. type MockOsFs struct {
  33. vfs.Fs
  34. hasVirtualFolders bool
  35. }
  36. // Name returns the name for the Fs implementation
  37. func (fs *MockOsFs) Name() string {
  38. return "mockOsFs"
  39. }
  40. // HasVirtualFolders returns true if folders are emulated
  41. func (fs *MockOsFs) HasVirtualFolders() bool {
  42. return fs.hasVirtualFolders
  43. }
  44. func (fs *MockOsFs) IsUploadResumeSupported() bool {
  45. return !fs.hasVirtualFolders
  46. }
  47. func (fs *MockOsFs) Chtimes(name string, atime, mtime time.Time, isUploading bool) error {
  48. return vfs.ErrVfsUnsupported
  49. }
  50. func newMockOsFs(hasVirtualFolders bool, connectionID, rootDir string) vfs.Fs {
  51. return &MockOsFs{
  52. Fs: vfs.NewOsFs(connectionID, rootDir, ""),
  53. hasVirtualFolders: hasVirtualFolders,
  54. }
  55. }
  56. func TestRemoveErrors(t *testing.T) {
  57. mappedPath := filepath.Join(os.TempDir(), "map")
  58. homePath := filepath.Join(os.TempDir(), "home")
  59. user := dataprovider.User{
  60. BaseUser: sdk.BaseUser{
  61. Username: "remove_errors_user",
  62. HomeDir: homePath,
  63. },
  64. VirtualFolders: []vfs.VirtualFolder{
  65. {
  66. BaseVirtualFolder: vfs.BaseVirtualFolder{
  67. Name: filepath.Base(mappedPath),
  68. MappedPath: mappedPath,
  69. },
  70. VirtualPath: "/virtualpath",
  71. },
  72. },
  73. }
  74. user.Permissions = make(map[string][]string)
  75. user.Permissions["/"] = []string{dataprovider.PermAny}
  76. fs := vfs.NewOsFs("", os.TempDir(), "")
  77. conn := NewBaseConnection("", ProtocolFTP, "", "", user)
  78. err := conn.IsRemoveDirAllowed(fs, mappedPath, "/virtualpath1")
  79. if assert.Error(t, err) {
  80. assert.Contains(t, err.Error(), "permission denied")
  81. }
  82. err = conn.RemoveFile(fs, filepath.Join(homePath, "missing_file"), "/missing_file",
  83. vfs.NewFileInfo("info", false, 100, time.Now(), false))
  84. assert.Error(t, err)
  85. }
  86. func TestSetStatMode(t *testing.T) {
  87. oldSetStatMode := Config.SetstatMode
  88. Config.SetstatMode = 1
  89. fakePath := "fake path"
  90. user := dataprovider.User{
  91. BaseUser: sdk.BaseUser{
  92. HomeDir: os.TempDir(),
  93. },
  94. }
  95. user.Permissions = make(map[string][]string)
  96. user.Permissions["/"] = []string{dataprovider.PermAny}
  97. fs := newMockOsFs(true, "", user.GetHomeDir())
  98. conn := NewBaseConnection("", ProtocolWebDAV, "", "", user)
  99. err := conn.handleChmod(fs, fakePath, fakePath, nil)
  100. assert.NoError(t, err)
  101. err = conn.handleChown(fs, fakePath, fakePath, nil)
  102. assert.NoError(t, err)
  103. err = conn.handleChtimes(fs, fakePath, fakePath, nil)
  104. assert.NoError(t, err)
  105. Config.SetstatMode = 2
  106. err = conn.handleChmod(fs, fakePath, fakePath, nil)
  107. assert.NoError(t, err)
  108. err = conn.handleChtimes(fs, fakePath, fakePath, &StatAttributes{
  109. Atime: time.Now(),
  110. Mtime: time.Now(),
  111. })
  112. assert.NoError(t, err)
  113. Config.SetstatMode = oldSetStatMode
  114. }
  115. func TestRecursiveRenameWalkError(t *testing.T) {
  116. fs := vfs.NewOsFs("", filepath.Clean(os.TempDir()), "")
  117. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{
  118. BaseUser: sdk.BaseUser{
  119. Permissions: map[string][]string{
  120. "/": {dataprovider.PermListItems, dataprovider.PermUpload,
  121. dataprovider.PermDownload, dataprovider.PermRenameDirs},
  122. },
  123. },
  124. })
  125. err := conn.checkRecursiveRenameDirPermissions(fs, fs, filepath.Join(os.TempDir(), "/source"),
  126. filepath.Join(os.TempDir(), "/target"), "/source", "/target",
  127. vfs.NewFileInfo("source", true, 0, time.Now(), false))
  128. assert.ErrorIs(t, err, os.ErrNotExist)
  129. conn.User.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
  130. dataprovider.PermDownload, dataprovider.PermRenameFiles}
  131. // no dir rename permission, the quick check path returns permission error without walking
  132. err = conn.checkRecursiveRenameDirPermissions(fs, fs, filepath.Join(os.TempDir(), "/source"),
  133. filepath.Join(os.TempDir(), "/target"), "/source", "/target",
  134. vfs.NewFileInfo("source", true, 0, time.Now(), false))
  135. if assert.Error(t, err) {
  136. assert.EqualError(t, err, conn.GetPermissionDeniedError().Error())
  137. }
  138. }
  139. func TestCrossRenameFsErrors(t *testing.T) {
  140. fs := vfs.NewOsFs("", os.TempDir(), "")
  141. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{})
  142. res := conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, "missingsource")
  143. assert.False(t, res)
  144. if runtime.GOOS != osWindows {
  145. dirPath := filepath.Join(os.TempDir(), "d")
  146. err := os.Mkdir(dirPath, os.ModePerm)
  147. assert.NoError(t, err)
  148. err = os.Chmod(dirPath, 0001)
  149. assert.NoError(t, err)
  150. res = conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, dirPath)
  151. assert.False(t, res)
  152. err = os.Chmod(dirPath, os.ModePerm)
  153. assert.NoError(t, err)
  154. err = os.Remove(dirPath)
  155. assert.NoError(t, err)
  156. }
  157. }
  158. func TestRenameVirtualFolders(t *testing.T) {
  159. vdir := "/avdir"
  160. u := dataprovider.User{}
  161. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  162. BaseVirtualFolder: vfs.BaseVirtualFolder{
  163. Name: "name",
  164. MappedPath: "mappedPath",
  165. },
  166. VirtualPath: vdir,
  167. })
  168. fs := vfs.NewOsFs("", os.TempDir(), "")
  169. conn := NewBaseConnection("", ProtocolFTP, "", "", u)
  170. res := conn.isRenamePermitted(fs, fs, "source", "target", vdir, "vdirtarget", nil)
  171. assert.False(t, res)
  172. }
  173. func TestRenamePerms(t *testing.T) {
  174. src := "source"
  175. target := "target"
  176. sub := "/sub"
  177. subTarget := sub + "/target"
  178. u := dataprovider.User{}
  179. u.Permissions = map[string][]string{}
  180. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermCreateSymlinks,
  181. dataprovider.PermDeleteFiles}
  182. conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
  183. assert.False(t, conn.hasRenamePerms(src, target, nil))
  184. u.Permissions["/"] = []string{dataprovider.PermRename}
  185. assert.True(t, conn.hasRenamePerms(src, target, nil))
  186. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteFiles,
  187. dataprovider.PermDeleteDirs}
  188. assert.False(t, conn.hasRenamePerms(src, target, nil))
  189. info := vfs.NewFileInfo(src, true, 0, time.Now(), false)
  190. u.Permissions["/"] = []string{dataprovider.PermRenameFiles}
  191. assert.False(t, conn.hasRenamePerms(src, target, info))
  192. u.Permissions["/"] = []string{dataprovider.PermRenameDirs}
  193. assert.True(t, conn.hasRenamePerms(src, target, info))
  194. u.Permissions["/"] = []string{dataprovider.PermRename}
  195. assert.True(t, conn.hasRenamePerms(src, target, info))
  196. u.Permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDeleteDirs}
  197. assert.False(t, conn.hasRenamePerms(src, target, info))
  198. // test with different permissions between source and target
  199. u.Permissions["/"] = []string{dataprovider.PermRename}
  200. u.Permissions[sub] = []string{dataprovider.PermRenameFiles}
  201. assert.False(t, conn.hasRenamePerms(src, subTarget, info))
  202. u.Permissions[sub] = []string{dataprovider.PermRenameDirs}
  203. assert.True(t, conn.hasRenamePerms(src, subTarget, info))
  204. // test files
  205. info = vfs.NewFileInfo(src, false, 0, time.Now(), false)
  206. u.Permissions["/"] = []string{dataprovider.PermRenameDirs}
  207. assert.False(t, conn.hasRenamePerms(src, target, info))
  208. u.Permissions["/"] = []string{dataprovider.PermRenameFiles}
  209. assert.True(t, conn.hasRenamePerms(src, target, info))
  210. u.Permissions["/"] = []string{dataprovider.PermRename}
  211. assert.True(t, conn.hasRenamePerms(src, target, info))
  212. // test with different permissions between source and target
  213. u.Permissions["/"] = []string{dataprovider.PermRename}
  214. u.Permissions[sub] = []string{dataprovider.PermRenameDirs}
  215. assert.False(t, conn.hasRenamePerms(src, subTarget, info))
  216. u.Permissions[sub] = []string{dataprovider.PermRenameFiles}
  217. assert.True(t, conn.hasRenamePerms(src, subTarget, info))
  218. }
  219. func TestUpdateQuotaAfterRename(t *testing.T) {
  220. user := dataprovider.User{
  221. BaseUser: sdk.BaseUser{
  222. Username: userTestUsername,
  223. HomeDir: filepath.Join(os.TempDir(), "home"),
  224. },
  225. }
  226. mappedPath := filepath.Join(os.TempDir(), "vdir")
  227. user.Permissions = make(map[string][]string)
  228. user.Permissions["/"] = []string{dataprovider.PermAny}
  229. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  230. BaseVirtualFolder: vfs.BaseVirtualFolder{
  231. MappedPath: mappedPath,
  232. },
  233. VirtualPath: "/vdir",
  234. QuotaFiles: -1,
  235. QuotaSize: -1,
  236. })
  237. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  238. BaseVirtualFolder: vfs.BaseVirtualFolder{
  239. MappedPath: mappedPath,
  240. },
  241. VirtualPath: "/vdir1",
  242. QuotaFiles: -1,
  243. QuotaSize: -1,
  244. })
  245. err := os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  246. assert.NoError(t, err)
  247. err = os.MkdirAll(mappedPath, os.ModePerm)
  248. assert.NoError(t, err)
  249. fs, err := user.GetFilesystem("id")
  250. assert.NoError(t, err)
  251. c := NewBaseConnection("", ProtocolSFTP, "", "", user)
  252. request := sftp.NewRequest("Rename", "/testfile")
  253. if runtime.GOOS != osWindows {
  254. request.Filepath = "/dir"
  255. request.Target = path.Join("/vdir", "dir")
  256. testDirPath := filepath.Join(mappedPath, "dir")
  257. err := os.MkdirAll(testDirPath, os.ModePerm)
  258. assert.NoError(t, err)
  259. err = os.Chmod(testDirPath, 0001)
  260. assert.NoError(t, err)
  261. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, testDirPath, 0)
  262. assert.Error(t, err)
  263. err = os.Chmod(testDirPath, os.ModePerm)
  264. assert.NoError(t, err)
  265. }
  266. testFile1 := "/testfile1"
  267. request.Target = testFile1
  268. request.Filepath = path.Join("/vdir", "file")
  269. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 0)
  270. assert.Error(t, err)
  271. err = os.WriteFile(filepath.Join(mappedPath, "file"), []byte("test content"), os.ModePerm)
  272. assert.NoError(t, err)
  273. request.Filepath = testFile1
  274. request.Target = path.Join("/vdir", "file")
  275. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  276. assert.NoError(t, err)
  277. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "testfile1"), []byte("test content"), os.ModePerm)
  278. assert.NoError(t, err)
  279. request.Target = testFile1
  280. request.Filepath = path.Join("/vdir", "file")
  281. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  282. assert.NoError(t, err)
  283. request.Target = path.Join("/vdir1", "file")
  284. request.Filepath = path.Join("/vdir", "file")
  285. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12)
  286. assert.NoError(t, err)
  287. err = os.RemoveAll(mappedPath)
  288. assert.NoError(t, err)
  289. err = os.RemoveAll(user.GetHomeDir())
  290. assert.NoError(t, err)
  291. }
  292. func TestErrorsMapping(t *testing.T) {
  293. fs := vfs.NewOsFs("", os.TempDir(), "")
  294. conn := NewBaseConnection("", ProtocolSFTP, "", "", dataprovider.User{BaseUser: sdk.BaseUser{HomeDir: os.TempDir()}})
  295. osErrorsProtocols := []string{ProtocolWebDAV, ProtocolFTP, ProtocolHTTP, ProtocolHTTPShare,
  296. ProtocolDataRetention, ProtocolOIDC, protocolEventAction}
  297. for _, protocol := range supportedProtocols {
  298. conn.SetProtocol(protocol)
  299. err := conn.GetFsError(fs, os.ErrNotExist)
  300. if protocol == ProtocolSFTP {
  301. assert.ErrorIs(t, err, sftp.ErrSSHFxNoSuchFile)
  302. } else if util.Contains(osErrorsProtocols, protocol) {
  303. assert.EqualError(t, err, os.ErrNotExist.Error())
  304. } else {
  305. assert.EqualError(t, err, ErrNotExist.Error())
  306. }
  307. err = conn.GetFsError(fs, os.ErrPermission)
  308. if protocol == ProtocolSFTP {
  309. assert.EqualError(t, err, sftp.ErrSSHFxPermissionDenied.Error())
  310. } else {
  311. assert.EqualError(t, err, ErrPermissionDenied.Error())
  312. }
  313. err = conn.GetFsError(fs, os.ErrClosed)
  314. if protocol == ProtocolSFTP {
  315. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  316. } else {
  317. assert.EqualError(t, err, ErrGenericFailure.Error())
  318. }
  319. err = conn.GetFsError(fs, ErrPermissionDenied)
  320. if protocol == ProtocolSFTP {
  321. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  322. } else {
  323. assert.EqualError(t, err, ErrPermissionDenied.Error())
  324. }
  325. err = conn.GetFsError(fs, vfs.ErrVfsUnsupported)
  326. if protocol == ProtocolSFTP {
  327. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  328. } else {
  329. assert.EqualError(t, err, ErrOpUnsupported.Error())
  330. }
  331. err = conn.GetFsError(fs, vfs.ErrStorageSizeUnavailable)
  332. if protocol == ProtocolSFTP {
  333. assert.ErrorIs(t, err, sftp.ErrSSHFxOpUnsupported)
  334. assert.Contains(t, err.Error(), vfs.ErrStorageSizeUnavailable.Error())
  335. } else {
  336. assert.EqualError(t, err, vfs.ErrStorageSizeUnavailable.Error())
  337. }
  338. err = conn.GetQuotaExceededError()
  339. assert.True(t, conn.IsQuotaExceededError(err))
  340. err = conn.GetReadQuotaExceededError()
  341. if protocol == ProtocolSFTP {
  342. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  343. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  344. } else {
  345. assert.ErrorIs(t, err, ErrReadQuotaExceeded)
  346. }
  347. err = conn.GetNotExistError()
  348. assert.True(t, conn.IsNotExistError(err))
  349. err = conn.GetFsError(fs, nil)
  350. assert.NoError(t, err)
  351. err = conn.GetOpUnsupportedError()
  352. if protocol == ProtocolSFTP {
  353. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  354. } else {
  355. assert.EqualError(t, err, ErrOpUnsupported.Error())
  356. }
  357. }
  358. }
  359. func TestMaxWriteSize(t *testing.T) {
  360. permissions := make(map[string][]string)
  361. permissions["/"] = []string{dataprovider.PermAny}
  362. user := dataprovider.User{
  363. BaseUser: sdk.BaseUser{
  364. Username: userTestUsername,
  365. Permissions: permissions,
  366. HomeDir: filepath.Clean(os.TempDir()),
  367. },
  368. }
  369. fs, err := user.GetFilesystem("123")
  370. assert.NoError(t, err)
  371. conn := NewBaseConnection("", ProtocolFTP, "", "", user)
  372. quotaResult := vfs.QuotaCheckResult{
  373. HasSpace: true,
  374. }
  375. size, err := conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  376. assert.NoError(t, err)
  377. assert.Equal(t, int64(0), size)
  378. conn.User.Filters.MaxUploadFileSize = 100
  379. size, err = conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  380. assert.NoError(t, err)
  381. assert.Equal(t, int64(100), size)
  382. quotaResult.QuotaSize = 1000
  383. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  384. assert.NoError(t, err)
  385. assert.Equal(t, int64(100), size)
  386. quotaResult.QuotaSize = 1000
  387. quotaResult.UsedSize = 990
  388. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  389. assert.NoError(t, err)
  390. assert.Equal(t, int64(60), size)
  391. quotaResult.QuotaSize = 0
  392. quotaResult.UsedSize = 0
  393. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  394. assert.True(t, conn.IsQuotaExceededError(err))
  395. assert.Equal(t, int64(0), size)
  396. size, err = conn.GetMaxWriteSize(quotaResult, true, 10, fs.IsUploadResumeSupported())
  397. assert.NoError(t, err)
  398. assert.Equal(t, int64(90), size)
  399. fs = newMockOsFs(true, fs.ConnectionID(), user.GetHomeDir())
  400. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  401. assert.EqualError(t, err, ErrOpUnsupported.Error())
  402. assert.Equal(t, int64(0), size)
  403. }
  404. func TestCheckParentDirsErrors(t *testing.T) {
  405. permissions := make(map[string][]string)
  406. permissions["/"] = []string{dataprovider.PermAny}
  407. user := dataprovider.User{
  408. BaseUser: sdk.BaseUser{
  409. Username: userTestUsername,
  410. Permissions: permissions,
  411. HomeDir: filepath.Clean(os.TempDir()),
  412. },
  413. FsConfig: vfs.Filesystem{
  414. Provider: sdk.CryptedFilesystemProvider,
  415. },
  416. }
  417. c := NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  418. err := c.CheckParentDirs("/a/dir")
  419. assert.Error(t, err)
  420. user.FsConfig.Provider = sdk.LocalFilesystemProvider
  421. user.VirtualFolders = nil
  422. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  423. BaseVirtualFolder: vfs.BaseVirtualFolder{
  424. FsConfig: vfs.Filesystem{
  425. Provider: sdk.CryptedFilesystemProvider,
  426. },
  427. },
  428. VirtualPath: "/vdir",
  429. })
  430. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  431. BaseVirtualFolder: vfs.BaseVirtualFolder{
  432. MappedPath: filepath.Clean(os.TempDir()),
  433. },
  434. VirtualPath: "/vdir/sub",
  435. })
  436. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  437. err = c.CheckParentDirs("/vdir/sub/dir")
  438. assert.Error(t, err)
  439. user = dataprovider.User{
  440. BaseUser: sdk.BaseUser{
  441. Username: userTestUsername,
  442. Permissions: permissions,
  443. HomeDir: filepath.Clean(os.TempDir()),
  444. },
  445. FsConfig: vfs.Filesystem{
  446. Provider: sdk.S3FilesystemProvider,
  447. S3Config: vfs.S3FsConfig{
  448. BaseS3FsConfig: sdk.BaseS3FsConfig{
  449. Bucket: "buck",
  450. Region: "us-east-1",
  451. AccessKey: "key",
  452. },
  453. AccessSecret: kms.NewPlainSecret("s3secret"),
  454. },
  455. },
  456. }
  457. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  458. err = c.CheckParentDirs("/a/dir")
  459. assert.NoError(t, err)
  460. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  461. BaseVirtualFolder: vfs.BaseVirtualFolder{
  462. MappedPath: filepath.Clean(os.TempDir()),
  463. },
  464. VirtualPath: "/local/dir",
  465. })
  466. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  467. err = c.CheckParentDirs("/local/dir/sub-dir")
  468. assert.NoError(t, err)
  469. err = os.RemoveAll(filepath.Join(os.TempDir(), "sub-dir"))
  470. assert.NoError(t, err)
  471. }