api_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. package api_test
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "net"
  7. "net/http"
  8. "net/http/httptest"
  9. "os"
  10. "path/filepath"
  11. "runtime"
  12. "strconv"
  13. "testing"
  14. "time"
  15. "github.com/go-chi/render"
  16. _ "github.com/go-sql-driver/mysql"
  17. _ "github.com/lib/pq"
  18. _ "github.com/mattn/go-sqlite3"
  19. "github.com/rs/zerolog"
  20. "github.com/drakkan/sftpgo/api"
  21. "github.com/drakkan/sftpgo/config"
  22. "github.com/drakkan/sftpgo/dataprovider"
  23. "github.com/drakkan/sftpgo/logger"
  24. "github.com/drakkan/sftpgo/sftpd"
  25. )
  26. const (
  27. defaultUsername = "test_user"
  28. defaultPassword = "test_password"
  29. testPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1"
  30. logSender = "APITesting"
  31. userPath = "/api/v1/user"
  32. activeConnectionsPath = "/api/v1/connection"
  33. quotaScanPath = "/api/v1/quota_scan"
  34. versionPath = "/api/v1/version"
  35. )
  36. var (
  37. defaultPerms = []string{dataprovider.PermAny}
  38. homeBasePath string
  39. testServer *httptest.Server
  40. )
  41. func TestMain(m *testing.M) {
  42. if runtime.GOOS == "windows" {
  43. homeBasePath = "C:\\"
  44. } else {
  45. homeBasePath = "/tmp"
  46. }
  47. configDir := ".."
  48. logfilePath := filepath.Join(configDir, "sftpgo_api_test.log")
  49. logger.InitLogger(logfilePath, 5, 1, 28, false, zerolog.DebugLevel)
  50. config.LoadConfig(configDir, "")
  51. providerConf := config.GetProviderConf()
  52. err := dataprovider.Initialize(providerConf, configDir)
  53. if err != nil {
  54. logger.Warn(logSender, "error initializing data provider: %v", err)
  55. os.Exit(1)
  56. }
  57. dataProvider := dataprovider.GetProvider()
  58. httpdConf := config.GetHTTPDConfig()
  59. router := api.GetHTTPRouter()
  60. httpdConf.BindPort = 8081
  61. api.SetBaseURL("http://127.0.0.1:8081")
  62. sftpd.SetDataProvider(dataProvider)
  63. api.SetDataProvider(dataProvider)
  64. go func() {
  65. logger.Debug(logSender, "initializing HTTP server with config %+v", httpdConf)
  66. s := &http.Server{
  67. Addr: fmt.Sprintf("%s:%d", httpdConf.BindAddress, httpdConf.BindPort),
  68. Handler: router,
  69. ReadTimeout: 300 * time.Second,
  70. WriteTimeout: 300 * time.Second,
  71. MaxHeaderBytes: 1 << 20, // 1MB
  72. }
  73. if err := s.ListenAndServe(); err != nil {
  74. logger.Error(logSender, "could not start HTTP server: %v", err)
  75. }
  76. }()
  77. testServer = httptest.NewServer(api.GetHTTPRouter())
  78. defer testServer.Close()
  79. waitTCPListening(fmt.Sprintf("%s:%d", httpdConf.BindAddress, httpdConf.BindPort))
  80. exitCode := m.Run()
  81. os.Remove(logfilePath)
  82. os.Exit(exitCode)
  83. }
  84. func TestBasicUserHandling(t *testing.T) {
  85. user, _, err := api.AddUser(getTestUser(), http.StatusOK)
  86. if err != nil {
  87. t.Errorf("unable to add user: %v", err)
  88. }
  89. user.MaxSessions = 10
  90. user.QuotaSize = 4096
  91. user.QuotaFiles = 2
  92. user.UploadBandwidth = 128
  93. user.DownloadBandwidth = 64
  94. user, _, err = api.UpdateUser(user, http.StatusOK)
  95. if err != nil {
  96. t.Errorf("unable to update user: %v", err)
  97. }
  98. users, _, err := api.GetUsers(0, 0, defaultUsername, http.StatusOK)
  99. if err != nil {
  100. t.Errorf("unable to get users: %v", err)
  101. }
  102. if len(users) != 1 {
  103. t.Errorf("number of users mismatch, expected: 1, actual: %v", len(users))
  104. }
  105. _, err = api.RemoveUser(user, http.StatusOK)
  106. if err != nil {
  107. t.Errorf("unable to remove: %v", err)
  108. }
  109. }
  110. func TestAddUserNoCredentials(t *testing.T) {
  111. u := getTestUser()
  112. u.Password = ""
  113. u.PublicKeys = []string{}
  114. _, _, err := api.AddUser(u, http.StatusBadRequest)
  115. if err != nil {
  116. t.Errorf("unexpected error adding user with no credentials: %v", err)
  117. }
  118. }
  119. func TestAddUserNoUsername(t *testing.T) {
  120. u := getTestUser()
  121. u.Username = ""
  122. _, _, err := api.AddUser(u, http.StatusBadRequest)
  123. if err != nil {
  124. t.Errorf("unexpected error adding user with no home dir: %v", err)
  125. }
  126. }
  127. func TestAddUserNoHomeDir(t *testing.T) {
  128. u := getTestUser()
  129. u.HomeDir = ""
  130. _, _, err := api.AddUser(u, http.StatusBadRequest)
  131. if err != nil {
  132. t.Errorf("unexpected error adding user with no home dir: %v", err)
  133. }
  134. }
  135. func TestAddUserInvalidHomeDir(t *testing.T) {
  136. u := getTestUser()
  137. u.HomeDir = "relative_path"
  138. _, _, err := api.AddUser(u, http.StatusBadRequest)
  139. if err != nil {
  140. t.Errorf("unexpected error adding user with invalid home dir: %v", err)
  141. }
  142. }
  143. func TestAddUserNoPerms(t *testing.T) {
  144. u := getTestUser()
  145. u.Permissions = []string{}
  146. _, _, err := api.AddUser(u, http.StatusBadRequest)
  147. if err != nil {
  148. t.Errorf("unexpected error adding user with no perms: %v", err)
  149. }
  150. }
  151. func TestAddUserInvalidPerms(t *testing.T) {
  152. u := getTestUser()
  153. u.Permissions = []string{"invalidPerm"}
  154. _, _, err := api.AddUser(u, http.StatusBadRequest)
  155. if err != nil {
  156. t.Errorf("unexpected error adding user with no perms: %v", err)
  157. }
  158. }
  159. func TestUserPublicKey(t *testing.T) {
  160. u := getTestUser()
  161. invalidPubKey := "invalid"
  162. validPubKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+djk7pxIf/0OhrxrchJTRZklofJ1NoIu4752Sq02mdXmarMVsqJ1cAjV5LBVy3D1F5U6XW4rppkXeVtd04Pxb09ehtH0pRRPaoHHlALiJt8CoMpbKYMA8b3KXPPriGxgGomvtU2T2RMURSwOZbMtpsugfjYSWenyYX+VORYhylWnSXL961LTyC21ehd6d6QnW9G7E5hYMITMY9TuQZz3bROYzXiTsgN0+g6Hn7exFQp50p45StUMfV/SftCMdCxlxuyGny2CrN/vfjO7xxOo2uv7q1qm10Q46KPWJQv+pgZ/OfL+EDjy07n5QVSKHlbx+2nT4Q0EgOSQaCTYwn3YjtABfIxWwgAFdyj6YlPulCL22qU4MYhDcA6PSBwDdf8hvxBfvsiHdM+JcSHvv8/VeJhk6CmnZxGY0fxBupov27z3yEO8nAg8k+6PaUiW1MSUfuGMF/ktB8LOstXsEPXSszuyXiOv4DaryOXUiSn7bmRqKcEFlJusO6aZP0= nicola@p1"
  163. u.PublicKeys = []string{invalidPubKey}
  164. _, _, err := api.AddUser(u, http.StatusBadRequest)
  165. if err != nil {
  166. t.Errorf("unexpected error adding user with invalid pub key: %v", err)
  167. }
  168. u.PublicKeys = []string{validPubKey}
  169. user, _, err := api.AddUser(u, http.StatusOK)
  170. if err != nil {
  171. t.Errorf("unable to add user: %v", err)
  172. }
  173. user.PublicKeys = []string{validPubKey, invalidPubKey}
  174. _, _, err = api.UpdateUser(user, http.StatusBadRequest)
  175. if err != nil {
  176. t.Errorf("update user with invalid public key must fail: %v", err)
  177. }
  178. user.PublicKeys = []string{validPubKey, validPubKey, validPubKey}
  179. _, _, err = api.UpdateUser(user, http.StatusOK)
  180. if err != nil {
  181. t.Errorf("unable to update user: %v", err)
  182. }
  183. _, err = api.RemoveUser(user, http.StatusOK)
  184. if err != nil {
  185. t.Errorf("unable to remove: %v", err)
  186. }
  187. }
  188. func TestUpdateUser(t *testing.T) {
  189. user, _, err := api.AddUser(getTestUser(), http.StatusOK)
  190. if err != nil {
  191. t.Errorf("unable to add user: %v", err)
  192. }
  193. user.HomeDir = filepath.Join(homeBasePath, "testmod")
  194. user.UID = 33
  195. user.GID = 101
  196. user.MaxSessions = 10
  197. user.QuotaSize = 4096
  198. user.QuotaFiles = 2
  199. user.Permissions = []string{dataprovider.PermCreateDirs, dataprovider.PermDelete, dataprovider.PermDownload}
  200. user.UploadBandwidth = 1024
  201. user.DownloadBandwidth = 512
  202. user, _, err = api.UpdateUser(user, http.StatusOK)
  203. if err != nil {
  204. t.Errorf("unable to update user: %v", err)
  205. }
  206. _, err = api.RemoveUser(user, http.StatusOK)
  207. if err != nil {
  208. t.Errorf("unable to remove: %v", err)
  209. }
  210. }
  211. func TestUpdateUserNoCredentials(t *testing.T) {
  212. user, _, err := api.AddUser(getTestUser(), http.StatusOK)
  213. if err != nil {
  214. t.Errorf("unable to add user: %v", err)
  215. }
  216. user.Password = ""
  217. user.PublicKeys = []string{}
  218. // password and public key will be omitted from json serialization if empty and so they will remain unchanged
  219. // and no validation error will be raised
  220. _, _, err = api.UpdateUser(user, http.StatusOK)
  221. if err != nil {
  222. t.Errorf("unexpected error updating user with no credentials: %v", err)
  223. }
  224. _, err = api.RemoveUser(user, http.StatusOK)
  225. if err != nil {
  226. t.Errorf("unable to remove: %v", err)
  227. }
  228. }
  229. func TestUpdateUserEmptyHomeDir(t *testing.T) {
  230. user, _, err := api.AddUser(getTestUser(), http.StatusOK)
  231. if err != nil {
  232. t.Errorf("unable to add user: %v", err)
  233. }
  234. user.HomeDir = ""
  235. _, _, err = api.UpdateUser(user, http.StatusBadRequest)
  236. if err != nil {
  237. t.Errorf("unexpected error updating user with empty home dir: %v", err)
  238. }
  239. _, err = api.RemoveUser(user, http.StatusOK)
  240. if err != nil {
  241. t.Errorf("unable to remove: %v", err)
  242. }
  243. }
  244. func TestUpdateUserInvalidHomeDir(t *testing.T) {
  245. user, _, err := api.AddUser(getTestUser(), http.StatusOK)
  246. if err != nil {
  247. t.Errorf("unable to add user: %v", err)
  248. }
  249. user.HomeDir = "relative_path"
  250. _, _, err = api.UpdateUser(user, http.StatusBadRequest)
  251. if err != nil {
  252. t.Errorf("unexpected error updating user with empty home dir: %v", err)
  253. }
  254. _, err = api.RemoveUser(user, http.StatusOK)
  255. if err != nil {
  256. t.Errorf("unable to remove: %v", err)
  257. }
  258. }
  259. func TestUpdateNonExistentUser(t *testing.T) {
  260. _, _, err := api.UpdateUser(getTestUser(), http.StatusNotFound)
  261. if err != nil {
  262. t.Errorf("unable to update user: %v", err)
  263. }
  264. }
  265. func TestGetNonExistentUser(t *testing.T) {
  266. _, _, err := api.GetUserByID(0, http.StatusNotFound)
  267. if err != nil {
  268. t.Errorf("unable to get user: %v", err)
  269. }
  270. }
  271. func TestDeleteNonExistentUser(t *testing.T) {
  272. _, err := api.RemoveUser(getTestUser(), http.StatusNotFound)
  273. if err != nil {
  274. t.Errorf("unable to remove user: %v", err)
  275. }
  276. }
  277. func TestAddDuplicateUser(t *testing.T) {
  278. user, _, err := api.AddUser(getTestUser(), http.StatusOK)
  279. if err != nil {
  280. t.Errorf("unable to add user: %v", err)
  281. }
  282. _, _, err = api.AddUser(getTestUser(), http.StatusInternalServerError)
  283. if err != nil {
  284. t.Errorf("unable to add second user: %v", err)
  285. }
  286. _, _, err = api.AddUser(getTestUser(), http.StatusOK)
  287. if err == nil {
  288. t.Errorf("adding a duplicate user must fail")
  289. }
  290. _, err = api.RemoveUser(user, http.StatusOK)
  291. if err != nil {
  292. t.Errorf("unable to remove user: %v", err)
  293. }
  294. }
  295. func TestGetUsers(t *testing.T) {
  296. user1, _, err := api.AddUser(getTestUser(), http.StatusOK)
  297. if err != nil {
  298. t.Errorf("unable to add user: %v", err)
  299. }
  300. u := getTestUser()
  301. u.Username = defaultUsername + "1"
  302. user2, _, err := api.AddUser(u, http.StatusOK)
  303. if err != nil {
  304. t.Errorf("unable to add second user: %v", err)
  305. }
  306. users, _, err := api.GetUsers(0, 0, "", http.StatusOK)
  307. if err != nil {
  308. t.Errorf("unable to get users: %v", err)
  309. }
  310. if len(users) < 2 {
  311. t.Errorf("at least 2 users are expected")
  312. }
  313. users, _, err = api.GetUsers(1, 0, "", http.StatusOK)
  314. if err != nil {
  315. t.Errorf("unable to get users: %v", err)
  316. }
  317. if len(users) != 1 {
  318. t.Errorf("1 user is expected")
  319. }
  320. users, _, err = api.GetUsers(1, 1, "", http.StatusOK)
  321. if err != nil {
  322. t.Errorf("unable to get users: %v", err)
  323. }
  324. if len(users) != 1 {
  325. t.Errorf("1 user is expected")
  326. }
  327. _, _, err = api.GetUsers(1, 1, "", http.StatusInternalServerError)
  328. if err == nil {
  329. t.Errorf("get users must succeed, we requested a fail for a good request")
  330. }
  331. _, err = api.RemoveUser(user1, http.StatusOK)
  332. if err != nil {
  333. t.Errorf("unable to remove user: %v", err)
  334. }
  335. _, err = api.RemoveUser(user2, http.StatusOK)
  336. if err != nil {
  337. t.Errorf("unable to remove user: %v", err)
  338. }
  339. }
  340. func TestGetQuotaScans(t *testing.T) {
  341. _, _, err := api.GetQuotaScans(http.StatusOK)
  342. if err != nil {
  343. t.Errorf("unable to get quota scans: %v", err)
  344. }
  345. _, _, err = api.GetQuotaScans(http.StatusInternalServerError)
  346. if err == nil {
  347. t.Errorf("quota scan request must succeed, we requested to check a wrong status code")
  348. }
  349. }
  350. func TestStartQuotaScan(t *testing.T) {
  351. user, _, err := api.AddUser(getTestUser(), http.StatusOK)
  352. if err != nil {
  353. t.Errorf("unable to add user: %v", err)
  354. }
  355. _, err = api.StartQuotaScan(user, http.StatusCreated)
  356. if err != nil {
  357. t.Errorf("unable to start quota scan: %v", err)
  358. }
  359. _, err = api.RemoveUser(user, http.StatusOK)
  360. if err != nil {
  361. t.Errorf("unable to remove user: %v", err)
  362. }
  363. }
  364. func TestGetVersion(t *testing.T) {
  365. _, _, err := api.GetVersion(http.StatusOK)
  366. if err != nil {
  367. t.Errorf("unable to get sftp version: %v", err)
  368. }
  369. _, _, err = api.GetVersion(http.StatusInternalServerError)
  370. if err == nil {
  371. t.Errorf("get version request must succeed, we requested to check a wrong status code")
  372. }
  373. }
  374. func TestGetConnections(t *testing.T) {
  375. _, _, err := api.GetConnections(http.StatusOK)
  376. if err != nil {
  377. t.Errorf("unable to get sftp connections: %v", err)
  378. }
  379. _, _, err = api.GetConnections(http.StatusInternalServerError)
  380. if err == nil {
  381. t.Errorf("get sftp connections request must succeed, we requested to check a wrong status code")
  382. }
  383. }
  384. func TestCloseActiveConnection(t *testing.T) {
  385. _, err := api.CloseConnection("non_existent_id", http.StatusNotFound)
  386. if err != nil {
  387. t.Errorf("unexpected error closing non existent sftp connection: %v", err)
  388. }
  389. }
  390. // test using mock http server
  391. func TestBasicUserHandlingMock(t *testing.T) {
  392. user := getTestUser()
  393. userAsJSON := getUserAsJSON(t, user)
  394. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  395. rr := executeRequest(req)
  396. checkResponseCode(t, http.StatusOK, rr.Code)
  397. err := render.DecodeJSON(rr.Body, &user)
  398. if err != nil {
  399. t.Errorf("Error get user: %v", err)
  400. }
  401. req, _ = http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  402. rr = executeRequest(req)
  403. checkResponseCode(t, http.StatusInternalServerError, rr.Code)
  404. user.MaxSessions = 10
  405. user.UploadBandwidth = 128
  406. userAsJSON = getUserAsJSON(t, user)
  407. req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
  408. rr = executeRequest(req)
  409. checkResponseCode(t, http.StatusOK, rr.Code)
  410. req, _ = http.NewRequest(http.MethodGet, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
  411. rr = executeRequest(req)
  412. checkResponseCode(t, http.StatusOK, rr.Code)
  413. var updatedUser dataprovider.User
  414. err = render.DecodeJSON(rr.Body, &updatedUser)
  415. if err != nil {
  416. t.Errorf("Error decoding updated user: %v", err)
  417. }
  418. if user.MaxSessions != updatedUser.MaxSessions || user.UploadBandwidth != updatedUser.UploadBandwidth {
  419. t.Errorf("Error modifying user actual: %v, %v", updatedUser.MaxSessions, updatedUser.UploadBandwidth)
  420. }
  421. req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
  422. rr = executeRequest(req)
  423. checkResponseCode(t, http.StatusOK, rr.Code)
  424. }
  425. func TestGetUserByIdInvalidParamsMock(t *testing.T) {
  426. req, _ := http.NewRequest(http.MethodGet, userPath+"/0", nil)
  427. rr := executeRequest(req)
  428. checkResponseCode(t, http.StatusNotFound, rr.Code)
  429. req, _ = http.NewRequest(http.MethodGet, userPath+"/a", nil)
  430. rr = executeRequest(req)
  431. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  432. }
  433. func TestAddUserNoUsernameMock(t *testing.T) {
  434. user := getTestUser()
  435. user.Username = ""
  436. userAsJSON := getUserAsJSON(t, user)
  437. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  438. rr := executeRequest(req)
  439. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  440. }
  441. func TestAddUserInvalidHomeDirMock(t *testing.T) {
  442. user := getTestUser()
  443. user.HomeDir = "relative_path"
  444. userAsJSON := getUserAsJSON(t, user)
  445. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  446. rr := executeRequest(req)
  447. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  448. }
  449. func TestAddUserInvalidPermsMock(t *testing.T) {
  450. user := getTestUser()
  451. user.Permissions = []string{}
  452. userAsJSON := getUserAsJSON(t, user)
  453. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  454. rr := executeRequest(req)
  455. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  456. }
  457. func TestAddUserInvalidJsonMock(t *testing.T) {
  458. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer([]byte("invalid json")))
  459. rr := executeRequest(req)
  460. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  461. }
  462. func TestUpdateUserInvalidJsonMock(t *testing.T) {
  463. user := getTestUser()
  464. userAsJSON := getUserAsJSON(t, user)
  465. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  466. rr := executeRequest(req)
  467. checkResponseCode(t, http.StatusOK, rr.Code)
  468. err := render.DecodeJSON(rr.Body, &user)
  469. if err != nil {
  470. t.Errorf("Error get user: %v", err)
  471. }
  472. req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer([]byte("Invalid json")))
  473. rr = executeRequest(req)
  474. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  475. req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
  476. rr = executeRequest(req)
  477. checkResponseCode(t, http.StatusOK, rr.Code)
  478. }
  479. func TestUpdateUserInvalidParamsMock(t *testing.T) {
  480. user := getTestUser()
  481. userAsJSON := getUserAsJSON(t, user)
  482. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  483. rr := executeRequest(req)
  484. checkResponseCode(t, http.StatusOK, rr.Code)
  485. err := render.DecodeJSON(rr.Body, &user)
  486. if err != nil {
  487. t.Errorf("Error get user: %v", err)
  488. }
  489. user.HomeDir = ""
  490. userAsJSON = getUserAsJSON(t, user)
  491. req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(user.ID, 10), bytes.NewBuffer(userAsJSON))
  492. rr = executeRequest(req)
  493. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  494. userID := user.ID
  495. user.ID = 0
  496. userAsJSON = getUserAsJSON(t, user)
  497. req, _ = http.NewRequest(http.MethodPut, userPath+"/"+strconv.FormatInt(userID, 10), bytes.NewBuffer(userAsJSON))
  498. rr = executeRequest(req)
  499. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  500. user.ID = userID
  501. req, _ = http.NewRequest(http.MethodPut, userPath+"/0", bytes.NewBuffer(userAsJSON))
  502. rr = executeRequest(req)
  503. checkResponseCode(t, http.StatusNotFound, rr.Code)
  504. req, _ = http.NewRequest(http.MethodPut, userPath+"/a", bytes.NewBuffer(userAsJSON))
  505. rr = executeRequest(req)
  506. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  507. req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
  508. rr = executeRequest(req)
  509. checkResponseCode(t, http.StatusOK, rr.Code)
  510. }
  511. func TestGetUsersMock(t *testing.T) {
  512. user := getTestUser()
  513. userAsJSON := getUserAsJSON(t, user)
  514. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  515. rr := executeRequest(req)
  516. checkResponseCode(t, http.StatusOK, rr.Code)
  517. err := render.DecodeJSON(rr.Body, &user)
  518. if err != nil {
  519. t.Errorf("Error get user: %v", err)
  520. }
  521. req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=510&offset=0&order=ASC&username="+defaultUsername, nil)
  522. rr = executeRequest(req)
  523. checkResponseCode(t, http.StatusOK, rr.Code)
  524. var users []dataprovider.User
  525. err = render.DecodeJSON(rr.Body, &users)
  526. if err != nil {
  527. t.Errorf("Error decoding users: %v", err)
  528. }
  529. if len(users) != 1 {
  530. t.Errorf("1 user is expected")
  531. }
  532. req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=a&offset=0&order=ASC", nil)
  533. rr = executeRequest(req)
  534. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  535. req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=a&order=ASC", nil)
  536. rr = executeRequest(req)
  537. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  538. req, _ = http.NewRequest(http.MethodGet, userPath+"?limit=1&offset=0&order=ASCa", nil)
  539. rr = executeRequest(req)
  540. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  541. req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
  542. rr = executeRequest(req)
  543. checkResponseCode(t, http.StatusOK, rr.Code)
  544. }
  545. func TestDeleteUserInvalidParamsMock(t *testing.T) {
  546. req, _ := http.NewRequest(http.MethodDelete, userPath+"/0", nil)
  547. rr := executeRequest(req)
  548. checkResponseCode(t, http.StatusNotFound, rr.Code)
  549. req, _ = http.NewRequest(http.MethodDelete, userPath+"/a", nil)
  550. rr = executeRequest(req)
  551. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  552. }
  553. func TestGetQuotaScansMock(t *testing.T) {
  554. req, err := http.NewRequest("GET", quotaScanPath, nil)
  555. if err != nil {
  556. t.Errorf("error get quota scan: %v", err)
  557. }
  558. rr := executeRequest(req)
  559. checkResponseCode(t, http.StatusOK, rr.Code)
  560. }
  561. func TestStartQuotaScanMock(t *testing.T) {
  562. user := getTestUser()
  563. userAsJSON := getUserAsJSON(t, user)
  564. req, _ := http.NewRequest(http.MethodPost, userPath, bytes.NewBuffer(userAsJSON))
  565. rr := executeRequest(req)
  566. checkResponseCode(t, http.StatusOK, rr.Code)
  567. err := render.DecodeJSON(rr.Body, &user)
  568. if err != nil {
  569. t.Errorf("Error get user: %v", err)
  570. }
  571. _, err = os.Stat(user.HomeDir)
  572. if err == nil {
  573. os.Remove(user.HomeDir)
  574. }
  575. // simulate a duplicate quota scan
  576. userAsJSON = getUserAsJSON(t, user)
  577. sftpd.AddQuotaScan(user.Username)
  578. req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
  579. rr = executeRequest(req)
  580. checkResponseCode(t, http.StatusConflict, rr.Code)
  581. sftpd.RemoveQuotaScan(user.Username)
  582. userAsJSON = getUserAsJSON(t, user)
  583. req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
  584. rr = executeRequest(req)
  585. checkResponseCode(t, http.StatusCreated, rr.Code)
  586. req, _ = http.NewRequest(http.MethodGet, quotaScanPath, nil)
  587. rr = executeRequest(req)
  588. checkResponseCode(t, http.StatusOK, rr.Code)
  589. var scans []sftpd.ActiveQuotaScan
  590. err = render.DecodeJSON(rr.Body, &scans)
  591. if err != nil {
  592. t.Errorf("Error get active scans: %v", err)
  593. }
  594. for len(scans) > 0 {
  595. req, _ = http.NewRequest(http.MethodGet, quotaScanPath, nil)
  596. rr = executeRequest(req)
  597. checkResponseCode(t, http.StatusOK, rr.Code)
  598. err = render.DecodeJSON(rr.Body, &scans)
  599. if err != nil {
  600. t.Errorf("Error get active scans: %v", err)
  601. break
  602. }
  603. }
  604. _, err = os.Stat(user.HomeDir)
  605. if err != nil && os.IsNotExist(err) {
  606. os.MkdirAll(user.HomeDir, 0777)
  607. }
  608. req, _ = http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
  609. rr = executeRequest(req)
  610. checkResponseCode(t, http.StatusCreated, rr.Code)
  611. req, _ = http.NewRequest(http.MethodDelete, userPath+"/"+strconv.FormatInt(user.ID, 10), nil)
  612. rr = executeRequest(req)
  613. checkResponseCode(t, http.StatusOK, rr.Code)
  614. }
  615. func TestStartQuotaScanBadUserMock(t *testing.T) {
  616. user := getTestUser()
  617. userAsJSON := getUserAsJSON(t, user)
  618. req, _ := http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer(userAsJSON))
  619. rr := executeRequest(req)
  620. checkResponseCode(t, http.StatusNotFound, rr.Code)
  621. }
  622. func TestStartQuotaScanNonExistentUserMock(t *testing.T) {
  623. req, _ := http.NewRequest(http.MethodPost, quotaScanPath, bytes.NewBuffer([]byte("invalid json")))
  624. rr := executeRequest(req)
  625. checkResponseCode(t, http.StatusBadRequest, rr.Code)
  626. }
  627. func TestGetVersionMock(t *testing.T) {
  628. req, _ := http.NewRequest(http.MethodGet, versionPath, nil)
  629. rr := executeRequest(req)
  630. checkResponseCode(t, http.StatusOK, rr.Code)
  631. }
  632. func TestGetConnectionsMock(t *testing.T) {
  633. req, _ := http.NewRequest(http.MethodGet, activeConnectionsPath, nil)
  634. rr := executeRequest(req)
  635. checkResponseCode(t, http.StatusOK, rr.Code)
  636. }
  637. func TestDeleteActiveConnectionMock(t *testing.T) {
  638. req, _ := http.NewRequest(http.MethodDelete, activeConnectionsPath+"/connectionID", nil)
  639. rr := executeRequest(req)
  640. checkResponseCode(t, http.StatusNotFound, rr.Code)
  641. }
  642. func TestNotFoundMock(t *testing.T) {
  643. req, _ := http.NewRequest(http.MethodGet, "/non/existing/path", nil)
  644. rr := executeRequest(req)
  645. checkResponseCode(t, http.StatusNotFound, rr.Code)
  646. }
  647. func TestMethodNotAllowedMock(t *testing.T) {
  648. req, _ := http.NewRequest(http.MethodPost, activeConnectionsPath, nil)
  649. rr := executeRequest(req)
  650. checkResponseCode(t, http.StatusMethodNotAllowed, rr.Code)
  651. }
  652. func waitTCPListening(address string) {
  653. for {
  654. conn, err := net.Dial("tcp", address)
  655. if err != nil {
  656. logger.WarnToConsole("tcp server %v not listening: %v\n", address, err)
  657. time.Sleep(100 * time.Millisecond)
  658. continue
  659. }
  660. logger.InfoToConsole("tcp server %v now listening\n", address)
  661. defer conn.Close()
  662. break
  663. }
  664. }
  665. func getTestUser() dataprovider.User {
  666. return dataprovider.User{
  667. Username: defaultUsername,
  668. Password: defaultPassword,
  669. HomeDir: filepath.Join(homeBasePath, defaultUsername),
  670. Permissions: defaultPerms,
  671. }
  672. }
  673. func getUserAsJSON(t *testing.T, user dataprovider.User) []byte {
  674. json, err := json.Marshal(user)
  675. if err != nil {
  676. t.Errorf("error get user as json: %v", err)
  677. return []byte("{}")
  678. }
  679. return json
  680. }
  681. func executeRequest(req *http.Request) *httptest.ResponseRecorder {
  682. rr := httptest.NewRecorder()
  683. testServer.Config.Handler.ServeHTTP(rr, req)
  684. return rr
  685. }
  686. func checkResponseCode(t *testing.T, expected, actual int) {
  687. if expected != actual {
  688. t.Errorf("Expected response code %d. Got %d", expected, actual)
  689. }
  690. }