ftpd_test.go 56 KB

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