transfer_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. // Copyright (C) 2019-2023 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package common
  15. import (
  16. "errors"
  17. "os"
  18. "path/filepath"
  19. "testing"
  20. "time"
  21. "github.com/sftpgo/sdk"
  22. "github.com/stretchr/testify/assert"
  23. "github.com/stretchr/testify/require"
  24. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  25. "github.com/drakkan/sftpgo/v2/internal/kms"
  26. "github.com/drakkan/sftpgo/v2/internal/vfs"
  27. )
  28. func TestTransferUpdateQuota(t *testing.T) {
  29. conn := NewBaseConnection("", ProtocolSFTP, "", "", dataprovider.User{})
  30. transfer := BaseTransfer{
  31. Connection: conn,
  32. transferType: TransferUpload,
  33. Fs: vfs.NewOsFs("", os.TempDir(), ""),
  34. }
  35. transfer.BytesReceived.Store(123)
  36. errFake := errors.New("fake error")
  37. transfer.TransferError(errFake)
  38. err := transfer.Close()
  39. if assert.Error(t, err) {
  40. assert.EqualError(t, err, errFake.Error())
  41. }
  42. mappedPath := filepath.Join(os.TempDir(), "vdir")
  43. vdirPath := "/vdir"
  44. conn.User.VirtualFolders = append(conn.User.VirtualFolders, vfs.VirtualFolder{
  45. BaseVirtualFolder: vfs.BaseVirtualFolder{
  46. MappedPath: mappedPath,
  47. },
  48. VirtualPath: vdirPath,
  49. QuotaFiles: -1,
  50. QuotaSize: -1,
  51. })
  52. transfer.ErrTransfer = nil
  53. transfer.BytesReceived.Store(1)
  54. transfer.requestPath = "/vdir/file"
  55. assert.True(t, transfer.updateQuota(1, 0))
  56. err = transfer.Close()
  57. assert.NoError(t, err)
  58. transfer.ErrTransfer = errFake
  59. transfer.Fs = newMockOsFs(true, "", "", "S3Fs fake", nil)
  60. assert.False(t, transfer.updateQuota(1, 0))
  61. }
  62. func TestTransferThrottling(t *testing.T) {
  63. u := dataprovider.User{
  64. BaseUser: sdk.BaseUser{
  65. Username: "test",
  66. UploadBandwidth: 50,
  67. DownloadBandwidth: 40,
  68. },
  69. }
  70. fs := vfs.NewOsFs("", os.TempDir(), "")
  71. testFileSize := int64(131072)
  72. wantedUploadElapsed := 1000 * (testFileSize / 1024) / u.UploadBandwidth
  73. wantedDownloadElapsed := 1000 * (testFileSize / 1024) / u.DownloadBandwidth
  74. // some tolerance
  75. wantedUploadElapsed -= wantedDownloadElapsed / 10
  76. wantedDownloadElapsed -= wantedDownloadElapsed / 10
  77. conn := NewBaseConnection("id", ProtocolSCP, "", "", u)
  78. transfer := NewBaseTransfer(nil, conn, nil, "", "", "", TransferUpload, 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  79. transfer.BytesReceived.Store(testFileSize)
  80. transfer.Connection.UpdateLastActivity()
  81. startTime := transfer.Connection.GetLastActivity()
  82. transfer.HandleThrottle()
  83. elapsed := time.Since(startTime).Nanoseconds() / 1000000
  84. assert.GreaterOrEqual(t, elapsed, wantedUploadElapsed, "upload bandwidth throttling not respected")
  85. err := transfer.Close()
  86. assert.NoError(t, err)
  87. transfer = NewBaseTransfer(nil, conn, nil, "", "", "", TransferDownload, 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  88. transfer.BytesSent.Store(testFileSize)
  89. transfer.Connection.UpdateLastActivity()
  90. startTime = transfer.Connection.GetLastActivity()
  91. transfer.HandleThrottle()
  92. elapsed = time.Since(startTime).Nanoseconds() / 1000000
  93. assert.GreaterOrEqual(t, elapsed, wantedDownloadElapsed, "download bandwidth throttling not respected")
  94. err = transfer.Close()
  95. assert.NoError(t, err)
  96. }
  97. func TestRealPath(t *testing.T) {
  98. testFile := filepath.Join(os.TempDir(), "afile.txt")
  99. fs := vfs.NewOsFs("123", os.TempDir(), "")
  100. u := dataprovider.User{
  101. BaseUser: sdk.BaseUser{
  102. Username: "user",
  103. HomeDir: os.TempDir(),
  104. },
  105. }
  106. u.Permissions = make(map[string][]string)
  107. u.Permissions["/"] = []string{dataprovider.PermAny}
  108. file, err := os.Create(testFile)
  109. require.NoError(t, err)
  110. conn := NewBaseConnection(fs.ConnectionID(), ProtocolSFTP, "", "", u)
  111. transfer := NewBaseTransfer(file, conn, nil, testFile, testFile, "/transfer_test_file",
  112. TransferUpload, 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  113. rPath := transfer.GetRealFsPath(testFile)
  114. assert.Equal(t, testFile, rPath)
  115. rPath = conn.getRealFsPath(testFile)
  116. assert.Equal(t, testFile, rPath)
  117. err = transfer.Close()
  118. assert.NoError(t, err)
  119. err = file.Close()
  120. assert.NoError(t, err)
  121. transfer.File = nil
  122. rPath = transfer.GetRealFsPath(testFile)
  123. assert.Equal(t, testFile, rPath)
  124. rPath = transfer.GetRealFsPath("")
  125. assert.Empty(t, rPath)
  126. err = os.Remove(testFile)
  127. assert.NoError(t, err)
  128. assert.Len(t, conn.GetTransfers(), 0)
  129. }
  130. func TestTruncate(t *testing.T) {
  131. testFile := filepath.Join(os.TempDir(), "transfer_test_file")
  132. fs := vfs.NewOsFs("123", os.TempDir(), "")
  133. u := dataprovider.User{
  134. BaseUser: sdk.BaseUser{
  135. Username: "user",
  136. HomeDir: os.TempDir(),
  137. },
  138. }
  139. u.Permissions = make(map[string][]string)
  140. u.Permissions["/"] = []string{dataprovider.PermAny}
  141. file, err := os.Create(testFile)
  142. if !assert.NoError(t, err) {
  143. assert.FailNow(t, "unable to open test file")
  144. }
  145. _, err = file.Write([]byte("hello"))
  146. assert.NoError(t, err)
  147. conn := NewBaseConnection(fs.ConnectionID(), ProtocolSFTP, "", "", u)
  148. transfer := NewBaseTransfer(file, conn, nil, testFile, testFile, "/transfer_test_file", TransferUpload, 0, 5,
  149. 100, 0, false, fs, dataprovider.TransferQuota{})
  150. err = conn.SetStat("/transfer_test_file", &StatAttributes{
  151. Size: 2,
  152. Flags: StatAttrSize,
  153. })
  154. assert.NoError(t, err)
  155. assert.Equal(t, int64(103), transfer.MaxWriteSize)
  156. err = transfer.Close()
  157. assert.NoError(t, err)
  158. err = file.Close()
  159. assert.NoError(t, err)
  160. fi, err := os.Stat(testFile)
  161. if assert.NoError(t, err) {
  162. assert.Equal(t, int64(2), fi.Size())
  163. }
  164. transfer = NewBaseTransfer(file, conn, nil, testFile, testFile, "/transfer_test_file", TransferUpload, 0, 0,
  165. 100, 0, true, fs, dataprovider.TransferQuota{})
  166. // file.Stat will fail on a closed file
  167. err = conn.SetStat("/transfer_test_file", &StatAttributes{
  168. Size: 2,
  169. Flags: StatAttrSize,
  170. })
  171. assert.Error(t, err)
  172. err = transfer.Close()
  173. assert.NoError(t, err)
  174. transfer = NewBaseTransfer(nil, conn, nil, testFile, testFile, "", TransferUpload, 0, 0, 0, 0, true,
  175. fs, dataprovider.TransferQuota{})
  176. _, err = transfer.Truncate("mismatch", 0)
  177. assert.EqualError(t, err, errTransferMismatch.Error())
  178. _, err = transfer.Truncate(testFile, 0)
  179. assert.NoError(t, err)
  180. _, err = transfer.Truncate(testFile, 1)
  181. assert.EqualError(t, err, vfs.ErrVfsUnsupported.Error())
  182. err = transfer.Close()
  183. assert.NoError(t, err)
  184. err = os.Remove(testFile)
  185. assert.NoError(t, err)
  186. assert.Len(t, conn.GetTransfers(), 0)
  187. }
  188. func TestTransferErrors(t *testing.T) {
  189. isCancelled := false
  190. cancelFn := func() {
  191. isCancelled = true
  192. }
  193. testFile := filepath.Join(os.TempDir(), "transfer_test_file")
  194. fs := vfs.NewOsFs("id", os.TempDir(), "")
  195. u := dataprovider.User{
  196. BaseUser: sdk.BaseUser{
  197. Username: "test",
  198. HomeDir: os.TempDir(),
  199. },
  200. }
  201. err := os.WriteFile(testFile, []byte("test data"), os.ModePerm)
  202. assert.NoError(t, err)
  203. file, err := os.Open(testFile)
  204. if !assert.NoError(t, err) {
  205. assert.FailNow(t, "unable to open test file")
  206. }
  207. conn := NewBaseConnection("id", ProtocolSFTP, "", "", u)
  208. transfer := NewBaseTransfer(file, conn, nil, testFile, testFile, "/transfer_test_file", TransferUpload,
  209. 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  210. assert.Nil(t, transfer.cancelFn)
  211. assert.Equal(t, testFile, transfer.GetFsPath())
  212. transfer.SetCancelFn(cancelFn)
  213. errFake := errors.New("err fake")
  214. transfer.BytesReceived.Store(9)
  215. transfer.TransferError(ErrQuotaExceeded)
  216. assert.True(t, isCancelled)
  217. transfer.TransferError(errFake)
  218. assert.Error(t, transfer.ErrTransfer, ErrQuotaExceeded.Error())
  219. // the file is closed from the embedding struct before to call close
  220. err = file.Close()
  221. assert.NoError(t, err)
  222. err = transfer.Close()
  223. if assert.Error(t, err) {
  224. assert.Error(t, err, ErrQuotaExceeded.Error())
  225. }
  226. assert.NoFileExists(t, testFile)
  227. err = os.WriteFile(testFile, []byte("test data"), os.ModePerm)
  228. assert.NoError(t, err)
  229. file, err = os.Open(testFile)
  230. if !assert.NoError(t, err) {
  231. assert.FailNow(t, "unable to open test file")
  232. }
  233. fsPath := filepath.Join(os.TempDir(), "test_file")
  234. transfer = NewBaseTransfer(file, conn, nil, fsPath, file.Name(), "/test_file", TransferUpload, 0, 0, 0, 0, true,
  235. fs, dataprovider.TransferQuota{})
  236. transfer.BytesReceived.Store(9)
  237. transfer.TransferError(errFake)
  238. assert.Error(t, transfer.ErrTransfer, errFake.Error())
  239. // the file is closed from the embedding struct before to call close
  240. err = file.Close()
  241. assert.NoError(t, err)
  242. err = transfer.Close()
  243. if assert.Error(t, err) {
  244. assert.Error(t, err, errFake.Error())
  245. }
  246. assert.NoFileExists(t, testFile)
  247. err = os.WriteFile(testFile, []byte("test data"), os.ModePerm)
  248. assert.NoError(t, err)
  249. file, err = os.Open(testFile)
  250. if !assert.NoError(t, err) {
  251. assert.FailNow(t, "unable to open test file")
  252. }
  253. transfer = NewBaseTransfer(file, conn, nil, fsPath, file.Name(), "/test_file", TransferUpload, 0, 0, 0, 0, true,
  254. fs, dataprovider.TransferQuota{})
  255. transfer.BytesReceived.Store(9)
  256. // the file is closed from the embedding struct before to call close
  257. err = file.Close()
  258. assert.NoError(t, err)
  259. err = transfer.Close()
  260. assert.NoError(t, err)
  261. assert.NoFileExists(t, testFile)
  262. assert.FileExists(t, fsPath)
  263. err = os.Remove(fsPath)
  264. assert.NoError(t, err)
  265. assert.Len(t, conn.GetTransfers(), 0)
  266. }
  267. func TestRemovePartialCryptoFile(t *testing.T) {
  268. testFile := filepath.Join(os.TempDir(), "transfer_test_file")
  269. fs, err := vfs.NewCryptFs("id", os.TempDir(), "", vfs.CryptFsConfig{Passphrase: kms.NewPlainSecret("secret")})
  270. require.NoError(t, err)
  271. u := dataprovider.User{
  272. BaseUser: sdk.BaseUser{
  273. Username: "test",
  274. HomeDir: os.TempDir(),
  275. },
  276. }
  277. conn := NewBaseConnection(fs.ConnectionID(), ProtocolSFTP, "", "", u)
  278. transfer := NewBaseTransfer(nil, conn, nil, testFile, testFile, "/transfer_test_file", TransferUpload,
  279. 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  280. transfer.ErrTransfer = errors.New("test error")
  281. _, _, err = transfer.getUploadFileSize()
  282. assert.Error(t, err)
  283. err = os.WriteFile(testFile, []byte("test data"), os.ModePerm)
  284. assert.NoError(t, err)
  285. size, deletedFiles, err := transfer.getUploadFileSize()
  286. assert.NoError(t, err)
  287. assert.Equal(t, int64(0), size)
  288. assert.Equal(t, 1, deletedFiles)
  289. assert.NoFileExists(t, testFile)
  290. }
  291. func TestFTPMode(t *testing.T) {
  292. conn := NewBaseConnection("", ProtocolFTP, "", "", dataprovider.User{})
  293. transfer := BaseTransfer{
  294. Connection: conn,
  295. transferType: TransferUpload,
  296. Fs: vfs.NewOsFs("", os.TempDir(), ""),
  297. }
  298. transfer.BytesReceived.Store(123)
  299. assert.Empty(t, transfer.ftpMode)
  300. transfer.SetFtpMode("active")
  301. assert.Equal(t, "active", transfer.ftpMode)
  302. }
  303. func TestTransferQuota(t *testing.T) {
  304. user := dataprovider.User{
  305. BaseUser: sdk.BaseUser{
  306. TotalDataTransfer: -1,
  307. UploadDataTransfer: -1,
  308. DownloadDataTransfer: -1,
  309. },
  310. }
  311. user.Filters.DataTransferLimits = []sdk.DataTransferLimit{
  312. {
  313. Sources: []string{"127.0.0.1/32", "192.168.1.0/24"},
  314. TotalDataTransfer: 100,
  315. UploadDataTransfer: 0,
  316. DownloadDataTransfer: 0,
  317. },
  318. {
  319. Sources: []string{"172.16.0.0/24"},
  320. TotalDataTransfer: 0,
  321. UploadDataTransfer: 120,
  322. DownloadDataTransfer: 150,
  323. },
  324. }
  325. ul, dl, total := user.GetDataTransferLimits("127.0.1.1")
  326. assert.Equal(t, int64(0), ul)
  327. assert.Equal(t, int64(0), dl)
  328. assert.Equal(t, int64(0), total)
  329. ul, dl, total = user.GetDataTransferLimits("127.0.0.1")
  330. assert.Equal(t, int64(0), ul)
  331. assert.Equal(t, int64(0), dl)
  332. assert.Equal(t, int64(100*1048576), total)
  333. ul, dl, total = user.GetDataTransferLimits("192.168.1.4")
  334. assert.Equal(t, int64(0), ul)
  335. assert.Equal(t, int64(0), dl)
  336. assert.Equal(t, int64(100*1048576), total)
  337. ul, dl, total = user.GetDataTransferLimits("172.16.0.2")
  338. assert.Equal(t, int64(120*1048576), ul)
  339. assert.Equal(t, int64(150*1048576), dl)
  340. assert.Equal(t, int64(0), total)
  341. transferQuota := dataprovider.TransferQuota{}
  342. assert.True(t, transferQuota.HasDownloadSpace())
  343. assert.True(t, transferQuota.HasUploadSpace())
  344. transferQuota.TotalSize = -1
  345. transferQuota.ULSize = -1
  346. transferQuota.DLSize = -1
  347. assert.True(t, transferQuota.HasDownloadSpace())
  348. assert.True(t, transferQuota.HasUploadSpace())
  349. transferQuota.TotalSize = 100
  350. transferQuota.AllowedTotalSize = 10
  351. assert.True(t, transferQuota.HasDownloadSpace())
  352. assert.True(t, transferQuota.HasUploadSpace())
  353. transferQuota.AllowedTotalSize = 0
  354. assert.False(t, transferQuota.HasDownloadSpace())
  355. assert.False(t, transferQuota.HasUploadSpace())
  356. transferQuota.TotalSize = 0
  357. transferQuota.DLSize = 100
  358. transferQuota.ULSize = 50
  359. transferQuota.AllowedTotalSize = 0
  360. assert.False(t, transferQuota.HasDownloadSpace())
  361. assert.False(t, transferQuota.HasUploadSpace())
  362. transferQuota.AllowedDLSize = 1
  363. transferQuota.AllowedULSize = 1
  364. assert.True(t, transferQuota.HasDownloadSpace())
  365. assert.True(t, transferQuota.HasUploadSpace())
  366. transferQuota.AllowedDLSize = -10
  367. transferQuota.AllowedULSize = -1
  368. assert.False(t, transferQuota.HasDownloadSpace())
  369. assert.False(t, transferQuota.HasUploadSpace())
  370. conn := NewBaseConnection("", ProtocolSFTP, "", "", user)
  371. transfer := NewBaseTransfer(nil, conn, nil, "file.txt", "file.txt", "/transfer_test_file", TransferUpload,
  372. 0, 0, 0, 0, true, vfs.NewOsFs("", os.TempDir(), ""), dataprovider.TransferQuota{})
  373. err := transfer.CheckRead()
  374. assert.NoError(t, err)
  375. err = transfer.CheckWrite()
  376. assert.NoError(t, err)
  377. transfer.transferQuota = dataprovider.TransferQuota{
  378. AllowedTotalSize: 10,
  379. }
  380. transfer.BytesReceived.Store(5)
  381. transfer.BytesSent.Store(4)
  382. err = transfer.CheckRead()
  383. assert.NoError(t, err)
  384. err = transfer.CheckWrite()
  385. assert.NoError(t, err)
  386. transfer.BytesSent.Store(6)
  387. err = transfer.CheckRead()
  388. if assert.Error(t, err) {
  389. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  390. }
  391. err = transfer.CheckWrite()
  392. assert.True(t, conn.IsQuotaExceededError(err))
  393. transferQuota = dataprovider.TransferQuota{
  394. AllowedTotalSize: 0,
  395. AllowedULSize: 10,
  396. AllowedDLSize: 5,
  397. }
  398. transfer.transferQuota = transferQuota
  399. assert.Equal(t, transferQuota, transfer.GetTransferQuota())
  400. err = transfer.CheckRead()
  401. if assert.Error(t, err) {
  402. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  403. }
  404. err = transfer.CheckWrite()
  405. assert.NoError(t, err)
  406. transfer.BytesReceived.Store(11)
  407. err = transfer.CheckRead()
  408. if assert.Error(t, err) {
  409. assert.Contains(t, err.Error(), ErrReadQuotaExceeded.Error())
  410. }
  411. err = transfer.CheckWrite()
  412. assert.True(t, conn.IsQuotaExceededError(err))
  413. }
  414. func TestUploadOutsideHomeRenameError(t *testing.T) {
  415. oldTempPath := Config.TempPath
  416. conn := NewBaseConnection("", ProtocolSFTP, "", "", dataprovider.User{})
  417. transfer := BaseTransfer{
  418. Connection: conn,
  419. transferType: TransferUpload,
  420. Fs: vfs.NewOsFs("", filepath.Join(os.TempDir(), "home"), ""),
  421. }
  422. transfer.BytesReceived.Store(123)
  423. fileName := filepath.Join(os.TempDir(), "_temp")
  424. err := os.WriteFile(fileName, []byte(`data`), 0644)
  425. assert.NoError(t, err)
  426. transfer.effectiveFsPath = fileName
  427. res := transfer.checkUploadOutsideHomeDir(os.ErrPermission)
  428. assert.Equal(t, 0, res)
  429. Config.TempPath = filepath.Clean(os.TempDir())
  430. res = transfer.checkUploadOutsideHomeDir(nil)
  431. assert.Equal(t, 0, res)
  432. assert.Greater(t, transfer.BytesReceived.Load(), int64(0))
  433. res = transfer.checkUploadOutsideHomeDir(os.ErrPermission)
  434. assert.Equal(t, 1, res)
  435. assert.Equal(t, int64(0), transfer.BytesReceived.Load())
  436. assert.NoFileExists(t, fileName)
  437. Config.TempPath = oldTempPath
  438. }