httpdtest.go 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341
  1. // Package httpdtest provides utilities for testing the exposed REST API.
  2. package httpdtest
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "net/http"
  10. "net/url"
  11. "path"
  12. "strconv"
  13. "strings"
  14. "github.com/go-chi/render"
  15. "github.com/drakkan/sftpgo/common"
  16. "github.com/drakkan/sftpgo/dataprovider"
  17. "github.com/drakkan/sftpgo/httpclient"
  18. "github.com/drakkan/sftpgo/httpd"
  19. "github.com/drakkan/sftpgo/kms"
  20. "github.com/drakkan/sftpgo/utils"
  21. "github.com/drakkan/sftpgo/version"
  22. "github.com/drakkan/sftpgo/vfs"
  23. )
  24. const (
  25. tokenPath = "/api/v2/token"
  26. activeConnectionsPath = "/api/v2/connections"
  27. quotaScanPath = "/api/v2/quota-scans"
  28. quotaScanVFolderPath = "/api/v2/folder-quota-scans"
  29. userPath = "/api/v2/users"
  30. versionPath = "/api/v2/version"
  31. folderPath = "/api/v2/folders"
  32. serverStatusPath = "/api/v2/status"
  33. dumpDataPath = "/api/v2/dumpdata"
  34. loadDataPath = "/api/v2/loaddata"
  35. updateUsedQuotaPath = "/api/v2/quota-update"
  36. updateFolderUsedQuotaPath = "/api/v2/folder-quota-update"
  37. defenderBanTime = "/api/v2/defender/bantime"
  38. defenderUnban = "/api/v2/defender/unban"
  39. defenderScore = "/api/v2/defender/score"
  40. adminPath = "/api/v2/admins"
  41. adminPwdPath = "/api/v2/changepwd/admin"
  42. )
  43. const (
  44. defaultTokenAuthUser = "admin"
  45. defaultTokenAuthPass = "password"
  46. )
  47. var (
  48. httpBaseURL = "http://127.0.0.1:8080"
  49. jwtToken = ""
  50. )
  51. // SetBaseURL sets the base url to use for HTTP requests.
  52. // Default URL is "http://127.0.0.1:8080"
  53. func SetBaseURL(url string) {
  54. httpBaseURL = url
  55. }
  56. // SetJWTToken sets the JWT token to use
  57. func SetJWTToken(token string) {
  58. jwtToken = token
  59. }
  60. func sendHTTPRequest(method, url string, body io.Reader, contentType, token string) (*http.Response, error) {
  61. req, err := http.NewRequest(method, url, body)
  62. if err != nil {
  63. return nil, err
  64. }
  65. if contentType != "" {
  66. req.Header.Set("Content-Type", "application/json")
  67. }
  68. if token != "" {
  69. req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
  70. }
  71. return httpclient.GetHTTPClient().Do(req)
  72. }
  73. func buildURLRelativeToBase(paths ...string) string {
  74. // we need to use path.Join and not filepath.Join
  75. // since filepath.Join will use backslash separator on Windows
  76. p := path.Join(paths...)
  77. return fmt.Sprintf("%s/%s", strings.TrimRight(httpBaseURL, "/"), strings.TrimLeft(p, "/"))
  78. }
  79. // GetToken tries to return a JWT token
  80. func GetToken(username, password string) (string, map[string]interface{}, error) {
  81. req, err := http.NewRequest(http.MethodGet, buildURLRelativeToBase(tokenPath), nil)
  82. if err != nil {
  83. return "", nil, err
  84. }
  85. req.SetBasicAuth(username, password)
  86. resp, err := httpclient.GetHTTPClient().Do(req)
  87. if err != nil {
  88. return "", nil, err
  89. }
  90. defer resp.Body.Close()
  91. err = checkResponse(resp.StatusCode, http.StatusOK)
  92. if err != nil {
  93. return "", nil, err
  94. }
  95. responseHolder := make(map[string]interface{})
  96. err = render.DecodeJSON(resp.Body, &responseHolder)
  97. if err != nil {
  98. return "", nil, err
  99. }
  100. return responseHolder["access_token"].(string), responseHolder, nil
  101. }
  102. func getDefaultToken() string {
  103. if jwtToken != "" {
  104. return jwtToken
  105. }
  106. token, _, err := GetToken(defaultTokenAuthUser, defaultTokenAuthPass)
  107. if err != nil {
  108. return ""
  109. }
  110. return token
  111. }
  112. // AddUser adds a new user and checks the received HTTP Status code against expectedStatusCode.
  113. func AddUser(user dataprovider.User, expectedStatusCode int) (dataprovider.User, []byte, error) {
  114. var newUser dataprovider.User
  115. var body []byte
  116. userAsJSON, _ := json.Marshal(user)
  117. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(userPath), bytes.NewBuffer(userAsJSON),
  118. "application/json", getDefaultToken())
  119. if err != nil {
  120. return newUser, body, err
  121. }
  122. defer resp.Body.Close()
  123. err = checkResponse(resp.StatusCode, expectedStatusCode)
  124. if expectedStatusCode != http.StatusCreated {
  125. body, _ = getResponseBody(resp)
  126. return newUser, body, err
  127. }
  128. if err == nil {
  129. err = render.DecodeJSON(resp.Body, &newUser)
  130. } else {
  131. body, _ = getResponseBody(resp)
  132. }
  133. if err == nil {
  134. err = checkUser(&user, &newUser)
  135. }
  136. return newUser, body, err
  137. }
  138. // UpdateUserWithJSON update a user using the provided JSON as POST body
  139. func UpdateUserWithJSON(user dataprovider.User, expectedStatusCode int, disconnect string, userAsJSON []byte) (dataprovider.User, []byte, error) {
  140. var newUser dataprovider.User
  141. var body []byte
  142. url, err := addDisconnectQueryParam(buildURLRelativeToBase(userPath, url.PathEscape(user.Username)), disconnect)
  143. if err != nil {
  144. return user, body, err
  145. }
  146. resp, err := sendHTTPRequest(http.MethodPut, url.String(), bytes.NewBuffer(userAsJSON), "application/json",
  147. getDefaultToken())
  148. if err != nil {
  149. return user, body, err
  150. }
  151. defer resp.Body.Close()
  152. body, _ = getResponseBody(resp)
  153. err = checkResponse(resp.StatusCode, expectedStatusCode)
  154. if expectedStatusCode != http.StatusOK {
  155. return newUser, body, err
  156. }
  157. if err == nil {
  158. newUser, body, err = GetUserByUsername(user.Username, expectedStatusCode)
  159. }
  160. if err == nil {
  161. err = checkUser(&user, &newUser)
  162. }
  163. return newUser, body, err
  164. }
  165. // UpdateUser updates an existing user and checks the received HTTP Status code against expectedStatusCode.
  166. func UpdateUser(user dataprovider.User, expectedStatusCode int, disconnect string) (dataprovider.User, []byte, error) {
  167. userAsJSON, _ := json.Marshal(user)
  168. return UpdateUserWithJSON(user, expectedStatusCode, disconnect, userAsJSON)
  169. }
  170. // RemoveUser removes an existing user and checks the received HTTP Status code against expectedStatusCode.
  171. func RemoveUser(user dataprovider.User, expectedStatusCode int) ([]byte, error) {
  172. var body []byte
  173. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(userPath, url.PathEscape(user.Username)),
  174. nil, "", getDefaultToken())
  175. if err != nil {
  176. return body, err
  177. }
  178. defer resp.Body.Close()
  179. body, _ = getResponseBody(resp)
  180. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  181. }
  182. // GetUserByUsername gets a user by username and checks the received HTTP Status code against expectedStatusCode.
  183. func GetUserByUsername(username string, expectedStatusCode int) (dataprovider.User, []byte, error) {
  184. var user dataprovider.User
  185. var body []byte
  186. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(userPath, url.PathEscape(username)),
  187. nil, "", getDefaultToken())
  188. if err != nil {
  189. return user, body, err
  190. }
  191. defer resp.Body.Close()
  192. err = checkResponse(resp.StatusCode, expectedStatusCode)
  193. if err == nil && expectedStatusCode == http.StatusOK {
  194. err = render.DecodeJSON(resp.Body, &user)
  195. } else {
  196. body, _ = getResponseBody(resp)
  197. }
  198. return user, body, err
  199. }
  200. // GetUsers returns a list of users and checks the received HTTP Status code against expectedStatusCode.
  201. // The number of results can be limited specifying a limit.
  202. // Some results can be skipped specifying an offset.
  203. func GetUsers(limit, offset int64, expectedStatusCode int) ([]dataprovider.User, []byte, error) {
  204. var users []dataprovider.User
  205. var body []byte
  206. url, err := addLimitAndOffsetQueryParams(buildURLRelativeToBase(userPath), limit, offset)
  207. if err != nil {
  208. return users, body, err
  209. }
  210. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  211. if err != nil {
  212. return users, body, err
  213. }
  214. defer resp.Body.Close()
  215. err = checkResponse(resp.StatusCode, expectedStatusCode)
  216. if err == nil && expectedStatusCode == http.StatusOK {
  217. err = render.DecodeJSON(resp.Body, &users)
  218. } else {
  219. body, _ = getResponseBody(resp)
  220. }
  221. return users, body, err
  222. }
  223. // AddAdmin adds a new user and checks the received HTTP Status code against expectedStatusCode.
  224. func AddAdmin(admin dataprovider.Admin, expectedStatusCode int) (dataprovider.Admin, []byte, error) {
  225. var newAdmin dataprovider.Admin
  226. var body []byte
  227. asJSON, _ := json.Marshal(admin)
  228. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(adminPath), bytes.NewBuffer(asJSON),
  229. "application/json", getDefaultToken())
  230. if err != nil {
  231. return newAdmin, body, err
  232. }
  233. defer resp.Body.Close()
  234. err = checkResponse(resp.StatusCode, expectedStatusCode)
  235. if expectedStatusCode != http.StatusCreated {
  236. body, _ = getResponseBody(resp)
  237. return newAdmin, body, err
  238. }
  239. if err == nil {
  240. err = render.DecodeJSON(resp.Body, &newAdmin)
  241. } else {
  242. body, _ = getResponseBody(resp)
  243. }
  244. if err == nil {
  245. err = checkAdmin(&admin, &newAdmin)
  246. }
  247. return newAdmin, body, err
  248. }
  249. // UpdateAdmin updates an existing admin and checks the received HTTP Status code against expectedStatusCode
  250. func UpdateAdmin(admin dataprovider.Admin, expectedStatusCode int) (dataprovider.Admin, []byte, error) {
  251. var newAdmin dataprovider.Admin
  252. var body []byte
  253. asJSON, _ := json.Marshal(admin)
  254. resp, err := sendHTTPRequest(http.MethodPut, buildURLRelativeToBase(adminPath, url.PathEscape(admin.Username)),
  255. bytes.NewBuffer(asJSON), "application/json", getDefaultToken())
  256. if err != nil {
  257. return newAdmin, body, err
  258. }
  259. defer resp.Body.Close()
  260. body, _ = getResponseBody(resp)
  261. err = checkResponse(resp.StatusCode, expectedStatusCode)
  262. if expectedStatusCode != http.StatusOK {
  263. return newAdmin, body, err
  264. }
  265. if err == nil {
  266. newAdmin, body, err = GetAdminByUsername(admin.Username, expectedStatusCode)
  267. }
  268. if err == nil {
  269. err = checkAdmin(&admin, &newAdmin)
  270. }
  271. return newAdmin, body, err
  272. }
  273. // RemoveAdmin removes an existing admin and checks the received HTTP Status code against expectedStatusCode.
  274. func RemoveAdmin(admin dataprovider.Admin, expectedStatusCode int) ([]byte, error) {
  275. var body []byte
  276. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(adminPath, url.PathEscape(admin.Username)),
  277. nil, "", getDefaultToken())
  278. if err != nil {
  279. return body, err
  280. }
  281. defer resp.Body.Close()
  282. body, _ = getResponseBody(resp)
  283. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  284. }
  285. // GetAdminByUsername gets an admin by username and checks the received HTTP Status code against expectedStatusCode.
  286. func GetAdminByUsername(username string, expectedStatusCode int) (dataprovider.Admin, []byte, error) {
  287. var admin dataprovider.Admin
  288. var body []byte
  289. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(adminPath, url.PathEscape(username)),
  290. nil, "", getDefaultToken())
  291. if err != nil {
  292. return admin, body, err
  293. }
  294. defer resp.Body.Close()
  295. err = checkResponse(resp.StatusCode, expectedStatusCode)
  296. if err == nil && expectedStatusCode == http.StatusOK {
  297. err = render.DecodeJSON(resp.Body, &admin)
  298. } else {
  299. body, _ = getResponseBody(resp)
  300. }
  301. return admin, body, err
  302. }
  303. // GetAdmins returns a list of admins and checks the received HTTP Status code against expectedStatusCode.
  304. // The number of results can be limited specifying a limit.
  305. // Some results can be skipped specifying an offset.
  306. func GetAdmins(limit, offset int64, expectedStatusCode int) ([]dataprovider.Admin, []byte, error) {
  307. var admins []dataprovider.Admin
  308. var body []byte
  309. url, err := addLimitAndOffsetQueryParams(buildURLRelativeToBase(adminPath), limit, offset)
  310. if err != nil {
  311. return admins, body, err
  312. }
  313. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  314. if err != nil {
  315. return admins, body, err
  316. }
  317. defer resp.Body.Close()
  318. err = checkResponse(resp.StatusCode, expectedStatusCode)
  319. if err == nil && expectedStatusCode == http.StatusOK {
  320. err = render.DecodeJSON(resp.Body, &admins)
  321. } else {
  322. body, _ = getResponseBody(resp)
  323. }
  324. return admins, body, err
  325. }
  326. // ChangeAdminPassword changes the password for an existing admin
  327. func ChangeAdminPassword(currentPassword, newPassword string, expectedStatusCode int) ([]byte, error) {
  328. var body []byte
  329. pwdChange := make(map[string]string)
  330. pwdChange["current_password"] = currentPassword
  331. pwdChange["new_password"] = newPassword
  332. asJSON, _ := json.Marshal(&pwdChange)
  333. resp, err := sendHTTPRequest(http.MethodPut, buildURLRelativeToBase(adminPwdPath),
  334. bytes.NewBuffer(asJSON), "application/json", getDefaultToken())
  335. if err != nil {
  336. return body, err
  337. }
  338. defer resp.Body.Close()
  339. err = checkResponse(resp.StatusCode, expectedStatusCode)
  340. body, _ = getResponseBody(resp)
  341. return body, err
  342. }
  343. // GetQuotaScans gets active quota scans for users and checks the received HTTP Status code against expectedStatusCode.
  344. func GetQuotaScans(expectedStatusCode int) ([]common.ActiveQuotaScan, []byte, error) {
  345. var quotaScans []common.ActiveQuotaScan
  346. var body []byte
  347. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(quotaScanPath), nil, "", getDefaultToken())
  348. if err != nil {
  349. return quotaScans, body, err
  350. }
  351. defer resp.Body.Close()
  352. err = checkResponse(resp.StatusCode, expectedStatusCode)
  353. if err == nil && expectedStatusCode == http.StatusOK {
  354. err = render.DecodeJSON(resp.Body, &quotaScans)
  355. } else {
  356. body, _ = getResponseBody(resp)
  357. }
  358. return quotaScans, body, err
  359. }
  360. // StartQuotaScan starts a new quota scan for the given user and checks the received HTTP Status code against expectedStatusCode.
  361. func StartQuotaScan(user dataprovider.User, expectedStatusCode int) ([]byte, error) {
  362. var body []byte
  363. userAsJSON, _ := json.Marshal(user)
  364. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(quotaScanPath), bytes.NewBuffer(userAsJSON),
  365. "", getDefaultToken())
  366. if err != nil {
  367. return body, err
  368. }
  369. defer resp.Body.Close()
  370. body, _ = getResponseBody(resp)
  371. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  372. }
  373. // UpdateQuotaUsage updates the user used quota limits and checks the received HTTP Status code against expectedStatusCode.
  374. func UpdateQuotaUsage(user dataprovider.User, mode string, expectedStatusCode int) ([]byte, error) {
  375. var body []byte
  376. userAsJSON, _ := json.Marshal(user)
  377. url, err := addModeQueryParam(buildURLRelativeToBase(updateUsedQuotaPath), mode)
  378. if err != nil {
  379. return body, err
  380. }
  381. resp, err := sendHTTPRequest(http.MethodPut, url.String(), bytes.NewBuffer(userAsJSON), "", getDefaultToken())
  382. if err != nil {
  383. return body, err
  384. }
  385. defer resp.Body.Close()
  386. body, _ = getResponseBody(resp)
  387. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  388. }
  389. // GetConnections returns status and stats for active SFTP/SCP connections
  390. func GetConnections(expectedStatusCode int) ([]common.ConnectionStatus, []byte, error) {
  391. var connections []common.ConnectionStatus
  392. var body []byte
  393. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(activeConnectionsPath), nil, "", getDefaultToken())
  394. if err != nil {
  395. return connections, body, err
  396. }
  397. defer resp.Body.Close()
  398. err = checkResponse(resp.StatusCode, expectedStatusCode)
  399. if err == nil && expectedStatusCode == http.StatusOK {
  400. err = render.DecodeJSON(resp.Body, &connections)
  401. } else {
  402. body, _ = getResponseBody(resp)
  403. }
  404. return connections, body, err
  405. }
  406. // CloseConnection closes an active connection identified by connectionID
  407. func CloseConnection(connectionID string, expectedStatusCode int) ([]byte, error) {
  408. var body []byte
  409. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(activeConnectionsPath, connectionID),
  410. nil, "", getDefaultToken())
  411. if err != nil {
  412. return body, err
  413. }
  414. defer resp.Body.Close()
  415. err = checkResponse(resp.StatusCode, expectedStatusCode)
  416. body, _ = getResponseBody(resp)
  417. return body, err
  418. }
  419. // AddFolder adds a new folder and checks the received HTTP Status code against expectedStatusCode
  420. func AddFolder(folder vfs.BaseVirtualFolder, expectedStatusCode int) (vfs.BaseVirtualFolder, []byte, error) {
  421. var newFolder vfs.BaseVirtualFolder
  422. var body []byte
  423. folderAsJSON, _ := json.Marshal(folder)
  424. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(folderPath), bytes.NewBuffer(folderAsJSON),
  425. "application/json", getDefaultToken())
  426. if err != nil {
  427. return newFolder, body, err
  428. }
  429. defer resp.Body.Close()
  430. err = checkResponse(resp.StatusCode, expectedStatusCode)
  431. if expectedStatusCode != http.StatusCreated {
  432. body, _ = getResponseBody(resp)
  433. return newFolder, body, err
  434. }
  435. if err == nil {
  436. err = render.DecodeJSON(resp.Body, &newFolder)
  437. } else {
  438. body, _ = getResponseBody(resp)
  439. }
  440. if err == nil {
  441. err = checkFolder(&folder, &newFolder)
  442. }
  443. return newFolder, body, err
  444. }
  445. // UpdateFolder updates an existing folder and checks the received HTTP Status code against expectedStatusCode.
  446. func UpdateFolder(folder vfs.BaseVirtualFolder, expectedStatusCode int) (vfs.BaseVirtualFolder, []byte, error) {
  447. var updatedFolder vfs.BaseVirtualFolder
  448. var body []byte
  449. folderAsJSON, _ := json.Marshal(folder)
  450. resp, err := sendHTTPRequest(http.MethodPut, buildURLRelativeToBase(folderPath, url.PathEscape(folder.Name)),
  451. bytes.NewBuffer(folderAsJSON), "application/json", getDefaultToken())
  452. if err != nil {
  453. return updatedFolder, body, err
  454. }
  455. defer resp.Body.Close()
  456. body, _ = getResponseBody(resp)
  457. err = checkResponse(resp.StatusCode, expectedStatusCode)
  458. if expectedStatusCode != http.StatusOK {
  459. return updatedFolder, body, err
  460. }
  461. if err == nil {
  462. updatedFolder, body, err = GetFolderByName(folder.Name, expectedStatusCode)
  463. }
  464. if err == nil {
  465. err = checkFolder(&folder, &updatedFolder)
  466. }
  467. return updatedFolder, body, err
  468. }
  469. // RemoveFolder removes an existing user and checks the received HTTP Status code against expectedStatusCode.
  470. func RemoveFolder(folder vfs.BaseVirtualFolder, expectedStatusCode int) ([]byte, error) {
  471. var body []byte
  472. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(folderPath, url.PathEscape(folder.Name)),
  473. nil, "", getDefaultToken())
  474. if err != nil {
  475. return body, err
  476. }
  477. defer resp.Body.Close()
  478. body, _ = getResponseBody(resp)
  479. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  480. }
  481. // GetFolderByName gets a folder by name and checks the received HTTP Status code against expectedStatusCode.
  482. func GetFolderByName(name string, expectedStatusCode int) (vfs.BaseVirtualFolder, []byte, error) {
  483. var folder vfs.BaseVirtualFolder
  484. var body []byte
  485. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(folderPath, url.PathEscape(name)),
  486. nil, "", getDefaultToken())
  487. if err != nil {
  488. return folder, body, err
  489. }
  490. defer resp.Body.Close()
  491. err = checkResponse(resp.StatusCode, expectedStatusCode)
  492. if err == nil && expectedStatusCode == http.StatusOK {
  493. err = render.DecodeJSON(resp.Body, &folder)
  494. } else {
  495. body, _ = getResponseBody(resp)
  496. }
  497. return folder, body, err
  498. }
  499. // GetFolders returns a list of folders and checks the received HTTP Status code against expectedStatusCode.
  500. // The number of results can be limited specifying a limit.
  501. // Some results can be skipped specifying an offset.
  502. // The results can be filtered specifying a folder path, the folder path filter is an exact match
  503. func GetFolders(limit int64, offset int64, expectedStatusCode int) ([]vfs.BaseVirtualFolder, []byte, error) {
  504. var folders []vfs.BaseVirtualFolder
  505. var body []byte
  506. url, err := addLimitAndOffsetQueryParams(buildURLRelativeToBase(folderPath), limit, offset)
  507. if err != nil {
  508. return folders, body, err
  509. }
  510. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  511. if err != nil {
  512. return folders, body, err
  513. }
  514. defer resp.Body.Close()
  515. err = checkResponse(resp.StatusCode, expectedStatusCode)
  516. if err == nil && expectedStatusCode == http.StatusOK {
  517. err = render.DecodeJSON(resp.Body, &folders)
  518. } else {
  519. body, _ = getResponseBody(resp)
  520. }
  521. return folders, body, err
  522. }
  523. // GetFoldersQuotaScans gets active quota scans for folders and checks the received HTTP Status code against expectedStatusCode.
  524. func GetFoldersQuotaScans(expectedStatusCode int) ([]common.ActiveVirtualFolderQuotaScan, []byte, error) {
  525. var quotaScans []common.ActiveVirtualFolderQuotaScan
  526. var body []byte
  527. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(quotaScanVFolderPath), nil, "", getDefaultToken())
  528. if err != nil {
  529. return quotaScans, body, err
  530. }
  531. defer resp.Body.Close()
  532. err = checkResponse(resp.StatusCode, expectedStatusCode)
  533. if err == nil && expectedStatusCode == http.StatusOK {
  534. err = render.DecodeJSON(resp.Body, &quotaScans)
  535. } else {
  536. body, _ = getResponseBody(resp)
  537. }
  538. return quotaScans, body, err
  539. }
  540. // StartFolderQuotaScan start a new quota scan for the given folder and checks the received HTTP Status code against expectedStatusCode.
  541. func StartFolderQuotaScan(folder vfs.BaseVirtualFolder, expectedStatusCode int) ([]byte, error) {
  542. var body []byte
  543. folderAsJSON, _ := json.Marshal(folder)
  544. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(quotaScanVFolderPath),
  545. bytes.NewBuffer(folderAsJSON), "", getDefaultToken())
  546. if err != nil {
  547. return body, err
  548. }
  549. defer resp.Body.Close()
  550. body, _ = getResponseBody(resp)
  551. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  552. }
  553. // UpdateFolderQuotaUsage updates the folder used quota limits and checks the received HTTP Status code against expectedStatusCode.
  554. func UpdateFolderQuotaUsage(folder vfs.BaseVirtualFolder, mode string, expectedStatusCode int) ([]byte, error) {
  555. var body []byte
  556. folderAsJSON, _ := json.Marshal(folder)
  557. url, err := addModeQueryParam(buildURLRelativeToBase(updateFolderUsedQuotaPath), mode)
  558. if err != nil {
  559. return body, err
  560. }
  561. resp, err := sendHTTPRequest(http.MethodPut, url.String(), bytes.NewBuffer(folderAsJSON), "", getDefaultToken())
  562. if err != nil {
  563. return body, err
  564. }
  565. defer resp.Body.Close()
  566. body, _ = getResponseBody(resp)
  567. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  568. }
  569. // GetVersion returns version details
  570. func GetVersion(expectedStatusCode int) (version.Info, []byte, error) {
  571. var appVersion version.Info
  572. var body []byte
  573. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(versionPath), nil, "", getDefaultToken())
  574. if err != nil {
  575. return appVersion, body, err
  576. }
  577. defer resp.Body.Close()
  578. err = checkResponse(resp.StatusCode, expectedStatusCode)
  579. if err == nil && expectedStatusCode == http.StatusOK {
  580. err = render.DecodeJSON(resp.Body, &appVersion)
  581. } else {
  582. body, _ = getResponseBody(resp)
  583. }
  584. return appVersion, body, err
  585. }
  586. // GetStatus returns the server status
  587. func GetStatus(expectedStatusCode int) (httpd.ServicesStatus, []byte, error) {
  588. var response httpd.ServicesStatus
  589. var body []byte
  590. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(serverStatusPath), nil, "", getDefaultToken())
  591. if err != nil {
  592. return response, body, err
  593. }
  594. defer resp.Body.Close()
  595. err = checkResponse(resp.StatusCode, expectedStatusCode)
  596. if err == nil && (expectedStatusCode == http.StatusOK) {
  597. err = render.DecodeJSON(resp.Body, &response)
  598. } else {
  599. body, _ = getResponseBody(resp)
  600. }
  601. return response, body, err
  602. }
  603. // GetBanTime returns the ban time for the given IP address
  604. func GetBanTime(ip string, expectedStatusCode int) (map[string]interface{}, []byte, error) {
  605. var response map[string]interface{}
  606. var body []byte
  607. url, err := url.Parse(buildURLRelativeToBase(defenderBanTime))
  608. if err != nil {
  609. return response, body, err
  610. }
  611. q := url.Query()
  612. q.Add("ip", ip)
  613. url.RawQuery = q.Encode()
  614. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  615. if err != nil {
  616. return response, body, err
  617. }
  618. defer resp.Body.Close()
  619. err = checkResponse(resp.StatusCode, expectedStatusCode)
  620. if err == nil && expectedStatusCode == http.StatusOK {
  621. err = render.DecodeJSON(resp.Body, &response)
  622. } else {
  623. body, _ = getResponseBody(resp)
  624. }
  625. return response, body, err
  626. }
  627. // GetScore returns the score for the given IP address
  628. func GetScore(ip string, expectedStatusCode int) (map[string]interface{}, []byte, error) {
  629. var response map[string]interface{}
  630. var body []byte
  631. url, err := url.Parse(buildURLRelativeToBase(defenderScore))
  632. if err != nil {
  633. return response, body, err
  634. }
  635. q := url.Query()
  636. q.Add("ip", ip)
  637. url.RawQuery = q.Encode()
  638. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  639. if err != nil {
  640. return response, body, err
  641. }
  642. defer resp.Body.Close()
  643. err = checkResponse(resp.StatusCode, expectedStatusCode)
  644. if err == nil && expectedStatusCode == http.StatusOK {
  645. err = render.DecodeJSON(resp.Body, &response)
  646. } else {
  647. body, _ = getResponseBody(resp)
  648. }
  649. return response, body, err
  650. }
  651. // UnbanIP unbans the given IP address
  652. func UnbanIP(ip string, expectedStatusCode int) error {
  653. postBody := make(map[string]string)
  654. postBody["ip"] = ip
  655. asJSON, _ := json.Marshal(postBody)
  656. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(defenderUnban), bytes.NewBuffer(asJSON),
  657. "", getDefaultToken())
  658. if err != nil {
  659. return err
  660. }
  661. defer resp.Body.Close()
  662. return checkResponse(resp.StatusCode, expectedStatusCode)
  663. }
  664. // Dumpdata requests a backup to outputFile.
  665. // outputFile is relative to the configured backups_path
  666. func Dumpdata(outputFile, outputData, indent string, expectedStatusCode int) (map[string]interface{}, []byte, error) {
  667. var response map[string]interface{}
  668. var body []byte
  669. url, err := url.Parse(buildURLRelativeToBase(dumpDataPath))
  670. if err != nil {
  671. return response, body, err
  672. }
  673. q := url.Query()
  674. if outputData != "" {
  675. q.Add("output-data", outputData)
  676. }
  677. if outputFile != "" {
  678. q.Add("output-file", outputFile)
  679. }
  680. if indent != "" {
  681. q.Add("indent", indent)
  682. }
  683. url.RawQuery = q.Encode()
  684. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  685. if err != nil {
  686. return response, body, err
  687. }
  688. defer resp.Body.Close()
  689. err = checkResponse(resp.StatusCode, expectedStatusCode)
  690. if err == nil && expectedStatusCode == http.StatusOK {
  691. err = render.DecodeJSON(resp.Body, &response)
  692. } else {
  693. body, _ = getResponseBody(resp)
  694. }
  695. return response, body, err
  696. }
  697. // Loaddata restores a backup.
  698. func Loaddata(inputFile, scanQuota, mode string, expectedStatusCode int) (map[string]interface{}, []byte, error) {
  699. var response map[string]interface{}
  700. var body []byte
  701. url, err := url.Parse(buildURLRelativeToBase(loadDataPath))
  702. if err != nil {
  703. return response, body, err
  704. }
  705. q := url.Query()
  706. q.Add("input-file", inputFile)
  707. if scanQuota != "" {
  708. q.Add("scan-quota", scanQuota)
  709. }
  710. if mode != "" {
  711. q.Add("mode", mode)
  712. }
  713. url.RawQuery = q.Encode()
  714. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  715. if err != nil {
  716. return response, body, err
  717. }
  718. defer resp.Body.Close()
  719. err = checkResponse(resp.StatusCode, expectedStatusCode)
  720. if err == nil && expectedStatusCode == http.StatusOK {
  721. err = render.DecodeJSON(resp.Body, &response)
  722. } else {
  723. body, _ = getResponseBody(resp)
  724. }
  725. return response, body, err
  726. }
  727. // LoaddataFromPostBody restores a backup
  728. func LoaddataFromPostBody(data []byte, scanQuota, mode string, expectedStatusCode int) (map[string]interface{}, []byte, error) {
  729. var response map[string]interface{}
  730. var body []byte
  731. url, err := url.Parse(buildURLRelativeToBase(loadDataPath))
  732. if err != nil {
  733. return response, body, err
  734. }
  735. q := url.Query()
  736. if scanQuota != "" {
  737. q.Add("scan-quota", scanQuota)
  738. }
  739. if mode != "" {
  740. q.Add("mode", mode)
  741. }
  742. url.RawQuery = q.Encode()
  743. resp, err := sendHTTPRequest(http.MethodPost, url.String(), bytes.NewReader(data), "", getDefaultToken())
  744. if err != nil {
  745. return response, body, err
  746. }
  747. defer resp.Body.Close()
  748. err = checkResponse(resp.StatusCode, expectedStatusCode)
  749. if err == nil && expectedStatusCode == http.StatusOK {
  750. err = render.DecodeJSON(resp.Body, &response)
  751. } else {
  752. body, _ = getResponseBody(resp)
  753. }
  754. return response, body, err
  755. }
  756. func checkResponse(actual int, expected int) error {
  757. if expected != actual {
  758. return fmt.Errorf("wrong status code: got %v want %v", actual, expected)
  759. }
  760. return nil
  761. }
  762. func getResponseBody(resp *http.Response) ([]byte, error) {
  763. return io.ReadAll(resp.Body)
  764. }
  765. func checkFolder(expected *vfs.BaseVirtualFolder, actual *vfs.BaseVirtualFolder) error {
  766. if expected.ID <= 0 {
  767. if actual.ID <= 0 {
  768. return errors.New("actual folder ID must be > 0")
  769. }
  770. } else {
  771. if actual.ID != expected.ID {
  772. return errors.New("folder ID mismatch")
  773. }
  774. }
  775. if expected.Name != actual.Name {
  776. return errors.New("name mismatch")
  777. }
  778. if expected.MappedPath != actual.MappedPath {
  779. return errors.New("mapped path mismatch")
  780. }
  781. if expected.Description != actual.Description {
  782. return errors.New("description mismatch")
  783. }
  784. if err := compareFsConfig(&expected.FsConfig, &actual.FsConfig); err != nil {
  785. return err
  786. }
  787. return nil
  788. }
  789. func checkAdmin(expected *dataprovider.Admin, actual *dataprovider.Admin) error {
  790. if actual.Password != "" {
  791. return errors.New("admin password must not be visible")
  792. }
  793. if expected.ID <= 0 {
  794. if actual.ID <= 0 {
  795. return errors.New("actual admin ID must be > 0")
  796. }
  797. } else {
  798. if actual.ID != expected.ID {
  799. return errors.New("admin ID mismatch")
  800. }
  801. }
  802. if err := compareAdminEqualFields(expected, actual); err != nil {
  803. return err
  804. }
  805. if len(expected.Permissions) != len(actual.Permissions) {
  806. return errors.New("permissions mismatch")
  807. }
  808. for _, p := range expected.Permissions {
  809. if !utils.IsStringInSlice(p, actual.Permissions) {
  810. return errors.New("permissions content mismatch")
  811. }
  812. }
  813. if len(expected.Filters.AllowList) != len(actual.Filters.AllowList) {
  814. return errors.New("allow list mismatch")
  815. }
  816. for _, v := range expected.Filters.AllowList {
  817. if !utils.IsStringInSlice(v, actual.Filters.AllowList) {
  818. return errors.New("allow list content mismatch")
  819. }
  820. }
  821. return nil
  822. }
  823. func compareAdminEqualFields(expected *dataprovider.Admin, actual *dataprovider.Admin) error {
  824. if expected.Username != actual.Username {
  825. return errors.New("sername mismatch")
  826. }
  827. if expected.Email != actual.Email {
  828. return errors.New("email mismatch")
  829. }
  830. if expected.Status != actual.Status {
  831. return errors.New("status mismatch")
  832. }
  833. if expected.Description != actual.Description {
  834. return errors.New("description mismatch")
  835. }
  836. if expected.AdditionalInfo != actual.AdditionalInfo {
  837. return errors.New("additional info mismatch")
  838. }
  839. return nil
  840. }
  841. func checkUser(expected *dataprovider.User, actual *dataprovider.User) error {
  842. if actual.Password != "" {
  843. return errors.New("user password must not be visible")
  844. }
  845. if expected.ID <= 0 {
  846. if actual.ID <= 0 {
  847. return errors.New("actual user ID must be > 0")
  848. }
  849. } else {
  850. if actual.ID != expected.ID {
  851. return errors.New("user ID mismatch")
  852. }
  853. }
  854. if len(expected.Permissions) != len(actual.Permissions) {
  855. return errors.New("permissions mismatch")
  856. }
  857. for dir, perms := range expected.Permissions {
  858. if actualPerms, ok := actual.Permissions[dir]; ok {
  859. for _, v := range actualPerms {
  860. if !utils.IsStringInSlice(v, perms) {
  861. return errors.New("permissions contents mismatch")
  862. }
  863. }
  864. } else {
  865. return errors.New("permissions directories mismatch")
  866. }
  867. }
  868. if err := compareUserFilters(expected, actual); err != nil {
  869. return err
  870. }
  871. if err := compareFsConfig(&expected.FsConfig, &actual.FsConfig); err != nil {
  872. return err
  873. }
  874. if err := compareUserVirtualFolders(expected, actual); err != nil {
  875. return err
  876. }
  877. return compareEqualsUserFields(expected, actual)
  878. }
  879. func compareUserVirtualFolders(expected *dataprovider.User, actual *dataprovider.User) error {
  880. if len(actual.VirtualFolders) != len(expected.VirtualFolders) {
  881. return errors.New("virtual folders len mismatch")
  882. }
  883. for _, v := range actual.VirtualFolders {
  884. found := false
  885. for _, v1 := range expected.VirtualFolders {
  886. if path.Clean(v.VirtualPath) == path.Clean(v1.VirtualPath) {
  887. if err := checkFolder(&v1.BaseVirtualFolder, &v.BaseVirtualFolder); err != nil {
  888. return err
  889. }
  890. if v.QuotaSize != v1.QuotaSize {
  891. return errors.New("vfolder quota size mismatch")
  892. }
  893. if (v.QuotaFiles) != (v1.QuotaFiles) {
  894. return errors.New("vfolder quota files mismatch")
  895. }
  896. found = true
  897. break
  898. }
  899. }
  900. if !found {
  901. return errors.New("virtual folders mismatch")
  902. }
  903. }
  904. return nil
  905. }
  906. func compareFsConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  907. if expected.Provider != actual.Provider {
  908. return errors.New("fs provider mismatch")
  909. }
  910. if err := compareS3Config(expected, actual); err != nil {
  911. return err
  912. }
  913. if err := compareGCSConfig(expected, actual); err != nil {
  914. return err
  915. }
  916. if err := compareAzBlobConfig(expected, actual); err != nil {
  917. return err
  918. }
  919. if err := checkEncryptedSecret(expected.CryptConfig.Passphrase, actual.CryptConfig.Passphrase); err != nil {
  920. return err
  921. }
  922. if err := compareSFTPFsConfig(expected, actual); err != nil {
  923. return err
  924. }
  925. return nil
  926. }
  927. func compareS3Config(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  928. if expected.S3Config.Bucket != actual.S3Config.Bucket {
  929. return errors.New("fs S3 bucket mismatch")
  930. }
  931. if expected.S3Config.Region != actual.S3Config.Region {
  932. return errors.New("fs S3 region mismatch")
  933. }
  934. if expected.S3Config.AccessKey != actual.S3Config.AccessKey {
  935. return errors.New("fs S3 access key mismatch")
  936. }
  937. if err := checkEncryptedSecret(expected.S3Config.AccessSecret, actual.S3Config.AccessSecret); err != nil {
  938. return fmt.Errorf("fs S3 access secret mismatch: %v", err)
  939. }
  940. if expected.S3Config.Endpoint != actual.S3Config.Endpoint {
  941. return errors.New("fs S3 endpoint mismatch")
  942. }
  943. if expected.S3Config.StorageClass != actual.S3Config.StorageClass {
  944. return errors.New("fs S3 storage class mismatch")
  945. }
  946. if expected.S3Config.UploadPartSize != actual.S3Config.UploadPartSize {
  947. return errors.New("fs S3 upload part size mismatch")
  948. }
  949. if expected.S3Config.UploadConcurrency != actual.S3Config.UploadConcurrency {
  950. return errors.New("fs S3 upload concurrency mismatch")
  951. }
  952. if expected.S3Config.KeyPrefix != actual.S3Config.KeyPrefix &&
  953. expected.S3Config.KeyPrefix+"/" != actual.S3Config.KeyPrefix {
  954. return errors.New("fs S3 key prefix mismatch")
  955. }
  956. return nil
  957. }
  958. func compareGCSConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  959. if expected.GCSConfig.Bucket != actual.GCSConfig.Bucket {
  960. return errors.New("GCS bucket mismatch")
  961. }
  962. if expected.GCSConfig.StorageClass != actual.GCSConfig.StorageClass {
  963. return errors.New("GCS storage class mismatch")
  964. }
  965. if expected.GCSConfig.KeyPrefix != actual.GCSConfig.KeyPrefix &&
  966. expected.GCSConfig.KeyPrefix+"/" != actual.GCSConfig.KeyPrefix {
  967. return errors.New("GCS key prefix mismatch")
  968. }
  969. if expected.GCSConfig.AutomaticCredentials != actual.GCSConfig.AutomaticCredentials {
  970. return errors.New("GCS automatic credentials mismatch")
  971. }
  972. return nil
  973. }
  974. func compareSFTPFsConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  975. if expected.SFTPConfig.Endpoint != actual.SFTPConfig.Endpoint {
  976. return errors.New("SFTPFs endpoint mismatch")
  977. }
  978. if expected.SFTPConfig.Username != actual.SFTPConfig.Username {
  979. return errors.New("SFTPFs username mismatch")
  980. }
  981. if expected.SFTPConfig.DisableCouncurrentReads != actual.SFTPConfig.DisableCouncurrentReads {
  982. return errors.New("SFTPFs disable_concurrent_reads mismatch")
  983. }
  984. if err := checkEncryptedSecret(expected.SFTPConfig.Password, actual.SFTPConfig.Password); err != nil {
  985. return fmt.Errorf("SFTPFs password mismatch: %v", err)
  986. }
  987. if err := checkEncryptedSecret(expected.SFTPConfig.PrivateKey, actual.SFTPConfig.PrivateKey); err != nil {
  988. return fmt.Errorf("SFTPFs private key mismatch: %v", err)
  989. }
  990. if expected.SFTPConfig.Prefix != actual.SFTPConfig.Prefix {
  991. if expected.SFTPConfig.Prefix != "" && actual.SFTPConfig.Prefix != "/" {
  992. return errors.New("SFTPFs prefix mismatch")
  993. }
  994. }
  995. if len(expected.SFTPConfig.Fingerprints) != len(actual.SFTPConfig.Fingerprints) {
  996. return errors.New("SFTPFs fingerprints mismatch")
  997. }
  998. for _, value := range actual.SFTPConfig.Fingerprints {
  999. if !utils.IsStringInSlice(value, expected.SFTPConfig.Fingerprints) {
  1000. return errors.New("SFTPFs fingerprints mismatch")
  1001. }
  1002. }
  1003. return nil
  1004. }
  1005. func compareAzBlobConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  1006. if expected.AzBlobConfig.Container != actual.AzBlobConfig.Container {
  1007. return errors.New("azure Blob container mismatch")
  1008. }
  1009. if expected.AzBlobConfig.AccountName != actual.AzBlobConfig.AccountName {
  1010. return errors.New("azure Blob account name mismatch")
  1011. }
  1012. if err := checkEncryptedSecret(expected.AzBlobConfig.AccountKey, actual.AzBlobConfig.AccountKey); err != nil {
  1013. return fmt.Errorf("azure Blob account key mismatch: %v", err)
  1014. }
  1015. if expected.AzBlobConfig.Endpoint != actual.AzBlobConfig.Endpoint {
  1016. return errors.New("azure Blob endpoint mismatch")
  1017. }
  1018. if expected.AzBlobConfig.SASURL != actual.AzBlobConfig.SASURL {
  1019. return errors.New("azure Blob SASL URL mismatch")
  1020. }
  1021. if expected.AzBlobConfig.UploadPartSize != actual.AzBlobConfig.UploadPartSize {
  1022. return errors.New("azure Blob upload part size mismatch")
  1023. }
  1024. if expected.AzBlobConfig.UploadConcurrency != actual.AzBlobConfig.UploadConcurrency {
  1025. return errors.New("azure Blob upload concurrency mismatch")
  1026. }
  1027. if expected.AzBlobConfig.KeyPrefix != actual.AzBlobConfig.KeyPrefix &&
  1028. expected.AzBlobConfig.KeyPrefix+"/" != actual.AzBlobConfig.KeyPrefix {
  1029. return errors.New("azure Blob key prefix mismatch")
  1030. }
  1031. if expected.AzBlobConfig.UseEmulator != actual.AzBlobConfig.UseEmulator {
  1032. return errors.New("azure Blob use emulator mismatch")
  1033. }
  1034. if expected.AzBlobConfig.AccessTier != actual.AzBlobConfig.AccessTier {
  1035. return errors.New("azure Blob access tier mismatch")
  1036. }
  1037. return nil
  1038. }
  1039. func areSecretEquals(expected, actual *kms.Secret) bool {
  1040. if expected == nil && actual == nil {
  1041. return true
  1042. }
  1043. if expected != nil && expected.IsEmpty() && actual == nil {
  1044. return true
  1045. }
  1046. if actual != nil && actual.IsEmpty() && expected == nil {
  1047. return true
  1048. }
  1049. return false
  1050. }
  1051. func checkEncryptedSecret(expected, actual *kms.Secret) error {
  1052. if areSecretEquals(expected, actual) {
  1053. return nil
  1054. }
  1055. if expected == nil && actual != nil && !actual.IsEmpty() {
  1056. return errors.New("secret mismatch")
  1057. }
  1058. if actual == nil && expected != nil && !expected.IsEmpty() {
  1059. return errors.New("secret mismatch")
  1060. }
  1061. if expected.IsPlain() && actual.IsEncrypted() {
  1062. if actual.GetPayload() == "" {
  1063. return errors.New("invalid secret payload")
  1064. }
  1065. if actual.GetAdditionalData() != "" {
  1066. return errors.New("invalid secret additional data")
  1067. }
  1068. if actual.GetKey() != "" {
  1069. return errors.New("invalid secret key")
  1070. }
  1071. } else {
  1072. if expected.GetStatus() != actual.GetStatus() || expected.GetPayload() != actual.GetPayload() {
  1073. return errors.New("secret mismatch")
  1074. }
  1075. }
  1076. return nil
  1077. }
  1078. func compareUserFilterSubStructs(expected *dataprovider.User, actual *dataprovider.User) error {
  1079. for _, IPMask := range expected.Filters.AllowedIP {
  1080. if !utils.IsStringInSlice(IPMask, actual.Filters.AllowedIP) {
  1081. return errors.New("allowed IP contents mismatch")
  1082. }
  1083. }
  1084. for _, IPMask := range expected.Filters.DeniedIP {
  1085. if !utils.IsStringInSlice(IPMask, actual.Filters.DeniedIP) {
  1086. return errors.New("denied IP contents mismatch")
  1087. }
  1088. }
  1089. for _, method := range expected.Filters.DeniedLoginMethods {
  1090. if !utils.IsStringInSlice(method, actual.Filters.DeniedLoginMethods) {
  1091. return errors.New("denied login methods contents mismatch")
  1092. }
  1093. }
  1094. for _, protocol := range expected.Filters.DeniedProtocols {
  1095. if !utils.IsStringInSlice(protocol, actual.Filters.DeniedProtocols) {
  1096. return errors.New("denied protocols contents mismatch")
  1097. }
  1098. }
  1099. return nil
  1100. }
  1101. func compareUserFilters(expected *dataprovider.User, actual *dataprovider.User) error {
  1102. if len(expected.Filters.AllowedIP) != len(actual.Filters.AllowedIP) {
  1103. return errors.New("allowed IP mismatch")
  1104. }
  1105. if len(expected.Filters.DeniedIP) != len(actual.Filters.DeniedIP) {
  1106. return errors.New("denied IP mismatch")
  1107. }
  1108. if len(expected.Filters.DeniedLoginMethods) != len(actual.Filters.DeniedLoginMethods) {
  1109. return errors.New("denied login methods mismatch")
  1110. }
  1111. if len(expected.Filters.DeniedProtocols) != len(actual.Filters.DeniedProtocols) {
  1112. return errors.New("denied protocols mismatch")
  1113. }
  1114. if expected.Filters.MaxUploadFileSize != actual.Filters.MaxUploadFileSize {
  1115. return errors.New("max upload file size mismatch")
  1116. }
  1117. if expected.Filters.TLSUsername != actual.Filters.TLSUsername {
  1118. return errors.New("TLSUsername mismatch")
  1119. }
  1120. if err := compareUserFilterSubStructs(expected, actual); err != nil {
  1121. return err
  1122. }
  1123. if err := compareUserFileExtensionsFilters(expected, actual); err != nil {
  1124. return err
  1125. }
  1126. return compareUserFilePatternsFilters(expected, actual)
  1127. }
  1128. func checkFilterMatch(expected []string, actual []string) bool {
  1129. if len(expected) != len(actual) {
  1130. return false
  1131. }
  1132. for _, e := range expected {
  1133. if !utils.IsStringInSlice(strings.ToLower(e), actual) {
  1134. return false
  1135. }
  1136. }
  1137. return true
  1138. }
  1139. func compareUserFilePatternsFilters(expected *dataprovider.User, actual *dataprovider.User) error {
  1140. if len(expected.Filters.FilePatterns) != len(actual.Filters.FilePatterns) {
  1141. return errors.New("file patterns mismatch")
  1142. }
  1143. for _, f := range expected.Filters.FilePatterns {
  1144. found := false
  1145. for _, f1 := range actual.Filters.FilePatterns {
  1146. if path.Clean(f.Path) == path.Clean(f1.Path) {
  1147. if !checkFilterMatch(f.AllowedPatterns, f1.AllowedPatterns) ||
  1148. !checkFilterMatch(f.DeniedPatterns, f1.DeniedPatterns) {
  1149. return errors.New("file patterns contents mismatch")
  1150. }
  1151. found = true
  1152. }
  1153. }
  1154. if !found {
  1155. return errors.New("file patterns contents mismatch")
  1156. }
  1157. }
  1158. return nil
  1159. }
  1160. func compareUserFileExtensionsFilters(expected *dataprovider.User, actual *dataprovider.User) error {
  1161. if len(expected.Filters.FileExtensions) != len(actual.Filters.FileExtensions) {
  1162. return errors.New("file extensions mismatch")
  1163. }
  1164. for _, f := range expected.Filters.FileExtensions {
  1165. found := false
  1166. for _, f1 := range actual.Filters.FileExtensions {
  1167. if path.Clean(f.Path) == path.Clean(f1.Path) {
  1168. if !checkFilterMatch(f.AllowedExtensions, f1.AllowedExtensions) ||
  1169. !checkFilterMatch(f.DeniedExtensions, f1.DeniedExtensions) {
  1170. return errors.New("file extensions contents mismatch")
  1171. }
  1172. found = true
  1173. }
  1174. }
  1175. if !found {
  1176. return errors.New("file extensions contents mismatch")
  1177. }
  1178. }
  1179. return nil
  1180. }
  1181. func compareEqualsUserFields(expected *dataprovider.User, actual *dataprovider.User) error {
  1182. if expected.Username != actual.Username {
  1183. return errors.New("username mismatch")
  1184. }
  1185. if expected.HomeDir != actual.HomeDir {
  1186. return errors.New("home dir mismatch")
  1187. }
  1188. if expected.UID != actual.UID {
  1189. return errors.New("UID mismatch")
  1190. }
  1191. if expected.GID != actual.GID {
  1192. return errors.New("GID mismatch")
  1193. }
  1194. if expected.MaxSessions != actual.MaxSessions {
  1195. return errors.New("MaxSessions mismatch")
  1196. }
  1197. if expected.QuotaSize != actual.QuotaSize {
  1198. return errors.New("QuotaSize mismatch")
  1199. }
  1200. if expected.QuotaFiles != actual.QuotaFiles {
  1201. return errors.New("QuotaFiles mismatch")
  1202. }
  1203. if len(expected.Permissions) != len(actual.Permissions) {
  1204. return errors.New("permissions mismatch")
  1205. }
  1206. if expected.UploadBandwidth != actual.UploadBandwidth {
  1207. return errors.New("UploadBandwidth mismatch")
  1208. }
  1209. if expected.DownloadBandwidth != actual.DownloadBandwidth {
  1210. return errors.New("DownloadBandwidth mismatch")
  1211. }
  1212. if expected.Status != actual.Status {
  1213. return errors.New("status mismatch")
  1214. }
  1215. if expected.ExpirationDate != actual.ExpirationDate {
  1216. return errors.New("ExpirationDate mismatch")
  1217. }
  1218. if expected.AdditionalInfo != actual.AdditionalInfo {
  1219. return errors.New("AdditionalInfo mismatch")
  1220. }
  1221. if expected.Description != actual.Description {
  1222. return errors.New("description mismatch")
  1223. }
  1224. return nil
  1225. }
  1226. func addLimitAndOffsetQueryParams(rawurl string, limit, offset int64) (*url.URL, error) {
  1227. url, err := url.Parse(rawurl)
  1228. if err != nil {
  1229. return nil, err
  1230. }
  1231. q := url.Query()
  1232. if limit > 0 {
  1233. q.Add("limit", strconv.FormatInt(limit, 10))
  1234. }
  1235. if offset > 0 {
  1236. q.Add("offset", strconv.FormatInt(offset, 10))
  1237. }
  1238. url.RawQuery = q.Encode()
  1239. return url, err
  1240. }
  1241. func addModeQueryParam(rawurl, mode string) (*url.URL, error) {
  1242. url, err := url.Parse(rawurl)
  1243. if err != nil {
  1244. return nil, err
  1245. }
  1246. q := url.Query()
  1247. if len(mode) > 0 {
  1248. q.Add("mode", mode)
  1249. }
  1250. url.RawQuery = q.Encode()
  1251. return url, err
  1252. }
  1253. func addDisconnectQueryParam(rawurl, disconnect string) (*url.URL, error) {
  1254. url, err := url.Parse(rawurl)
  1255. if err != nil {
  1256. return nil, err
  1257. }
  1258. q := url.Query()
  1259. if len(disconnect) > 0 {
  1260. q.Add("disconnect", disconnect)
  1261. }
  1262. url.RawQuery = q.Encode()
  1263. return url, err
  1264. }