connection_test.go 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. // Copyright (C) 2019 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. "fmt"
  18. "io"
  19. "os"
  20. "path"
  21. "path/filepath"
  22. "runtime"
  23. "strconv"
  24. "testing"
  25. "time"
  26. "github.com/pkg/sftp"
  27. "github.com/rs/xid"
  28. "github.com/sftpgo/sdk"
  29. "github.com/stretchr/testify/assert"
  30. "github.com/stretchr/testify/require"
  31. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  32. "github.com/drakkan/sftpgo/v2/internal/kms"
  33. "github.com/drakkan/sftpgo/v2/internal/util"
  34. "github.com/drakkan/sftpgo/v2/internal/vfs"
  35. )
  36. var (
  37. errWalkDir = errors.New("err walk dir")
  38. )
  39. // MockOsFs mockable OsFs
  40. type MockOsFs struct {
  41. vfs.Fs
  42. hasVirtualFolders bool
  43. name string
  44. err error
  45. }
  46. // Name returns the name for the Fs implementation
  47. func (fs *MockOsFs) Name() string {
  48. if fs.name != "" {
  49. return fs.name
  50. }
  51. return "mockOsFs"
  52. }
  53. // HasVirtualFolders returns true if folders are emulated
  54. func (fs *MockOsFs) HasVirtualFolders() bool {
  55. return fs.hasVirtualFolders
  56. }
  57. func (fs *MockOsFs) IsUploadResumeSupported() bool {
  58. return !fs.hasVirtualFolders
  59. }
  60. func (fs *MockOsFs) Chtimes(_ string, _, _ time.Time, _ bool) error {
  61. return vfs.ErrVfsUnsupported
  62. }
  63. func (fs *MockOsFs) Lstat(name string) (os.FileInfo, error) {
  64. if fs.err != nil {
  65. return nil, fs.err
  66. }
  67. return fs.Fs.Lstat(name)
  68. }
  69. // Walk returns a duplicate path for testing
  70. func (fs *MockOsFs) Walk(_ string, walkFn filepath.WalkFunc) error {
  71. if fs.err == errWalkDir {
  72. walkFn("fsdpath", vfs.NewFileInfo("dpath", true, 0, time.Now(), false), nil) //nolint:errcheck
  73. return walkFn("fsdpath", vfs.NewFileInfo("dpath", true, 0, time.Now(), false), nil) //nolint:errcheck
  74. }
  75. walkFn("fsfpath", vfs.NewFileInfo("fpath", false, 0, time.Now(), false), nil) //nolint:errcheck
  76. return fs.err
  77. }
  78. func newMockOsFs(hasVirtualFolders bool, connectionID, rootDir, name string, err error) vfs.Fs {
  79. return &MockOsFs{
  80. Fs: vfs.NewOsFs(connectionID, rootDir, "", nil),
  81. name: name,
  82. hasVirtualFolders: hasVirtualFolders,
  83. err: err,
  84. }
  85. }
  86. func TestRemoveErrors(t *testing.T) {
  87. mappedPath := filepath.Join(os.TempDir(), "map")
  88. homePath := filepath.Join(os.TempDir(), "home")
  89. user := dataprovider.User{
  90. BaseUser: sdk.BaseUser{
  91. Username: "remove_errors_user",
  92. HomeDir: homePath,
  93. },
  94. VirtualFolders: []vfs.VirtualFolder{
  95. {
  96. BaseVirtualFolder: vfs.BaseVirtualFolder{
  97. Name: filepath.Base(mappedPath),
  98. MappedPath: mappedPath,
  99. },
  100. VirtualPath: "/virtualpath",
  101. },
  102. },
  103. }
  104. user.Permissions = make(map[string][]string)
  105. user.Permissions["/"] = []string{dataprovider.PermAny}
  106. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  107. conn := NewBaseConnection("", ProtocolFTP, "", "", user)
  108. err := conn.IsRemoveDirAllowed(fs, mappedPath, "/virtualpath1")
  109. if assert.Error(t, err) {
  110. assert.Contains(t, err.Error(), "permission denied")
  111. }
  112. err = conn.RemoveFile(fs, filepath.Join(homePath, "missing_file"), "/missing_file",
  113. vfs.NewFileInfo("info", false, 100, time.Now(), false))
  114. assert.Error(t, err)
  115. }
  116. func TestSetStatMode(t *testing.T) {
  117. oldSetStatMode := Config.SetstatMode
  118. Config.SetstatMode = 1
  119. fakePath := "fake path"
  120. user := dataprovider.User{
  121. BaseUser: sdk.BaseUser{
  122. HomeDir: os.TempDir(),
  123. },
  124. }
  125. user.Permissions = make(map[string][]string)
  126. user.Permissions["/"] = []string{dataprovider.PermAny}
  127. fs := newMockOsFs(true, "", user.GetHomeDir(), "", nil)
  128. conn := NewBaseConnection("", ProtocolWebDAV, "", "", user)
  129. err := conn.handleChmod(fs, fakePath, fakePath, nil)
  130. assert.NoError(t, err)
  131. err = conn.handleChown(fs, fakePath, fakePath, nil)
  132. assert.NoError(t, err)
  133. err = conn.handleChtimes(fs, fakePath, fakePath, nil)
  134. assert.NoError(t, err)
  135. Config.SetstatMode = 2
  136. err = conn.handleChmod(fs, fakePath, fakePath, nil)
  137. assert.NoError(t, err)
  138. err = conn.handleChtimes(fs, fakePath, fakePath, &StatAttributes{
  139. Atime: time.Now(),
  140. Mtime: time.Now(),
  141. })
  142. assert.NoError(t, err)
  143. Config.SetstatMode = oldSetStatMode
  144. }
  145. func TestRecursiveRenameWalkError(t *testing.T) {
  146. fs := vfs.NewOsFs("", filepath.Clean(os.TempDir()), "", nil)
  147. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{
  148. BaseUser: sdk.BaseUser{
  149. Permissions: map[string][]string{
  150. "/": {dataprovider.PermListItems, dataprovider.PermUpload,
  151. dataprovider.PermDownload, dataprovider.PermRenameDirs},
  152. },
  153. },
  154. })
  155. err := conn.checkRecursiveRenameDirPermissions(fs, fs, filepath.Join(os.TempDir(), "/source"),
  156. filepath.Join(os.TempDir(), "/target"), "/source", "/target",
  157. vfs.NewFileInfo("source", true, 0, time.Now(), false))
  158. assert.ErrorIs(t, err, os.ErrNotExist)
  159. fs = newMockOsFs(false, "mockID", filepath.Clean(os.TempDir()), "S3Fs", errWalkDir)
  160. err = conn.checkRecursiveRenameDirPermissions(fs, fs, filepath.Join(os.TempDir(), "/source"),
  161. filepath.Join(os.TempDir(), "/target"), "/source", "/target",
  162. vfs.NewFileInfo("source", true, 0, time.Now(), false))
  163. if assert.Error(t, err) {
  164. assert.Equal(t, err.Error(), conn.GetOpUnsupportedError().Error())
  165. }
  166. conn.User.Permissions["/"] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
  167. dataprovider.PermDownload, dataprovider.PermRenameFiles}
  168. // no dir rename permission, the quick check path returns permission error without walking
  169. err = conn.checkRecursiveRenameDirPermissions(fs, fs, filepath.Join(os.TempDir(), "/source"),
  170. filepath.Join(os.TempDir(), "/target"), "/source", "/target",
  171. vfs.NewFileInfo("source", true, 0, time.Now(), false))
  172. if assert.Error(t, err) {
  173. assert.EqualError(t, err, conn.GetPermissionDeniedError().Error())
  174. }
  175. }
  176. func TestCrossRenameFsErrors(t *testing.T) {
  177. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  178. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{})
  179. res := conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, "missingsource")
  180. assert.False(t, res)
  181. if runtime.GOOS != osWindows {
  182. dirPath := filepath.Join(os.TempDir(), "d")
  183. err := os.Mkdir(dirPath, os.ModePerm)
  184. assert.NoError(t, err)
  185. err = os.Chmod(dirPath, 0001)
  186. assert.NoError(t, err)
  187. res = conn.hasSpaceForCrossRename(fs, vfs.QuotaCheckResult{}, 1, dirPath)
  188. assert.False(t, res)
  189. err = os.Chmod(dirPath, os.ModePerm)
  190. assert.NoError(t, err)
  191. err = os.Remove(dirPath)
  192. assert.NoError(t, err)
  193. }
  194. }
  195. func TestRenameVirtualFolders(t *testing.T) {
  196. vdir := "/avdir"
  197. u := dataprovider.User{}
  198. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  199. BaseVirtualFolder: vfs.BaseVirtualFolder{
  200. Name: "name",
  201. MappedPath: "mappedPath",
  202. },
  203. VirtualPath: vdir,
  204. })
  205. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  206. conn := NewBaseConnection("", ProtocolFTP, "", "", u)
  207. res := conn.isRenamePermitted(fs, fs, "source", "target", vdir, "vdirtarget", nil)
  208. assert.False(t, res)
  209. }
  210. func TestRenamePerms(t *testing.T) {
  211. src := "source"
  212. target := "target"
  213. sub := "/sub"
  214. subTarget := sub + "/target"
  215. u := dataprovider.User{}
  216. u.Permissions = map[string][]string{}
  217. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermCreateSymlinks,
  218. dataprovider.PermDeleteFiles}
  219. conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
  220. assert.False(t, conn.hasRenamePerms(src, target, nil))
  221. u.Permissions["/"] = []string{dataprovider.PermRename}
  222. assert.True(t, conn.hasRenamePerms(src, target, nil))
  223. u.Permissions["/"] = []string{dataprovider.PermCreateDirs, dataprovider.PermUpload, dataprovider.PermDeleteFiles,
  224. dataprovider.PermDeleteDirs}
  225. assert.False(t, conn.hasRenamePerms(src, target, nil))
  226. info := vfs.NewFileInfo(src, true, 0, time.Now(), false)
  227. u.Permissions["/"] = []string{dataprovider.PermRenameFiles}
  228. assert.False(t, conn.hasRenamePerms(src, target, info))
  229. u.Permissions["/"] = []string{dataprovider.PermRenameDirs}
  230. assert.True(t, conn.hasRenamePerms(src, target, info))
  231. u.Permissions["/"] = []string{dataprovider.PermRename}
  232. assert.True(t, conn.hasRenamePerms(src, target, info))
  233. u.Permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermDeleteDirs}
  234. assert.False(t, conn.hasRenamePerms(src, target, info))
  235. // test with different permissions between source and target
  236. u.Permissions["/"] = []string{dataprovider.PermRename}
  237. u.Permissions[sub] = []string{dataprovider.PermRenameFiles}
  238. assert.False(t, conn.hasRenamePerms(src, subTarget, info))
  239. u.Permissions[sub] = []string{dataprovider.PermRenameDirs}
  240. assert.True(t, conn.hasRenamePerms(src, subTarget, info))
  241. // test files
  242. info = vfs.NewFileInfo(src, false, 0, time.Now(), false)
  243. u.Permissions["/"] = []string{dataprovider.PermRenameDirs}
  244. assert.False(t, conn.hasRenamePerms(src, target, info))
  245. u.Permissions["/"] = []string{dataprovider.PermRenameFiles}
  246. assert.True(t, conn.hasRenamePerms(src, target, info))
  247. u.Permissions["/"] = []string{dataprovider.PermRename}
  248. assert.True(t, conn.hasRenamePerms(src, target, info))
  249. // test with different permissions between source and target
  250. u.Permissions["/"] = []string{dataprovider.PermRename}
  251. u.Permissions[sub] = []string{dataprovider.PermRenameDirs}
  252. assert.False(t, conn.hasRenamePerms(src, subTarget, info))
  253. u.Permissions[sub] = []string{dataprovider.PermRenameFiles}
  254. assert.True(t, conn.hasRenamePerms(src, subTarget, info))
  255. }
  256. func TestRenameNestedFolders(t *testing.T) {
  257. u := dataprovider.User{}
  258. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  259. BaseVirtualFolder: vfs.BaseVirtualFolder{
  260. Name: "vfolder",
  261. MappedPath: filepath.Join(os.TempDir(), "f"),
  262. },
  263. VirtualPath: "/vdirs/f",
  264. })
  265. conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
  266. err := conn.checkFolderRename(nil, nil, filepath.Clean(os.TempDir()), filepath.Join(os.TempDir(), "subdir"), "/src", "/dst", nil)
  267. assert.Error(t, err)
  268. err = conn.checkFolderRename(nil, nil, filepath.Join(os.TempDir(), "subdir"), filepath.Clean(os.TempDir()), "/src", "/dst", nil)
  269. assert.Error(t, err)
  270. err = conn.checkFolderRename(nil, nil, "", "", "/src/sub", "/src", nil)
  271. assert.Error(t, err)
  272. err = conn.checkFolderRename(nil, nil, filepath.Join(os.TempDir(), "src"), filepath.Join(os.TempDir(), "vdirs"), "/src", "/vdirs", nil)
  273. assert.Error(t, err)
  274. }
  275. func TestUpdateQuotaAfterRename(t *testing.T) {
  276. user := dataprovider.User{
  277. BaseUser: sdk.BaseUser{
  278. Username: userTestUsername,
  279. HomeDir: filepath.Join(os.TempDir(), "home"),
  280. },
  281. }
  282. mappedPath := filepath.Join(os.TempDir(), "vdir")
  283. user.Permissions = make(map[string][]string)
  284. user.Permissions["/"] = []string{dataprovider.PermAny}
  285. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  286. BaseVirtualFolder: vfs.BaseVirtualFolder{
  287. MappedPath: mappedPath,
  288. },
  289. VirtualPath: "/vdir",
  290. QuotaFiles: -1,
  291. QuotaSize: -1,
  292. })
  293. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  294. BaseVirtualFolder: vfs.BaseVirtualFolder{
  295. MappedPath: mappedPath,
  296. },
  297. VirtualPath: "/vdir1",
  298. QuotaFiles: -1,
  299. QuotaSize: -1,
  300. })
  301. err := os.MkdirAll(user.GetHomeDir(), os.ModePerm)
  302. assert.NoError(t, err)
  303. err = os.MkdirAll(mappedPath, os.ModePerm)
  304. assert.NoError(t, err)
  305. fs, err := user.GetFilesystem("id")
  306. assert.NoError(t, err)
  307. c := NewBaseConnection("", ProtocolSFTP, "", "", user)
  308. request := sftp.NewRequest("Rename", "/testfile")
  309. if runtime.GOOS != osWindows {
  310. request.Filepath = "/dir"
  311. request.Target = path.Join("/vdir", "dir")
  312. testDirPath := filepath.Join(mappedPath, "dir")
  313. err := os.MkdirAll(testDirPath, os.ModePerm)
  314. assert.NoError(t, err)
  315. err = os.Chmod(testDirPath, 0001)
  316. assert.NoError(t, err)
  317. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, testDirPath, 0, -1, -1)
  318. assert.Error(t, err)
  319. err = os.Chmod(testDirPath, os.ModePerm)
  320. assert.NoError(t, err)
  321. }
  322. testFile1 := "/testfile1"
  323. request.Target = testFile1
  324. request.Filepath = path.Join("/vdir", "file")
  325. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 0, -1, -1)
  326. assert.Error(t, err)
  327. err = os.WriteFile(filepath.Join(mappedPath, "file"), []byte("test content"), os.ModePerm)
  328. assert.NoError(t, err)
  329. request.Filepath = testFile1
  330. request.Target = path.Join("/vdir", "file")
  331. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12, -1, -1)
  332. assert.NoError(t, err)
  333. err = os.WriteFile(filepath.Join(user.GetHomeDir(), "testfile1"), []byte("test content"), os.ModePerm)
  334. assert.NoError(t, err)
  335. request.Target = testFile1
  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. request.Target = path.Join("/vdir1", "file")
  340. request.Filepath = path.Join("/vdir", "file")
  341. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12, -1, -1)
  342. assert.NoError(t, err)
  343. err = c.updateQuotaAfterRename(fs, request.Filepath, request.Target, filepath.Join(mappedPath, "file"), 12, 1, 100)
  344. assert.NoError(t, err)
  345. err = os.RemoveAll(mappedPath)
  346. assert.NoError(t, err)
  347. err = os.RemoveAll(user.GetHomeDir())
  348. assert.NoError(t, err)
  349. }
  350. func TestErrorsMapping(t *testing.T) {
  351. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  352. conn := NewBaseConnection("", ProtocolSFTP, "", "", dataprovider.User{BaseUser: sdk.BaseUser{HomeDir: os.TempDir()}})
  353. osErrorsProtocols := []string{ProtocolWebDAV, ProtocolFTP, ProtocolHTTP, ProtocolHTTPShare,
  354. ProtocolDataRetention, ProtocolOIDC, protocolEventAction}
  355. for _, protocol := range supportedProtocols {
  356. conn.SetProtocol(protocol)
  357. err := conn.GetFsError(fs, os.ErrNotExist)
  358. if protocol == ProtocolSFTP {
  359. assert.ErrorIs(t, err, sftp.ErrSSHFxNoSuchFile)
  360. } else if util.Contains(osErrorsProtocols, protocol) {
  361. assert.EqualError(t, err, os.ErrNotExist.Error())
  362. } else {
  363. assert.EqualError(t, err, ErrNotExist.Error())
  364. }
  365. err = conn.GetFsError(fs, os.ErrPermission)
  366. if protocol == ProtocolSFTP {
  367. assert.EqualError(t, err, sftp.ErrSSHFxPermissionDenied.Error())
  368. } else {
  369. assert.EqualError(t, err, ErrPermissionDenied.Error())
  370. }
  371. err = conn.GetFsError(fs, os.ErrClosed)
  372. if protocol == ProtocolSFTP {
  373. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  374. } else {
  375. assert.EqualError(t, err, ErrGenericFailure.Error())
  376. }
  377. err = conn.GetFsError(fs, ErrPermissionDenied)
  378. if protocol == ProtocolSFTP {
  379. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  380. } else {
  381. assert.EqualError(t, err, ErrPermissionDenied.Error())
  382. }
  383. err = conn.GetFsError(fs, vfs.ErrVfsUnsupported)
  384. if protocol == ProtocolSFTP {
  385. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  386. } else {
  387. assert.EqualError(t, err, ErrOpUnsupported.Error())
  388. }
  389. err = conn.GetFsError(fs, vfs.ErrStorageSizeUnavailable)
  390. if protocol == ProtocolSFTP {
  391. assert.ErrorIs(t, err, sftp.ErrSSHFxOpUnsupported)
  392. assert.Contains(t, err.Error(), vfs.ErrStorageSizeUnavailable.Error())
  393. } else {
  394. assert.EqualError(t, err, vfs.ErrStorageSizeUnavailable.Error())
  395. }
  396. err = conn.GetQuotaExceededError()
  397. assert.True(t, conn.IsQuotaExceededError(err))
  398. err = conn.GetReadQuotaExceededError()
  399. if protocol == ProtocolSFTP {
  400. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  401. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  402. } else {
  403. assert.ErrorIs(t, err, ErrReadQuotaExceeded)
  404. }
  405. err = conn.GetNotExistError()
  406. assert.True(t, conn.IsNotExistError(err))
  407. err = conn.GetFsError(fs, nil)
  408. assert.NoError(t, err)
  409. err = conn.GetOpUnsupportedError()
  410. if protocol == ProtocolSFTP {
  411. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  412. } else {
  413. assert.EqualError(t, err, ErrOpUnsupported.Error())
  414. }
  415. err = conn.GetFsError(fs, ErrShuttingDown)
  416. if protocol == ProtocolSFTP {
  417. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  418. assert.Contains(t, err.Error(), ErrShuttingDown.Error())
  419. } else {
  420. assert.EqualError(t, err, ErrShuttingDown.Error())
  421. }
  422. }
  423. }
  424. func TestMaxWriteSize(t *testing.T) {
  425. permissions := make(map[string][]string)
  426. permissions["/"] = []string{dataprovider.PermAny}
  427. user := dataprovider.User{
  428. BaseUser: sdk.BaseUser{
  429. Username: userTestUsername,
  430. Permissions: permissions,
  431. HomeDir: filepath.Clean(os.TempDir()),
  432. },
  433. }
  434. fs, err := user.GetFilesystem("123")
  435. assert.NoError(t, err)
  436. conn := NewBaseConnection("", ProtocolFTP, "", "", user)
  437. quotaResult := vfs.QuotaCheckResult{
  438. HasSpace: true,
  439. }
  440. size, err := conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  441. assert.NoError(t, err)
  442. assert.Equal(t, int64(0), size)
  443. conn.User.Filters.MaxUploadFileSize = 100
  444. size, err = conn.GetMaxWriteSize(quotaResult, false, 0, fs.IsUploadResumeSupported())
  445. assert.NoError(t, err)
  446. assert.Equal(t, int64(100), size)
  447. quotaResult.QuotaSize = 1000
  448. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  449. assert.NoError(t, err)
  450. assert.Equal(t, int64(100), size)
  451. quotaResult.QuotaSize = 1000
  452. quotaResult.UsedSize = 990
  453. size, err = conn.GetMaxWriteSize(quotaResult, false, 50, fs.IsUploadResumeSupported())
  454. assert.NoError(t, err)
  455. assert.Equal(t, int64(60), size)
  456. quotaResult.QuotaSize = 0
  457. quotaResult.UsedSize = 0
  458. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  459. assert.True(t, conn.IsQuotaExceededError(err))
  460. assert.Equal(t, int64(0), size)
  461. size, err = conn.GetMaxWriteSize(quotaResult, true, 10, fs.IsUploadResumeSupported())
  462. assert.NoError(t, err)
  463. assert.Equal(t, int64(90), size)
  464. fs = newMockOsFs(true, fs.ConnectionID(), user.GetHomeDir(), "", nil)
  465. size, err = conn.GetMaxWriteSize(quotaResult, true, 100, fs.IsUploadResumeSupported())
  466. assert.EqualError(t, err, ErrOpUnsupported.Error())
  467. assert.Equal(t, int64(0), size)
  468. }
  469. func TestCheckParentDirsErrors(t *testing.T) {
  470. permissions := make(map[string][]string)
  471. permissions["/"] = []string{dataprovider.PermAny}
  472. user := dataprovider.User{
  473. BaseUser: sdk.BaseUser{
  474. Username: userTestUsername,
  475. Permissions: permissions,
  476. HomeDir: filepath.Clean(os.TempDir()),
  477. },
  478. FsConfig: vfs.Filesystem{
  479. Provider: sdk.CryptedFilesystemProvider,
  480. },
  481. }
  482. c := NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  483. err := c.CheckParentDirs("/a/dir")
  484. assert.Error(t, err)
  485. user.FsConfig.Provider = sdk.LocalFilesystemProvider
  486. user.VirtualFolders = nil
  487. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  488. BaseVirtualFolder: vfs.BaseVirtualFolder{
  489. FsConfig: vfs.Filesystem{
  490. Provider: sdk.CryptedFilesystemProvider,
  491. },
  492. },
  493. VirtualPath: "/vdir",
  494. })
  495. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  496. BaseVirtualFolder: vfs.BaseVirtualFolder{
  497. MappedPath: filepath.Clean(os.TempDir()),
  498. },
  499. VirtualPath: "/vdir/sub",
  500. })
  501. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  502. err = c.CheckParentDirs("/vdir/sub/dir")
  503. assert.Error(t, err)
  504. user = dataprovider.User{
  505. BaseUser: sdk.BaseUser{
  506. Username: userTestUsername,
  507. Permissions: permissions,
  508. HomeDir: filepath.Clean(os.TempDir()),
  509. },
  510. FsConfig: vfs.Filesystem{
  511. Provider: sdk.S3FilesystemProvider,
  512. S3Config: vfs.S3FsConfig{
  513. BaseS3FsConfig: sdk.BaseS3FsConfig{
  514. Bucket: "buck",
  515. Region: "us-east-1",
  516. AccessKey: "key",
  517. },
  518. AccessSecret: kms.NewPlainSecret("s3secret"),
  519. },
  520. },
  521. }
  522. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  523. err = c.CheckParentDirs("/a/dir")
  524. assert.NoError(t, err)
  525. user.VirtualFolders = append(user.VirtualFolders, vfs.VirtualFolder{
  526. BaseVirtualFolder: vfs.BaseVirtualFolder{
  527. MappedPath: filepath.Clean(os.TempDir()),
  528. },
  529. VirtualPath: "/local/dir",
  530. })
  531. c = NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  532. err = c.CheckParentDirs("/local/dir/sub-dir")
  533. assert.NoError(t, err)
  534. err = os.RemoveAll(filepath.Join(os.TempDir(), "sub-dir"))
  535. assert.NoError(t, err)
  536. }
  537. func TestErrorResolvePath(t *testing.T) {
  538. u := dataprovider.User{
  539. BaseUser: sdk.BaseUser{
  540. HomeDir: filepath.Join(os.TempDir(), "u"),
  541. Status: 1,
  542. Permissions: map[string][]string{
  543. "/": {dataprovider.PermAny},
  544. },
  545. },
  546. }
  547. u.FsConfig.Provider = sdk.GCSFilesystemProvider
  548. u.FsConfig.GCSConfig.Bucket = "test"
  549. u.FsConfig.GCSConfig.Credentials = kms.NewPlainSecret("invalid JSON for credentials")
  550. u.VirtualFolders = []vfs.VirtualFolder{
  551. {
  552. BaseVirtualFolder: vfs.BaseVirtualFolder{
  553. Name: "f",
  554. MappedPath: filepath.Join(os.TempDir(), "f"),
  555. },
  556. VirtualPath: "/f",
  557. },
  558. }
  559. conn := NewBaseConnection("", ProtocolSFTP, "", "", u)
  560. err := conn.doRecursiveRemoveDirEntry("/vpath", nil, 0)
  561. assert.Error(t, err)
  562. err = conn.doRecursiveRemove(nil, "/fspath", "/vpath", vfs.NewFileInfo("vpath", true, 0, time.Now(), false), 2000)
  563. assert.Error(t, err, util.ErrRecursionTooDeep)
  564. err = conn.checkCopy(vfs.NewFileInfo("name", true, 0, time.Unix(0, 0), false), nil, "/source", "/target")
  565. assert.Error(t, err)
  566. sourceFile := filepath.Join(os.TempDir(), "f", "source")
  567. err = os.MkdirAll(filepath.Dir(sourceFile), os.ModePerm)
  568. assert.NoError(t, err)
  569. err = os.WriteFile(sourceFile, []byte(""), 0666)
  570. assert.NoError(t, err)
  571. err = conn.checkCopy(vfs.NewFileInfo("name", true, 0, time.Unix(0, 0), false), nil, "/f/source", "/target")
  572. assert.Error(t, err)
  573. 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")
  574. assert.Error(t, err)
  575. err = os.RemoveAll(filepath.Dir(sourceFile))
  576. assert.NoError(t, err)
  577. }
  578. func TestConnectionKeepAlive(t *testing.T) {
  579. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{})
  580. lastActivity := conn.GetLastActivity()
  581. done := make(chan bool)
  582. go func() {
  583. time.Sleep(200 * time.Millisecond)
  584. close(done)
  585. }()
  586. keepConnectionAlive(conn, done, 50*time.Millisecond)
  587. assert.Greater(t, conn.GetLastActivity(), lastActivity)
  588. }
  589. func TestFsFileCopier(t *testing.T) {
  590. fs := vfs.Fs(&vfs.AzureBlobFs{})
  591. _, ok := fs.(vfs.FsFileCopier)
  592. assert.True(t, ok)
  593. fs = vfs.Fs(&vfs.OsFs{})
  594. _, ok = fs.(vfs.FsFileCopier)
  595. assert.False(t, ok)
  596. fs = vfs.Fs(&vfs.SFTPFs{})
  597. _, ok = fs.(vfs.FsFileCopier)
  598. assert.False(t, ok)
  599. fs = vfs.Fs(&vfs.GCSFs{})
  600. _, ok = fs.(vfs.FsFileCopier)
  601. assert.True(t, ok)
  602. fs = vfs.Fs(&vfs.S3Fs{})
  603. _, ok = fs.(vfs.FsFileCopier)
  604. assert.True(t, ok)
  605. }
  606. func TestFilePatterns(t *testing.T) {
  607. filters := dataprovider.UserFilters{
  608. BaseUserFilters: sdk.BaseUserFilters{
  609. FilePatterns: []sdk.PatternsFilter{
  610. {
  611. Path: "/dir1",
  612. DenyPolicy: sdk.DenyPolicyDefault,
  613. AllowedPatterns: []string{"*.jpg"},
  614. },
  615. {
  616. Path: "/dir2",
  617. DenyPolicy: sdk.DenyPolicyHide,
  618. AllowedPatterns: []string{"*.jpg"},
  619. },
  620. {
  621. Path: "/dir3",
  622. DenyPolicy: sdk.DenyPolicyDefault,
  623. DeniedPatterns: []string{"*.jpg"},
  624. },
  625. {
  626. Path: "/dir4",
  627. DenyPolicy: sdk.DenyPolicyHide,
  628. DeniedPatterns: []string{"*"},
  629. },
  630. },
  631. },
  632. }
  633. virtualFolders := []vfs.VirtualFolder{
  634. {
  635. VirtualPath: "/dir1/vdir1",
  636. },
  637. {
  638. VirtualPath: "/dir1/vdir2",
  639. },
  640. {
  641. VirtualPath: "/dir1/vdir3",
  642. },
  643. {
  644. VirtualPath: "/dir2/vdir1",
  645. },
  646. {
  647. VirtualPath: "/dir2/vdir2",
  648. },
  649. {
  650. VirtualPath: "/dir2/vdir3.jpg",
  651. },
  652. }
  653. user := dataprovider.User{
  654. Filters: filters,
  655. VirtualFolders: virtualFolders,
  656. }
  657. getFilteredInfo := func(dirContents []os.FileInfo, virtualPath string) []os.FileInfo {
  658. result := user.FilterListDir(dirContents, virtualPath)
  659. result = append(result, user.GetVirtualFoldersInfo(virtualPath)...)
  660. return result
  661. }
  662. dirContents := []os.FileInfo{
  663. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  664. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  665. }
  666. // dirContents are modified in place, we need to redefine them each time
  667. filtered := getFilteredInfo(dirContents, "/dir1")
  668. assert.Len(t, filtered, 5)
  669. dirContents = []os.FileInfo{
  670. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  671. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  672. }
  673. filtered = getFilteredInfo(dirContents, "/dir1/vdir1")
  674. assert.Len(t, filtered, 2)
  675. dirContents = []os.FileInfo{
  676. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  677. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  678. }
  679. filtered = getFilteredInfo(dirContents, "/dir2/vdir2")
  680. require.Len(t, filtered, 1)
  681. assert.Equal(t, "file1.jpg", filtered[0].Name())
  682. dirContents = []os.FileInfo{
  683. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  684. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  685. }
  686. filtered = getFilteredInfo(dirContents, "/dir2/vdir2/sub")
  687. require.Len(t, filtered, 1)
  688. assert.Equal(t, "file1.jpg", filtered[0].Name())
  689. res, _ := user.IsFileAllowed("/dir1/vdir1/file.txt")
  690. assert.False(t, res)
  691. res, _ = user.IsFileAllowed("/dir1/vdir1/sub/file.txt")
  692. assert.False(t, res)
  693. res, _ = user.IsFileAllowed("/dir1/vdir1/file.jpg")
  694. assert.True(t, res)
  695. res, _ = user.IsFileAllowed("/dir1/vdir1/sub/file.jpg")
  696. assert.True(t, res)
  697. res, _ = user.IsFileAllowed("/dir3/file.jpg")
  698. assert.False(t, res)
  699. res, _ = user.IsFileAllowed("/dir3/dir1/file.jpg")
  700. assert.False(t, res)
  701. res, _ = user.IsFileAllowed("/dir3/dir1/sub/file.jpg")
  702. assert.False(t, res)
  703. res, _ = user.IsFileAllowed("/dir4/file.jpg")
  704. assert.False(t, res)
  705. res, _ = user.IsFileAllowed("/dir4/dir1/sub/file.jpg")
  706. assert.False(t, res)
  707. dirContents = []os.FileInfo{
  708. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  709. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  710. }
  711. filtered = getFilteredInfo(dirContents, "/dir4")
  712. require.Len(t, filtered, 0)
  713. dirContents = []os.FileInfo{
  714. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  715. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  716. }
  717. filtered = getFilteredInfo(dirContents, "/dir4/vdir2/sub")
  718. require.Len(t, filtered, 0)
  719. dirContents = []os.FileInfo{
  720. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  721. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  722. }
  723. filtered = getFilteredInfo(dirContents, "/dir2")
  724. assert.Len(t, filtered, 2)
  725. dirContents = []os.FileInfo{
  726. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  727. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  728. }
  729. filtered = getFilteredInfo(dirContents, "/dir4")
  730. assert.Len(t, filtered, 0)
  731. dirContents = []os.FileInfo{
  732. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  733. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  734. }
  735. filtered = getFilteredInfo(dirContents, "/dir4/sub")
  736. assert.Len(t, filtered, 0)
  737. dirContents = []os.FileInfo{
  738. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  739. vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
  740. }
  741. filtered = getFilteredInfo(dirContents, "/dir1")
  742. assert.Len(t, filtered, 5)
  743. filtered = getFilteredInfo(dirContents, "/dir2")
  744. if assert.Len(t, filtered, 1) {
  745. assert.True(t, filtered[0].IsDir())
  746. }
  747. user.VirtualFolders = nil
  748. dirContents = []os.FileInfo{
  749. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  750. vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
  751. }
  752. filtered = getFilteredInfo(dirContents, "/dir1")
  753. assert.Len(t, filtered, 2)
  754. dirContents = []os.FileInfo{
  755. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  756. vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
  757. }
  758. filtered = getFilteredInfo(dirContents, "/dir2")
  759. if assert.Len(t, filtered, 1) {
  760. assert.False(t, filtered[0].IsDir())
  761. }
  762. dirContents = []os.FileInfo{
  763. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  764. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  765. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  766. vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
  767. }
  768. filtered = getFilteredInfo(dirContents, "/dir2")
  769. if assert.Len(t, filtered, 2) {
  770. assert.False(t, filtered[0].IsDir())
  771. assert.False(t, filtered[1].IsDir())
  772. }
  773. user.VirtualFolders = virtualFolders
  774. user.Filters = filters
  775. filtered = getFilteredInfo(nil, "/dir1")
  776. assert.Len(t, filtered, 3)
  777. filtered = getFilteredInfo(nil, "/dir2")
  778. assert.Len(t, filtered, 1)
  779. dirContents = []os.FileInfo{
  780. vfs.NewFileInfo("file1.jPg", false, 123, time.Now(), false),
  781. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  782. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  783. vfs.NewFileInfo("vdir3.jpg", false, 456, time.Now(), false),
  784. }
  785. filtered = getFilteredInfo(dirContents, "/dir2")
  786. assert.Len(t, filtered, 2)
  787. user = dataprovider.User{
  788. Filters: dataprovider.UserFilters{
  789. BaseUserFilters: sdk.BaseUserFilters{
  790. FilePatterns: []sdk.PatternsFilter{
  791. {
  792. Path: "/dir3",
  793. AllowedPatterns: []string{"ic35"},
  794. DeniedPatterns: []string{"*"},
  795. DenyPolicy: sdk.DenyPolicyHide,
  796. },
  797. },
  798. },
  799. },
  800. }
  801. dirContents = []os.FileInfo{
  802. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  803. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  804. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  805. vfs.NewFileInfo("vdir3.jpg", false, 456, time.Now(), false),
  806. }
  807. filtered = getFilteredInfo(dirContents, "/dir3")
  808. assert.Len(t, filtered, 0)
  809. dirContents = nil
  810. for i := 0; i < 100; i++ {
  811. dirContents = append(dirContents, vfs.NewFileInfo(fmt.Sprintf("ic%02d", i), i%2 == 0, int64(i), time.Now(), false))
  812. }
  813. dirContents = append(dirContents, vfs.NewFileInfo("ic350", false, 123, time.Now(), false))
  814. dirContents = append(dirContents, vfs.NewFileInfo(".ic35", false, 123, time.Now(), false))
  815. dirContents = append(dirContents, vfs.NewFileInfo("ic35.", false, 123, time.Now(), false))
  816. dirContents = append(dirContents, vfs.NewFileInfo("*ic35", false, 123, time.Now(), false))
  817. dirContents = append(dirContents, vfs.NewFileInfo("ic35*", false, 123, time.Now(), false))
  818. dirContents = append(dirContents, vfs.NewFileInfo("ic35.*", false, 123, time.Now(), false))
  819. dirContents = append(dirContents, vfs.NewFileInfo("file.jpg", false, 123, time.Now(), false))
  820. filtered = getFilteredInfo(dirContents, "/dir3")
  821. require.Len(t, filtered, 1)
  822. assert.Equal(t, "ic35", filtered[0].Name())
  823. dirContents = []os.FileInfo{
  824. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  825. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  826. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  827. }
  828. filtered = getFilteredInfo(dirContents, "/dir3/ic36")
  829. require.Len(t, filtered, 0)
  830. dirContents = []os.FileInfo{
  831. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  832. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  833. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  834. }
  835. filtered = getFilteredInfo(dirContents, "/dir3/ic35")
  836. require.Len(t, filtered, 3)
  837. dirContents = []os.FileInfo{
  838. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  839. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  840. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  841. }
  842. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub")
  843. require.Len(t, filtered, 3)
  844. res, _ = user.IsFileAllowed("/dir3/file.txt")
  845. assert.False(t, res)
  846. res, _ = user.IsFileAllowed("/dir3/ic35a")
  847. assert.False(t, res)
  848. res, policy := user.IsFileAllowed("/dir3/ic35a/file")
  849. assert.False(t, res)
  850. assert.Equal(t, sdk.DenyPolicyHide, policy)
  851. res, _ = user.IsFileAllowed("/dir3/ic35")
  852. assert.True(t, res)
  853. res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
  854. assert.True(t, res)
  855. res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
  856. assert.True(t, res)
  857. res, _ = user.IsFileAllowed("/dir3/ic35/sub/file.txt")
  858. assert.True(t, res)
  859. dirContents = []os.FileInfo{
  860. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  861. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  862. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  863. }
  864. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub")
  865. require.Len(t, filtered, 3)
  866. user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
  867. Path: "/dir3/ic35/sub1",
  868. AllowedPatterns: []string{"*.jpg"},
  869. DenyPolicy: sdk.DenyPolicyDefault,
  870. })
  871. user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
  872. Path: "/dir3/ic35/sub2",
  873. DeniedPatterns: []string{"*.jpg"},
  874. DenyPolicy: sdk.DenyPolicyHide,
  875. })
  876. dirContents = []os.FileInfo{
  877. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  878. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  879. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  880. }
  881. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub1")
  882. require.Len(t, filtered, 3)
  883. dirContents = []os.FileInfo{
  884. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  885. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  886. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  887. }
  888. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub2")
  889. require.Len(t, filtered, 2)
  890. dirContents = []os.FileInfo{
  891. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  892. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  893. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  894. }
  895. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub2/sub1")
  896. require.Len(t, filtered, 2)
  897. res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
  898. assert.True(t, res)
  899. res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
  900. assert.True(t, res)
  901. res, _ = user.IsFileAllowed("/dir3/ic35/sub/dir/file.txt")
  902. assert.True(t, res)
  903. res, _ = user.IsFileAllowed("/dir3/ic35/sub/dir/file.jpg")
  904. assert.True(t, res)
  905. res, _ = user.IsFileAllowed("/dir3/ic35/sub1/file.jpg")
  906. assert.True(t, res)
  907. res, _ = user.IsFileAllowed("/dir3/ic35/sub1/file.txt")
  908. assert.False(t, res)
  909. res, _ = user.IsFileAllowed("/dir3/ic35/sub1/sub/file.jpg")
  910. assert.True(t, res)
  911. res, _ = user.IsFileAllowed("/dir3/ic35/sub1/sub2/file.txt")
  912. assert.False(t, res)
  913. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.jpg")
  914. assert.False(t, res)
  915. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.txt")
  916. assert.True(t, res)
  917. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub/file.jpg")
  918. assert.False(t, res)
  919. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub1/file.txt")
  920. assert.True(t, res)
  921. user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
  922. Path: "/dir3/ic35",
  923. DeniedPatterns: []string{"*.txt"},
  924. DenyPolicy: sdk.DenyPolicyHide,
  925. })
  926. res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
  927. assert.True(t, res)
  928. res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
  929. assert.False(t, res)
  930. res, _ = user.IsFileAllowed("/dir3/ic35/adir/sub/file.jpg")
  931. assert.True(t, res)
  932. res, _ = user.IsFileAllowed("/dir3/ic35/adir/file.txt")
  933. assert.False(t, res)
  934. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.jpg")
  935. assert.False(t, res)
  936. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.txt")
  937. assert.True(t, res)
  938. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub/file.jpg")
  939. assert.False(t, res)
  940. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub1/file.txt")
  941. assert.True(t, res)
  942. dirContents = []os.FileInfo{
  943. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  944. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  945. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  946. }
  947. filtered = getFilteredInfo(dirContents, "/dir3/ic35")
  948. require.Len(t, filtered, 1)
  949. dirContents = []os.FileInfo{
  950. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  951. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  952. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  953. }
  954. filtered = getFilteredInfo(dirContents, "/dir3/ic35/abc")
  955. require.Len(t, filtered, 1)
  956. }
  957. func TestListerAt(t *testing.T) {
  958. dir := t.TempDir()
  959. user := dataprovider.User{
  960. BaseUser: sdk.BaseUser{
  961. Username: "u",
  962. Password: "p",
  963. HomeDir: dir,
  964. Status: 1,
  965. Permissions: map[string][]string{
  966. "/": {"*"},
  967. },
  968. },
  969. }
  970. conn := NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  971. lister, err := conn.ListDir("/")
  972. require.NoError(t, err)
  973. files, err := lister.Next(1)
  974. require.ErrorIs(t, err, io.EOF)
  975. require.Len(t, files, 0)
  976. err = lister.Close()
  977. require.NoError(t, err)
  978. conn.User.VirtualFolders = []vfs.VirtualFolder{
  979. {
  980. VirtualPath: "p1",
  981. },
  982. {
  983. VirtualPath: "p2",
  984. },
  985. {
  986. VirtualPath: "p3",
  987. },
  988. }
  989. lister, err = conn.ListDir("/")
  990. require.NoError(t, err)
  991. files, err = lister.Next(2)
  992. // virtual directories exceeds the limit
  993. require.ErrorIs(t, err, io.EOF)
  994. require.Len(t, files, 3)
  995. files, err = lister.Next(2)
  996. require.ErrorIs(t, err, io.EOF)
  997. require.Len(t, files, 0)
  998. _, err = lister.Next(-1)
  999. require.ErrorContains(t, err, "invalid limit")
  1000. err = lister.Close()
  1001. require.NoError(t, err)
  1002. lister, err = conn.ListDir("/")
  1003. require.NoError(t, err)
  1004. _, err = lister.ListAt(nil, 0)
  1005. require.ErrorContains(t, err, "zero size")
  1006. err = lister.Close()
  1007. require.NoError(t, err)
  1008. for i := 0; i < 100; i++ {
  1009. f, err := os.Create(filepath.Join(dir, strconv.Itoa(i)))
  1010. require.NoError(t, err)
  1011. err = f.Close()
  1012. require.NoError(t, err)
  1013. }
  1014. lister, err = conn.ListDir("/")
  1015. require.NoError(t, err)
  1016. files = make([]os.FileInfo, 18)
  1017. n, err := lister.ListAt(files, 0)
  1018. require.NoError(t, err)
  1019. require.Equal(t, 18, n)
  1020. n, err = lister.ListAt(files, 0)
  1021. require.NoError(t, err)
  1022. require.Equal(t, 18, n)
  1023. files = make([]os.FileInfo, 100)
  1024. n, err = lister.ListAt(files, 0)
  1025. require.NoError(t, err)
  1026. require.Equal(t, 64+3, n)
  1027. n, err = lister.ListAt(files, 0)
  1028. require.ErrorIs(t, err, io.EOF)
  1029. require.Equal(t, 0, n)
  1030. n, err = lister.ListAt(files, 0)
  1031. require.ErrorIs(t, err, io.EOF)
  1032. require.Equal(t, 0, n)
  1033. err = lister.Close()
  1034. require.NoError(t, err)
  1035. n, err = lister.ListAt(files, 0)
  1036. require.Error(t, err)
  1037. require.NotErrorIs(t, err, io.EOF)
  1038. require.Equal(t, 0, n)
  1039. lister, err = conn.ListDir("/")
  1040. require.NoError(t, err)
  1041. lister.Add(vfs.NewFileInfo("..", true, 0, time.Unix(0, 0), false))
  1042. lister.Add(vfs.NewFileInfo(".", true, 0, time.Unix(0, 0), false))
  1043. files = make([]os.FileInfo, 1)
  1044. n, err = lister.ListAt(files, 0)
  1045. require.NoError(t, err)
  1046. require.Equal(t, 1, n)
  1047. assert.Equal(t, ".", files[0].Name())
  1048. files = make([]os.FileInfo, 2)
  1049. n, err = lister.ListAt(files, 0)
  1050. require.NoError(t, err)
  1051. require.Equal(t, 2, n)
  1052. assert.Equal(t, "..", files[0].Name())
  1053. assert.Equal(t, "p3", files[1].Name())
  1054. files = make([]os.FileInfo, 200)
  1055. n, err = lister.ListAt(files, 0)
  1056. require.NoError(t, err)
  1057. require.Equal(t, 102, n)
  1058. assert.Equal(t, "p2", files[0].Name())
  1059. assert.Equal(t, "p1", files[1].Name())
  1060. err = lister.Close()
  1061. require.NoError(t, err)
  1062. }