webdavd_test.go 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
  1. package webdavd_test
  2. import (
  3. "crypto/rand"
  4. "encoding/json"
  5. "errors"
  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. "sync"
  17. "testing"
  18. "time"
  19. "github.com/rs/zerolog"
  20. "github.com/stretchr/testify/assert"
  21. "github.com/studio-b12/gowebdav"
  22. "github.com/drakkan/sftpgo/common"
  23. "github.com/drakkan/sftpgo/config"
  24. "github.com/drakkan/sftpgo/dataprovider"
  25. "github.com/drakkan/sftpgo/httpclient"
  26. "github.com/drakkan/sftpgo/httpd"
  27. "github.com/drakkan/sftpgo/logger"
  28. "github.com/drakkan/sftpgo/vfs"
  29. "github.com/drakkan/sftpgo/webdavd"
  30. )
  31. const (
  32. logSender = "webavdTesting"
  33. webDavServerAddr = "127.0.0.1:9090"
  34. webDavServerPort = 9090
  35. defaultUsername = "test_user_dav"
  36. defaultPassword = "test_password"
  37. configDir = ".."
  38. osWindows = "windows"
  39. webDavCert = `-----BEGIN CERTIFICATE-----
  40. MIICHTCCAaKgAwIBAgIUHnqw7QnB1Bj9oUsNpdb+ZkFPOxMwCgYIKoZIzj0EAwIw
  41. RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
  42. dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDAyMDQwOTUzMDRaFw0zMDAyMDEw
  43. OTUzMDRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
  44. VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQA
  45. IgNiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVqWvrJ51t5OxV0v25NsOgR82CA
  46. NXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIVCzgWkxiz7XE4lgUwX44FCXZM
  47. 3+JeUbKjUzBRMB0GA1UdDgQWBBRhLw+/o3+Z02MI/d4tmaMui9W16jAfBgNVHSME
  48. GDAWgBRhLw+/o3+Z02MI/d4tmaMui9W16jAPBgNVHRMBAf8EBTADAQH/MAoGCCqG
  49. SM49BAMCA2kAMGYCMQDqLt2lm8mE+tGgtjDmtFgdOcI72HSbRQ74D5rYTzgST1rY
  50. /8wTi5xl8TiFUyLMUsICMQC5ViVxdXbhuG7gX6yEqSkMKZICHpO8hqFwOD/uaFVI
  51. dV4vKmHUzwK/eIx+8Ay3neE=
  52. -----END CERTIFICATE-----`
  53. webDavKey = `-----BEGIN EC PARAMETERS-----
  54. BgUrgQQAIg==
  55. -----END EC PARAMETERS-----
  56. -----BEGIN EC PRIVATE KEY-----
  57. MIGkAgEBBDCfMNsN6miEE3rVyUPwElfiJSWaR5huPCzUenZOfJT04GAcQdWvEju3
  58. UM2lmBLIXpGgBwYFK4EEACKhZANiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVq
  59. WvrJ51t5OxV0v25NsOgR82CANXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIV
  60. CzgWkxiz7XE4lgUwX44FCXZM3+JeUbI=
  61. -----END EC PRIVATE KEY-----`
  62. testFileName = "test_file_dav.dat"
  63. testDLFileName = "test_download_dav.dat"
  64. )
  65. var (
  66. allPerms = []string{dataprovider.PermAny}
  67. homeBasePath string
  68. hookCmdPath string
  69. extAuthPath string
  70. preLoginPath string
  71. postConnectPath string
  72. logFilePath string
  73. certPath string
  74. keyPath string
  75. )
  76. func TestMain(m *testing.M) {
  77. logFilePath = filepath.Join(configDir, "sftpgo_webdavd_test.log")
  78. logger.InitLogger(logFilePath, 5, 1, 28, false, zerolog.DebugLevel)
  79. err := config.LoadConfig(configDir, "")
  80. if err != nil {
  81. logger.ErrorToConsole("error loading configuration: %v", err)
  82. os.Exit(1)
  83. }
  84. providerConf := config.GetProviderConf()
  85. logger.InfoToConsole("Starting WebDAVD tests, provider: %v", providerConf.Driver)
  86. commonConf := config.GetCommonConfig()
  87. commonConf.UploadMode = 2
  88. homeBasePath = os.TempDir()
  89. if runtime.GOOS != osWindows {
  90. commonConf.Actions.ExecuteOn = []string{"download", "upload", "rename", "delete"}
  91. commonConf.Actions.Hook = hookCmdPath
  92. hookCmdPath, err = exec.LookPath("true")
  93. if err != nil {
  94. logger.Warn(logSender, "", "unable to get hook command: %v", err)
  95. logger.WarnToConsole("unable to get hook command: %v", err)
  96. }
  97. }
  98. certPath = filepath.Join(os.TempDir(), "test_dav.crt")
  99. keyPath = filepath.Join(os.TempDir(), "test_dav.key")
  100. err = ioutil.WriteFile(certPath, []byte(webDavCert), os.ModePerm)
  101. if err != nil {
  102. logger.ErrorToConsole("error writing WebDAV certificate: %v", err)
  103. os.Exit(1)
  104. }
  105. err = ioutil.WriteFile(keyPath, []byte(webDavKey), os.ModePerm)
  106. if err != nil {
  107. logger.ErrorToConsole("error writing WebDAV private key: %v", err)
  108. os.Exit(1)
  109. }
  110. common.Initialize(commonConf)
  111. err = dataprovider.Initialize(providerConf, configDir)
  112. if err != nil {
  113. logger.ErrorToConsole("error initializing data provider: %v", err)
  114. os.Exit(1)
  115. }
  116. httpConfig := config.GetHTTPConfig()
  117. httpConfig.Initialize(configDir)
  118. httpdConf := config.GetHTTPDConfig()
  119. httpdConf.BindPort = 8078
  120. httpd.SetBaseURLAndCredentials("http://127.0.0.1:8078", "", "")
  121. webDavConf := config.GetWebDAVDConfig()
  122. webDavConf.BindPort = webDavServerPort
  123. webDavConf.Cors = webdavd.Cors{
  124. Enabled: true,
  125. AllowedOrigins: []string{"*"},
  126. AllowedMethods: []string{
  127. http.MethodHead,
  128. http.MethodGet,
  129. http.MethodPost,
  130. http.MethodPut,
  131. http.MethodPatch,
  132. http.MethodDelete,
  133. },
  134. AllowedHeaders: []string{"*"},
  135. AllowCredentials: true,
  136. }
  137. extAuthPath = filepath.Join(homeBasePath, "extauth.sh")
  138. preLoginPath = filepath.Join(homeBasePath, "prelogin.sh")
  139. postConnectPath = filepath.Join(homeBasePath, "postconnect.sh")
  140. go func() {
  141. logger.Debug(logSender, "", "initializing WebDAV server with config %+v", webDavConf)
  142. if err := webDavConf.Initialize(configDir); err != nil {
  143. logger.ErrorToConsole("could not start WebDAV server: %v", err)
  144. os.Exit(1)
  145. }
  146. }()
  147. go func() {
  148. if err := httpdConf.Initialize(configDir, false); err != nil {
  149. logger.ErrorToConsole("could not start HTTP server: %v", err)
  150. os.Exit(1)
  151. }
  152. }()
  153. waitTCPListening(fmt.Sprintf("%s:%d", webDavConf.BindAddress, webDavConf.BindPort))
  154. waitTCPListening(fmt.Sprintf("%s:%d", httpdConf.BindAddress, httpdConf.BindPort))
  155. webdavd.ReloadTLSCertificate() //nolint:errcheck
  156. exitCode := m.Run()
  157. os.Remove(logFilePath)
  158. os.Remove(extAuthPath)
  159. os.Remove(preLoginPath)
  160. os.Remove(postConnectPath)
  161. os.Remove(certPath)
  162. os.Remove(keyPath)
  163. os.Exit(exitCode)
  164. }
  165. func TestInitialization(t *testing.T) {
  166. cfg := webdavd.Configuration{
  167. BindPort: 1234,
  168. CertificateFile: "missing path",
  169. CertificateKeyFile: "bad path",
  170. }
  171. err := cfg.Initialize(configDir)
  172. assert.Error(t, err)
  173. cfg.Cache = config.GetWebDAVDConfig().Cache
  174. cfg.BindPort = webDavServerPort
  175. cfg.CertificateFile = certPath
  176. cfg.CertificateKeyFile = keyPath
  177. err = cfg.Initialize(configDir)
  178. assert.Error(t, err)
  179. err = webdavd.ReloadTLSCertificate()
  180. assert.NoError(t, err)
  181. }
  182. func TestBasicHandling(t *testing.T) {
  183. u := getTestUser()
  184. u.QuotaSize = 6553600
  185. user, _, err := httpd.AddUser(u, http.StatusOK)
  186. assert.NoError(t, err)
  187. client := getWebDavClient(user)
  188. assert.NoError(t, checkBasicFunc(client))
  189. testFilePath := filepath.Join(homeBasePath, testFileName)
  190. testFileSize := int64(65535)
  191. expectedQuotaSize := user.UsedQuotaSize + testFileSize
  192. expectedQuotaFiles := user.UsedQuotaFiles + 1
  193. err = createTestFile(testFilePath, testFileSize)
  194. assert.NoError(t, err)
  195. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  196. assert.NoError(t, err)
  197. // overwrite an existing file
  198. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  199. assert.NoError(t, err)
  200. localDownloadPath := filepath.Join(homeBasePath, testDLFileName)
  201. err = downloadFile(testFileName, localDownloadPath, testFileSize, client)
  202. assert.NoError(t, err)
  203. user, _, err = httpd.GetUserByID(user.ID, http.StatusOK)
  204. assert.NoError(t, err)
  205. assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles)
  206. assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize)
  207. err = client.Rename(testFileName, testFileName+"1", false)
  208. assert.NoError(t, err)
  209. _, err = client.Stat(testFileName)
  210. assert.Error(t, err)
  211. // the webdav client hide the error we check the quota
  212. err = client.Remove(testFileName)
  213. assert.NoError(t, err)
  214. user, _, err = httpd.GetUserByID(user.ID, http.StatusOK)
  215. assert.NoError(t, err)
  216. assert.Equal(t, expectedQuotaFiles, user.UsedQuotaFiles)
  217. assert.Equal(t, expectedQuotaSize, user.UsedQuotaSize)
  218. err = client.Remove(testFileName + "1")
  219. assert.NoError(t, err)
  220. user, _, err = httpd.GetUserByID(user.ID, http.StatusOK)
  221. assert.NoError(t, err)
  222. assert.Equal(t, expectedQuotaFiles-1, user.UsedQuotaFiles)
  223. assert.Equal(t, expectedQuotaSize-testFileSize, user.UsedQuotaSize)
  224. err = downloadFile(testFileName, localDownloadPath, testFileSize, client)
  225. assert.Error(t, err)
  226. testDir := "testdir"
  227. err = client.Mkdir(testDir, os.ModePerm)
  228. assert.NoError(t, err)
  229. err = client.MkdirAll(path.Join(testDir, "sub", "sub"), os.ModePerm)
  230. assert.NoError(t, err)
  231. err = client.MkdirAll(path.Join(testDir, "sub1", "sub1"), os.ModePerm)
  232. assert.NoError(t, err)
  233. err = client.MkdirAll(path.Join(testDir, "sub2", "sub2"), os.ModePerm)
  234. assert.NoError(t, err)
  235. err = uploadFile(testFilePath, path.Join(testDir, testFileName+".txt"), testFileSize, client)
  236. assert.NoError(t, err)
  237. err = uploadFile(testFilePath, path.Join(testDir, testFileName), testFileSize, client)
  238. assert.NoError(t, err)
  239. files, err := client.ReadDir(testDir)
  240. assert.NoError(t, err)
  241. assert.Len(t, files, 5)
  242. err = client.Copy(testDir, testDir+"_copy", false)
  243. assert.NoError(t, err)
  244. err = client.RemoveAll(testDir)
  245. assert.NoError(t, err)
  246. err = os.Remove(testFilePath)
  247. assert.NoError(t, err)
  248. err = os.Remove(localDownloadPath)
  249. assert.NoError(t, err)
  250. _, err = httpd.RemoveUser(user, http.StatusOK)
  251. assert.NoError(t, err)
  252. err = os.RemoveAll(user.GetHomeDir())
  253. assert.NoError(t, err)
  254. assert.Len(t, common.Connections.GetStats(), 0)
  255. }
  256. func TestLoginInvalidPwd(t *testing.T) {
  257. u := getTestUser()
  258. user, _, err := httpd.AddUser(u, http.StatusOK)
  259. assert.NoError(t, err)
  260. client := getWebDavClient(user)
  261. assert.NoError(t, checkBasicFunc(client))
  262. user.Password = "wrong"
  263. client = getWebDavClient(user)
  264. assert.Error(t, checkBasicFunc(client))
  265. _, err = httpd.RemoveUser(user, http.StatusOK)
  266. assert.NoError(t, err)
  267. }
  268. func TestLoginInvalidURL(t *testing.T) {
  269. u := getTestUser()
  270. user, _, err := httpd.AddUser(u, http.StatusOK)
  271. assert.NoError(t, err)
  272. u1 := getTestUser()
  273. u1.Username = user.Username + "1"
  274. user1, _, err := httpd.AddUser(u1, http.StatusOK)
  275. assert.NoError(t, err)
  276. rootPath := fmt.Sprintf("http://%v/%v", webDavServerAddr, user.Username+"1")
  277. client := gowebdav.NewClient(rootPath, user.Username, defaultPassword)
  278. client.SetTimeout(5 * time.Second)
  279. assert.Error(t, checkBasicFunc(client))
  280. _, err = httpd.RemoveUser(user, http.StatusOK)
  281. assert.NoError(t, err)
  282. _, err = httpd.RemoveUser(user1, http.StatusOK)
  283. assert.NoError(t, err)
  284. }
  285. func TestRootRedirect(t *testing.T) {
  286. errRedirect := errors.New("redirect error")
  287. u := getTestUser()
  288. user, _, err := httpd.AddUser(u, http.StatusOK)
  289. assert.NoError(t, err)
  290. client := getWebDavClient(user)
  291. assert.NoError(t, checkBasicFunc(client))
  292. rootPath := fmt.Sprintf("http://%v/", webDavServerAddr)
  293. httpClient := httpclient.GetHTTPClient()
  294. httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
  295. return errRedirect
  296. }
  297. req, err := http.NewRequest(http.MethodOptions, rootPath, nil)
  298. assert.NoError(t, err)
  299. req.SetBasicAuth(u.Username, u.Password)
  300. resp, err := httpClient.Do(req)
  301. if assert.Error(t, err) {
  302. assert.Contains(t, err.Error(), errRedirect.Error())
  303. }
  304. err = resp.Body.Close()
  305. assert.NoError(t, err)
  306. req, err = http.NewRequest(http.MethodGet, rootPath, nil)
  307. assert.NoError(t, err)
  308. req.SetBasicAuth(u.Username, u.Password)
  309. resp, err = httpClient.Do(req)
  310. if assert.Error(t, err) {
  311. assert.Contains(t, err.Error(), errRedirect.Error())
  312. }
  313. err = resp.Body.Close()
  314. assert.NoError(t, err)
  315. req, err = http.NewRequest("PROPFIND", rootPath, nil)
  316. assert.NoError(t, err)
  317. req.SetBasicAuth(u.Username, u.Password)
  318. resp, err = httpClient.Do(req)
  319. if assert.Error(t, err) {
  320. assert.Contains(t, err.Error(), errRedirect.Error())
  321. }
  322. err = resp.Body.Close()
  323. assert.NoError(t, err)
  324. _, err = httpd.RemoveUser(user, http.StatusOK)
  325. assert.NoError(t, err)
  326. }
  327. func TestLoginExternalAuth(t *testing.T) {
  328. if runtime.GOOS == osWindows {
  329. t.Skip("this test is not available on Windows")
  330. }
  331. u := getTestUser()
  332. err := dataprovider.Close()
  333. assert.NoError(t, err)
  334. err = config.LoadConfig(configDir, "")
  335. assert.NoError(t, err)
  336. providerConf := config.GetProviderConf()
  337. err = ioutil.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
  338. assert.NoError(t, err)
  339. providerConf.ExternalAuthHook = extAuthPath
  340. providerConf.ExternalAuthScope = 0
  341. err = dataprovider.Initialize(providerConf, configDir)
  342. assert.NoError(t, err)
  343. client := getWebDavClient(u)
  344. assert.NoError(t, checkBasicFunc(client))
  345. u.Username = defaultUsername + "1"
  346. client = getWebDavClient(u)
  347. assert.Error(t, checkBasicFunc(client))
  348. users, _, err := httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
  349. assert.NoError(t, err)
  350. if assert.Len(t, users, 1) {
  351. user := users[0]
  352. assert.Equal(t, defaultUsername, user.Username)
  353. _, err = httpd.RemoveUser(user, http.StatusOK)
  354. assert.NoError(t, err)
  355. err = os.RemoveAll(user.GetHomeDir())
  356. assert.NoError(t, err)
  357. }
  358. err = dataprovider.Close()
  359. assert.NoError(t, err)
  360. err = config.LoadConfig(configDir, "")
  361. assert.NoError(t, err)
  362. providerConf = config.GetProviderConf()
  363. err = dataprovider.Initialize(providerConf, configDir)
  364. assert.NoError(t, err)
  365. err = os.Remove(extAuthPath)
  366. assert.NoError(t, err)
  367. }
  368. func TestPreLoginHook(t *testing.T) {
  369. if runtime.GOOS == osWindows {
  370. t.Skip("this test is not available on Windows")
  371. }
  372. u := getTestUser()
  373. err := dataprovider.Close()
  374. assert.NoError(t, err)
  375. err = config.LoadConfig(configDir, "")
  376. assert.NoError(t, err)
  377. providerConf := config.GetProviderConf()
  378. err = ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(u, false), os.ModePerm)
  379. assert.NoError(t, err)
  380. providerConf.PreLoginHook = preLoginPath
  381. err = dataprovider.Initialize(providerConf, configDir)
  382. assert.NoError(t, err)
  383. users, _, err := httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
  384. assert.NoError(t, err)
  385. assert.Equal(t, 0, len(users))
  386. client := getWebDavClient(u)
  387. assert.NoError(t, checkBasicFunc(client))
  388. users, _, err = httpd.GetUsers(0, 0, defaultUsername, http.StatusOK)
  389. assert.NoError(t, err)
  390. assert.Equal(t, 1, len(users))
  391. user := users[0]
  392. // test login with an existing user
  393. client = getWebDavClient(user)
  394. assert.NoError(t, checkBasicFunc(client))
  395. err = ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(user, true), os.ModePerm)
  396. assert.NoError(t, err)
  397. // update the user to remove it from the cache
  398. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  399. assert.NoError(t, err)
  400. client = getWebDavClient(user)
  401. assert.Error(t, checkBasicFunc(client))
  402. // update the user to remove it from the cache
  403. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  404. assert.NoError(t, err)
  405. user.Status = 0
  406. err = ioutil.WriteFile(preLoginPath, getPreLoginScriptContent(user, false), os.ModePerm)
  407. assert.NoError(t, err)
  408. client = getWebDavClient(user)
  409. assert.Error(t, checkBasicFunc(client))
  410. _, err = httpd.RemoveUser(user, http.StatusOK)
  411. assert.NoError(t, err)
  412. err = os.RemoveAll(user.GetHomeDir())
  413. assert.NoError(t, err)
  414. err = dataprovider.Close()
  415. assert.NoError(t, err)
  416. err = config.LoadConfig(configDir, "")
  417. assert.NoError(t, err)
  418. providerConf = config.GetProviderConf()
  419. err = dataprovider.Initialize(providerConf, configDir)
  420. assert.NoError(t, err)
  421. err = os.Remove(preLoginPath)
  422. assert.NoError(t, err)
  423. }
  424. func TestPostConnectHook(t *testing.T) {
  425. if runtime.GOOS == osWindows {
  426. t.Skip("this test is not available on Windows")
  427. }
  428. common.Config.PostConnectHook = postConnectPath
  429. u := getTestUser()
  430. user, _, err := httpd.AddUser(u, http.StatusOK)
  431. assert.NoError(t, err)
  432. err = ioutil.WriteFile(postConnectPath, getPostConnectScriptContent(0), os.ModePerm)
  433. assert.NoError(t, err)
  434. client := getWebDavClient(user)
  435. assert.NoError(t, checkBasicFunc(client))
  436. err = ioutil.WriteFile(postConnectPath, getPostConnectScriptContent(1), os.ModePerm)
  437. assert.NoError(t, err)
  438. assert.Error(t, checkBasicFunc(client))
  439. common.Config.PostConnectHook = "http://127.0.0.1:8078/api/v1/version"
  440. assert.NoError(t, checkBasicFunc(client))
  441. common.Config.PostConnectHook = "http://127.0.0.1:8078/notfound"
  442. assert.Error(t, checkBasicFunc(client))
  443. _, err = httpd.RemoveUser(user, http.StatusOK)
  444. assert.NoError(t, err)
  445. err = os.RemoveAll(user.GetHomeDir())
  446. assert.NoError(t, err)
  447. common.Config.PostConnectHook = ""
  448. }
  449. func TestMaxSessions(t *testing.T) {
  450. u := getTestUser()
  451. u.MaxSessions = 1
  452. user, _, err := httpd.AddUser(u, http.StatusOK)
  453. assert.NoError(t, err)
  454. client := getWebDavClient(user)
  455. assert.NoError(t, checkBasicFunc(client))
  456. // now add a fake connection
  457. fs := vfs.NewOsFs("id", os.TempDir(), nil)
  458. connection := &webdavd.Connection{
  459. BaseConnection: common.NewBaseConnection(fs.ConnectionID(), common.ProtocolWebDAV, user, fs),
  460. }
  461. common.Connections.Add(connection)
  462. assert.Error(t, checkBasicFunc(client))
  463. common.Connections.Remove(connection.GetID())
  464. _, err = httpd.RemoveUser(user, http.StatusOK)
  465. assert.NoError(t, err)
  466. err = os.RemoveAll(user.GetHomeDir())
  467. assert.NoError(t, err)
  468. assert.Len(t, common.Connections.GetStats(), 0)
  469. }
  470. func TestLoginWithIPilters(t *testing.T) {
  471. u := getTestUser()
  472. u.Filters.DeniedIP = []string{"192.167.0.0/24", "172.18.0.0/16"}
  473. u.Filters.AllowedIP = []string{"172.19.0.0/16"}
  474. user, _, err := httpd.AddUser(u, http.StatusOK)
  475. assert.NoError(t, err)
  476. client := getWebDavClient(user)
  477. assert.Error(t, checkBasicFunc(client))
  478. _, err = httpd.RemoveUser(user, http.StatusOK)
  479. assert.NoError(t, err)
  480. err = os.RemoveAll(user.GetHomeDir())
  481. assert.NoError(t, err)
  482. }
  483. func TestDownloadErrors(t *testing.T) {
  484. u := getTestUser()
  485. u.QuotaFiles = 1
  486. subDir1 := "sub1"
  487. subDir2 := "sub2"
  488. u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems}
  489. u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
  490. dataprovider.PermDelete, dataprovider.PermDownload}
  491. // use an unknown mime to trigger content type detection
  492. u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
  493. {
  494. Path: "/sub2",
  495. AllowedExtensions: []string{},
  496. DeniedExtensions: []string{".zipp"},
  497. },
  498. }
  499. u.Filters.FilePatterns = []dataprovider.PatternsFilter{
  500. {
  501. Path: "/sub2",
  502. AllowedPatterns: []string{},
  503. DeniedPatterns: []string{"*.jpg"},
  504. },
  505. }
  506. user, _, err := httpd.AddUser(u, http.StatusOK)
  507. assert.NoError(t, err)
  508. client := getWebDavClient(user)
  509. testFilePath1 := filepath.Join(user.HomeDir, subDir1, "file.zipp")
  510. testFilePath2 := filepath.Join(user.HomeDir, subDir2, "file.zipp")
  511. testFilePath3 := filepath.Join(user.HomeDir, subDir2, "file.jpg")
  512. err = os.MkdirAll(filepath.Dir(testFilePath1), os.ModePerm)
  513. assert.NoError(t, err)
  514. err = os.MkdirAll(filepath.Dir(testFilePath2), os.ModePerm)
  515. assert.NoError(t, err)
  516. err = ioutil.WriteFile(testFilePath1, []byte("file1"), os.ModePerm)
  517. assert.NoError(t, err)
  518. err = ioutil.WriteFile(testFilePath2, []byte("file2"), os.ModePerm)
  519. assert.NoError(t, err)
  520. err = ioutil.WriteFile(testFilePath3, []byte("file3"), os.ModePerm)
  521. assert.NoError(t, err)
  522. localDownloadPath := filepath.Join(homeBasePath, testDLFileName)
  523. err = downloadFile(path.Join("/", subDir1, "file.zipp"), localDownloadPath, 5, client)
  524. assert.Error(t, err)
  525. err = downloadFile(path.Join("/", subDir2, "file.zipp"), localDownloadPath, 5, client)
  526. assert.Error(t, err)
  527. err = downloadFile(path.Join("/", subDir2, "file.jpg"), localDownloadPath, 5, client)
  528. assert.Error(t, err)
  529. err = downloadFile(path.Join("missing.zip"), localDownloadPath, 5, client)
  530. assert.Error(t, err)
  531. err = os.Remove(localDownloadPath)
  532. assert.NoError(t, err)
  533. _, err = httpd.RemoveUser(user, http.StatusOK)
  534. assert.NoError(t, err)
  535. err = os.RemoveAll(user.GetHomeDir())
  536. assert.NoError(t, err)
  537. }
  538. func TestUploadErrors(t *testing.T) {
  539. u := getTestUser()
  540. u.QuotaSize = 65535
  541. subDir1 := "sub1"
  542. subDir2 := "sub2"
  543. // we need download permission to get size since PROPFIND will open the file
  544. u.Permissions[path.Join("/", subDir1)] = []string{dataprovider.PermListItems, dataprovider.PermDownload}
  545. u.Permissions[path.Join("/", subDir2)] = []string{dataprovider.PermListItems, dataprovider.PermUpload,
  546. dataprovider.PermDelete, dataprovider.PermDownload}
  547. u.Filters.FileExtensions = []dataprovider.ExtensionsFilter{
  548. {
  549. Path: "/sub2",
  550. AllowedExtensions: []string{},
  551. DeniedExtensions: []string{".zip"},
  552. },
  553. }
  554. user, _, err := httpd.AddUser(u, http.StatusOK)
  555. assert.NoError(t, err)
  556. client := getWebDavClient(user)
  557. testFilePath := filepath.Join(homeBasePath, testFileName)
  558. testFileSize := user.QuotaSize
  559. err = createTestFile(testFilePath, testFileSize)
  560. assert.NoError(t, err)
  561. err = client.Mkdir(subDir1, os.ModePerm)
  562. assert.NoError(t, err)
  563. err = client.Mkdir(subDir2, os.ModePerm)
  564. assert.NoError(t, err)
  565. err = uploadFile(testFilePath, path.Join(subDir1, testFileName), testFileSize, client)
  566. assert.Error(t, err)
  567. err = uploadFile(testFilePath, path.Join(subDir2, testFileName+".zip"), testFileSize, client)
  568. assert.Error(t, err)
  569. err = uploadFile(testFilePath, path.Join(subDir2, testFileName), testFileSize, client)
  570. assert.NoError(t, err)
  571. err = client.Rename(path.Join(subDir2, testFileName), path.Join(subDir1, testFileName), false)
  572. assert.Error(t, err)
  573. err = uploadFile(testFilePath, path.Join(subDir2, testFileName), testFileSize, client)
  574. assert.Error(t, err)
  575. err = uploadFile(testFilePath, subDir1, testFileSize, client)
  576. assert.Error(t, err)
  577. // overquota
  578. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  579. assert.Error(t, err)
  580. err = client.Remove(path.Join(subDir2, testFileName))
  581. assert.NoError(t, err)
  582. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  583. assert.NoError(t, err)
  584. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  585. assert.Error(t, err)
  586. err = os.Remove(testFilePath)
  587. assert.NoError(t, err)
  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 TestDeniedLoginMethod(t *testing.T) {
  594. u := getTestUser()
  595. u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword}
  596. user, _, err := httpd.AddUser(u, http.StatusOK)
  597. assert.NoError(t, err)
  598. client := getWebDavClient(user)
  599. assert.Error(t, checkBasicFunc(client))
  600. user.Filters.DeniedLoginMethods = []string{dataprovider.SSHLoginMethodPublicKey, dataprovider.SSHLoginMethodKeyAndKeyboardInt}
  601. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  602. assert.NoError(t, err)
  603. client = getWebDavClient(user)
  604. assert.NoError(t, checkBasicFunc(client))
  605. _, err = httpd.RemoveUser(user, http.StatusOK)
  606. assert.NoError(t, err)
  607. err = os.RemoveAll(user.GetHomeDir())
  608. assert.NoError(t, err)
  609. }
  610. func TestDeniedProtocols(t *testing.T) {
  611. u := getTestUser()
  612. u.Filters.DeniedProtocols = []string{common.ProtocolWebDAV}
  613. user, _, err := httpd.AddUser(u, http.StatusOK)
  614. assert.NoError(t, err)
  615. client := getWebDavClient(user)
  616. assert.Error(t, checkBasicFunc(client))
  617. user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolFTP}
  618. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  619. assert.NoError(t, err)
  620. client = getWebDavClient(user)
  621. assert.NoError(t, checkBasicFunc(client))
  622. _, err = httpd.RemoveUser(user, http.StatusOK)
  623. assert.NoError(t, err)
  624. err = os.RemoveAll(user.GetHomeDir())
  625. assert.NoError(t, err)
  626. }
  627. func TestQuotaLimits(t *testing.T) {
  628. u := getTestUser()
  629. u.QuotaFiles = 1
  630. user, _, err := httpd.AddUser(u, http.StatusOK)
  631. assert.NoError(t, err)
  632. testFileSize := int64(65535)
  633. testFilePath := filepath.Join(homeBasePath, testFileName)
  634. err = createTestFile(testFilePath, testFileSize)
  635. assert.NoError(t, err)
  636. testFileSize1 := int64(131072)
  637. testFileName1 := "test_file1.dat"
  638. testFilePath1 := filepath.Join(homeBasePath, testFileName1)
  639. err = createTestFile(testFilePath1, testFileSize1)
  640. assert.NoError(t, err)
  641. testFileSize2 := int64(32768)
  642. testFileName2 := "test_file2.dat"
  643. testFilePath2 := filepath.Join(homeBasePath, testFileName2)
  644. err = createTestFile(testFilePath2, testFileSize2)
  645. assert.NoError(t, err)
  646. client := getWebDavClient(user)
  647. // test quota files
  648. err = uploadFile(testFilePath, testFileName+".quota", testFileSize, client)
  649. assert.NoError(t, err)
  650. err = uploadFile(testFilePath, testFileName+".quota1", testFileSize, client)
  651. assert.Error(t, err)
  652. err = client.Rename(testFileName+".quota", testFileName, false)
  653. assert.NoError(t, err)
  654. files, err := client.ReadDir("/")
  655. assert.NoError(t, err)
  656. assert.Len(t, files, 1)
  657. // test quota size
  658. user.QuotaSize = testFileSize - 1
  659. user.QuotaFiles = 0
  660. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  661. assert.NoError(t, err)
  662. err = uploadFile(testFilePath, testFileName+".quota", testFileSize, client)
  663. assert.Error(t, err)
  664. err = client.Rename(testFileName, testFileName+".quota", false)
  665. assert.NoError(t, err)
  666. // now test quota limits while uploading the current file, we have 1 bytes remaining
  667. user.QuotaSize = testFileSize + 1
  668. user.QuotaFiles = 0
  669. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  670. assert.NoError(t, err)
  671. err = uploadFile(testFilePath1, testFileName1, testFileSize1, client)
  672. assert.Error(t, err)
  673. _, err = client.Stat(testFileName1)
  674. assert.Error(t, err)
  675. err = client.Rename(testFileName+".quota", testFileName, false)
  676. assert.NoError(t, err)
  677. // overwriting an existing file will work if the resulting size is lesser or equal than the current one
  678. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  679. assert.NoError(t, err)
  680. err = uploadFile(testFilePath2, testFileName, testFileSize2, client)
  681. assert.NoError(t, err)
  682. err = uploadFile(testFilePath1, testFileName, testFileSize1, client)
  683. assert.Error(t, err)
  684. err = uploadFile(testFilePath2, testFileName, testFileSize2, client)
  685. assert.NoError(t, err)
  686. err = os.Remove(testFilePath)
  687. assert.NoError(t, err)
  688. err = os.Remove(testFilePath1)
  689. assert.NoError(t, err)
  690. err = os.Remove(testFilePath2)
  691. assert.NoError(t, err)
  692. _, err = httpd.RemoveUser(user, http.StatusOK)
  693. assert.NoError(t, err)
  694. err = os.RemoveAll(user.GetHomeDir())
  695. assert.NoError(t, err)
  696. }
  697. func TestUploadMaxSize(t *testing.T) {
  698. testFileSize := int64(65535)
  699. u := getTestUser()
  700. u.Filters.MaxUploadFileSize = testFileSize + 1
  701. user, _, err := httpd.AddUser(u, http.StatusOK)
  702. assert.NoError(t, err)
  703. testFilePath := filepath.Join(homeBasePath, testFileName)
  704. err = createTestFile(testFilePath, testFileSize)
  705. assert.NoError(t, err)
  706. testFileSize1 := int64(131072)
  707. testFileName1 := "test_file_dav1.dat"
  708. testFilePath1 := filepath.Join(homeBasePath, testFileName1)
  709. err = createTestFile(testFilePath1, testFileSize1)
  710. assert.NoError(t, err)
  711. client := getWebDavClient(user)
  712. err = uploadFile(testFilePath1, testFileName1, testFileSize1, client)
  713. assert.Error(t, err)
  714. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  715. assert.NoError(t, err)
  716. // now test overwrite an existing file with a size bigger than the allowed one
  717. err = createTestFile(filepath.Join(user.GetHomeDir(), testFileName1), testFileSize1)
  718. assert.NoError(t, err)
  719. err = uploadFile(testFilePath1, testFileName1, testFileSize1, client)
  720. assert.Error(t, err)
  721. err = os.Remove(testFilePath)
  722. assert.NoError(t, err)
  723. err = os.Remove(testFilePath1)
  724. assert.NoError(t, err)
  725. _, err = httpd.RemoveUser(user, http.StatusOK)
  726. assert.NoError(t, err)
  727. err = os.RemoveAll(user.GetHomeDir())
  728. assert.NoError(t, err)
  729. }
  730. func TestClientClose(t *testing.T) {
  731. u := getTestUser()
  732. u.UploadBandwidth = 64
  733. u.DownloadBandwidth = 64
  734. user, _, err := httpd.AddUser(u, http.StatusOK)
  735. assert.NoError(t, err)
  736. testFileSize := int64(1048576)
  737. testFilePath := filepath.Join(homeBasePath, testFileName)
  738. err = createTestFile(testFilePath, testFileSize)
  739. assert.NoError(t, err)
  740. client := getWebDavClient(user)
  741. assert.NoError(t, checkBasicFunc(client))
  742. var wg sync.WaitGroup
  743. wg.Add(1)
  744. go func() {
  745. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  746. assert.Error(t, err)
  747. wg.Done()
  748. }()
  749. assert.Eventually(t, func() bool {
  750. for _, stat := range common.Connections.GetStats() {
  751. if len(stat.Transfers) > 0 {
  752. return true
  753. }
  754. }
  755. return false
  756. }, 1*time.Second, 50*time.Millisecond)
  757. for _, stat := range common.Connections.GetStats() {
  758. common.Connections.Close(stat.ConnectionID)
  759. }
  760. wg.Wait()
  761. assert.Eventually(t, func() bool { return len(common.Connections.GetStats()) == 0 },
  762. 1*time.Second, 100*time.Millisecond)
  763. err = os.Remove(testFilePath)
  764. assert.NoError(t, err)
  765. testFilePath = filepath.Join(user.HomeDir, testFileName)
  766. err = createTestFile(testFilePath, testFileSize)
  767. assert.NoError(t, err)
  768. localDownloadPath := filepath.Join(homeBasePath, testDLFileName)
  769. wg.Add(1)
  770. go func() {
  771. err = downloadFile(testFileName, localDownloadPath, testFileSize, client)
  772. assert.Error(t, err)
  773. wg.Done()
  774. }()
  775. assert.Eventually(t, func() bool {
  776. for _, stat := range common.Connections.GetStats() {
  777. if len(stat.Transfers) > 0 {
  778. return true
  779. }
  780. }
  781. return false
  782. }, 1*time.Second, 50*time.Millisecond)
  783. for _, stat := range common.Connections.GetStats() {
  784. common.Connections.Close(stat.ConnectionID)
  785. }
  786. wg.Wait()
  787. assert.Eventually(t, func() bool { return len(common.Connections.GetStats()) == 0 },
  788. 1*time.Second, 100*time.Millisecond)
  789. err = os.Remove(localDownloadPath)
  790. assert.NoError(t, err)
  791. _, err = httpd.RemoveUser(user, http.StatusOK)
  792. assert.NoError(t, err)
  793. err = os.RemoveAll(user.GetHomeDir())
  794. assert.NoError(t, err)
  795. }
  796. func TestLoginWithDatabaseCredentials(t *testing.T) {
  797. u := getTestUser()
  798. u.FsConfig.Provider = dataprovider.GCSFilesystemProvider
  799. u.FsConfig.GCSConfig.Bucket = "test"
  800. u.FsConfig.GCSConfig.Credentials = []byte(`{ "type": "service_account" }`)
  801. providerConf := config.GetProviderConf()
  802. providerConf.PreferDatabaseCredentials = true
  803. credentialsFile := filepath.Join(providerConf.CredentialsPath, fmt.Sprintf("%v_gcs_credentials.json", u.Username))
  804. if !filepath.IsAbs(credentialsFile) {
  805. credentialsFile = filepath.Join(configDir, credentialsFile)
  806. }
  807. assert.NoError(t, dataprovider.Close())
  808. err := dataprovider.Initialize(providerConf, configDir)
  809. assert.NoError(t, err)
  810. if _, err = os.Stat(credentialsFile); err == nil {
  811. // remove the credentials file
  812. assert.NoError(t, os.Remove(credentialsFile))
  813. }
  814. user, _, err := httpd.AddUser(u, http.StatusOK)
  815. assert.NoError(t, err)
  816. _, err = os.Stat(credentialsFile)
  817. assert.Error(t, err)
  818. client := getWebDavClient(user)
  819. err = client.Connect()
  820. assert.NoError(t, err)
  821. _, err = httpd.RemoveUser(user, http.StatusOK)
  822. assert.NoError(t, err)
  823. err = os.RemoveAll(user.GetHomeDir())
  824. assert.NoError(t, err)
  825. assert.NoError(t, dataprovider.Close())
  826. assert.NoError(t, config.LoadConfig(configDir, ""))
  827. providerConf = config.GetProviderConf()
  828. assert.NoError(t, dataprovider.Initialize(providerConf, configDir))
  829. }
  830. func TestLoginInvalidFs(t *testing.T) {
  831. u := getTestUser()
  832. u.FsConfig.Provider = dataprovider.GCSFilesystemProvider
  833. u.FsConfig.GCSConfig.Bucket = "test"
  834. u.FsConfig.GCSConfig.Credentials = []byte("invalid JSON for credentials")
  835. user, _, err := httpd.AddUser(u, http.StatusOK)
  836. assert.NoError(t, err)
  837. providerConf := config.GetProviderConf()
  838. credentialsFile := filepath.Join(providerConf.CredentialsPath, fmt.Sprintf("%v_gcs_credentials.json", u.Username))
  839. if !filepath.IsAbs(credentialsFile) {
  840. credentialsFile = filepath.Join(configDir, credentialsFile)
  841. }
  842. // now remove the credentials file so the filesystem creation will fail
  843. err = os.Remove(credentialsFile)
  844. assert.NoError(t, err)
  845. client := getWebDavClient(user)
  846. assert.Error(t, checkBasicFunc(client))
  847. _, err = httpd.RemoveUser(user, http.StatusOK)
  848. assert.NoError(t, err)
  849. err = os.RemoveAll(user.GetHomeDir())
  850. assert.NoError(t, err)
  851. }
  852. func TestBytesRangeRequests(t *testing.T) {
  853. u := getTestUser()
  854. user, _, err := httpd.AddUser(u, http.StatusOK)
  855. assert.NoError(t, err)
  856. testFileName := "test_file.txt"
  857. testFilePath := filepath.Join(homeBasePath, testFileName)
  858. fileContent := []byte("test file contents")
  859. err = ioutil.WriteFile(testFilePath, fileContent, os.ModePerm)
  860. assert.NoError(t, err)
  861. client := getWebDavClient(user)
  862. err = uploadFile(testFilePath, testFileName, int64(len(fileContent)), client)
  863. assert.NoError(t, err)
  864. remotePath := fmt.Sprintf("http://%v/%v/%v", webDavServerAddr, user.Username, testFileName)
  865. req, err := http.NewRequest(http.MethodGet, remotePath, nil)
  866. if assert.NoError(t, err) {
  867. httpClient := httpclient.GetHTTPClient()
  868. req.SetBasicAuth(user.Username, defaultPassword)
  869. req.Header.Set("Range", "bytes=5-")
  870. resp, err := httpClient.Do(req)
  871. if assert.NoError(t, err) {
  872. defer resp.Body.Close()
  873. assert.Equal(t, http.StatusPartialContent, resp.StatusCode)
  874. bodyBytes, err := ioutil.ReadAll(resp.Body)
  875. assert.NoError(t, err)
  876. assert.Equal(t, "file contents", string(bodyBytes))
  877. }
  878. req.Header.Set("Range", "bytes=5-8")
  879. resp, err = httpClient.Do(req)
  880. if assert.NoError(t, err) {
  881. defer resp.Body.Close()
  882. assert.Equal(t, http.StatusPartialContent, resp.StatusCode)
  883. bodyBytes, err := ioutil.ReadAll(resp.Body)
  884. assert.NoError(t, err)
  885. assert.Equal(t, "file", string(bodyBytes))
  886. }
  887. }
  888. assert.NoError(t, err)
  889. err = os.Remove(testFilePath)
  890. assert.NoError(t, err)
  891. _, err = httpd.RemoveUser(user, http.StatusOK)
  892. assert.NoError(t, err)
  893. err = os.RemoveAll(user.GetHomeDir())
  894. assert.NoError(t, err)
  895. }
  896. func TestGETAsPROPFIND(t *testing.T) {
  897. u := getTestUser()
  898. subDir1 := "/sub1"
  899. u.Permissions[subDir1] = []string{dataprovider.PermUpload, dataprovider.PermCreateDirs}
  900. user, _, err := httpd.AddUser(u, http.StatusOK)
  901. assert.NoError(t, err)
  902. rootPath := fmt.Sprintf("http://%v/%v", webDavServerAddr, user.Username)
  903. httpClient := httpclient.GetHTTPClient()
  904. req, err := http.NewRequest(http.MethodGet, rootPath, nil)
  905. if assert.NoError(t, err) {
  906. req.SetBasicAuth(u.Username, u.Password)
  907. resp, err := httpClient.Do(req)
  908. if assert.NoError(t, err) {
  909. assert.Equal(t, http.StatusMultiStatus, resp.StatusCode)
  910. resp.Body.Close()
  911. }
  912. }
  913. client := getWebDavClient(user)
  914. err = client.MkdirAll(path.Join(subDir1, "sub", "sub1"), os.ModePerm)
  915. assert.NoError(t, err)
  916. subPath := fmt.Sprintf("http://%v/%v", webDavServerAddr, path.Join(user.Username, subDir1))
  917. req, err = http.NewRequest(http.MethodGet, subPath, nil)
  918. if assert.NoError(t, err) {
  919. req.SetBasicAuth(u.Username, u.Password)
  920. resp, err := httpClient.Do(req)
  921. if assert.NoError(t, err) {
  922. // before the performance patch we have a 500 here, now we have 207 but an empty list
  923. //assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
  924. assert.Equal(t, http.StatusMultiStatus, resp.StatusCode)
  925. resp.Body.Close()
  926. }
  927. }
  928. // we cannot stat the sub at all
  929. subPath1 := fmt.Sprintf("http://%v/%v", webDavServerAddr, path.Join(user.Username, subDir1, "sub"))
  930. req, err = http.NewRequest(http.MethodGet, subPath1, nil)
  931. if assert.NoError(t, err) {
  932. req.SetBasicAuth(u.Username, u.Password)
  933. resp, err := httpClient.Do(req)
  934. if assert.NoError(t, err) {
  935. // here the stat will fail, so the request will not be changed in propfind
  936. assert.Equal(t, http.StatusForbidden, resp.StatusCode)
  937. resp.Body.Close()
  938. }
  939. }
  940. // we have no permission, we get an empty list
  941. files, err := client.ReadDir(subDir1)
  942. assert.NoError(t, err)
  943. assert.Len(t, files, 0)
  944. // if we grant the permissions the files are listed
  945. user.Permissions[subDir1] = []string{dataprovider.PermDownload, dataprovider.PermListItems}
  946. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  947. assert.NoError(t, err)
  948. files, err = client.ReadDir(subDir1)
  949. assert.NoError(t, err)
  950. assert.Len(t, files, 1)
  951. _, err = httpd.RemoveUser(user, http.StatusOK)
  952. assert.NoError(t, err)
  953. err = os.RemoveAll(user.GetHomeDir())
  954. assert.NoError(t, err)
  955. }
  956. func TestStat(t *testing.T) {
  957. u := getTestUser()
  958. u.Permissions["/subdir"] = []string{dataprovider.PermUpload, dataprovider.PermListItems, dataprovider.PermDownload}
  959. user, _, err := httpd.AddUser(u, http.StatusOK)
  960. assert.NoError(t, err)
  961. client := getWebDavClient(user)
  962. subDir := "subdir"
  963. testFilePath := filepath.Join(homeBasePath, testFileName)
  964. testFileSize := int64(65535)
  965. err = createTestFile(testFilePath, testFileSize)
  966. assert.NoError(t, err)
  967. err = client.Mkdir(subDir, os.ModePerm)
  968. assert.NoError(t, err)
  969. err = uploadFile(testFilePath, testFileName, testFileSize, client)
  970. assert.NoError(t, err)
  971. err = uploadFile(testFilePath, path.Join("/", subDir, testFileName), testFileSize, client)
  972. assert.NoError(t, err)
  973. user.Permissions["/subdir"] = []string{dataprovider.PermUpload, dataprovider.PermDownload}
  974. user, _, err = httpd.UpdateUser(user, http.StatusOK, "")
  975. assert.NoError(t, err)
  976. _, err = client.Stat(testFileName)
  977. assert.NoError(t, err)
  978. _, err = client.Stat(path.Join("/", subDir, testFileName))
  979. assert.Error(t, err)
  980. err = os.Remove(testFilePath)
  981. assert.NoError(t, err)
  982. _, err = httpd.RemoveUser(user, http.StatusOK)
  983. assert.NoError(t, err)
  984. err = os.RemoveAll(user.GetHomeDir())
  985. assert.NoError(t, err)
  986. }
  987. func TestUploadOverwriteVfolder(t *testing.T) {
  988. u := getTestUser()
  989. vdir := "/vdir"
  990. mappedPath := filepath.Join(os.TempDir(), "mappedDir")
  991. u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
  992. BaseVirtualFolder: vfs.BaseVirtualFolder{
  993. MappedPath: mappedPath,
  994. },
  995. VirtualPath: vdir,
  996. QuotaSize: -1,
  997. QuotaFiles: -1,
  998. })
  999. err := os.MkdirAll(mappedPath, os.ModePerm)
  1000. assert.NoError(t, err)
  1001. user, _, err := httpd.AddUser(u, http.StatusOK)
  1002. assert.NoError(t, err)
  1003. client := getWebDavClient(user)
  1004. files, err := client.ReadDir(".")
  1005. assert.NoError(t, err)
  1006. vdirFound := false
  1007. for _, info := range files {
  1008. if info.Name() == path.Base(vdir) {
  1009. vdirFound = true
  1010. break
  1011. }
  1012. }
  1013. assert.True(t, vdirFound)
  1014. info, err := client.Stat(vdir)
  1015. if assert.NoError(t, err) {
  1016. assert.Equal(t, path.Base(vdir), info.Name())
  1017. }
  1018. testFilePath := filepath.Join(homeBasePath, testFileName)
  1019. testFileSize := int64(65535)
  1020. err = createTestFile(testFilePath, testFileSize)
  1021. assert.NoError(t, err)
  1022. err = uploadFile(testFilePath, path.Join(vdir, testFileName), testFileSize, client)
  1023. assert.NoError(t, err)
  1024. folder, _, err := httpd.GetFolders(0, 0, mappedPath, http.StatusOK)
  1025. assert.NoError(t, err)
  1026. if assert.Len(t, folder, 1) {
  1027. f := folder[0]
  1028. assert.Equal(t, testFileSize, f.UsedQuotaSize)
  1029. assert.Equal(t, 1, f.UsedQuotaFiles)
  1030. }
  1031. err = uploadFile(testFilePath, path.Join(vdir, testFileName), testFileSize, client)
  1032. assert.NoError(t, err)
  1033. folder, _, err = httpd.GetFolders(0, 0, mappedPath, http.StatusOK)
  1034. assert.NoError(t, err)
  1035. if assert.Len(t, folder, 1) {
  1036. f := folder[0]
  1037. assert.Equal(t, testFileSize, f.UsedQuotaSize)
  1038. assert.Equal(t, 1, f.UsedQuotaFiles)
  1039. }
  1040. err = os.Remove(testFilePath)
  1041. assert.NoError(t, err)
  1042. _, err = httpd.RemoveUser(user, http.StatusOK)
  1043. assert.NoError(t, err)
  1044. _, err = httpd.RemoveFolder(vfs.BaseVirtualFolder{MappedPath: mappedPath}, http.StatusOK)
  1045. assert.NoError(t, err)
  1046. err = os.RemoveAll(user.GetHomeDir())
  1047. assert.NoError(t, err)
  1048. err = os.RemoveAll(mappedPath)
  1049. assert.NoError(t, err)
  1050. }
  1051. func TestMiscCommands(t *testing.T) {
  1052. u := getTestUser()
  1053. u.QuotaFiles = 100
  1054. user, _, err := httpd.AddUser(u, http.StatusOK)
  1055. assert.NoError(t, err)
  1056. dir := "testDir"
  1057. client := getWebDavClient(user)
  1058. err = client.MkdirAll(path.Join(dir, "sub1", "sub2"), os.ModePerm)
  1059. assert.NoError(t, err)
  1060. testFilePath := filepath.Join(homeBasePath, testFileName)
  1061. testFileSize := int64(65535)
  1062. err = createTestFile(testFilePath, testFileSize)
  1063. assert.NoError(t, err)
  1064. err = uploadFile(testFilePath, path.Join(dir, testFileName), testFileSize, client)
  1065. assert.NoError(t, err)
  1066. err = uploadFile(testFilePath, path.Join(dir, "sub1", testFileName), testFileSize, client)
  1067. assert.NoError(t, err)
  1068. err = uploadFile(testFilePath, path.Join(dir, "sub1", "sub2", testFileName), testFileSize, client)
  1069. assert.NoError(t, err)
  1070. err = client.Copy(dir, dir+"_copy", false)
  1071. assert.NoError(t, err)
  1072. user, _, err = httpd.GetUserByID(user.ID, http.StatusOK)
  1073. assert.NoError(t, err)
  1074. assert.Equal(t, 6, user.UsedQuotaFiles)
  1075. assert.Equal(t, 6*testFileSize, user.UsedQuotaSize)
  1076. err = client.Copy(dir, dir+"_copy1", false)
  1077. assert.NoError(t, err)
  1078. err = client.Copy(dir+"_copy", dir+"_copy1", false)
  1079. assert.Error(t, err)
  1080. err = client.Copy(dir+"_copy", dir+"_copy1", true)
  1081. assert.NoError(t, err)
  1082. user, _, err = httpd.GetUserByID(user.ID, http.StatusOK)
  1083. assert.NoError(t, err)
  1084. assert.Equal(t, 9, user.UsedQuotaFiles)
  1085. assert.Equal(t, 9*testFileSize, user.UsedQuotaSize)
  1086. err = client.Rename(dir+"_copy1", dir+"_copy2", false)
  1087. assert.NoError(t, err)
  1088. err = client.Remove(path.Join(dir+"_copy", testFileName))
  1089. assert.NoError(t, err)
  1090. err = client.Rename(dir+"_copy2", dir+"_copy", true)
  1091. assert.NoError(t, err)
  1092. err = client.Copy(dir+"_copy", dir+"_copy1", false)
  1093. assert.NoError(t, err)
  1094. err = client.RemoveAll(dir + "_copy1")
  1095. assert.NoError(t, err)
  1096. user, _, err = httpd.GetUserByID(user.ID, http.StatusOK)
  1097. assert.NoError(t, err)
  1098. assert.Equal(t, 6, user.UsedQuotaFiles)
  1099. assert.Equal(t, 6*testFileSize, user.UsedQuotaSize)
  1100. err = os.Remove(testFilePath)
  1101. assert.NoError(t, err)
  1102. _, err = httpd.RemoveUser(user, http.StatusOK)
  1103. assert.NoError(t, err)
  1104. err = os.RemoveAll(user.GetHomeDir())
  1105. assert.NoError(t, err)
  1106. }
  1107. func checkBasicFunc(client *gowebdav.Client) error {
  1108. err := client.Connect()
  1109. if err != nil {
  1110. return err
  1111. }
  1112. _, err = client.ReadDir("/")
  1113. return err
  1114. }
  1115. func uploadFile(localSourcePath string, remoteDestPath string, expectedSize int64, client *gowebdav.Client) error {
  1116. srcFile, err := os.Open(localSourcePath)
  1117. if err != nil {
  1118. return err
  1119. }
  1120. defer srcFile.Close()
  1121. err = client.WriteStream(remoteDestPath, srcFile, os.ModePerm)
  1122. if err != nil {
  1123. return err
  1124. }
  1125. if expectedSize > 0 {
  1126. info, err := client.Stat(remoteDestPath)
  1127. if err != nil {
  1128. return err
  1129. }
  1130. if info.Size() != expectedSize {
  1131. return fmt.Errorf("uploaded file size does not match, actual: %v, expected: %v", info.Size(), expectedSize)
  1132. }
  1133. }
  1134. return nil
  1135. }
  1136. func downloadFile(remoteSourcePath string, localDestPath string, expectedSize int64, client *gowebdav.Client) error {
  1137. downloadDest, err := os.Create(localDestPath)
  1138. if err != nil {
  1139. return err
  1140. }
  1141. defer downloadDest.Close()
  1142. reader, err := client.ReadStream(remoteSourcePath)
  1143. if err != nil {
  1144. return err
  1145. }
  1146. defer reader.Close()
  1147. written, err := io.Copy(downloadDest, reader)
  1148. if err != nil {
  1149. return err
  1150. }
  1151. if written != expectedSize {
  1152. return fmt.Errorf("downloaded file size does not match, actual: %v, expected: %v", written, expectedSize)
  1153. }
  1154. return nil
  1155. }
  1156. func getWebDavClient(user dataprovider.User) *gowebdav.Client {
  1157. rootPath := fmt.Sprintf("http://%v/%v", webDavServerAddr, user.Username)
  1158. pwd := defaultPassword
  1159. if len(user.Password) > 0 {
  1160. pwd = user.Password
  1161. }
  1162. client := gowebdav.NewClient(rootPath, user.Username, pwd)
  1163. client.SetTimeout(5 * time.Second)
  1164. return client
  1165. }
  1166. func waitTCPListening(address string) {
  1167. for {
  1168. conn, err := net.Dial("tcp", address)
  1169. if err != nil {
  1170. logger.WarnToConsole("tcp server %v not listening: %v\n", address, err)
  1171. time.Sleep(100 * time.Millisecond)
  1172. continue
  1173. }
  1174. logger.InfoToConsole("tcp server %v now listening\n", address)
  1175. conn.Close()
  1176. break
  1177. }
  1178. }
  1179. func getTestUser() dataprovider.User {
  1180. user := dataprovider.User{
  1181. Username: defaultUsername,
  1182. Password: defaultPassword,
  1183. HomeDir: filepath.Join(homeBasePath, defaultUsername),
  1184. Status: 1,
  1185. ExpirationDate: 0,
  1186. }
  1187. user.Permissions = make(map[string][]string)
  1188. user.Permissions["/"] = allPerms
  1189. return user
  1190. }
  1191. func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool, username string) []byte {
  1192. extAuthContent := []byte("#!/bin/sh\n\n")
  1193. extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...)
  1194. if len(username) > 0 {
  1195. user.Username = username
  1196. }
  1197. u, _ := json.Marshal(user)
  1198. if nonJSONResponse {
  1199. extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
  1200. } else {
  1201. extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...)
  1202. }
  1203. extAuthContent = append(extAuthContent, []byte("else\n")...)
  1204. if nonJSONResponse {
  1205. extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
  1206. } else {
  1207. extAuthContent = append(extAuthContent, []byte("echo '{\"username\":\"\"}'\n")...)
  1208. }
  1209. extAuthContent = append(extAuthContent, []byte("fi\n")...)
  1210. return extAuthContent
  1211. }
  1212. func getPreLoginScriptContent(user dataprovider.User, nonJSONResponse bool) []byte {
  1213. content := []byte("#!/bin/sh\n\n")
  1214. if nonJSONResponse {
  1215. content = append(content, []byte("echo 'text response'\n")...)
  1216. return content
  1217. }
  1218. if len(user.Username) > 0 {
  1219. u, _ := json.Marshal(user)
  1220. content = append(content, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...)
  1221. }
  1222. return content
  1223. }
  1224. func getPostConnectScriptContent(exitCode int) []byte {
  1225. content := []byte("#!/bin/sh\n\n")
  1226. content = append(content, []byte(fmt.Sprintf("exit %v", exitCode))...)
  1227. return content
  1228. }
  1229. func createTestFile(path string, size int64) error {
  1230. baseDir := filepath.Dir(path)
  1231. if _, err := os.Stat(baseDir); os.IsNotExist(err) {
  1232. err = os.MkdirAll(baseDir, os.ModePerm)
  1233. if err != nil {
  1234. return err
  1235. }
  1236. }
  1237. content := make([]byte, size)
  1238. _, err := rand.Read(content)
  1239. if err != nil {
  1240. return err
  1241. }
  1242. return ioutil.WriteFile(path, content, os.ModePerm)
  1243. }