transfer_test.go 15 KB

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