httpdtest.go 44 KB

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