connection_test.go 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161
  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.doRecursiveCopy("/src", "/dst", vfs.NewFileInfo("src", true, 0, time.Now(), false), false, 2000)
  565. assert.Error(t, err, util.ErrRecursionTooDeep)
  566. err = conn.checkCopy(vfs.NewFileInfo("name", true, 0, time.Unix(0, 0), false), nil, "/source", "/target")
  567. assert.Error(t, err)
  568. sourceFile := filepath.Join(os.TempDir(), "f", "source")
  569. err = os.MkdirAll(filepath.Dir(sourceFile), os.ModePerm)
  570. assert.NoError(t, err)
  571. err = os.WriteFile(sourceFile, []byte(""), 0666)
  572. assert.NoError(t, err)
  573. err = conn.checkCopy(vfs.NewFileInfo("name", true, 0, time.Unix(0, 0), false), nil, "/f/source", "/target")
  574. assert.Error(t, err)
  575. 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")
  576. assert.Error(t, err)
  577. err = os.RemoveAll(filepath.Dir(sourceFile))
  578. assert.NoError(t, err)
  579. }
  580. func TestConnectionKeepAlive(t *testing.T) {
  581. conn := NewBaseConnection("", ProtocolWebDAV, "", "", dataprovider.User{})
  582. lastActivity := conn.GetLastActivity()
  583. done := make(chan bool)
  584. go func() {
  585. time.Sleep(200 * time.Millisecond)
  586. close(done)
  587. }()
  588. keepConnectionAlive(conn, done, 50*time.Millisecond)
  589. assert.Greater(t, conn.GetLastActivity(), lastActivity)
  590. }
  591. func TestFsFileCopier(t *testing.T) {
  592. fs := vfs.Fs(&vfs.AzureBlobFs{})
  593. _, ok := fs.(vfs.FsFileCopier)
  594. assert.True(t, ok)
  595. fs = vfs.Fs(&vfs.OsFs{})
  596. _, ok = fs.(vfs.FsFileCopier)
  597. assert.False(t, ok)
  598. fs = vfs.Fs(&vfs.SFTPFs{})
  599. _, ok = fs.(vfs.FsFileCopier)
  600. assert.False(t, ok)
  601. fs = vfs.Fs(&vfs.GCSFs{})
  602. _, ok = fs.(vfs.FsFileCopier)
  603. assert.True(t, ok)
  604. fs = vfs.Fs(&vfs.S3Fs{})
  605. _, ok = fs.(vfs.FsFileCopier)
  606. assert.True(t, ok)
  607. }
  608. func TestFilePatterns(t *testing.T) {
  609. filters := dataprovider.UserFilters{
  610. BaseUserFilters: sdk.BaseUserFilters{
  611. FilePatterns: []sdk.PatternsFilter{
  612. {
  613. Path: "/dir1",
  614. DenyPolicy: sdk.DenyPolicyDefault,
  615. AllowedPatterns: []string{"*.jpg"},
  616. },
  617. {
  618. Path: "/dir2",
  619. DenyPolicy: sdk.DenyPolicyHide,
  620. AllowedPatterns: []string{"*.jpg"},
  621. },
  622. {
  623. Path: "/dir3",
  624. DenyPolicy: sdk.DenyPolicyDefault,
  625. DeniedPatterns: []string{"*.jpg"},
  626. },
  627. {
  628. Path: "/dir4",
  629. DenyPolicy: sdk.DenyPolicyHide,
  630. DeniedPatterns: []string{"*"},
  631. },
  632. },
  633. },
  634. }
  635. virtualFolders := []vfs.VirtualFolder{
  636. {
  637. VirtualPath: "/dir1/vdir1",
  638. },
  639. {
  640. VirtualPath: "/dir1/vdir2",
  641. },
  642. {
  643. VirtualPath: "/dir1/vdir3",
  644. },
  645. {
  646. VirtualPath: "/dir2/vdir1",
  647. },
  648. {
  649. VirtualPath: "/dir2/vdir2",
  650. },
  651. {
  652. VirtualPath: "/dir2/vdir3.jpg",
  653. },
  654. }
  655. user := dataprovider.User{
  656. Filters: filters,
  657. VirtualFolders: virtualFolders,
  658. }
  659. getFilteredInfo := func(dirContents []os.FileInfo, virtualPath string) []os.FileInfo {
  660. result := user.FilterListDir(dirContents, virtualPath)
  661. result = append(result, user.GetVirtualFoldersInfo(virtualPath)...)
  662. return result
  663. }
  664. dirContents := []os.FileInfo{
  665. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  666. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  667. }
  668. // dirContents are modified in place, we need to redefine them each time
  669. filtered := getFilteredInfo(dirContents, "/dir1")
  670. assert.Len(t, filtered, 5)
  671. dirContents = []os.FileInfo{
  672. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  673. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  674. }
  675. filtered = getFilteredInfo(dirContents, "/dir1/vdir1")
  676. assert.Len(t, filtered, 2)
  677. dirContents = []os.FileInfo{
  678. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  679. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  680. }
  681. filtered = getFilteredInfo(dirContents, "/dir2/vdir2")
  682. require.Len(t, filtered, 1)
  683. assert.Equal(t, "file1.jpg", filtered[0].Name())
  684. dirContents = []os.FileInfo{
  685. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  686. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  687. }
  688. filtered = getFilteredInfo(dirContents, "/dir2/vdir2/sub")
  689. require.Len(t, filtered, 1)
  690. assert.Equal(t, "file1.jpg", filtered[0].Name())
  691. res, _ := user.IsFileAllowed("/dir1/vdir1/file.txt")
  692. assert.False(t, res)
  693. res, _ = user.IsFileAllowed("/dir1/vdir1/sub/file.txt")
  694. assert.False(t, res)
  695. res, _ = user.IsFileAllowed("/dir1/vdir1/file.jpg")
  696. assert.True(t, res)
  697. res, _ = user.IsFileAllowed("/dir1/vdir1/sub/file.jpg")
  698. assert.True(t, res)
  699. res, _ = user.IsFileAllowed("/dir3/file.jpg")
  700. assert.False(t, res)
  701. res, _ = user.IsFileAllowed("/dir3/dir1/file.jpg")
  702. assert.False(t, res)
  703. res, _ = user.IsFileAllowed("/dir3/dir1/sub/file.jpg")
  704. assert.False(t, res)
  705. res, _ = user.IsFileAllowed("/dir4/file.jpg")
  706. assert.False(t, res)
  707. res, _ = user.IsFileAllowed("/dir4/dir1/sub/file.jpg")
  708. assert.False(t, res)
  709. dirContents = []os.FileInfo{
  710. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  711. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  712. }
  713. filtered = getFilteredInfo(dirContents, "/dir4")
  714. require.Len(t, filtered, 0)
  715. dirContents = []os.FileInfo{
  716. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  717. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  718. }
  719. filtered = getFilteredInfo(dirContents, "/dir4/vdir2/sub")
  720. require.Len(t, filtered, 0)
  721. dirContents = []os.FileInfo{
  722. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  723. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  724. }
  725. filtered = getFilteredInfo(dirContents, "/dir2")
  726. assert.Len(t, filtered, 2)
  727. dirContents = []os.FileInfo{
  728. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  729. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  730. }
  731. filtered = getFilteredInfo(dirContents, "/dir4")
  732. assert.Len(t, filtered, 0)
  733. dirContents = []os.FileInfo{
  734. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  735. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  736. }
  737. filtered = getFilteredInfo(dirContents, "/dir4/sub")
  738. assert.Len(t, filtered, 0)
  739. dirContents = []os.FileInfo{
  740. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  741. vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
  742. }
  743. filtered = getFilteredInfo(dirContents, "/dir1")
  744. assert.Len(t, filtered, 5)
  745. filtered = getFilteredInfo(dirContents, "/dir2")
  746. if assert.Len(t, filtered, 1) {
  747. assert.True(t, filtered[0].IsDir())
  748. }
  749. user.VirtualFolders = nil
  750. dirContents = []os.FileInfo{
  751. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  752. vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
  753. }
  754. filtered = getFilteredInfo(dirContents, "/dir1")
  755. assert.Len(t, filtered, 2)
  756. dirContents = []os.FileInfo{
  757. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  758. vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
  759. }
  760. filtered = getFilteredInfo(dirContents, "/dir2")
  761. if assert.Len(t, filtered, 1) {
  762. assert.False(t, filtered[0].IsDir())
  763. }
  764. dirContents = []os.FileInfo{
  765. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  766. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  767. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  768. vfs.NewFileInfo("vdir3.jpg", false, 123, time.Now(), false),
  769. }
  770. filtered = getFilteredInfo(dirContents, "/dir2")
  771. if assert.Len(t, filtered, 2) {
  772. assert.False(t, filtered[0].IsDir())
  773. assert.False(t, filtered[1].IsDir())
  774. }
  775. user.VirtualFolders = virtualFolders
  776. user.Filters = filters
  777. filtered = getFilteredInfo(nil, "/dir1")
  778. assert.Len(t, filtered, 3)
  779. filtered = getFilteredInfo(nil, "/dir2")
  780. assert.Len(t, filtered, 1)
  781. dirContents = []os.FileInfo{
  782. vfs.NewFileInfo("file1.jPg", false, 123, time.Now(), false),
  783. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  784. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  785. vfs.NewFileInfo("vdir3.jpg", false, 456, time.Now(), false),
  786. }
  787. filtered = getFilteredInfo(dirContents, "/dir2")
  788. assert.Len(t, filtered, 2)
  789. user = dataprovider.User{
  790. Filters: dataprovider.UserFilters{
  791. BaseUserFilters: sdk.BaseUserFilters{
  792. FilePatterns: []sdk.PatternsFilter{
  793. {
  794. Path: "/dir3",
  795. AllowedPatterns: []string{"ic35"},
  796. DeniedPatterns: []string{"*"},
  797. DenyPolicy: sdk.DenyPolicyHide,
  798. },
  799. },
  800. },
  801. },
  802. }
  803. dirContents = []os.FileInfo{
  804. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  805. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  806. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  807. vfs.NewFileInfo("vdir3.jpg", false, 456, time.Now(), false),
  808. }
  809. filtered = getFilteredInfo(dirContents, "/dir3")
  810. assert.Len(t, filtered, 0)
  811. dirContents = nil
  812. for i := 0; i < 100; i++ {
  813. dirContents = append(dirContents, vfs.NewFileInfo(fmt.Sprintf("ic%02d", i), i%2 == 0, int64(i), time.Now(), false))
  814. }
  815. dirContents = append(dirContents, vfs.NewFileInfo("ic350", 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("ic35*", false, 123, time.Now(), false))
  820. dirContents = append(dirContents, vfs.NewFileInfo("ic35.*", false, 123, time.Now(), false))
  821. dirContents = append(dirContents, vfs.NewFileInfo("file.jpg", false, 123, time.Now(), false))
  822. filtered = getFilteredInfo(dirContents, "/dir3")
  823. require.Len(t, filtered, 1)
  824. assert.Equal(t, "ic35", filtered[0].Name())
  825. dirContents = []os.FileInfo{
  826. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  827. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  828. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  829. }
  830. filtered = getFilteredInfo(dirContents, "/dir3/ic36")
  831. require.Len(t, filtered, 0)
  832. dirContents = []os.FileInfo{
  833. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  834. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  835. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  836. }
  837. filtered = getFilteredInfo(dirContents, "/dir3/ic35")
  838. require.Len(t, filtered, 3)
  839. dirContents = []os.FileInfo{
  840. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  841. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  842. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  843. }
  844. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub")
  845. require.Len(t, filtered, 3)
  846. res, _ = user.IsFileAllowed("/dir3/file.txt")
  847. assert.False(t, res)
  848. res, _ = user.IsFileAllowed("/dir3/ic35a")
  849. assert.False(t, res)
  850. res, policy := user.IsFileAllowed("/dir3/ic35a/file")
  851. assert.False(t, res)
  852. assert.Equal(t, sdk.DenyPolicyHide, policy)
  853. res, _ = user.IsFileAllowed("/dir3/ic35")
  854. assert.True(t, res)
  855. res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
  856. assert.True(t, res)
  857. res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
  858. assert.True(t, res)
  859. res, _ = user.IsFileAllowed("/dir3/ic35/sub/file.txt")
  860. assert.True(t, res)
  861. dirContents = []os.FileInfo{
  862. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  863. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  864. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  865. }
  866. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub")
  867. require.Len(t, filtered, 3)
  868. user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
  869. Path: "/dir3/ic35/sub1",
  870. AllowedPatterns: []string{"*.jpg"},
  871. DenyPolicy: sdk.DenyPolicyDefault,
  872. })
  873. user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
  874. Path: "/dir3/ic35/sub2",
  875. DeniedPatterns: []string{"*.jpg"},
  876. DenyPolicy: sdk.DenyPolicyHide,
  877. })
  878. dirContents = []os.FileInfo{
  879. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  880. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  881. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  882. }
  883. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub1")
  884. require.Len(t, filtered, 3)
  885. dirContents = []os.FileInfo{
  886. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  887. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  888. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  889. }
  890. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub2")
  891. require.Len(t, filtered, 2)
  892. dirContents = []os.FileInfo{
  893. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  894. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  895. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  896. }
  897. filtered = getFilteredInfo(dirContents, "/dir3/ic35/sub2/sub1")
  898. require.Len(t, filtered, 2)
  899. res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
  900. assert.True(t, res)
  901. res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
  902. assert.True(t, res)
  903. res, _ = user.IsFileAllowed("/dir3/ic35/sub/dir/file.txt")
  904. assert.True(t, res)
  905. res, _ = user.IsFileAllowed("/dir3/ic35/sub/dir/file.jpg")
  906. assert.True(t, res)
  907. res, _ = user.IsFileAllowed("/dir3/ic35/sub1/file.jpg")
  908. assert.True(t, res)
  909. res, _ = user.IsFileAllowed("/dir3/ic35/sub1/file.txt")
  910. assert.False(t, res)
  911. res, _ = user.IsFileAllowed("/dir3/ic35/sub1/sub/file.jpg")
  912. assert.True(t, res)
  913. res, _ = user.IsFileAllowed("/dir3/ic35/sub1/sub2/file.txt")
  914. assert.False(t, res)
  915. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.jpg")
  916. assert.False(t, res)
  917. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.txt")
  918. assert.True(t, res)
  919. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub/file.jpg")
  920. assert.False(t, res)
  921. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub1/file.txt")
  922. assert.True(t, res)
  923. user.Filters.FilePatterns = append(user.Filters.FilePatterns, sdk.PatternsFilter{
  924. Path: "/dir3/ic35",
  925. DeniedPatterns: []string{"*.txt"},
  926. DenyPolicy: sdk.DenyPolicyHide,
  927. })
  928. res, _ = user.IsFileAllowed("/dir3/ic35/file.jpg")
  929. assert.True(t, res)
  930. res, _ = user.IsFileAllowed("/dir3/ic35/file.txt")
  931. assert.False(t, res)
  932. res, _ = user.IsFileAllowed("/dir3/ic35/adir/sub/file.jpg")
  933. assert.True(t, res)
  934. res, _ = user.IsFileAllowed("/dir3/ic35/adir/file.txt")
  935. assert.False(t, res)
  936. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.jpg")
  937. assert.False(t, res)
  938. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/file.txt")
  939. assert.True(t, res)
  940. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub/file.jpg")
  941. assert.False(t, res)
  942. res, _ = user.IsFileAllowed("/dir3/ic35/sub2/sub1/file.txt")
  943. assert.True(t, res)
  944. dirContents = []os.FileInfo{
  945. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  946. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  947. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  948. }
  949. filtered = getFilteredInfo(dirContents, "/dir3/ic35")
  950. require.Len(t, filtered, 1)
  951. dirContents = []os.FileInfo{
  952. vfs.NewFileInfo("file1.jpg", false, 123, time.Now(), false),
  953. vfs.NewFileInfo("file1.txt", false, 123, time.Now(), false),
  954. vfs.NewFileInfo("file2.txt", false, 123, time.Now(), false),
  955. }
  956. filtered = getFilteredInfo(dirContents, "/dir3/ic35/abc")
  957. require.Len(t, filtered, 1)
  958. }
  959. func TestListerAt(t *testing.T) {
  960. dir := t.TempDir()
  961. user := dataprovider.User{
  962. BaseUser: sdk.BaseUser{
  963. Username: "u",
  964. Password: "p",
  965. HomeDir: dir,
  966. Status: 1,
  967. Permissions: map[string][]string{
  968. "/": {"*"},
  969. },
  970. },
  971. }
  972. conn := NewBaseConnection(xid.New().String(), ProtocolSFTP, "", "", user)
  973. lister, err := conn.ListDir("/")
  974. require.NoError(t, err)
  975. files, err := lister.Next(1)
  976. require.ErrorIs(t, err, io.EOF)
  977. require.Len(t, files, 0)
  978. err = lister.Close()
  979. require.NoError(t, err)
  980. conn.User.VirtualFolders = []vfs.VirtualFolder{
  981. {
  982. VirtualPath: "p1",
  983. },
  984. {
  985. VirtualPath: "p2",
  986. },
  987. {
  988. VirtualPath: "p3",
  989. },
  990. }
  991. lister, err = conn.ListDir("/")
  992. require.NoError(t, err)
  993. files, err = lister.Next(2)
  994. // virtual directories exceeds the limit
  995. require.ErrorIs(t, err, io.EOF)
  996. require.Len(t, files, 3)
  997. files, err = lister.Next(2)
  998. require.ErrorIs(t, err, io.EOF)
  999. require.Len(t, files, 0)
  1000. _, err = lister.Next(-1)
  1001. require.ErrorContains(t, err, "invalid limit")
  1002. err = lister.Close()
  1003. require.NoError(t, err)
  1004. lister, err = conn.ListDir("/")
  1005. require.NoError(t, err)
  1006. _, err = lister.ListAt(nil, 0)
  1007. require.ErrorContains(t, err, "zero size")
  1008. err = lister.Close()
  1009. require.NoError(t, err)
  1010. for i := 0; i < 100; i++ {
  1011. f, err := os.Create(filepath.Join(dir, strconv.Itoa(i)))
  1012. require.NoError(t, err)
  1013. err = f.Close()
  1014. require.NoError(t, err)
  1015. }
  1016. lister, err = conn.ListDir("/")
  1017. require.NoError(t, err)
  1018. files = make([]os.FileInfo, 18)
  1019. n, err := lister.ListAt(files, 0)
  1020. require.NoError(t, err)
  1021. require.Equal(t, 18, n)
  1022. n, err = lister.ListAt(files, 0)
  1023. require.NoError(t, err)
  1024. require.Equal(t, 18, n)
  1025. files = make([]os.FileInfo, 100)
  1026. n, err = lister.ListAt(files, 0)
  1027. require.NoError(t, err)
  1028. require.Equal(t, 64+3, n)
  1029. n, err = lister.ListAt(files, 0)
  1030. require.ErrorIs(t, err, io.EOF)
  1031. require.Equal(t, 0, n)
  1032. n, err = lister.ListAt(files, 0)
  1033. require.ErrorIs(t, err, io.EOF)
  1034. require.Equal(t, 0, n)
  1035. err = lister.Close()
  1036. require.NoError(t, err)
  1037. n, err = lister.ListAt(files, 0)
  1038. assert.Error(t, err)
  1039. assert.NotErrorIs(t, err, io.EOF)
  1040. require.Equal(t, 0, n)
  1041. lister, err = conn.ListDir("/")
  1042. require.NoError(t, err)
  1043. lister.Prepend(vfs.NewFileInfo("..", true, 0, time.Unix(0, 0), false))
  1044. lister.Prepend(vfs.NewFileInfo(".", true, 0, time.Unix(0, 0), false))
  1045. files = make([]os.FileInfo, 1)
  1046. n, err = lister.ListAt(files, 0)
  1047. require.NoError(t, err)
  1048. require.Equal(t, 1, n)
  1049. assert.Equal(t, ".", files[0].Name())
  1050. files = make([]os.FileInfo, 2)
  1051. n, err = lister.ListAt(files, 0)
  1052. require.NoError(t, err)
  1053. require.Equal(t, 2, n)
  1054. assert.Equal(t, "..", files[0].Name())
  1055. vfolders := []string{files[1].Name()}
  1056. files = make([]os.FileInfo, 200)
  1057. n, err = lister.ListAt(files, 0)
  1058. require.NoError(t, err)
  1059. require.Equal(t, 102, n)
  1060. vfolders = append(vfolders, files[0].Name())
  1061. vfolders = append(vfolders, files[1].Name())
  1062. assert.Contains(t, vfolders, "p1")
  1063. assert.Contains(t, vfolders, "p2")
  1064. assert.Contains(t, vfolders, "p3")
  1065. err = lister.Close()
  1066. require.NoError(t, err)
  1067. }