connection_test.go 39 KB

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