ftpd_test.go 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453
  1. package ftpd_test
  2. import (
  3. "crypto/rand"
  4. "crypto/tls"
  5. "encoding/base64"
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "net"
  11. "net/http"
  12. "os"
  13. "os/exec"
  14. "path"
  15. "path/filepath"
  16. "runtime"
  17. "testing"
  18. "time"
  19. "github.com/jlaffaye/ftp"
  20. "github.com/rs/zerolog"
  21. "github.com/stretchr/testify/assert"
  22. "github.com/drakkan/sftpgo/common"
  23. "github.com/drakkan/sftpgo/config"
  24. "github.com/drakkan/sftpgo/dataprovider"
  25. "github.com/drakkan/sftpgo/ftpd"
  26. "github.com/drakkan/sftpgo/httpd"
  27. "github.com/drakkan/sftpgo/logger"
  28. "github.com/drakkan/sftpgo/vfs"
  29. )
  30. const (
  31. logSender = "ftpdTesting"
  32. ftpServerAddr = "127.0.0.1:2121"
  33. defaultUsername = "test_user_ftp"
  34. defaultPassword = "test_password"
  35. configDir = ".."
  36. osWindows = "windows"
  37. ftpsCert = `-----BEGIN CERTIFICATE-----
  38. MIICHTCCAaKgAwIBAgIUHnqw7QnB1Bj9oUsNpdb+ZkFPOxMwCgYIKoZIzj0EAwIw
  39. RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
  40. dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDAyMDQwOTUzMDRaFw0zMDAyMDEw
  41. OTUzMDRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
  42. VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA
  43. IgNiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVqWvrJ51t5OxV0v25NsOgR82CA
  44. NXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIVCzgWkxiz7XE4lgUwX44FCXZM
  45. 3+JeUbKjUzBRMB0GA1UdDgQWBBRhLw+/o3+Z02MI/d4tmaMui9W16jAfBgNVHSME
  46. GDAWgBRhLw+/o3+Z02MI/d4tmaMui9W16jAPBgNVHRMBAf8EBTADAQH/MAoGCCqG
  47. SM49BAMCA2kAMGYCMQDqLt2lm8mE+tGgtjDmtFgdOcI72HSbRQ74D5rYTzgST1rY
  48. /8wTi5xl8TiFUyLMUsICMQC5ViVxdXbhuG7gX6yEqSkMKZICHpO8hqFwOD/uaFVI
  49. dV4vKmHUzwK/eIx+8Ay3neE=
  50. -----END CERTIFICATE-----`
  51. ftpsKey = `-----BEGIN EC PARAMETERS-----
  52. BgUrgQQAIg==
  53. -----END EC PARAMETERS-----
  54. -----BEGIN EC PRIVATE KEY-----
  55. MIGkAgEBBDCfMNsN6miEE3rVyUPwElfiJSWaR5huPCzUenZOfJT04GAcQdWvEju3
  56. UM2lmBLIXpGgBwYFK4EEACKhZANiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVq
  57. WvrJ51t5OxV0v25NsOgR82CANXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIV
  58. CzgWkxiz7XE4lgUwX44FCXZM3+JeUbI=
  59. -----END EC PRIVATE KEY-----`
  60. testFileName = "test_file_ftp.dat"
  61. testDLFileName = "test_download_ftp.dat"
  62. )
  63. var (
  64. allPerms = []string{dataprovider.PermAny}
  65. homeBasePath string
  66. hookCmdPath string
  67. extAuthPath string
  68. preLoginPath string
  69. postConnectPath string
  70. logFilePath string
  71. )
  72. func TestMain(m *testing.M) {
  73. logFilePath = filepath.Join(configDir, "sftpgo_ftpd_test.log")
  74. bannerFileName := "banner_file"
  75. bannerFile := filepath.Join(configDir, bannerFileName)
  76. logger.InitLogger(logFilePath, 5, 1, 28, false, zerolog.DebugLevel)
  77. err := ioutil.WriteFile(bannerFile, []byte("SFTPGo test ready\nsimple banner line\n"), os.ModePerm)
  78. if err != nil {
  79. logger.ErrorToConsole("error creating banner file: %v", err)
  80. }
  81. err = config.LoadConfig(configDir, "")
  82. if err != nil {
  83. logger.ErrorToConsole("error loading configuration: %v", err)
  84. os.Exit(1)
  85. }
  86. providerConf := config.GetProviderConf()
  87. logger.InfoToConsole("Starting FTPD tests, provider: %v", providerConf.Driver)
  88. commonConf := config.GetCommonConfig()
  89. // we run the test cases with UploadMode atomic and resume support. The non atomic code path
  90. // simply does not execute some code so if it works in atomic mode will
  91. // work in non atomic mode too
  92. commonConf.UploadMode = 2
  93. homeBasePath = os.TempDir()
  94. if runtime.GOOS != osWindows {
  95. commonConf.Actions.ExecuteOn = []string{"download", "upload", "rename", "delete"}
  96. commonConf.Actions.Hook = hookCmdPath
  97. hookCmdPath, err = exec.LookPath("true")
  98. if err != nil {
  99. logger.Warn(logSender, "", "unable to get hook command: %v", err)
  100. logger.WarnToConsole("unable to get hook command: %v", err)
  101. }
  102. }
  103. certPath := filepath.Join(os.TempDir(), "test_ftpd.crt")
  104. keyPath := filepath.Join(os.TempDir(), "test_ftpd.key")
  105. err = ioutil.WriteFile(certPath, []byte(ftpsCert), os.ModePerm)
  106. if err != nil {
  107. logger.ErrorToConsole("error writing FTPS certificate: %v", err)
  108. os.Exit(1)
  109. }
  110. err = ioutil.WriteFile(keyPath, []byte(ftpsKey), os.ModePerm)
  111. if err != nil {
  112. logger.ErrorToConsole("error writing FTPS private key: %v", err)
  113. os.Exit(1)
  114. }
  115. common.Initialize(commonConf)
  116. err = dataprovider.Initialize(providerConf, configDir)
  117. if err != nil {
  118. logger.ErrorToConsole("error initializing data provider: %v", err)
  119. os.Exit(1)
  120. }
  121. httpConfig := config.GetHTTPConfig()
  122. httpConfig.Initialize(configDir)
  123. httpdConf := config.GetHTTPDConfig()
  124. httpdConf.BindPort = 8079
  125. httpd.SetBaseURLAndCredentials("http://127.0.0.1:8079", "", "")
  126. ftpdConf := config.GetFTPDConfig()
  127. ftpdConf.BindPort = 2121
  128. ftpdConf.PassivePortRange.Start = 0
  129. ftpdConf.PassivePortRange.End = 0
  130. ftpdConf.BannerFile = bannerFileName
  131. ftpdConf.CertificateFile = certPath
  132. ftpdConf.CertificateKeyFile = keyPath
  133. extAuthPath = filepath.Join(homeBasePath, "extauth.sh")
  134. preLoginPath = filepath.Join(homeBasePath, "prelogin.sh")
  135. postConnectPath = filepath.Join(homeBasePath, "postconnect.sh")
  136. go func() {
  137. logger.Debug(logSender, "", "initializing FTP server with config %+v", ftpdConf)
  138. if err := ftpdConf.Initialize(configDir); err != nil {
  139. logger.ErrorToConsole("could not start FTP server: %v", err)
  140. os.Exit(1)
  141. }
  142. }()
  143. go func() {
  144. if err := httpdConf.Initialize(configDir, false); err != nil {
  145. logger.ErrorToConsole("could not start HTTP server: %v", err)
  146. os.Exit(1)
  147. }
  148. }()
  149. waitTCPListening(fmt.Sprintf("%s:%d", ftpdConf.BindAddress, ftpdConf.BindPort))
  150. waitTCPListening(fmt.Sprintf("%s:%d", httpdConf.BindAddress, httpdConf.BindPort))
  151. ftpd.ReloadTLSCertificate() //nolint:errcheck
  152. exitCode := m.Run()
  153. os.Remove(logFilePath)
  154. os.Remove(bannerFile)
  155. os.Remove(extAuthPath)
  156. os.Remove(preLoginPath)
  157. os.Remove(postConnectPath)
  158. os.Remove(certPath)
  159. os.Remove(keyPath)
  160. os.Exit(exitCode)
  161. }
  162. func TestBasicFTPHandling(t *testing.T) {
  163. u := getTestUser()
  164. u.QuotaSize = 6553600
  165. user, _, err := httpd.AddUser(u, http.StatusOK)
  166. assert.NoError(t, err)
  167. client, err := getFTPClient(user, true)
  168. if assert.NoError(t, err) {
  169. assert.Len(t, common.Connections.GetStats(), 1)
  170. testFilePath := filepath.Join(homeBasePath, testFileName)
  171. testFileSize := int64(65535)
  172. expectedQuotaSize := user.UsedQuotaSize + testFileSize
  173. expectedQuotaFiles := user.UsedQuotaFiles + 1
  174. err = createTestFile(testFilePath, testFileSize)
  175. assert.NoError(t, err)
  176. err = checkBasicFTP(client)
  177. assert.NoError(t, err)
  178. err = ftpUploadFile(testFilePath, path.Join("/missing_dir", testFileName), testFileSize, client, 0)
  179. assert.Error(t, err)
  180. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  181. assert.NoError(t, err)
  182. // overwrite an existing file
  183. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  184. assert.NoError(t, err)
  185. localDownloadPath := filepath.Join(homeBasePath, testDLFileName)
  186. err = ftpDownloadFile(testFileName, localDownloadPath, testFileSize, client, 0)
  187. assert.NoError(t, err)
  188. user, _, err = httpd.GetUserByID(user.ID, http.StatusOK)
  189. assert.NoError(t, err)
  190. assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles)
  191. assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize)
  192. err = client.Rename(testFileName, testFileName+"1")
  193. assert.NoError(t, err)
  194. err = client.Delete(testFileName)
  195. assert.Error(t, err)
  196. err = client.Delete(testFileName + "1")
  197. assert.NoError(t, err)
  198. user, _, err = httpd.GetUserByID(user.ID, http.StatusOK)
  199. assert.NoError(t, err)
  200. assert.Equal(t, expectedQuotaFiles-1, user.UsedQuotaFiles)
  201. assert.Equal(t, expectedQuotaSize-testFileSize, user.UsedQuotaSize)
  202. curDir, err := client.CurrentDir()
  203. if assert.NoError(t, err) {
  204. assert.Equal(t, "/", curDir)
  205. }
  206. testDir := "testDir"
  207. err = client.MakeDir(testDir)
  208. assert.NoError(t, err)
  209. err = client.ChangeDir(testDir)
  210. assert.NoError(t, err)
  211. curDir, err = client.CurrentDir()
  212. if assert.NoError(t, err) {
  213. assert.Equal(t, path.Join("/", testDir), curDir)
  214. }
  215. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  216. assert.NoError(t, err)
  217. size, err := client.FileSize(path.Join("/", testDir, testFileName))
  218. assert.NoError(t, err)
  219. assert.Equal(t, testFileSize, size)
  220. err = client.ChangeDirToParent()
  221. assert.NoError(t, err)
  222. curDir, err = client.CurrentDir()
  223. if assert.NoError(t, err) {
  224. assert.Equal(t, "/", curDir)
  225. }
  226. err = client.Delete(path.Join("/", testDir, testFileName))
  227. assert.NoError(t, err)
  228. err = client.Delete(testDir)
  229. assert.Error(t, err)
  230. err = client.RemoveDir(testDir)
  231. assert.NoError(t, err)
  232. err = os.Remove(testFilePath)
  233. assert.NoError(t, err)
  234. err = os.Remove(localDownloadPath)
  235. assert.NoError(t, err)
  236. err = client.Quit()
  237. assert.NoError(t, err)
  238. }
  239. _, err = httpd.RemoveUser(user, http.StatusOK)
  240. assert.NoError(t, err)
  241. err = os.RemoveAll(user.GetHomeDir())
  242. assert.NoError(t, err)
  243. assert.Eventually(t, func() bool { return len(common.Connections.GetStats()) == 0 }, 1*time.Second, 50*time.Millisecond)
  244. }
  245. func TestLoginInvalidPwd(t *testing.T) {
  246. u := getTestUser()
  247. user, _, err := httpd.AddUser(u, http.StatusOK)
  248. assert.NoError(t, err)
  249. user.Password = "wrong"
  250. _, err = getFTPClient(user, false)
  251. assert.Error(t, err)
  252. _, err = httpd.RemoveUser(user, http.StatusOK)
  253. assert.NoError(t, err)
  254. }
  255. func TestLoginExternalAuth(t *testing.T) {
  256. if runtime.GOOS == osWindows {
  257. t.Skip("this test is not available on Windows")
  258. }
  259. u := getTestUser()
  260. err := dataprovider.Close()
  261. assert.NoError(t, err)
  262. err = config.LoadConfig(configDir, "")
  263. assert.NoError(t, err)
  264. providerConf := config.GetProviderConf()
  265. err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
  266. assert.NoError(t, err)
  267. providerConf.ExternalAuthHook = extAuthPath
  268. providerConf.ExternalAuthScope = 0
  269. err = dataprovider.Initialize(providerConf, configDir)
  270. assert.NoError(t, err)
  271. client, err := getFTPClient(u, true)
  272. if assert.NoError(t, err) {
  273. err = checkBasicFTP(client)
  274. assert.NoError(t, err)
  275. err := client.Quit()
  276. assert.NoError(t, err)
  277. }
  278. u.Username = defaultUsername + "1"
  279. client, err = getFTPClient(u, true)
  280. if !assert.Error(t, err) {
  281. err := client.Quit()
  282. assert.NoError(t, err)
  283. }
  284. users, _, err := httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
  285. assert.NoError(t, err)
  286. if assert.Len(t, users, 1) {
  287. user := users[0]
  288. assert.Equal(t, defaultUsername, user.Username)
  289. _, err = httpd.RemoveUser(user, http.StatusOK)
  290. assert.NoError(t, err)
  291. err = os.RemoveAll(user.GetHomeDir())
  292. assert.NoError(t, err)
  293. }
  294. err = dataprovider.Close()
  295. assert.NoError(t, err)
  296. err = config.LoadConfig(configDir, "")
  297. assert.NoError(t, err)
  298. providerConf = config.GetProviderConf()
  299. err = dataprovider.Initialize(providerConf, configDir)
  300. assert.NoError(t, err)
  301. err = os.Remove(extAuthPath)
  302. assert.NoError(t, err)
  303. }
  304. func TestPreLoginHook(t *testing.T) {
  305. if runtime.GOOS == osWindows {
  306. t.Skip("this test is not available on Windows")
  307. }
  308. u := getTestUser()
  309. err := dataprovider.Close()
  310. assert.NoError(t, err)
  311. err = config.LoadConfig(configDir, "")
  312. assert.NoError(t, err)
  313. providerConf := config.GetProviderConf()
  314. err = ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(u, false), os.ModePerm)
  315. assert.NoError(t, err)
  316. providerConf.PreLoginHook = preLoginPath
  317. err = dataprovider.Initialize(providerConf, configDir)
  318. assert.NoError(t, err)
  319. users, _, err := httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
  320. assert.NoError(t, err)
  321. assert.Equal(t, 0, len(users))
  322. client, err := getFTPClient(u, false)
  323. if assert.NoError(t, err) {
  324. err = checkBasicFTP(client)
  325. assert.NoError(t, err)
  326. err := client.Quit()
  327. assert.NoError(t, err)
  328. }
  329. users, _, err = httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
  330. assert.NoError(t, err)
  331. assert.Equal(t, 1, len(users))
  332. user := users[0]
  333. // test login with an existing user
  334. client, err = getFTPClient(user, true)
  335. if assert.NoError(t, err) {
  336. err = checkBasicFTP(client)
  337. assert.NoError(t, err)
  338. err := client.Quit()
  339. assert.NoError(t, err)
  340. }
  341. err = ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(user, true), os.ModePerm)
  342. assert.NoError(t, err)
  343. client, err = getFTPClient(u, false)
  344. if !assert.Error(t, err) {
  345. err := client.Quit()
  346. assert.NoError(t, err)
  347. }
  348. user.Status = 0
  349. err = ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(user, false), os.ModePerm)
  350. assert.NoError(t, err)
  351. client, err = getFTPClient(u, false)
  352. if !assert.Error(t, err, "pre-login script returned a disabled user, login must fail") {
  353. err := client.Quit()
  354. assert.NoError(t, err)
  355. }
  356. _, err = httpd.RemoveUser(user, http.StatusOK)
  357. assert.NoError(t, err)
  358. err = os.RemoveAll(user.GetHomeDir())
  359. assert.NoError(t, err)
  360. err = dataprovider.Close()
  361. assert.NoError(t, err)
  362. err = config.LoadConfig(configDir, "")
  363. assert.NoError(t, err)
  364. providerConf = config.GetProviderConf()
  365. err = dataprovider.Initialize(providerConf, configDir)
  366. assert.NoError(t, err)
  367. err = os.Remove(preLoginPath)
  368. assert.NoError(t, err)
  369. }
  370. func TestPostConnectHook(t *testing.T) {
  371. if runtime.GOOS == osWindows {
  372. t.Skip("this test is not available on Windows")
  373. }
  374. common.Config.PostConnectHook = postConnectPath
  375. u := getTestUser()
  376. user, _, err := httpd.AddUser(u, http.StatusOK)
  377. assert.NoError(t, err)
  378. err = ioutil.WriteFile(postConnectPath, getPostConnectScriptContent(0), os.ModePerm)
  379. assert.NoError(t, err)
  380. client, err := getFTPClient(user, true)
  381. if assert.NoError(t, err) {
  382. err = checkBasicFTP(client)
  383. assert.NoError(t, err)
  384. err := client.Quit()
  385. assert.NoError(t, err)
  386. }
  387. err = ioutil.WriteFile(postConnectPath, getPostConnectScriptContent(1), os.ModePerm)
  388. assert.NoError(t, err)
  389. client, err = getFTPClient(user, true)
  390. if !assert.Error(t, err) {
  391. err := client.Quit()
  392. assert.NoError(t, err)
  393. }
  394. common.Config.PostConnectHook = "http://127.0.0.1:8079/api/v1/version"
  395. client, err = getFTPClient(user, false)
  396. if assert.NoError(t, err) {
  397. err = checkBasicFTP(client)
  398. assert.NoError(t, err)
  399. err := client.Quit()
  400. assert.NoError(t, err)
  401. }
  402. common.Config.PostConnectHook = "http://127.0.0.1:8079/notfound"
  403. client, err = getFTPClient(user, true)
  404. if !assert.Error(t, err) {
  405. err := client.Quit()
  406. assert.NoError(t, err)
  407. }
  408. _, err = httpd.RemoveUser(user, http.StatusOK)
  409. assert.NoError(t, err)
  410. err = os.RemoveAll(user.GetHomeDir())
  411. assert.NoError(t, err)
  412. common.Config.PostConnectHook = ""
  413. }
  414. func TestMaxSessions(t *testing.T) {
  415. u := getTestUser()
  416. u.MaxSessions = 1
  417. user, _, err := httpd.AddUser(u, http.StatusOK)
  418. assert.NoError(t, err)
  419. client, err := getFTPClient(user, true)
  420. if assert.NoError(t, err) {
  421. err = checkBasicFTP(client)
  422. assert.NoError(t, err)
  423. _, err = getFTPClient(user, false)
  424. assert.Error(t, err)
  425. err = client.Quit()
  426. assert.NoError(t, err)
  427. }
  428. _, err = httpd.RemoveUser(user, http.StatusOK)
  429. assert.NoError(t, err)
  430. err = os.RemoveAll(user.GetHomeDir())
  431. assert.NoError(t, err)
  432. }
  433. func TestZeroBytesTransfers(t *testing.T) {
  434. u := getTestUser()
  435. user, _, err := httpd.AddUser(u, http.StatusOK)
  436. assert.NoError(t, err)
  437. for _, useTLS := range []bool{true, false} {
  438. client, err := getFTPClient(user, useTLS)
  439. if assert.NoError(t, err) {
  440. testFileName := "testfilename"
  441. err = checkBasicFTP(client)
  442. assert.NoError(t, err)
  443. localDownloadPath := filepath.Join(homeBasePath, "empty_download")
  444. err = ioutil.WriteFile(localDownloadPath, []byte(""), os.ModePerm)
  445. assert.NoError(t, err)
  446. err = ftpUploadFile(localDownloadPath, testFileName, 0, client, 0)
  447. assert.NoError(t, err)
  448. size, err := client.FileSize(testFileName)
  449. assert.NoError(t, err)
  450. assert.Equal(t, int64(0), size)
  451. err = os.Remove(localDownloadPath)
  452. assert.NoError(t, err)
  453. assert.NoFileExists(t, localDownloadPath)
  454. err = ftpDownloadFile(testFileName, localDownloadPath, 0, client, 0)
  455. assert.NoError(t, err)
  456. assert.FileExists(t, localDownloadPath)
  457. err = client.Quit()
  458. assert.NoError(t, err)
  459. err = os.Remove(localDownloadPath)
  460. assert.NoError(t, err)
  461. }
  462. }
  463. _, err = httpd.RemoveUser(user, http.StatusOK)
  464. assert.NoError(t, err)
  465. err = os.RemoveAll(user.GetHomeDir())
  466. assert.NoError(t, err)
  467. }
  468. func TestDownloadErrors(t *testing.T) {
  469. u := getTestUser()
  470. u.QuotaFiles = 1
  471. subDir1 := "sub1"
  472. subDir2 := "sub2"
  473. u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems}
  474. u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
  475. dataprovider.PermDelete, dataprovider.PermDownload}
  476. u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
  477. {
  478. Path: "/sub2",
  479. AllowedExtensions: []string{},
  480. DeniedExtensions: []string{".zip"},
  481. },
  482. }
  483. user, _, err := httpd.AddUser(u, http.StatusOK)
  484. assert.NoError(t, err)
  485. client, err := getFTPClient(user, true)
  486. if assert.NoError(t, err) {
  487. testFilePath1 := filepath.Join(user.HomeDir, subDir1, "file.zip")
  488. testFilePath2 := filepath.Join(user.HomeDir, subDir2, "file.zip")
  489. err = os.MkdirAll(filepath.Dir(testFilePath1), os.ModePerm)
  490. assert.NoError(t, err)
  491. err = os.MkdirAll(filepath.Dir(testFilePath2), os.ModePerm)
  492. assert.NoError(t, err)
  493. err = ioutil.WriteFile(testFilePath1, []byte("file1"), os.ModePerm)
  494. assert.NoError(t, err)
  495. err = ioutil.WriteFile(testFilePath2, []byte("file2"), os.ModePerm)
  496. assert.NoError(t, err)
  497. localDownloadPath := filepath.Join(homeBasePath, testDLFileName)
  498. err = ftpDownloadFile(path.Join("/", subDir1, "file.zip"), localDownloadPath, 5, client, 0)
  499. assert.Error(t, err)
  500. err = ftpDownloadFile(path.Join("/", subDir2, "file.zip"), localDownloadPath, 5, client, 0)
  501. assert.Error(t, err)
  502. err = ftpDownloadFile("/missing.zip", localDownloadPath, 5, client, 0)
  503. assert.Error(t, err)
  504. err = client.Quit()
  505. assert.NoError(t, err)
  506. err = os.Remove(localDownloadPath)
  507. assert.NoError(t, err)
  508. }
  509. _, err = httpd.RemoveUser(user, http.StatusOK)
  510. assert.NoError(t, err)
  511. err = os.RemoveAll(user.GetHomeDir())
  512. assert.NoError(t, err)
  513. }
  514. func TestUploadErrors(t *testing.T) {
  515. u := getTestUser()
  516. u.QuotaSize = 65535
  517. subDir1 := "sub1"
  518. subDir2 := "sub2"
  519. u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems}
  520. u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
  521. dataprovider.PermDelete}
  522. u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
  523. {
  524. Path: "/sub2",
  525. AllowedExtensions: []string{},
  526. DeniedExtensions: []string{".zip"},
  527. },
  528. }
  529. user, _, err := httpd.AddUser(u, http.StatusOK)
  530. assert.NoError(t, err)
  531. client, err := getFTPClient(user, true)
  532. if assert.NoError(t, err) {
  533. testFilePath := filepath.Join(homeBasePath, testFileName)
  534. testFileSize := user.QuotaSize
  535. err = createTestFile(testFilePath, testFileSize)
  536. assert.NoError(t, err)
  537. err = client.MakeDir(subDir1)
  538. assert.NoError(t, err)
  539. err = client.MakeDir(subDir2)
  540. assert.NoError(t, err)
  541. err = client.ChangeDir(subDir1)
  542. assert.NoError(t, err)
  543. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  544. assert.Error(t, err)
  545. err = client.ChangeDirToParent()
  546. assert.NoError(t, err)
  547. err = client.ChangeDir(subDir2)
  548. assert.NoError(t, err)
  549. err = ftpUploadFile(testFilePath, testFileName+".zip", testFileSize, client, 0)
  550. assert.Error(t, err)
  551. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  552. assert.NoError(t, err)
  553. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  554. assert.Error(t, err)
  555. err = client.ChangeDir("/")
  556. assert.NoError(t, err)
  557. err = ftpUploadFile(testFilePath, subDir1, testFileSize, client, 0)
  558. assert.Error(t, err)
  559. // overquota
  560. err = ftpUploadFile(testFilePath, testFileName+"1", testFileSize, client, 0)
  561. assert.Error(t, err)
  562. err = client.Delete(path.Join("/", subDir2, testFileName))
  563. assert.NoError(t, err)
  564. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  565. assert.NoError(t, err)
  566. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  567. assert.Error(t, err)
  568. err = client.Quit()
  569. assert.NoError(t, err)
  570. err = os.Remove(testFilePath)
  571. assert.NoError(t, err)
  572. }
  573. _, err = httpd.RemoveUser(user, http.StatusOK)
  574. assert.NoError(t, err)
  575. err = os.RemoveAll(user.GetHomeDir())
  576. assert.NoError(t, err)
  577. }
  578. func TestResume(t *testing.T) {
  579. u := getTestUser()
  580. user, _, err := httpd.AddUser(u, http.StatusOK)
  581. assert.NoError(t, err)
  582. client, err := getFTPClient(user, true)
  583. if assert.NoError(t, err) {
  584. testFilePath := filepath.Join(homeBasePath, testFileName)
  585. data := []byte("test data")
  586. err = ioutil.WriteFile(testFilePath, data, os.ModePerm)
  587. assert.NoError(t, err)
  588. err = ftpUploadFile(testFilePath, testFileName, int64(len(data)), client, 0)
  589. assert.NoError(t, err)
  590. err = ftpUploadFile(testFilePath, testFileName, int64(len(data)+5), client, 5)
  591. assert.NoError(t, err)
  592. readed, err := ioutil.ReadFile(filepath.Join(user.GetHomeDir(), testFileName))
  593. assert.NoError(t, err)
  594. assert.Equal(t, "test test data", string(readed))
  595. localDownloadPath := filepath.Join(homeBasePath, testDLFileName)
  596. err = ftpDownloadFile(testFileName, localDownloadPath, int64(len(data)), client, 5)
  597. assert.NoError(t, err)
  598. readed, err = ioutil.ReadFile(localDownloadPath)
  599. assert.NoError(t, err)
  600. assert.Equal(t, data, readed)
  601. err = client.Delete(testFileName)
  602. assert.NoError(t, err)
  603. err = ftpUploadFile(testFilePath, testFileName, int64(len(data)), client, 0)
  604. assert.NoError(t, err)
  605. // now append to a file
  606. srcFile, err := os.Open(testFilePath)
  607. if assert.NoError(t, err) {
  608. err = client.Append(testFileName, srcFile)
  609. assert.NoError(t, err)
  610. err = srcFile.Close()
  611. assert.NoError(t, err)
  612. size, err := client.FileSize(testFileName)
  613. assert.NoError(t, err)
  614. assert.Equal(t, int64(2*len(data)), size)
  615. err = ftpDownloadFile(testFileName, localDownloadPath, int64(2*len(data)), client, 0)
  616. assert.NoError(t, err)
  617. readed, err = ioutil.ReadFile(localDownloadPath)
  618. assert.NoError(t, err)
  619. expected := append(data, data...)
  620. assert.Equal(t, expected, readed)
  621. }
  622. err = client.Quit()
  623. assert.NoError(t, err)
  624. err = os.Remove(testFilePath)
  625. assert.NoError(t, err)
  626. err = os.Remove(localDownloadPath)
  627. assert.NoError(t, err)
  628. }
  629. _, err = httpd.RemoveUser(user, http.StatusOK)
  630. assert.NoError(t, err)
  631. err = os.RemoveAll(user.GetHomeDir())
  632. assert.NoError(t, err)
  633. }
  634. //nolint:dupl
  635. func TestDeniedLoginMethod(t *testing.T) {
  636. u := getTestUser()
  637. u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword}
  638. user, _, err := httpd.AddUser(u, http.StatusOK)
  639. assert.NoError(t, err)
  640. _, err = getFTPClient(user, false)
  641. assert.Error(t, err)
  642. user.Filters.DeniedLoginMethods = []string{dataprovider.SSHLoginMethodPublicKey, dataprovider.SSHLoginMethodKeyAndPassword}
  643. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  644. assert.NoError(t, err)
  645. client, err := getFTPClient(user, true)
  646. if assert.NoError(t, err) {
  647. assert.NoError(t, checkBasicFTP(client))
  648. err = client.Quit()
  649. assert.NoError(t, err)
  650. }
  651. _, err = httpd.RemoveUser(user, http.StatusOK)
  652. assert.NoError(t, err)
  653. err = os.RemoveAll(user.GetHomeDir())
  654. assert.NoError(t, err)
  655. }
  656. //nolint:dupl
  657. func TestDeniedProtocols(t *testing.T) {
  658. u := getTestUser()
  659. u.Filters.DeniedProtocols = []string{common.ProtocolFTP}
  660. user, _, err := httpd.AddUser(u, http.StatusOK)
  661. assert.NoError(t, err)
  662. _, err = getFTPClient(user, false)
  663. assert.Error(t, err)
  664. user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolWebDAV}
  665. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  666. assert.NoError(t, err)
  667. client, err := getFTPClient(user, true)
  668. if assert.NoError(t, err) {
  669. assert.NoError(t, checkBasicFTP(client))
  670. err = client.Quit()
  671. assert.NoError(t, err)
  672. }
  673. _, err = httpd.RemoveUser(user, http.StatusOK)
  674. assert.NoError(t, err)
  675. err = os.RemoveAll(user.GetHomeDir())
  676. assert.NoError(t, err)
  677. }
  678. func TestQuotaLimits(t *testing.T) {
  679. u := getTestUser()
  680. u.QuotaFiles = 1
  681. user, _, err := httpd.AddUser(u, http.StatusOK)
  682. assert.NoError(t, err)
  683. testFileSize := int64(65535)
  684. testFilePath := filepath.Join(homeBasePath, testFileName)
  685. err = createTestFile(testFilePath, testFileSize)
  686. assert.NoError(t, err)
  687. testFileSize1 := int64(131072)
  688. testFileName1 := "test_file1.dat"
  689. testFilePath1 := filepath.Join(homeBasePath, testFileName1)
  690. err = createTestFile(testFilePath1, testFileSize1)
  691. assert.NoError(t, err)
  692. testFileSize2 := int64(32768)
  693. testFileName2 := "test_file2.dat"
  694. testFilePath2 := filepath.Join(homeBasePath, testFileName2)
  695. err = createTestFile(testFilePath2, testFileSize2)
  696. assert.NoError(t, err)
  697. // test quota files
  698. client, err := getFTPClient(user, false)
  699. if assert.NoError(t, err) {
  700. err = ftpUploadFile(testFilePath, testFileName+".quota", testFileSize, client, 0)
  701. assert.NoError(t, err)
  702. err = ftpUploadFile(testFilePath, testFileName+".quota1", testFileSize, client, 0)
  703. assert.Error(t, err)
  704. err = client.Rename(testFileName+".quota", testFileName)
  705. assert.NoError(t, err)
  706. err = client.Quit()
  707. assert.NoError(t, err)
  708. }
  709. // test quota size
  710. user.QuotaSize = testFileSize - 1
  711. user.QuotaFiles = 0
  712. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  713. assert.NoError(t, err)
  714. client, err = getFTPClient(user, true)
  715. if assert.NoError(t, err) {
  716. err = ftpUploadFile(testFilePath, testFileName+".quota", testFileSize, client, 0)
  717. assert.Error(t, err)
  718. err = client.Rename(testFileName, testFileName+".quota")
  719. assert.NoError(t, err)
  720. err = client.Quit()
  721. assert.NoError(t, err)
  722. }
  723. // now test quota limits while uploading the current file, we have 1 bytes remaining
  724. user.QuotaSize = testFileSize + 1
  725. user.QuotaFiles = 0
  726. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  727. assert.NoError(t, err)
  728. client, err = getFTPClient(user, false)
  729. if assert.NoError(t, err) {
  730. err = ftpUploadFile(testFilePath1, testFileName1, testFileSize1, client, 0)
  731. assert.Error(t, err)
  732. _, err = client.FileSize(testFileName1)
  733. assert.Error(t, err)
  734. err = client.Rename(testFileName+".quota", testFileName)
  735. assert.NoError(t, err)
  736. // overwriting an existing file will work if the resulting size is lesser or equal than the current one
  737. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  738. assert.NoError(t, err)
  739. err = ftpUploadFile(testFilePath2, testFileName, testFileSize2, client, 0)
  740. assert.NoError(t, err)
  741. err = ftpUploadFile(testFilePath1, testFileName, testFileSize1, client, 0)
  742. assert.Error(t, err)
  743. err = ftpUploadFile(testFilePath1, testFileName, testFileSize1, client, 10)
  744. assert.Error(t, err)
  745. err = ftpUploadFile(testFilePath2, testFileName, testFileSize2, client, 0)
  746. assert.NoError(t, err)
  747. err = client.Quit()
  748. assert.NoError(t, err)
  749. }
  750. err = os.Remove(testFilePath)
  751. assert.NoError(t, err)
  752. err = os.Remove(testFilePath1)
  753. assert.NoError(t, err)
  754. err = os.Remove(testFilePath2)
  755. assert.NoError(t, err)
  756. _, err = httpd.RemoveUser(user, http.StatusOK)
  757. assert.NoError(t, err)
  758. err = os.RemoveAll(user.GetHomeDir())
  759. assert.NoError(t, err)
  760. }
  761. func TestUploadMaxSize(t *testing.T) {
  762. testFileSize := int64(65535)
  763. u := getTestUser()
  764. u.Filters.MaxUploadFileSize = testFileSize + 1
  765. user, _, err := httpd.AddUser(u, http.StatusOK)
  766. assert.NoError(t, err)
  767. testFilePath := filepath.Join(homeBasePath, testFileName)
  768. err = createTestFile(testFilePath, testFileSize)
  769. assert.NoError(t, err)
  770. testFileSize1 := int64(131072)
  771. testFileName1 := "test_file1.dat"
  772. testFilePath1 := filepath.Join(homeBasePath, testFileName1)
  773. err = createTestFile(testFilePath1, testFileSize1)
  774. assert.NoError(t, err)
  775. client, err := getFTPClient(user, false)
  776. if assert.NoError(t, err) {
  777. err = ftpUploadFile(testFilePath1, testFileName1, testFileSize1, client, 0)
  778. assert.Error(t, err)
  779. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  780. assert.NoError(t, err)
  781. err = client.Quit()
  782. assert.NoError(t, err)
  783. }
  784. err = os.Remove(testFilePath)
  785. assert.NoError(t, err)
  786. err = os.Remove(testFilePath1)
  787. assert.NoError(t, err)
  788. _, err = httpd.RemoveUser(user, http.StatusOK)
  789. assert.NoError(t, err)
  790. err = os.RemoveAll(user.GetHomeDir())
  791. assert.NoError(t, err)
  792. }
  793. func TestLoginWithIPilters(t *testing.T) {
  794. u := getTestUser()
  795. u.Filters.DeniedIP = []string{"192.167.0.0/24", "172.18.0.0/16"}
  796. u.Filters.AllowedIP = []string{"172.19.0.0/16"}
  797. user, _, err := httpd.AddUser(u, http.StatusOK)
  798. assert.NoError(t, err)
  799. client, err := getFTPClient(user, true)
  800. if !assert.Error(t, err) {
  801. err = client.Quit()
  802. assert.NoError(t, err)
  803. }
  804. _, err = httpd.RemoveUser(user, http.StatusOK)
  805. assert.NoError(t, err)
  806. err = os.RemoveAll(user.GetHomeDir())
  807. assert.NoError(t, err)
  808. }
  809. func TestLoginInvalidFs(t *testing.T) {
  810. u := getTestUser()
  811. u.FsConfig.Provider = 2
  812. u.FsConfig.GCSConfig.Bucket = "test"
  813. u.FsConfig.GCSConfig.Credentials = base64.StdEncoding.EncodeToString([]byte("invalid JSON for credentials"))
  814. user, _, err := httpd.AddUser(u, http.StatusOK)
  815. assert.NoError(t, err)
  816. // now remove the credentials file so the filesystem creation will fail
  817. providerConf := config.GetProviderConf()
  818. credentialsFile := filepath.Join(providerConf.CredentialsPath, fmt.Sprintf("%v_gcs_credentials.json", u.Username))
  819. if !filepath.IsAbs(credentialsFile) {
  820. credentialsFile = filepath.Join(configDir, credentialsFile)
  821. }
  822. err = os.Remove(credentialsFile)
  823. assert.NoError(t, err)
  824. client, err := getFTPClient(user, false)
  825. if !assert.Error(t, err) {
  826. err = client.Quit()
  827. assert.NoError(t, err)
  828. }
  829. _, err = httpd.RemoveUser(user, http.StatusOK)
  830. assert.NoError(t, err)
  831. err = os.RemoveAll(user.GetHomeDir())
  832. assert.NoError(t, err)
  833. }
  834. func TestClientClose(t *testing.T) {
  835. u := getTestUser()
  836. user, _, err := httpd.AddUser(u, http.StatusOK)
  837. assert.NoError(t, err)
  838. client, err := getFTPClient(user, true)
  839. if assert.NoError(t, err) {
  840. err = checkBasicFTP(client)
  841. assert.NoError(t, err)
  842. stats := common.Connections.GetStats()
  843. if assert.Len(t, stats, 1) {
  844. common.Connections.Close(stats[0].ConnectionID)
  845. assert.Eventually(t, func() bool { return len(common.Connections.GetStats()) == 0 },
  846. 1*time.Second, 50*time.Millisecond)
  847. }
  848. }
  849. _, err = httpd.RemoveUser(user, http.StatusOK)
  850. assert.NoError(t, err)
  851. err = os.RemoveAll(user.GetHomeDir())
  852. assert.NoError(t, err)
  853. }
  854. func TestRename(t *testing.T) {
  855. u := getTestUser()
  856. user, _, err := httpd.AddUser(u, http.StatusOK)
  857. assert.NoError(t, err)
  858. testDir := "adir"
  859. testFilePath := filepath.Join(homeBasePath, testFileName)
  860. testFileSize := int64(65535)
  861. err = createTestFile(testFilePath, testFileSize)
  862. assert.NoError(t, err)
  863. client, err := getFTPClient(user, false)
  864. if assert.NoError(t, err) {
  865. err = checkBasicFTP(client)
  866. assert.NoError(t, err)
  867. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  868. assert.NoError(t, err)
  869. err = client.MakeDir(testDir)
  870. assert.NoError(t, err)
  871. err = client.Rename(testFileName, path.Join("missing", testFileName))
  872. assert.Error(t, err)
  873. err = client.Rename(testFileName, path.Join(testDir, testFileName))
  874. assert.NoError(t, err)
  875. size, err := client.FileSize(path.Join(testDir, testFileName))
  876. assert.NoError(t, err)
  877. assert.Equal(t, testFileSize, size)
  878. if runtime.GOOS != osWindows {
  879. otherDir := "dir"
  880. err = client.MakeDir(otherDir)
  881. assert.NoError(t, err)
  882. err = client.MakeDir(path.Join(otherDir, testDir))
  883. assert.NoError(t, err)
  884. code, response, err := client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 0001 %v", otherDir))
  885. assert.NoError(t, err)
  886. assert.Equal(t, ftp.StatusCommandOK, code)
  887. assert.Equal(t, "SITE CHMOD command successful", response)
  888. err = client.Rename(testDir, path.Join(otherDir, testDir))
  889. assert.Error(t, err)
  890. code, response, err = client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 755 %v", otherDir))
  891. assert.NoError(t, err)
  892. assert.Equal(t, ftp.StatusCommandOK, code)
  893. assert.Equal(t, "SITE CHMOD command successful", response)
  894. }
  895. err = client.Quit()
  896. assert.NoError(t, err)
  897. }
  898. user.Permissions[path.Join("/", testDir)] = []string{dataprovider.PermListItems}
  899. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  900. assert.NoError(t, err)
  901. client, err = getFTPClient(user, false)
  902. if assert.NoError(t, err) {
  903. err = client.Rename(path.Join(testDir, testFileName), testFileName)
  904. assert.Error(t, err)
  905. err := client.Quit()
  906. assert.NoError(t, err)
  907. }
  908. err = os.Remove(testFilePath)
  909. assert.NoError(t, err)
  910. _, err = httpd.RemoveUser(user, http.StatusOK)
  911. assert.NoError(t, err)
  912. err = os.RemoveAll(user.GetHomeDir())
  913. assert.NoError(t, err)
  914. }
  915. func TestSymlink(t *testing.T) {
  916. u := getTestUser()
  917. user, _, err := httpd.AddUser(u, http.StatusOK)
  918. assert.NoError(t, err)
  919. testFilePath := filepath.Join(homeBasePath, testFileName)
  920. testFileSize := int64(65535)
  921. err = createTestFile(testFilePath, testFileSize)
  922. assert.NoError(t, err)
  923. client, err := getFTPClient(user, false)
  924. if assert.NoError(t, err) {
  925. err = checkBasicFTP(client)
  926. assert.NoError(t, err)
  927. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  928. assert.NoError(t, err)
  929. code, _, err := client.SendCustomCommand(fmt.Sprintf("SITE SYMLINK %v %v", testFileName, testFileName+".link"))
  930. assert.NoError(t, err)
  931. assert.Equal(t, ftp.StatusCommandOK, code)
  932. if runtime.GOOS != osWindows {
  933. testDir := "adir"
  934. otherDir := "dir"
  935. err = client.MakeDir(otherDir)
  936. assert.NoError(t, err)
  937. err = client.MakeDir(path.Join(otherDir, testDir))
  938. assert.NoError(t, err)
  939. code, response, err := client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 0001 %v", otherDir))
  940. assert.NoError(t, err)
  941. assert.Equal(t, ftp.StatusCommandOK, code)
  942. assert.Equal(t, "SITE CHMOD command successful", response)
  943. code, _, err = client.SendCustomCommand(fmt.Sprintf("SITE SYMLINK %v %v", testDir, path.Join(otherDir, testDir)))
  944. assert.NoError(t, err)
  945. assert.Equal(t, ftp.StatusFileUnavailable, code)
  946. code, response, err = client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 755 %v", otherDir))
  947. assert.NoError(t, err)
  948. assert.Equal(t, ftp.StatusCommandOK, code)
  949. assert.Equal(t, "SITE CHMOD command successful", response)
  950. }
  951. err = client.Quit()
  952. assert.NoError(t, err)
  953. }
  954. err = os.Remove(testFilePath)
  955. assert.NoError(t, err)
  956. _, err = httpd.RemoveUser(user, http.StatusOK)
  957. assert.NoError(t, err)
  958. err = os.RemoveAll(user.GetHomeDir())
  959. assert.NoError(t, err)
  960. }
  961. func TestStat(t *testing.T) {
  962. u := getTestUser()
  963. u.Permissions["/subdir"] = []string{dataprovider.PermUpload}
  964. user, _, err := httpd.AddUser(u, http.StatusOK)
  965. assert.NoError(t, err)
  966. client, err := getFTPClient(user, false)
  967. if assert.NoError(t, err) {
  968. subDir := "subdir"
  969. testFilePath := filepath.Join(homeBasePath, testFileName)
  970. testFileSize := int64(65535)
  971. err = createTestFile(testFilePath, testFileSize)
  972. assert.NoError(t, err)
  973. err = client.MakeDir(subDir)
  974. assert.NoError(t, err)
  975. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  976. assert.NoError(t, err)
  977. err = ftpUploadFile(testFilePath, path.Join("/", subDir, testFileName), testFileSize, client, 0)
  978. assert.Error(t, err)
  979. size, err := client.FileSize(testFileName)
  980. assert.NoError(t, err)
  981. assert.Equal(t, testFileSize, size)
  982. _, err = client.FileSize(path.Join("/", subDir, testFileName))
  983. assert.Error(t, err)
  984. _, err = client.FileSize("missing file")
  985. assert.Error(t, err)
  986. err = client.Quit()
  987. assert.NoError(t, err)
  988. err = os.Remove(testFilePath)
  989. assert.NoError(t, err)
  990. }
  991. _, err = httpd.RemoveUser(user, http.StatusOK)
  992. assert.NoError(t, err)
  993. err = os.RemoveAll(user.GetHomeDir())
  994. assert.NoError(t, err)
  995. }
  996. func TestUploadOverwriteVfolder(t *testing.T) {
  997. u := getTestUser()
  998. vdir := "/vdir"
  999. mappedPath := filepath.Join(os.TempDir(), "vdir")
  1000. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  1001. BaseVirtualFolder: vfs.BaseVirtualFolder{
  1002. MappedPath: mappedPath,
  1003. },
  1004. VirtualPath: vdir,
  1005. QuotaSize: -1,
  1006. QuotaFiles: -1,
  1007. })
  1008. err := os.MkdirAll(mappedPath, os.ModePerm)
  1009. assert.NoError(t, err)
  1010. user, _, err := httpd.AddUser(u, http.StatusOK)
  1011. assert.NoError(t, err)
  1012. client, err := getFTPClient(user, false)
  1013. if assert.NoError(t, err) {
  1014. testFilePath := filepath.Join(homeBasePath, testFileName)
  1015. testFileSize := int64(65535)
  1016. err = createTestFile(testFilePath, testFileSize)
  1017. assert.NoError(t, err)
  1018. err = ftpUploadFile(testFilePath, path.Join(vdir, testFileName), testFileSize, client, 0)
  1019. assert.NoError(t, err)
  1020. folder, _, err := httpd.GetFolders(0, 0, mappedPath, http.StatusOK)
  1021. assert.NoError(t, err)
  1022. if assert.Len(t, folder, 1) {
  1023. f := folder[0]
  1024. assert.Equal(t, testFileSize, f.UsedQuotaSize)
  1025. assert.Equal(t, 1, f.UsedQuotaFiles)
  1026. }
  1027. err = ftpUploadFile(testFilePath, path.Join(vdir, testFileName), testFileSize, client, 0)
  1028. assert.NoError(t, err)
  1029. folder, _, err = httpd.GetFolders(0, 0, mappedPath, http.StatusOK)
  1030. assert.NoError(t, err)
  1031. if assert.Len(t, folder, 1) {
  1032. f := folder[0]
  1033. assert.Equal(t, testFileSize, f.UsedQuotaSize)
  1034. assert.Equal(t, 1, f.UsedQuotaFiles)
  1035. }
  1036. err = client.Quit()
  1037. assert.NoError(t, err)
  1038. err = os.Remove(testFilePath)
  1039. assert.NoError(t, err)
  1040. }
  1041. _, err = httpd.RemoveUser(user, http.StatusOK)
  1042. assert.NoError(t, err)
  1043. _, err = httpd.RemoveFolder(vfs.BaseVirtualFolder{MappedPath: mappedPath}, http.StatusOK)
  1044. assert.NoError(t, err)
  1045. err = os.RemoveAll(user.GetHomeDir())
  1046. assert.NoError(t, err)
  1047. err = os.RemoveAll(mappedPath)
  1048. assert.NoError(t, err)
  1049. }
  1050. func TestAllocate(t *testing.T) {
  1051. u := getTestUser()
  1052. mappedPath := filepath.Join(os.TempDir(), "vdir")
  1053. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  1054. BaseVirtualFolder: vfs.BaseVirtualFolder{
  1055. MappedPath: mappedPath,
  1056. },
  1057. VirtualPath: "/vdir",
  1058. QuotaSize: 110,
  1059. })
  1060. err := os.MkdirAll(mappedPath, os.ModePerm)
  1061. assert.NoError(t, err)
  1062. user, _, err := httpd.AddUser(u, http.StatusOK)
  1063. assert.NoError(t, err)
  1064. client, err := getFTPClient(user, false)
  1065. if assert.NoError(t, err) {
  1066. code, response, err := client.SendCustomCommand("allo 2000000")
  1067. assert.NoError(t, err)
  1068. assert.Equal(t, ftp.StatusCommandOK, code)
  1069. assert.Equal(t, "Done !", response)
  1070. err = client.Quit()
  1071. assert.NoError(t, err)
  1072. }
  1073. user.QuotaSize = 100
  1074. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  1075. assert.NoError(t, err)
  1076. client, err = getFTPClient(user, false)
  1077. if assert.NoError(t, err) {
  1078. testFilePath := filepath.Join(homeBasePath, testFileName)
  1079. testFileSize := user.QuotaSize - 1
  1080. err = createTestFile(testFilePath, testFileSize)
  1081. assert.NoError(t, err)
  1082. code, response, err := client.SendCustomCommand("allo 99")
  1083. assert.NoError(t, err)
  1084. assert.Equal(t, ftp.StatusCommandOK, code)
  1085. assert.Equal(t, "Done !", response)
  1086. code, response, err = client.SendCustomCommand("allo 100")
  1087. assert.NoError(t, err)
  1088. assert.Equal(t, ftp.StatusCommandOK, code)
  1089. assert.Equal(t, "Done !", response)
  1090. code, response, err = client.SendCustomCommand("allo 150")
  1091. assert.NoError(t, err)
  1092. assert.Equal(t, ftp.StatusFileUnavailable, code)
  1093. assert.Contains(t, response, common.ErrQuotaExceeded.Error())
  1094. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  1095. assert.NoError(t, err)
  1096. // we still have space in vdir
  1097. code, response, err = client.SendCustomCommand("allo 50")
  1098. assert.NoError(t, err)
  1099. assert.Equal(t, ftp.StatusCommandOK, code)
  1100. assert.Equal(t, "Done !", response)
  1101. err = ftpUploadFile(testFilePath, path.Join("/vdir", testFileName), testFileSize, client, 0)
  1102. assert.NoError(t, err)
  1103. code, response, err = client.SendCustomCommand("allo 50")
  1104. assert.NoError(t, err)
  1105. assert.Equal(t, ftp.StatusFileUnavailable, code)
  1106. assert.Contains(t, response, common.ErrQuotaExceeded.Error())
  1107. err = client.Quit()
  1108. assert.NoError(t, err)
  1109. err = os.Remove(testFilePath)
  1110. assert.NoError(t, err)
  1111. }
  1112. user.Filters.MaxUploadFileSize = 100
  1113. user.QuotaSize = 0
  1114. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  1115. assert.NoError(t, err)
  1116. client, err = getFTPClient(user, false)
  1117. if assert.NoError(t, err) {
  1118. code, response, err := client.SendCustomCommand("allo 99")
  1119. assert.NoError(t, err)
  1120. assert.Equal(t, ftp.StatusCommandOK, code)
  1121. assert.Equal(t, "Done !", response)
  1122. code, response, err = client.SendCustomCommand("allo 150")
  1123. assert.NoError(t, err)
  1124. assert.Equal(t, ftp.StatusFileUnavailable, code)
  1125. assert.Contains(t, response, common.ErrQuotaExceeded.Error())
  1126. err = client.Quit()
  1127. assert.NoError(t, err)
  1128. }
  1129. _, err = httpd.RemoveUser(user, http.StatusOK)
  1130. assert.NoError(t, err)
  1131. _, err = httpd.RemoveFolder(vfs.BaseVirtualFolder{MappedPath: mappedPath}, http.StatusOK)
  1132. assert.NoError(t, err)
  1133. err = os.RemoveAll(user.GetHomeDir())
  1134. assert.NoError(t, err)
  1135. err = os.RemoveAll(mappedPath)
  1136. assert.NoError(t, err)
  1137. }
  1138. func TestChtimes(t *testing.T) {
  1139. u := getTestUser()
  1140. user, _, err := httpd.AddUser(u, http.StatusOK)
  1141. assert.NoError(t, err)
  1142. client, err := getFTPClient(user, false)
  1143. if assert.NoError(t, err) {
  1144. testFilePath := filepath.Join(homeBasePath, testFileName)
  1145. testFileSize := int64(65535)
  1146. err = createTestFile(testFilePath, testFileSize)
  1147. assert.NoError(t, err)
  1148. err = checkBasicFTP(client)
  1149. assert.NoError(t, err)
  1150. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  1151. assert.NoError(t, err)
  1152. mtime := time.Now().Format("20060102150405")
  1153. code, response, err := client.SendCustomCommand(fmt.Sprintf("MFMT %v %v", mtime, testFileName))
  1154. assert.NoError(t, err)
  1155. assert.Equal(t, ftp.StatusFile, code)
  1156. assert.Equal(t, fmt.Sprintf("Modify=%v; %v", mtime, testFileName), response)
  1157. err = client.Quit()
  1158. assert.NoError(t, err)
  1159. err = os.Remove(testFilePath)
  1160. assert.NoError(t, err)
  1161. }
  1162. _, err = httpd.RemoveUser(user, http.StatusOK)
  1163. assert.NoError(t, err)
  1164. err = os.RemoveAll(user.GetHomeDir())
  1165. assert.NoError(t, err)
  1166. }
  1167. func TestChmod(t *testing.T) {
  1168. if runtime.GOOS == osWindows {
  1169. t.Skip("chmod is partially supported on Windows")
  1170. }
  1171. u := getTestUser()
  1172. user, _, err := httpd.AddUser(u, http.StatusOK)
  1173. assert.NoError(t, err)
  1174. client, err := getFTPClient(user, true)
  1175. if assert.NoError(t, err) {
  1176. testFilePath := filepath.Join(homeBasePath, testFileName)
  1177. testFileSize := int64(131072)
  1178. err = createTestFile(testFilePath, testFileSize)
  1179. assert.NoError(t, err)
  1180. err = checkBasicFTP(client)
  1181. assert.NoError(t, err)
  1182. err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
  1183. assert.NoError(t, err)
  1184. code, response, err := client.SendCustomCommand(fmt.Sprintf("SITE CHMOD 600 %v", testFileName))
  1185. assert.NoError(t, err)
  1186. assert.Equal(t, ftp.StatusCommandOK, code)
  1187. assert.Equal(t, "SITE CHMOD command successful", response)
  1188. fi, err := os.Stat(filepath.Join(user.HomeDir, testFileName))
  1189. if assert.NoError(t, err) {
  1190. assert.Equal(t, os.FileMode(0600), fi.Mode().Perm())
  1191. }
  1192. err = client.Quit()
  1193. assert.NoError(t, err)
  1194. err = os.Remove(testFilePath)
  1195. assert.NoError(t, err)
  1196. }
  1197. _, err = httpd.RemoveUser(user, http.StatusOK)
  1198. assert.NoError(t, err)
  1199. err = os.RemoveAll(user.GetHomeDir())
  1200. assert.NoError(t, err)
  1201. }
  1202. func checkBasicFTP(client *ftp.ServerConn) error {
  1203. _, err := client.CurrentDir()
  1204. if err != nil {
  1205. return err
  1206. }
  1207. err = client.NoOp()
  1208. if err != nil {
  1209. return err
  1210. }
  1211. _, err = client.List(".")
  1212. if err != nil {
  1213. return err
  1214. }
  1215. return nil
  1216. }
  1217. func ftpUploadFile(localSourcePath string, remoteDestPath string, expectedSize int64, client *ftp.ServerConn, offset uint64) error {
  1218. srcFile, err := os.Open(localSourcePath)
  1219. if err != nil {
  1220. return err
  1221. }
  1222. defer srcFile.Close()
  1223. if offset > 0 {
  1224. err = client.StorFrom(remoteDestPath, srcFile, offset)
  1225. } else {
  1226. err = client.Stor(remoteDestPath, srcFile)
  1227. }
  1228. if err != nil {
  1229. return err
  1230. }
  1231. if expectedSize > 0 {
  1232. size, err := client.FileSize(remoteDestPath)
  1233. if err != nil {
  1234. return err
  1235. }
  1236. if size != expectedSize {
  1237. return fmt.Errorf("uploaded file size does not match, actual: %v, expected: %v", size, expectedSize)
  1238. }
  1239. }
  1240. return nil
  1241. }
  1242. func ftpDownloadFile(remoteSourcePath string, localDestPath string, expectedSize int64, client *ftp.ServerConn, offset uint64) error {
  1243. downloadDest, err := os.Create(localDestPath)
  1244. if err != nil {
  1245. return err
  1246. }
  1247. defer downloadDest.Close()
  1248. var r *ftp.Response
  1249. if offset > 0 {
  1250. r, err = client.RetrFrom(remoteSourcePath, offset)
  1251. } else {
  1252. r, err = client.Retr(remoteSourcePath)
  1253. }
  1254. if err != nil {
  1255. return err
  1256. }
  1257. defer r.Close()
  1258. written, err := io.Copy(downloadDest, r)
  1259. if err != nil {
  1260. return err
  1261. }
  1262. if written != expectedSize {
  1263. return fmt.Errorf("downloaded file size does not match, actual: %v, expected: %v", written, expectedSize)
  1264. }
  1265. return nil
  1266. }
  1267. func getFTPClient(user dataprovider.User, useTLS bool) (*ftp.ServerConn, error) {
  1268. ftpOptions := []ftp.DialOption{ftp.DialWithTimeout(5 * time.Second)}
  1269. if useTLS {
  1270. tlsConfig := &tls.Config{
  1271. ServerName: "localhost",
  1272. InsecureSkipVerify: true, // use this for tests only
  1273. MinVersion: tls.VersionTLS12,
  1274. }
  1275. ftpOptions = append(ftpOptions, ftp.DialWithExplicitTLS(tlsConfig))
  1276. }
  1277. client, err := ftp.Dial(ftpServerAddr, ftpOptions...)
  1278. if err != nil {
  1279. return nil, err
  1280. }
  1281. pwd := defaultPassword
  1282. if len(user.Password) > 0 {
  1283. pwd = user.Password
  1284. }
  1285. err = client.Login(user.Username, pwd)
  1286. if err != nil {
  1287. return nil, err
  1288. }
  1289. return client, err
  1290. }
  1291. func waitTCPListening(address string) {
  1292. for {
  1293. conn, err := net.Dial("tcp", address)
  1294. if err != nil {
  1295. logger.WarnToConsole("tcp server %v not listening: %v\n", address, err)
  1296. time.Sleep(100 * time.Millisecond)
  1297. continue
  1298. }
  1299. logger.InfoToConsole("tcp server %v now listening\n", address)
  1300. conn.Close()
  1301. break
  1302. }
  1303. }
  1304. func getTestUser() dataprovider.User {
  1305. user := dataprovider.User{
  1306. Username: defaultUsername,
  1307. Password: defaultPassword,
  1308. HomeDir: filepath.Join(homeBasePath, defaultUsername),
  1309. Status: 1,
  1310. ExpirationDate: 0,
  1311. }
  1312. user.Permissions = make(map[string][]string)
  1313. user.Permissions["/"] = allPerms
  1314. return user
  1315. }
  1316. func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool, username string) []byte {
  1317. extAuthContent := []byte("#!/bin/sh\n\n")
  1318. extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...)
  1319. if len(username) > 0 {
  1320. user.Username = username
  1321. }
  1322. u, _ := json.Marshal(user)
  1323. if nonJSONResponse {
  1324. extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
  1325. } else {
  1326. extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...)
  1327. }
  1328. extAuthContent = append(extAuthContent, []byte("else\n")...)
  1329. if nonJSONResponse {
  1330. extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
  1331. } else {
  1332. extAuthContent = append(extAuthContent, []byte("echo '{\"username\":\"\"}'\n")...)
  1333. }
  1334. extAuthContent = append(extAuthContent, []byte("fi\n")...)
  1335. return extAuthContent
  1336. }
  1337. func getPreLoginScriptContent(user dataprovider.User, nonJSONResponse bool) []byte {
  1338. content := []byte("#!/bin/sh\n\n")
  1339. if nonJSONResponse {
  1340. content = append(content, []byte("echo 'text response'\n")...)
  1341. return content
  1342. }
  1343. if len(user.Username) > 0 {
  1344. u, _ := json.Marshal(user)
  1345. content = append(content, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...)
  1346. }
  1347. return content
  1348. }
  1349. func getPostConnectScriptContent(exitCode int) []byte {
  1350. content := []byte("#!/bin/sh\n\n")
  1351. content = append(content, []byte(fmt.Sprintf("exit %v", exitCode))...)
  1352. return content
  1353. }
  1354. func createTestFile(path string, size int64) error {
  1355. baseDir := filepath.Dir(path)
  1356. if _, err := os.Stat(baseDir); os.IsNotExist(err) {
  1357. err = os.MkdirAll(baseDir, os.ModePerm)
  1358. if err != nil {
  1359. return err
  1360. }
  1361. }
  1362. content := make([]byte, size)
  1363. _, err := rand.Read(content)
  1364. if err != nil {
  1365. return err
  1366. }
  1367. return ioutil.WriteFile(path, content, os.ModePerm)
  1368. }