httpdtest.go 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948
  1. // Package httpdtest provides utilities for testing the exposed REST API.
  2. package httpdtest
  3. import (
  4. "bytes"
  5. "encoding/hex"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "net/http"
  11. "net/url"
  12. "path"
  13. "strconv"
  14. "strings"
  15. "github.com/go-chi/render"
  16. "github.com/sftpgo/sdk"
  17. "github.com/drakkan/sftpgo/v2/common"
  18. "github.com/drakkan/sftpgo/v2/dataprovider"
  19. "github.com/drakkan/sftpgo/v2/httpclient"
  20. "github.com/drakkan/sftpgo/v2/httpd"
  21. "github.com/drakkan/sftpgo/v2/kms"
  22. "github.com/drakkan/sftpgo/v2/util"
  23. "github.com/drakkan/sftpgo/v2/version"
  24. "github.com/drakkan/sftpgo/v2/vfs"
  25. )
  26. const (
  27. tokenPath = "/api/v2/token"
  28. activeConnectionsPath = "/api/v2/connections"
  29. quotasBasePath = "/api/v2/quotas"
  30. quotaScanPath = "/api/v2/quotas/users/scans"
  31. quotaScanVFolderPath = "/api/v2/quotas/folders/scans"
  32. userPath = "/api/v2/users"
  33. groupPath = "/api/v2/groups"
  34. versionPath = "/api/v2/version"
  35. folderPath = "/api/v2/folders"
  36. serverStatusPath = "/api/v2/status"
  37. dumpDataPath = "/api/v2/dumpdata"
  38. loadDataPath = "/api/v2/loaddata"
  39. defenderHosts = "/api/v2/defender/hosts"
  40. defenderBanTime = "/api/v2/defender/bantime"
  41. defenderUnban = "/api/v2/defender/unban"
  42. defenderScore = "/api/v2/defender/score"
  43. adminPath = "/api/v2/admins"
  44. adminPwdPath = "/api/v2/admin/changepwd"
  45. apiKeysPath = "/api/v2/apikeys"
  46. retentionBasePath = "/api/v2/retention/users"
  47. retentionChecksPath = "/api/v2/retention/users/checks"
  48. )
  49. const (
  50. defaultTokenAuthUser = "admin"
  51. defaultTokenAuthPass = "password"
  52. )
  53. var (
  54. httpBaseURL = "http://127.0.0.1:8080"
  55. jwtToken = ""
  56. )
  57. // SetBaseURL sets the base url to use for HTTP requests.
  58. // Default URL is "http://127.0.0.1:8080"
  59. func SetBaseURL(url string) {
  60. httpBaseURL = url
  61. }
  62. // SetJWTToken sets the JWT token to use
  63. func SetJWTToken(token string) {
  64. jwtToken = token
  65. }
  66. func sendHTTPRequest(method, url string, body io.Reader, contentType, token string) (*http.Response, error) {
  67. req, err := http.NewRequest(method, url, body)
  68. if err != nil {
  69. return nil, err
  70. }
  71. if contentType != "" {
  72. req.Header.Set("Content-Type", "application/json")
  73. }
  74. if token != "" {
  75. req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
  76. }
  77. return httpclient.GetHTTPClient().Do(req)
  78. }
  79. func buildURLRelativeToBase(paths ...string) string {
  80. // we need to use path.Join and not filepath.Join
  81. // since filepath.Join will use backslash separator on Windows
  82. p := path.Join(paths...)
  83. return fmt.Sprintf("%s/%s", strings.TrimRight(httpBaseURL, "/"), strings.TrimLeft(p, "/"))
  84. }
  85. // GetToken tries to return a JWT token
  86. func GetToken(username, password string) (string, map[string]any, error) {
  87. req, err := http.NewRequest(http.MethodGet, buildURLRelativeToBase(tokenPath), nil)
  88. if err != nil {
  89. return "", nil, err
  90. }
  91. req.SetBasicAuth(username, password)
  92. resp, err := httpclient.GetHTTPClient().Do(req)
  93. if err != nil {
  94. return "", nil, err
  95. }
  96. defer resp.Body.Close()
  97. err = checkResponse(resp.StatusCode, http.StatusOK)
  98. if err != nil {
  99. return "", nil, err
  100. }
  101. responseHolder := make(map[string]any)
  102. err = render.DecodeJSON(resp.Body, &responseHolder)
  103. if err != nil {
  104. return "", nil, err
  105. }
  106. return responseHolder["access_token"].(string), responseHolder, nil
  107. }
  108. func getDefaultToken() string {
  109. if jwtToken != "" {
  110. return jwtToken
  111. }
  112. token, _, err := GetToken(defaultTokenAuthUser, defaultTokenAuthPass)
  113. if err != nil {
  114. return ""
  115. }
  116. return token
  117. }
  118. // AddUser adds a new user and checks the received HTTP Status code against expectedStatusCode.
  119. func AddUser(user dataprovider.User, expectedStatusCode int) (dataprovider.User, []byte, error) {
  120. var newUser dataprovider.User
  121. var body []byte
  122. userAsJSON, _ := json.Marshal(user)
  123. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(userPath), bytes.NewBuffer(userAsJSON),
  124. "application/json", getDefaultToken())
  125. if err != nil {
  126. return newUser, body, err
  127. }
  128. defer resp.Body.Close()
  129. err = checkResponse(resp.StatusCode, expectedStatusCode)
  130. if expectedStatusCode != http.StatusCreated {
  131. body, _ = getResponseBody(resp)
  132. return newUser, body, err
  133. }
  134. if err == nil {
  135. err = render.DecodeJSON(resp.Body, &newUser)
  136. } else {
  137. body, _ = getResponseBody(resp)
  138. }
  139. if err == nil {
  140. err = checkUser(&user, &newUser)
  141. }
  142. return newUser, body, err
  143. }
  144. // UpdateUserWithJSON update a user using the provided JSON as POST body
  145. func UpdateUserWithJSON(user dataprovider.User, expectedStatusCode int, disconnect string, userAsJSON []byte) (dataprovider.User, []byte, error) {
  146. var newUser dataprovider.User
  147. var body []byte
  148. url, err := addDisconnectQueryParam(buildURLRelativeToBase(userPath, url.PathEscape(user.Username)), disconnect)
  149. if err != nil {
  150. return user, body, err
  151. }
  152. resp, err := sendHTTPRequest(http.MethodPut, url.String(), bytes.NewBuffer(userAsJSON), "application/json",
  153. getDefaultToken())
  154. if err != nil {
  155. return user, body, err
  156. }
  157. defer resp.Body.Close()
  158. body, _ = getResponseBody(resp)
  159. err = checkResponse(resp.StatusCode, expectedStatusCode)
  160. if expectedStatusCode != http.StatusOK {
  161. return newUser, body, err
  162. }
  163. if err == nil {
  164. newUser, body, err = GetUserByUsername(user.Username, expectedStatusCode)
  165. }
  166. if err == nil {
  167. err = checkUser(&user, &newUser)
  168. }
  169. return newUser, body, err
  170. }
  171. // UpdateUser updates an existing user and checks the received HTTP Status code against expectedStatusCode.
  172. func UpdateUser(user dataprovider.User, expectedStatusCode int, disconnect string) (dataprovider.User, []byte, error) {
  173. userAsJSON, _ := json.Marshal(user)
  174. return UpdateUserWithJSON(user, expectedStatusCode, disconnect, userAsJSON)
  175. }
  176. // RemoveUser removes an existing user and checks the received HTTP Status code against expectedStatusCode.
  177. func RemoveUser(user dataprovider.User, expectedStatusCode int) ([]byte, error) {
  178. var body []byte
  179. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(userPath, url.PathEscape(user.Username)),
  180. nil, "", getDefaultToken())
  181. if err != nil {
  182. return body, err
  183. }
  184. defer resp.Body.Close()
  185. body, _ = getResponseBody(resp)
  186. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  187. }
  188. // GetUserByUsername gets a user by username and checks the received HTTP Status code against expectedStatusCode.
  189. func GetUserByUsername(username string, expectedStatusCode int) (dataprovider.User, []byte, error) {
  190. var user dataprovider.User
  191. var body []byte
  192. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(userPath, url.PathEscape(username)),
  193. nil, "", getDefaultToken())
  194. if err != nil {
  195. return user, body, err
  196. }
  197. defer resp.Body.Close()
  198. err = checkResponse(resp.StatusCode, expectedStatusCode)
  199. if err == nil && expectedStatusCode == http.StatusOK {
  200. err = render.DecodeJSON(resp.Body, &user)
  201. } else {
  202. body, _ = getResponseBody(resp)
  203. }
  204. return user, body, err
  205. }
  206. // GetUsers returns a list of users and checks the received HTTP Status code against expectedStatusCode.
  207. // The number of results can be limited specifying a limit.
  208. // Some results can be skipped specifying an offset.
  209. func GetUsers(limit, offset int64, expectedStatusCode int) ([]dataprovider.User, []byte, error) {
  210. var users []dataprovider.User
  211. var body []byte
  212. url, err := addLimitAndOffsetQueryParams(buildURLRelativeToBase(userPath), limit, offset)
  213. if err != nil {
  214. return users, body, err
  215. }
  216. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  217. if err != nil {
  218. return users, body, err
  219. }
  220. defer resp.Body.Close()
  221. err = checkResponse(resp.StatusCode, expectedStatusCode)
  222. if err == nil && expectedStatusCode == http.StatusOK {
  223. err = render.DecodeJSON(resp.Body, &users)
  224. } else {
  225. body, _ = getResponseBody(resp)
  226. }
  227. return users, body, err
  228. }
  229. // AddGroup adds a new group and checks the received HTTP Status code against expectedStatusCode.
  230. func AddGroup(group dataprovider.Group, expectedStatusCode int) (dataprovider.Group, []byte, error) {
  231. var newGroup dataprovider.Group
  232. var body []byte
  233. asJSON, _ := json.Marshal(group)
  234. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(groupPath), bytes.NewBuffer(asJSON),
  235. "application/json", getDefaultToken())
  236. if err != nil {
  237. return newGroup, body, err
  238. }
  239. defer resp.Body.Close()
  240. err = checkResponse(resp.StatusCode, expectedStatusCode)
  241. if expectedStatusCode != http.StatusCreated {
  242. body, _ = getResponseBody(resp)
  243. return newGroup, body, err
  244. }
  245. if err == nil {
  246. err = render.DecodeJSON(resp.Body, &newGroup)
  247. } else {
  248. body, _ = getResponseBody(resp)
  249. }
  250. if err == nil {
  251. err = checkGroup(group, newGroup)
  252. }
  253. return newGroup, body, err
  254. }
  255. // UpdateGroup updates an existing group and checks the received HTTP Status code against expectedStatusCode
  256. func UpdateGroup(group dataprovider.Group, expectedStatusCode int) (dataprovider.Group, []byte, error) {
  257. var newGroup dataprovider.Group
  258. var body []byte
  259. asJSON, _ := json.Marshal(group)
  260. resp, err := sendHTTPRequest(http.MethodPut, buildURLRelativeToBase(groupPath, url.PathEscape(group.Name)),
  261. bytes.NewBuffer(asJSON), "application/json", getDefaultToken())
  262. if err != nil {
  263. return newGroup, body, err
  264. }
  265. defer resp.Body.Close()
  266. body, _ = getResponseBody(resp)
  267. err = checkResponse(resp.StatusCode, expectedStatusCode)
  268. if expectedStatusCode != http.StatusOK {
  269. return newGroup, body, err
  270. }
  271. if err == nil {
  272. newGroup, body, err = GetGroupByName(group.Name, expectedStatusCode)
  273. }
  274. if err == nil {
  275. err = checkGroup(group, newGroup)
  276. }
  277. return newGroup, body, err
  278. }
  279. // RemoveGroup removes an existing group and checks the received HTTP Status code against expectedStatusCode.
  280. func RemoveGroup(group dataprovider.Group, expectedStatusCode int) ([]byte, error) {
  281. var body []byte
  282. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(groupPath, url.PathEscape(group.Name)),
  283. nil, "", getDefaultToken())
  284. if err != nil {
  285. return body, err
  286. }
  287. defer resp.Body.Close()
  288. body, _ = getResponseBody(resp)
  289. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  290. }
  291. // GetGroupByName gets a group by name and checks the received HTTP Status code against expectedStatusCode.
  292. func GetGroupByName(name string, expectedStatusCode int) (dataprovider.Group, []byte, error) {
  293. var group dataprovider.Group
  294. var body []byte
  295. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(groupPath, url.PathEscape(name)),
  296. nil, "", getDefaultToken())
  297. if err != nil {
  298. return group, body, err
  299. }
  300. defer resp.Body.Close()
  301. err = checkResponse(resp.StatusCode, expectedStatusCode)
  302. if err == nil && expectedStatusCode == http.StatusOK {
  303. err = render.DecodeJSON(resp.Body, &group)
  304. } else {
  305. body, _ = getResponseBody(resp)
  306. }
  307. return group, body, err
  308. }
  309. // GetGroups returns a list of groups and checks the received HTTP Status code against expectedStatusCode.
  310. // The number of results can be limited specifying a limit.
  311. // Some results can be skipped specifying an offset.
  312. func GetGroups(limit, offset int64, expectedStatusCode int) ([]dataprovider.Group, []byte, error) {
  313. var groups []dataprovider.Group
  314. var body []byte
  315. url, err := addLimitAndOffsetQueryParams(buildURLRelativeToBase(groupPath), limit, offset)
  316. if err != nil {
  317. return groups, body, err
  318. }
  319. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  320. if err != nil {
  321. return groups, body, err
  322. }
  323. defer resp.Body.Close()
  324. err = checkResponse(resp.StatusCode, expectedStatusCode)
  325. if err == nil && expectedStatusCode == http.StatusOK {
  326. err = render.DecodeJSON(resp.Body, &groups)
  327. } else {
  328. body, _ = getResponseBody(resp)
  329. }
  330. return groups, body, err
  331. }
  332. // AddAdmin adds a new admin and checks the received HTTP Status code against expectedStatusCode.
  333. func AddAdmin(admin dataprovider.Admin, expectedStatusCode int) (dataprovider.Admin, []byte, error) {
  334. var newAdmin dataprovider.Admin
  335. var body []byte
  336. asJSON, _ := json.Marshal(admin)
  337. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(adminPath), bytes.NewBuffer(asJSON),
  338. "application/json", getDefaultToken())
  339. if err != nil {
  340. return newAdmin, body, err
  341. }
  342. defer resp.Body.Close()
  343. err = checkResponse(resp.StatusCode, expectedStatusCode)
  344. if expectedStatusCode != http.StatusCreated {
  345. body, _ = getResponseBody(resp)
  346. return newAdmin, body, err
  347. }
  348. if err == nil {
  349. err = render.DecodeJSON(resp.Body, &newAdmin)
  350. } else {
  351. body, _ = getResponseBody(resp)
  352. }
  353. if err == nil {
  354. err = checkAdmin(&admin, &newAdmin)
  355. }
  356. return newAdmin, body, err
  357. }
  358. // UpdateAdmin updates an existing admin and checks the received HTTP Status code against expectedStatusCode
  359. func UpdateAdmin(admin dataprovider.Admin, expectedStatusCode int) (dataprovider.Admin, []byte, error) {
  360. var newAdmin dataprovider.Admin
  361. var body []byte
  362. asJSON, _ := json.Marshal(admin)
  363. resp, err := sendHTTPRequest(http.MethodPut, buildURLRelativeToBase(adminPath, url.PathEscape(admin.Username)),
  364. bytes.NewBuffer(asJSON), "application/json", getDefaultToken())
  365. if err != nil {
  366. return newAdmin, body, err
  367. }
  368. defer resp.Body.Close()
  369. body, _ = getResponseBody(resp)
  370. err = checkResponse(resp.StatusCode, expectedStatusCode)
  371. if expectedStatusCode != http.StatusOK {
  372. return newAdmin, body, err
  373. }
  374. if err == nil {
  375. newAdmin, body, err = GetAdminByUsername(admin.Username, expectedStatusCode)
  376. }
  377. if err == nil {
  378. err = checkAdmin(&admin, &newAdmin)
  379. }
  380. return newAdmin, body, err
  381. }
  382. // RemoveAdmin removes an existing admin and checks the received HTTP Status code against expectedStatusCode.
  383. func RemoveAdmin(admin dataprovider.Admin, expectedStatusCode int) ([]byte, error) {
  384. var body []byte
  385. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(adminPath, url.PathEscape(admin.Username)),
  386. nil, "", getDefaultToken())
  387. if err != nil {
  388. return body, err
  389. }
  390. defer resp.Body.Close()
  391. body, _ = getResponseBody(resp)
  392. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  393. }
  394. // GetAdminByUsername gets an admin by username and checks the received HTTP Status code against expectedStatusCode.
  395. func GetAdminByUsername(username string, expectedStatusCode int) (dataprovider.Admin, []byte, error) {
  396. var admin dataprovider.Admin
  397. var body []byte
  398. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(adminPath, url.PathEscape(username)),
  399. nil, "", getDefaultToken())
  400. if err != nil {
  401. return admin, body, err
  402. }
  403. defer resp.Body.Close()
  404. err = checkResponse(resp.StatusCode, expectedStatusCode)
  405. if err == nil && expectedStatusCode == http.StatusOK {
  406. err = render.DecodeJSON(resp.Body, &admin)
  407. } else {
  408. body, _ = getResponseBody(resp)
  409. }
  410. return admin, body, err
  411. }
  412. // GetAdmins returns a list of admins and checks the received HTTP Status code against expectedStatusCode.
  413. // The number of results can be limited specifying a limit.
  414. // Some results can be skipped specifying an offset.
  415. func GetAdmins(limit, offset int64, expectedStatusCode int) ([]dataprovider.Admin, []byte, error) {
  416. var admins []dataprovider.Admin
  417. var body []byte
  418. url, err := addLimitAndOffsetQueryParams(buildURLRelativeToBase(adminPath), limit, offset)
  419. if err != nil {
  420. return admins, body, err
  421. }
  422. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  423. if err != nil {
  424. return admins, body, err
  425. }
  426. defer resp.Body.Close()
  427. err = checkResponse(resp.StatusCode, expectedStatusCode)
  428. if err == nil && expectedStatusCode == http.StatusOK {
  429. err = render.DecodeJSON(resp.Body, &admins)
  430. } else {
  431. body, _ = getResponseBody(resp)
  432. }
  433. return admins, body, err
  434. }
  435. // ChangeAdminPassword changes the password for an existing admin
  436. func ChangeAdminPassword(currentPassword, newPassword string, expectedStatusCode int) ([]byte, error) {
  437. var body []byte
  438. pwdChange := make(map[string]string)
  439. pwdChange["current_password"] = currentPassword
  440. pwdChange["new_password"] = newPassword
  441. asJSON, _ := json.Marshal(&pwdChange)
  442. resp, err := sendHTTPRequest(http.MethodPut, buildURLRelativeToBase(adminPwdPath),
  443. bytes.NewBuffer(asJSON), "application/json", getDefaultToken())
  444. if err != nil {
  445. return body, err
  446. }
  447. defer resp.Body.Close()
  448. err = checkResponse(resp.StatusCode, expectedStatusCode)
  449. body, _ = getResponseBody(resp)
  450. return body, err
  451. }
  452. // GetAPIKeys returns a list of API keys and checks the received HTTP Status code against expectedStatusCode.
  453. // The number of results can be limited specifying a limit.
  454. // Some results can be skipped specifying an offset.
  455. func GetAPIKeys(limit, offset int64, expectedStatusCode int) ([]dataprovider.APIKey, []byte, error) {
  456. var apiKeys []dataprovider.APIKey
  457. var body []byte
  458. url, err := addLimitAndOffsetQueryParams(buildURLRelativeToBase(apiKeysPath), limit, offset)
  459. if err != nil {
  460. return apiKeys, body, err
  461. }
  462. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  463. if err != nil {
  464. return apiKeys, body, err
  465. }
  466. defer resp.Body.Close()
  467. err = checkResponse(resp.StatusCode, expectedStatusCode)
  468. if err == nil && expectedStatusCode == http.StatusOK {
  469. err = render.DecodeJSON(resp.Body, &apiKeys)
  470. } else {
  471. body, _ = getResponseBody(resp)
  472. }
  473. return apiKeys, body, err
  474. }
  475. // AddAPIKey adds a new API key and checks the received HTTP Status code against expectedStatusCode.
  476. func AddAPIKey(apiKey dataprovider.APIKey, expectedStatusCode int) (dataprovider.APIKey, []byte, error) {
  477. var newAPIKey dataprovider.APIKey
  478. var body []byte
  479. asJSON, _ := json.Marshal(apiKey)
  480. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(apiKeysPath), bytes.NewBuffer(asJSON),
  481. "application/json", getDefaultToken())
  482. if err != nil {
  483. return newAPIKey, body, err
  484. }
  485. defer resp.Body.Close()
  486. err = checkResponse(resp.StatusCode, expectedStatusCode)
  487. if expectedStatusCode != http.StatusCreated {
  488. body, _ = getResponseBody(resp)
  489. return newAPIKey, body, err
  490. }
  491. if err != nil {
  492. body, _ = getResponseBody(resp)
  493. return newAPIKey, body, err
  494. }
  495. response := make(map[string]string)
  496. err = render.DecodeJSON(resp.Body, &response)
  497. if err == nil {
  498. newAPIKey, body, err = GetAPIKeyByID(resp.Header.Get("X-Object-ID"), http.StatusOK)
  499. }
  500. if err == nil {
  501. err = checkAPIKey(&apiKey, &newAPIKey)
  502. }
  503. newAPIKey.Key = response["key"]
  504. return newAPIKey, body, err
  505. }
  506. // UpdateAPIKey updates an existing API key and checks the received HTTP Status code against expectedStatusCode
  507. func UpdateAPIKey(apiKey dataprovider.APIKey, expectedStatusCode int) (dataprovider.APIKey, []byte, error) {
  508. var newAPIKey dataprovider.APIKey
  509. var body []byte
  510. asJSON, _ := json.Marshal(apiKey)
  511. resp, err := sendHTTPRequest(http.MethodPut, buildURLRelativeToBase(apiKeysPath, url.PathEscape(apiKey.KeyID)),
  512. bytes.NewBuffer(asJSON), "application/json", getDefaultToken())
  513. if err != nil {
  514. return newAPIKey, body, err
  515. }
  516. defer resp.Body.Close()
  517. body, _ = getResponseBody(resp)
  518. err = checkResponse(resp.StatusCode, expectedStatusCode)
  519. if expectedStatusCode != http.StatusOK {
  520. return newAPIKey, body, err
  521. }
  522. if err == nil {
  523. newAPIKey, body, err = GetAPIKeyByID(apiKey.KeyID, expectedStatusCode)
  524. }
  525. if err == nil {
  526. err = checkAPIKey(&apiKey, &newAPIKey)
  527. }
  528. return newAPIKey, body, err
  529. }
  530. // RemoveAPIKey removes an existing API key and checks the received HTTP Status code against expectedStatusCode.
  531. func RemoveAPIKey(apiKey dataprovider.APIKey, expectedStatusCode int) ([]byte, error) {
  532. var body []byte
  533. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(apiKeysPath, url.PathEscape(apiKey.KeyID)),
  534. nil, "", getDefaultToken())
  535. if err != nil {
  536. return body, err
  537. }
  538. defer resp.Body.Close()
  539. body, _ = getResponseBody(resp)
  540. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  541. }
  542. // GetAPIKeyByID gets a API key by ID and checks the received HTTP Status code against expectedStatusCode.
  543. func GetAPIKeyByID(keyID string, expectedStatusCode int) (dataprovider.APIKey, []byte, error) {
  544. var apiKey dataprovider.APIKey
  545. var body []byte
  546. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(apiKeysPath, url.PathEscape(keyID)),
  547. nil, "", getDefaultToken())
  548. if err != nil {
  549. return apiKey, body, err
  550. }
  551. defer resp.Body.Close()
  552. err = checkResponse(resp.StatusCode, expectedStatusCode)
  553. if err == nil && expectedStatusCode == http.StatusOK {
  554. err = render.DecodeJSON(resp.Body, &apiKey)
  555. } else {
  556. body, _ = getResponseBody(resp)
  557. }
  558. return apiKey, body, err
  559. }
  560. // GetQuotaScans gets active quota scans for users and checks the received HTTP Status code against expectedStatusCode.
  561. func GetQuotaScans(expectedStatusCode int) ([]common.ActiveQuotaScan, []byte, error) {
  562. var quotaScans []common.ActiveQuotaScan
  563. var body []byte
  564. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(quotaScanPath), nil, "", getDefaultToken())
  565. if err != nil {
  566. return quotaScans, body, err
  567. }
  568. defer resp.Body.Close()
  569. err = checkResponse(resp.StatusCode, expectedStatusCode)
  570. if err == nil && expectedStatusCode == http.StatusOK {
  571. err = render.DecodeJSON(resp.Body, &quotaScans)
  572. } else {
  573. body, _ = getResponseBody(resp)
  574. }
  575. return quotaScans, body, err
  576. }
  577. // StartQuotaScan starts a new quota scan for the given user and checks the received HTTP Status code against expectedStatusCode.
  578. func StartQuotaScan(user dataprovider.User, expectedStatusCode int) ([]byte, error) {
  579. var body []byte
  580. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(quotasBasePath, "users", user.Username, "scan"),
  581. nil, "", getDefaultToken())
  582. if err != nil {
  583. return body, err
  584. }
  585. defer resp.Body.Close()
  586. body, _ = getResponseBody(resp)
  587. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  588. }
  589. // UpdateQuotaUsage updates the user used quota limits and checks the received
  590. // HTTP Status code against expectedStatusCode.
  591. func UpdateQuotaUsage(user dataprovider.User, mode string, expectedStatusCode int) ([]byte, error) {
  592. var body []byte
  593. userAsJSON, _ := json.Marshal(user)
  594. url, err := addModeQueryParam(buildURLRelativeToBase(quotasBasePath, "users", user.Username, "usage"), mode)
  595. if err != nil {
  596. return body, err
  597. }
  598. resp, err := sendHTTPRequest(http.MethodPut, url.String(), bytes.NewBuffer(userAsJSON), "application/json",
  599. getDefaultToken())
  600. if err != nil {
  601. return body, err
  602. }
  603. defer resp.Body.Close()
  604. body, _ = getResponseBody(resp)
  605. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  606. }
  607. // UpdateTransferQuotaUsage updates the user used transfer quota limits and checks the received
  608. // HTTP Status code against expectedStatusCode.
  609. func UpdateTransferQuotaUsage(user dataprovider.User, mode string, expectedStatusCode int) ([]byte, error) {
  610. var body []byte
  611. userAsJSON, _ := json.Marshal(user)
  612. url, err := addModeQueryParam(buildURLRelativeToBase(quotasBasePath, "users", user.Username, "transfer-usage"), mode)
  613. if err != nil {
  614. return body, err
  615. }
  616. resp, err := sendHTTPRequest(http.MethodPut, url.String(), bytes.NewBuffer(userAsJSON), "application/json",
  617. getDefaultToken())
  618. if err != nil {
  619. return body, err
  620. }
  621. defer resp.Body.Close()
  622. body, _ = getResponseBody(resp)
  623. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  624. }
  625. // GetRetentionChecks returns the active retention checks
  626. func GetRetentionChecks(expectedStatusCode int) ([]common.ActiveRetentionChecks, []byte, error) {
  627. var checks []common.ActiveRetentionChecks
  628. var body []byte
  629. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(retentionChecksPath), nil, "", getDefaultToken())
  630. if err != nil {
  631. return checks, body, err
  632. }
  633. defer resp.Body.Close()
  634. err = checkResponse(resp.StatusCode, expectedStatusCode)
  635. if err == nil && expectedStatusCode == http.StatusOK {
  636. err = render.DecodeJSON(resp.Body, &checks)
  637. } else {
  638. body, _ = getResponseBody(resp)
  639. }
  640. return checks, body, err
  641. }
  642. // StartRetentionCheck starts a new retention check
  643. func StartRetentionCheck(username string, retention []common.FolderRetention, expectedStatusCode int) ([]byte, error) {
  644. var body []byte
  645. asJSON, _ := json.Marshal(retention)
  646. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(retentionBasePath, username, "check"),
  647. bytes.NewBuffer(asJSON), "application/json", getDefaultToken())
  648. if err != nil {
  649. return body, err
  650. }
  651. defer resp.Body.Close()
  652. body, _ = getResponseBody(resp)
  653. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  654. }
  655. // GetConnections returns status and stats for active SFTP/SCP connections
  656. func GetConnections(expectedStatusCode int) ([]common.ConnectionStatus, []byte, error) {
  657. var connections []common.ConnectionStatus
  658. var body []byte
  659. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(activeConnectionsPath), nil, "", getDefaultToken())
  660. if err != nil {
  661. return connections, body, err
  662. }
  663. defer resp.Body.Close()
  664. err = checkResponse(resp.StatusCode, expectedStatusCode)
  665. if err == nil && expectedStatusCode == http.StatusOK {
  666. err = render.DecodeJSON(resp.Body, &connections)
  667. } else {
  668. body, _ = getResponseBody(resp)
  669. }
  670. return connections, body, err
  671. }
  672. // CloseConnection closes an active connection identified by connectionID
  673. func CloseConnection(connectionID string, expectedStatusCode int) ([]byte, error) {
  674. var body []byte
  675. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(activeConnectionsPath, connectionID),
  676. nil, "", getDefaultToken())
  677. if err != nil {
  678. return body, err
  679. }
  680. defer resp.Body.Close()
  681. err = checkResponse(resp.StatusCode, expectedStatusCode)
  682. body, _ = getResponseBody(resp)
  683. return body, err
  684. }
  685. // AddFolder adds a new folder and checks the received HTTP Status code against expectedStatusCode
  686. func AddFolder(folder vfs.BaseVirtualFolder, expectedStatusCode int) (vfs.BaseVirtualFolder, []byte, error) {
  687. var newFolder vfs.BaseVirtualFolder
  688. var body []byte
  689. folderAsJSON, _ := json.Marshal(folder)
  690. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(folderPath), bytes.NewBuffer(folderAsJSON),
  691. "application/json", getDefaultToken())
  692. if err != nil {
  693. return newFolder, body, err
  694. }
  695. defer resp.Body.Close()
  696. err = checkResponse(resp.StatusCode, expectedStatusCode)
  697. if expectedStatusCode != http.StatusCreated {
  698. body, _ = getResponseBody(resp)
  699. return newFolder, body, err
  700. }
  701. if err == nil {
  702. err = render.DecodeJSON(resp.Body, &newFolder)
  703. } else {
  704. body, _ = getResponseBody(resp)
  705. }
  706. if err == nil {
  707. err = checkFolder(&folder, &newFolder)
  708. }
  709. return newFolder, body, err
  710. }
  711. // UpdateFolder updates an existing folder and checks the received HTTP Status code against expectedStatusCode.
  712. func UpdateFolder(folder vfs.BaseVirtualFolder, expectedStatusCode int) (vfs.BaseVirtualFolder, []byte, error) {
  713. var updatedFolder vfs.BaseVirtualFolder
  714. var body []byte
  715. folderAsJSON, _ := json.Marshal(folder)
  716. resp, err := sendHTTPRequest(http.MethodPut, buildURLRelativeToBase(folderPath, url.PathEscape(folder.Name)),
  717. bytes.NewBuffer(folderAsJSON), "application/json", getDefaultToken())
  718. if err != nil {
  719. return updatedFolder, body, err
  720. }
  721. defer resp.Body.Close()
  722. body, _ = getResponseBody(resp)
  723. err = checkResponse(resp.StatusCode, expectedStatusCode)
  724. if expectedStatusCode != http.StatusOK {
  725. return updatedFolder, body, err
  726. }
  727. if err == nil {
  728. updatedFolder, body, err = GetFolderByName(folder.Name, expectedStatusCode)
  729. }
  730. if err == nil {
  731. err = checkFolder(&folder, &updatedFolder)
  732. }
  733. return updatedFolder, body, err
  734. }
  735. // RemoveFolder removes an existing user and checks the received HTTP Status code against expectedStatusCode.
  736. func RemoveFolder(folder vfs.BaseVirtualFolder, expectedStatusCode int) ([]byte, error) {
  737. var body []byte
  738. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(folderPath, url.PathEscape(folder.Name)),
  739. nil, "", getDefaultToken())
  740. if err != nil {
  741. return body, err
  742. }
  743. defer resp.Body.Close()
  744. body, _ = getResponseBody(resp)
  745. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  746. }
  747. // GetFolderByName gets a folder by name and checks the received HTTP Status code against expectedStatusCode.
  748. func GetFolderByName(name string, expectedStatusCode int) (vfs.BaseVirtualFolder, []byte, error) {
  749. var folder vfs.BaseVirtualFolder
  750. var body []byte
  751. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(folderPath, url.PathEscape(name)),
  752. nil, "", getDefaultToken())
  753. if err != nil {
  754. return folder, body, err
  755. }
  756. defer resp.Body.Close()
  757. err = checkResponse(resp.StatusCode, expectedStatusCode)
  758. if err == nil && expectedStatusCode == http.StatusOK {
  759. err = render.DecodeJSON(resp.Body, &folder)
  760. } else {
  761. body, _ = getResponseBody(resp)
  762. }
  763. return folder, body, err
  764. }
  765. // GetFolders returns a list of folders and checks the received HTTP Status code against expectedStatusCode.
  766. // The number of results can be limited specifying a limit.
  767. // Some results can be skipped specifying an offset.
  768. // The results can be filtered specifying a folder path, the folder path filter is an exact match
  769. func GetFolders(limit int64, offset int64, expectedStatusCode int) ([]vfs.BaseVirtualFolder, []byte, error) {
  770. var folders []vfs.BaseVirtualFolder
  771. var body []byte
  772. url, err := addLimitAndOffsetQueryParams(buildURLRelativeToBase(folderPath), limit, offset)
  773. if err != nil {
  774. return folders, body, err
  775. }
  776. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  777. if err != nil {
  778. return folders, body, err
  779. }
  780. defer resp.Body.Close()
  781. err = checkResponse(resp.StatusCode, expectedStatusCode)
  782. if err == nil && expectedStatusCode == http.StatusOK {
  783. err = render.DecodeJSON(resp.Body, &folders)
  784. } else {
  785. body, _ = getResponseBody(resp)
  786. }
  787. return folders, body, err
  788. }
  789. // GetFoldersQuotaScans gets active quota scans for folders and checks the received HTTP Status code against expectedStatusCode.
  790. func GetFoldersQuotaScans(expectedStatusCode int) ([]common.ActiveVirtualFolderQuotaScan, []byte, error) {
  791. var quotaScans []common.ActiveVirtualFolderQuotaScan
  792. var body []byte
  793. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(quotaScanVFolderPath), nil, "", getDefaultToken())
  794. if err != nil {
  795. return quotaScans, body, err
  796. }
  797. defer resp.Body.Close()
  798. err = checkResponse(resp.StatusCode, expectedStatusCode)
  799. if err == nil && expectedStatusCode == http.StatusOK {
  800. err = render.DecodeJSON(resp.Body, &quotaScans)
  801. } else {
  802. body, _ = getResponseBody(resp)
  803. }
  804. return quotaScans, body, err
  805. }
  806. // StartFolderQuotaScan start a new quota scan for the given folder and checks the received HTTP Status code against expectedStatusCode.
  807. func StartFolderQuotaScan(folder vfs.BaseVirtualFolder, expectedStatusCode int) ([]byte, error) {
  808. var body []byte
  809. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(quotasBasePath, "folders", folder.Name, "scan"),
  810. nil, "", getDefaultToken())
  811. if err != nil {
  812. return body, err
  813. }
  814. defer resp.Body.Close()
  815. body, _ = getResponseBody(resp)
  816. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  817. }
  818. // UpdateFolderQuotaUsage updates the folder used quota limits and checks the received HTTP Status code against expectedStatusCode.
  819. func UpdateFolderQuotaUsage(folder vfs.BaseVirtualFolder, mode string, expectedStatusCode int) ([]byte, error) {
  820. var body []byte
  821. folderAsJSON, _ := json.Marshal(folder)
  822. url, err := addModeQueryParam(buildURLRelativeToBase(quotasBasePath, "folders", folder.Name, "usage"), mode)
  823. if err != nil {
  824. return body, err
  825. }
  826. resp, err := sendHTTPRequest(http.MethodPut, url.String(), bytes.NewBuffer(folderAsJSON), "", getDefaultToken())
  827. if err != nil {
  828. return body, err
  829. }
  830. defer resp.Body.Close()
  831. body, _ = getResponseBody(resp)
  832. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  833. }
  834. // GetVersion returns version details
  835. func GetVersion(expectedStatusCode int) (version.Info, []byte, error) {
  836. var appVersion version.Info
  837. var body []byte
  838. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(versionPath), nil, "", getDefaultToken())
  839. if err != nil {
  840. return appVersion, body, err
  841. }
  842. defer resp.Body.Close()
  843. err = checkResponse(resp.StatusCode, expectedStatusCode)
  844. if err == nil && expectedStatusCode == http.StatusOK {
  845. err = render.DecodeJSON(resp.Body, &appVersion)
  846. } else {
  847. body, _ = getResponseBody(resp)
  848. }
  849. return appVersion, body, err
  850. }
  851. // GetStatus returns the server status
  852. func GetStatus(expectedStatusCode int) (httpd.ServicesStatus, []byte, error) {
  853. var response httpd.ServicesStatus
  854. var body []byte
  855. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(serverStatusPath), nil, "", getDefaultToken())
  856. if err != nil {
  857. return response, body, err
  858. }
  859. defer resp.Body.Close()
  860. err = checkResponse(resp.StatusCode, expectedStatusCode)
  861. if err == nil && (expectedStatusCode == http.StatusOK) {
  862. err = render.DecodeJSON(resp.Body, &response)
  863. } else {
  864. body, _ = getResponseBody(resp)
  865. }
  866. return response, body, err
  867. }
  868. // GetDefenderHosts returns hosts that are banned or for which some violations have been detected
  869. func GetDefenderHosts(expectedStatusCode int) ([]dataprovider.DefenderEntry, []byte, error) {
  870. var response []dataprovider.DefenderEntry
  871. var body []byte
  872. url, err := url.Parse(buildURLRelativeToBase(defenderHosts))
  873. if err != nil {
  874. return response, body, err
  875. }
  876. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  877. if err != nil {
  878. return response, body, err
  879. }
  880. defer resp.Body.Close()
  881. err = checkResponse(resp.StatusCode, expectedStatusCode)
  882. if err == nil && expectedStatusCode == http.StatusOK {
  883. err = render.DecodeJSON(resp.Body, &response)
  884. } else {
  885. body, _ = getResponseBody(resp)
  886. }
  887. return response, body, err
  888. }
  889. // GetDefenderHostByIP returns the host with the given IP, if it exists
  890. func GetDefenderHostByIP(ip string, expectedStatusCode int) (dataprovider.DefenderEntry, []byte, error) {
  891. var host dataprovider.DefenderEntry
  892. var body []byte
  893. id := hex.EncodeToString([]byte(ip))
  894. resp, err := sendHTTPRequest(http.MethodGet, buildURLRelativeToBase(defenderHosts, id),
  895. nil, "", getDefaultToken())
  896. if err != nil {
  897. return host, body, err
  898. }
  899. defer resp.Body.Close()
  900. err = checkResponse(resp.StatusCode, expectedStatusCode)
  901. if err == nil && expectedStatusCode == http.StatusOK {
  902. err = render.DecodeJSON(resp.Body, &host)
  903. } else {
  904. body, _ = getResponseBody(resp)
  905. }
  906. return host, body, err
  907. }
  908. // RemoveDefenderHostByIP removes the host with the given IP from the defender list
  909. func RemoveDefenderHostByIP(ip string, expectedStatusCode int) ([]byte, error) {
  910. var body []byte
  911. id := hex.EncodeToString([]byte(ip))
  912. resp, err := sendHTTPRequest(http.MethodDelete, buildURLRelativeToBase(defenderHosts, id), nil, "", getDefaultToken())
  913. if err != nil {
  914. return body, err
  915. }
  916. defer resp.Body.Close()
  917. body, _ = getResponseBody(resp)
  918. return body, checkResponse(resp.StatusCode, expectedStatusCode)
  919. }
  920. // GetBanTime returns the ban time for the given IP address
  921. func GetBanTime(ip string, expectedStatusCode int) (map[string]any, []byte, error) {
  922. var response map[string]any
  923. var body []byte
  924. url, err := url.Parse(buildURLRelativeToBase(defenderBanTime))
  925. if err != nil {
  926. return response, body, err
  927. }
  928. q := url.Query()
  929. q.Add("ip", ip)
  930. url.RawQuery = q.Encode()
  931. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  932. if err != nil {
  933. return response, body, err
  934. }
  935. defer resp.Body.Close()
  936. err = checkResponse(resp.StatusCode, expectedStatusCode)
  937. if err == nil && expectedStatusCode == http.StatusOK {
  938. err = render.DecodeJSON(resp.Body, &response)
  939. } else {
  940. body, _ = getResponseBody(resp)
  941. }
  942. return response, body, err
  943. }
  944. // GetScore returns the score for the given IP address
  945. func GetScore(ip string, expectedStatusCode int) (map[string]any, []byte, error) {
  946. var response map[string]any
  947. var body []byte
  948. url, err := url.Parse(buildURLRelativeToBase(defenderScore))
  949. if err != nil {
  950. return response, body, err
  951. }
  952. q := url.Query()
  953. q.Add("ip", ip)
  954. url.RawQuery = q.Encode()
  955. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  956. if err != nil {
  957. return response, body, err
  958. }
  959. defer resp.Body.Close()
  960. err = checkResponse(resp.StatusCode, expectedStatusCode)
  961. if err == nil && expectedStatusCode == http.StatusOK {
  962. err = render.DecodeJSON(resp.Body, &response)
  963. } else {
  964. body, _ = getResponseBody(resp)
  965. }
  966. return response, body, err
  967. }
  968. // UnbanIP unbans the given IP address
  969. func UnbanIP(ip string, expectedStatusCode int) error {
  970. postBody := make(map[string]string)
  971. postBody["ip"] = ip
  972. asJSON, _ := json.Marshal(postBody)
  973. resp, err := sendHTTPRequest(http.MethodPost, buildURLRelativeToBase(defenderUnban), bytes.NewBuffer(asJSON),
  974. "", getDefaultToken())
  975. if err != nil {
  976. return err
  977. }
  978. defer resp.Body.Close()
  979. return checkResponse(resp.StatusCode, expectedStatusCode)
  980. }
  981. // Dumpdata requests a backup to outputFile.
  982. // outputFile is relative to the configured backups_path
  983. func Dumpdata(outputFile, outputData, indent string, expectedStatusCode int) (map[string]any, []byte, error) {
  984. var response map[string]any
  985. var body []byte
  986. url, err := url.Parse(buildURLRelativeToBase(dumpDataPath))
  987. if err != nil {
  988. return response, body, err
  989. }
  990. q := url.Query()
  991. if outputData != "" {
  992. q.Add("output-data", outputData)
  993. }
  994. if outputFile != "" {
  995. q.Add("output-file", outputFile)
  996. }
  997. if indent != "" {
  998. q.Add("indent", indent)
  999. }
  1000. url.RawQuery = q.Encode()
  1001. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  1002. if err != nil {
  1003. return response, body, err
  1004. }
  1005. defer resp.Body.Close()
  1006. err = checkResponse(resp.StatusCode, expectedStatusCode)
  1007. if err == nil && expectedStatusCode == http.StatusOK {
  1008. err = render.DecodeJSON(resp.Body, &response)
  1009. } else {
  1010. body, _ = getResponseBody(resp)
  1011. }
  1012. return response, body, err
  1013. }
  1014. // Loaddata restores a backup.
  1015. func Loaddata(inputFile, scanQuota, mode string, expectedStatusCode int) (map[string]any, []byte, error) {
  1016. var response map[string]any
  1017. var body []byte
  1018. url, err := url.Parse(buildURLRelativeToBase(loadDataPath))
  1019. if err != nil {
  1020. return response, body, err
  1021. }
  1022. q := url.Query()
  1023. q.Add("input-file", inputFile)
  1024. if scanQuota != "" {
  1025. q.Add("scan-quota", scanQuota)
  1026. }
  1027. if mode != "" {
  1028. q.Add("mode", mode)
  1029. }
  1030. url.RawQuery = q.Encode()
  1031. resp, err := sendHTTPRequest(http.MethodGet, url.String(), nil, "", getDefaultToken())
  1032. if err != nil {
  1033. return response, body, err
  1034. }
  1035. defer resp.Body.Close()
  1036. err = checkResponse(resp.StatusCode, expectedStatusCode)
  1037. if err == nil && expectedStatusCode == http.StatusOK {
  1038. err = render.DecodeJSON(resp.Body, &response)
  1039. } else {
  1040. body, _ = getResponseBody(resp)
  1041. }
  1042. return response, body, err
  1043. }
  1044. // LoaddataFromPostBody restores a backup
  1045. func LoaddataFromPostBody(data []byte, scanQuota, mode string, expectedStatusCode int) (map[string]any, []byte, error) {
  1046. var response map[string]any
  1047. var body []byte
  1048. url, err := url.Parse(buildURLRelativeToBase(loadDataPath))
  1049. if err != nil {
  1050. return response, body, err
  1051. }
  1052. q := url.Query()
  1053. if scanQuota != "" {
  1054. q.Add("scan-quota", scanQuota)
  1055. }
  1056. if mode != "" {
  1057. q.Add("mode", mode)
  1058. }
  1059. url.RawQuery = q.Encode()
  1060. resp, err := sendHTTPRequest(http.MethodPost, url.String(), bytes.NewReader(data), "", getDefaultToken())
  1061. if err != nil {
  1062. return response, body, err
  1063. }
  1064. defer resp.Body.Close()
  1065. err = checkResponse(resp.StatusCode, expectedStatusCode)
  1066. if err == nil && expectedStatusCode == http.StatusOK {
  1067. err = render.DecodeJSON(resp.Body, &response)
  1068. } else {
  1069. body, _ = getResponseBody(resp)
  1070. }
  1071. return response, body, err
  1072. }
  1073. func checkResponse(actual int, expected int) error {
  1074. if expected != actual {
  1075. return fmt.Errorf("wrong status code: got %v want %v", actual, expected)
  1076. }
  1077. return nil
  1078. }
  1079. func getResponseBody(resp *http.Response) ([]byte, error) {
  1080. return io.ReadAll(resp.Body)
  1081. }
  1082. func checkGroup(expected dataprovider.Group, actual dataprovider.Group) error {
  1083. if expected.ID <= 0 {
  1084. if actual.ID <= 0 {
  1085. return errors.New("actual group ID must be > 0")
  1086. }
  1087. } else {
  1088. if actual.ID != expected.ID {
  1089. return errors.New("group ID mismatch")
  1090. }
  1091. }
  1092. if dataprovider.ConvertName(expected.Name) != actual.Name {
  1093. return errors.New("name mismatch")
  1094. }
  1095. if expected.Description != actual.Description {
  1096. return errors.New("description mismatch")
  1097. }
  1098. if err := compareEqualGroupSettingsFields(expected.UserSettings.BaseGroupUserSettings,
  1099. actual.UserSettings.BaseGroupUserSettings); err != nil {
  1100. return err
  1101. }
  1102. if err := compareVirtualFolders(expected.VirtualFolders, actual.VirtualFolders); err != nil {
  1103. return err
  1104. }
  1105. if err := compareUserFilters(expected.UserSettings.Filters, actual.UserSettings.Filters); err != nil {
  1106. return err
  1107. }
  1108. return compareFsConfig(&expected.UserSettings.FsConfig, &actual.UserSettings.FsConfig)
  1109. }
  1110. func checkFolder(expected *vfs.BaseVirtualFolder, actual *vfs.BaseVirtualFolder) error {
  1111. if expected.ID <= 0 {
  1112. if actual.ID <= 0 {
  1113. return errors.New("actual folder ID must be > 0")
  1114. }
  1115. } else {
  1116. if actual.ID != expected.ID {
  1117. return errors.New("folder ID mismatch")
  1118. }
  1119. }
  1120. if dataprovider.ConvertName(expected.Name) != actual.Name {
  1121. return errors.New("name mismatch")
  1122. }
  1123. if expected.MappedPath != actual.MappedPath {
  1124. return errors.New("mapped path mismatch")
  1125. }
  1126. if expected.Description != actual.Description {
  1127. return errors.New("description mismatch")
  1128. }
  1129. return compareFsConfig(&expected.FsConfig, &actual.FsConfig)
  1130. }
  1131. func checkAPIKey(expected, actual *dataprovider.APIKey) error {
  1132. if actual.Key != "" {
  1133. return errors.New("key must not be visible")
  1134. }
  1135. if actual.KeyID == "" {
  1136. return errors.New("actual key_id cannot be empty")
  1137. }
  1138. if expected.Name != actual.Name {
  1139. return errors.New("name mismatch")
  1140. }
  1141. if expected.Scope != actual.Scope {
  1142. return errors.New("scope mismatch")
  1143. }
  1144. if actual.CreatedAt == 0 {
  1145. return errors.New("created_at cannot be 0")
  1146. }
  1147. if actual.UpdatedAt == 0 {
  1148. return errors.New("updated_at cannot be 0")
  1149. }
  1150. if expected.ExpiresAt != actual.ExpiresAt {
  1151. return errors.New("expires_at mismatch")
  1152. }
  1153. if expected.Description != actual.Description {
  1154. return errors.New("description mismatch")
  1155. }
  1156. if expected.User != actual.User {
  1157. return errors.New("user mismatch")
  1158. }
  1159. if expected.Admin != actual.Admin {
  1160. return errors.New("admin mismatch")
  1161. }
  1162. return nil
  1163. }
  1164. func checkAdmin(expected, actual *dataprovider.Admin) error {
  1165. if actual.Password != "" {
  1166. return errors.New("admin password must not be visible")
  1167. }
  1168. if expected.ID <= 0 {
  1169. if actual.ID <= 0 {
  1170. return errors.New("actual admin ID must be > 0")
  1171. }
  1172. } else {
  1173. if actual.ID != expected.ID {
  1174. return errors.New("admin ID mismatch")
  1175. }
  1176. }
  1177. if expected.CreatedAt > 0 {
  1178. if expected.CreatedAt != actual.CreatedAt {
  1179. return fmt.Errorf("created_at mismatch %v != %v", expected.CreatedAt, actual.CreatedAt)
  1180. }
  1181. }
  1182. if err := compareAdminEqualFields(expected, actual); err != nil {
  1183. return err
  1184. }
  1185. if len(expected.Permissions) != len(actual.Permissions) {
  1186. return errors.New("permissions mismatch")
  1187. }
  1188. for _, p := range expected.Permissions {
  1189. if !util.Contains(actual.Permissions, p) {
  1190. return errors.New("permissions content mismatch")
  1191. }
  1192. }
  1193. if len(expected.Filters.AllowList) != len(actual.Filters.AllowList) {
  1194. return errors.New("allow list mismatch")
  1195. }
  1196. if expected.Filters.AllowAPIKeyAuth != actual.Filters.AllowAPIKeyAuth {
  1197. return errors.New("allow_api_key_auth mismatch")
  1198. }
  1199. for _, v := range expected.Filters.AllowList {
  1200. if !util.Contains(actual.Filters.AllowList, v) {
  1201. return errors.New("allow list content mismatch")
  1202. }
  1203. }
  1204. return nil
  1205. }
  1206. func compareAdminEqualFields(expected *dataprovider.Admin, actual *dataprovider.Admin) error {
  1207. if dataprovider.ConvertName(expected.Username) != actual.Username {
  1208. return errors.New("sername mismatch")
  1209. }
  1210. if expected.Email != actual.Email {
  1211. return errors.New("email mismatch")
  1212. }
  1213. if expected.Status != actual.Status {
  1214. return errors.New("status mismatch")
  1215. }
  1216. if expected.Description != actual.Description {
  1217. return errors.New("description mismatch")
  1218. }
  1219. if expected.AdditionalInfo != actual.AdditionalInfo {
  1220. return errors.New("additional info mismatch")
  1221. }
  1222. return nil
  1223. }
  1224. func checkUser(expected *dataprovider.User, actual *dataprovider.User) error {
  1225. if actual.Password != "" {
  1226. return errors.New("user password must not be visible")
  1227. }
  1228. if expected.ID <= 0 {
  1229. if actual.ID <= 0 {
  1230. return errors.New("actual user ID must be > 0")
  1231. }
  1232. } else {
  1233. if actual.ID != expected.ID {
  1234. return errors.New("user ID mismatch")
  1235. }
  1236. }
  1237. if expected.CreatedAt > 0 {
  1238. if expected.CreatedAt != actual.CreatedAt {
  1239. return fmt.Errorf("created_at mismatch %v != %v", expected.CreatedAt, actual.CreatedAt)
  1240. }
  1241. }
  1242. if expected.Email != actual.Email {
  1243. return errors.New("email mismatch")
  1244. }
  1245. if err := compareUserPermissions(expected.Permissions, actual.Permissions); err != nil {
  1246. return err
  1247. }
  1248. if err := compareUserFilters(expected.Filters.BaseUserFilters, actual.Filters.BaseUserFilters); err != nil {
  1249. return err
  1250. }
  1251. if err := compareFsConfig(&expected.FsConfig, &actual.FsConfig); err != nil {
  1252. return err
  1253. }
  1254. if err := compareUserGroups(expected, actual); err != nil {
  1255. return err
  1256. }
  1257. if err := compareVirtualFolders(expected.VirtualFolders, actual.VirtualFolders); err != nil {
  1258. return err
  1259. }
  1260. return compareEqualsUserFields(expected, actual)
  1261. }
  1262. func compareUserPermissions(expected map[string][]string, actual map[string][]string) error {
  1263. if len(expected) != len(actual) {
  1264. return errors.New("permissions mismatch")
  1265. }
  1266. for dir, perms := range expected {
  1267. if actualPerms, ok := actual[dir]; ok {
  1268. for _, v := range actualPerms {
  1269. if !util.Contains(perms, v) {
  1270. return errors.New("permissions contents mismatch")
  1271. }
  1272. }
  1273. } else {
  1274. return errors.New("permissions directories mismatch")
  1275. }
  1276. }
  1277. return nil
  1278. }
  1279. func compareUserGroups(expected *dataprovider.User, actual *dataprovider.User) error {
  1280. if len(actual.Groups) != len(expected.Groups) {
  1281. return errors.New("groups len mismatch")
  1282. }
  1283. for _, g := range actual.Groups {
  1284. found := false
  1285. for _, g1 := range expected.Groups {
  1286. if g1.Name == g.Name {
  1287. found = true
  1288. if g1.Type != g.Type {
  1289. return fmt.Errorf("type mismatch for group %s", g.Name)
  1290. }
  1291. }
  1292. }
  1293. if !found {
  1294. return errors.New("groups mismatch")
  1295. }
  1296. }
  1297. return nil
  1298. }
  1299. func compareVirtualFolders(expected []vfs.VirtualFolder, actual []vfs.VirtualFolder) error {
  1300. if len(actual) != len(expected) {
  1301. return errors.New("virtual folders len mismatch")
  1302. }
  1303. for _, v := range actual {
  1304. found := false
  1305. for _, v1 := range expected {
  1306. if path.Clean(v.VirtualPath) == path.Clean(v1.VirtualPath) {
  1307. if err := checkFolder(&v1.BaseVirtualFolder, &v.BaseVirtualFolder); err != nil {
  1308. return err
  1309. }
  1310. if v.QuotaSize != v1.QuotaSize {
  1311. return errors.New("vfolder quota size mismatch")
  1312. }
  1313. if (v.QuotaFiles) != (v1.QuotaFiles) {
  1314. return errors.New("vfolder quota files mismatch")
  1315. }
  1316. found = true
  1317. break
  1318. }
  1319. }
  1320. if !found {
  1321. return errors.New("virtual folders mismatch")
  1322. }
  1323. }
  1324. return nil
  1325. }
  1326. func compareFsConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  1327. if expected.Provider != actual.Provider {
  1328. return errors.New("fs provider mismatch")
  1329. }
  1330. if err := compareS3Config(expected, actual); err != nil {
  1331. return err
  1332. }
  1333. if err := compareGCSConfig(expected, actual); err != nil {
  1334. return err
  1335. }
  1336. if err := compareAzBlobConfig(expected, actual); err != nil {
  1337. return err
  1338. }
  1339. if err := checkEncryptedSecret(expected.CryptConfig.Passphrase, actual.CryptConfig.Passphrase); err != nil {
  1340. return err
  1341. }
  1342. if err := compareSFTPFsConfig(expected, actual); err != nil {
  1343. return err
  1344. }
  1345. return compareHTTPFsConfig(expected, actual)
  1346. }
  1347. func compareS3Config(expected *vfs.Filesystem, actual *vfs.Filesystem) error { //nolint:gocyclo
  1348. if expected.S3Config.Bucket != actual.S3Config.Bucket {
  1349. return errors.New("fs S3 bucket mismatch")
  1350. }
  1351. if expected.S3Config.Region != actual.S3Config.Region {
  1352. return errors.New("fs S3 region mismatch")
  1353. }
  1354. if expected.S3Config.AccessKey != actual.S3Config.AccessKey {
  1355. return errors.New("fs S3 access key mismatch")
  1356. }
  1357. if expected.S3Config.RoleARN != actual.S3Config.RoleARN {
  1358. return errors.New("fs S3 role ARN mismatch")
  1359. }
  1360. if err := checkEncryptedSecret(expected.S3Config.AccessSecret, actual.S3Config.AccessSecret); err != nil {
  1361. return fmt.Errorf("fs S3 access secret mismatch: %v", err)
  1362. }
  1363. if expected.S3Config.Endpoint != actual.S3Config.Endpoint {
  1364. return errors.New("fs S3 endpoint mismatch")
  1365. }
  1366. if expected.S3Config.StorageClass != actual.S3Config.StorageClass {
  1367. return errors.New("fs S3 storage class mismatch")
  1368. }
  1369. if expected.S3Config.ACL != actual.S3Config.ACL {
  1370. return errors.New("fs S3 ACL mismatch")
  1371. }
  1372. if expected.S3Config.UploadPartSize != actual.S3Config.UploadPartSize {
  1373. return errors.New("fs S3 upload part size mismatch")
  1374. }
  1375. if expected.S3Config.UploadConcurrency != actual.S3Config.UploadConcurrency {
  1376. return errors.New("fs S3 upload concurrency mismatch")
  1377. }
  1378. if expected.S3Config.DownloadPartSize != actual.S3Config.DownloadPartSize {
  1379. return errors.New("fs S3 download part size mismatch")
  1380. }
  1381. if expected.S3Config.DownloadConcurrency != actual.S3Config.DownloadConcurrency {
  1382. return errors.New("fs S3 download concurrency mismatch")
  1383. }
  1384. if expected.S3Config.ForcePathStyle != actual.S3Config.ForcePathStyle {
  1385. return errors.New("fs S3 force path style mismatch")
  1386. }
  1387. if expected.S3Config.DownloadPartMaxTime != actual.S3Config.DownloadPartMaxTime {
  1388. return errors.New("fs S3 download part max time mismatch")
  1389. }
  1390. if expected.S3Config.UploadPartMaxTime != actual.S3Config.UploadPartMaxTime {
  1391. return errors.New("fs S3 upload part max time mismatch")
  1392. }
  1393. if expected.S3Config.KeyPrefix != actual.S3Config.KeyPrefix &&
  1394. expected.S3Config.KeyPrefix+"/" != actual.S3Config.KeyPrefix {
  1395. return errors.New("fs S3 key prefix mismatch")
  1396. }
  1397. return nil
  1398. }
  1399. func compareGCSConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  1400. if expected.GCSConfig.Bucket != actual.GCSConfig.Bucket {
  1401. return errors.New("GCS bucket mismatch")
  1402. }
  1403. if expected.GCSConfig.StorageClass != actual.GCSConfig.StorageClass {
  1404. return errors.New("GCS storage class mismatch")
  1405. }
  1406. if expected.GCSConfig.ACL != actual.GCSConfig.ACL {
  1407. return errors.New("GCS ACL mismatch")
  1408. }
  1409. if expected.GCSConfig.KeyPrefix != actual.GCSConfig.KeyPrefix &&
  1410. expected.GCSConfig.KeyPrefix+"/" != actual.GCSConfig.KeyPrefix {
  1411. return errors.New("GCS key prefix mismatch")
  1412. }
  1413. if expected.GCSConfig.AutomaticCredentials != actual.GCSConfig.AutomaticCredentials {
  1414. return errors.New("GCS automatic credentials mismatch")
  1415. }
  1416. return nil
  1417. }
  1418. func compareHTTPFsConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  1419. if expected.HTTPConfig.Endpoint != actual.HTTPConfig.Endpoint {
  1420. return errors.New("HTTPFs endpoint mismatch")
  1421. }
  1422. if expected.HTTPConfig.Username != actual.HTTPConfig.Username {
  1423. return errors.New("HTTPFs username mismatch")
  1424. }
  1425. if expected.HTTPConfig.SkipTLSVerify != actual.HTTPConfig.SkipTLSVerify {
  1426. return errors.New("HTTPFs skip_tls_verify mismatch")
  1427. }
  1428. if err := checkEncryptedSecret(expected.HTTPConfig.Password, actual.HTTPConfig.Password); err != nil {
  1429. return fmt.Errorf("HTTPFs password mismatch: %v", err)
  1430. }
  1431. if err := checkEncryptedSecret(expected.HTTPConfig.APIKey, actual.HTTPConfig.APIKey); err != nil {
  1432. return fmt.Errorf("HTTPFs API key mismatch: %v", err)
  1433. }
  1434. return nil
  1435. }
  1436. func compareSFTPFsConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  1437. if expected.SFTPConfig.Endpoint != actual.SFTPConfig.Endpoint {
  1438. return errors.New("SFTPFs endpoint mismatch")
  1439. }
  1440. if expected.SFTPConfig.Username != actual.SFTPConfig.Username {
  1441. return errors.New("SFTPFs username mismatch")
  1442. }
  1443. if expected.SFTPConfig.DisableCouncurrentReads != actual.SFTPConfig.DisableCouncurrentReads {
  1444. return errors.New("SFTPFs disable_concurrent_reads mismatch")
  1445. }
  1446. if expected.SFTPConfig.BufferSize != actual.SFTPConfig.BufferSize {
  1447. return errors.New("SFTPFs buffer_size mismatch")
  1448. }
  1449. if err := checkEncryptedSecret(expected.SFTPConfig.Password, actual.SFTPConfig.Password); err != nil {
  1450. return fmt.Errorf("SFTPFs password mismatch: %v", err)
  1451. }
  1452. if err := checkEncryptedSecret(expected.SFTPConfig.PrivateKey, actual.SFTPConfig.PrivateKey); err != nil {
  1453. return fmt.Errorf("SFTPFs private key mismatch: %v", err)
  1454. }
  1455. if err := checkEncryptedSecret(expected.SFTPConfig.KeyPassphrase, actual.SFTPConfig.KeyPassphrase); err != nil {
  1456. return fmt.Errorf("SFTPFs private key passphrase mismatch: %v", err)
  1457. }
  1458. if expected.SFTPConfig.Prefix != actual.SFTPConfig.Prefix {
  1459. if expected.SFTPConfig.Prefix != "" && actual.SFTPConfig.Prefix != "/" {
  1460. return errors.New("SFTPFs prefix mismatch")
  1461. }
  1462. }
  1463. if len(expected.SFTPConfig.Fingerprints) != len(actual.SFTPConfig.Fingerprints) {
  1464. return errors.New("SFTPFs fingerprints mismatch")
  1465. }
  1466. for _, value := range actual.SFTPConfig.Fingerprints {
  1467. if !util.Contains(expected.SFTPConfig.Fingerprints, value) {
  1468. return errors.New("SFTPFs fingerprints mismatch")
  1469. }
  1470. }
  1471. return nil
  1472. }
  1473. func compareAzBlobConfig(expected *vfs.Filesystem, actual *vfs.Filesystem) error {
  1474. if expected.AzBlobConfig.Container != actual.AzBlobConfig.Container {
  1475. return errors.New("azure Blob container mismatch")
  1476. }
  1477. if expected.AzBlobConfig.AccountName != actual.AzBlobConfig.AccountName {
  1478. return errors.New("azure Blob account name mismatch")
  1479. }
  1480. if err := checkEncryptedSecret(expected.AzBlobConfig.AccountKey, actual.AzBlobConfig.AccountKey); err != nil {
  1481. return fmt.Errorf("azure Blob account key mismatch: %v", err)
  1482. }
  1483. if expected.AzBlobConfig.Endpoint != actual.AzBlobConfig.Endpoint {
  1484. return errors.New("azure Blob endpoint mismatch")
  1485. }
  1486. if err := checkEncryptedSecret(expected.AzBlobConfig.SASURL, actual.AzBlobConfig.SASURL); err != nil {
  1487. return fmt.Errorf("azure Blob SAS URL mismatch: %v", err)
  1488. }
  1489. if expected.AzBlobConfig.UploadPartSize != actual.AzBlobConfig.UploadPartSize {
  1490. return errors.New("azure Blob upload part size mismatch")
  1491. }
  1492. if expected.AzBlobConfig.UploadConcurrency != actual.AzBlobConfig.UploadConcurrency {
  1493. return errors.New("azure Blob upload concurrency mismatch")
  1494. }
  1495. if expected.AzBlobConfig.DownloadPartSize != actual.AzBlobConfig.DownloadPartSize {
  1496. return errors.New("azure Blob download part size mismatch")
  1497. }
  1498. if expected.AzBlobConfig.DownloadConcurrency != actual.AzBlobConfig.DownloadConcurrency {
  1499. return errors.New("azure Blob download concurrency mismatch")
  1500. }
  1501. if expected.AzBlobConfig.KeyPrefix != actual.AzBlobConfig.KeyPrefix &&
  1502. expected.AzBlobConfig.KeyPrefix+"/" != actual.AzBlobConfig.KeyPrefix {
  1503. return errors.New("azure Blob key prefix mismatch")
  1504. }
  1505. if expected.AzBlobConfig.UseEmulator != actual.AzBlobConfig.UseEmulator {
  1506. return errors.New("azure Blob use emulator mismatch")
  1507. }
  1508. if expected.AzBlobConfig.AccessTier != actual.AzBlobConfig.AccessTier {
  1509. return errors.New("azure Blob access tier mismatch")
  1510. }
  1511. return nil
  1512. }
  1513. func areSecretEquals(expected, actual *kms.Secret) bool {
  1514. if expected == nil && actual == nil {
  1515. return true
  1516. }
  1517. if expected != nil && expected.IsEmpty() && actual == nil {
  1518. return true
  1519. }
  1520. if actual != nil && actual.IsEmpty() && expected == nil {
  1521. return true
  1522. }
  1523. return false
  1524. }
  1525. func checkEncryptedSecret(expected, actual *kms.Secret) error {
  1526. if areSecretEquals(expected, actual) {
  1527. return nil
  1528. }
  1529. if expected == nil && actual != nil && !actual.IsEmpty() {
  1530. return errors.New("secret mismatch")
  1531. }
  1532. if actual == nil && expected != nil && !expected.IsEmpty() {
  1533. return errors.New("secret mismatch")
  1534. }
  1535. if expected.IsPlain() && actual.IsEncrypted() {
  1536. if actual.GetPayload() == "" {
  1537. return errors.New("invalid secret payload")
  1538. }
  1539. if actual.GetAdditionalData() != "" {
  1540. return errors.New("invalid secret additional data")
  1541. }
  1542. if actual.GetKey() != "" {
  1543. return errors.New("invalid secret key")
  1544. }
  1545. } else {
  1546. if expected.GetStatus() != actual.GetStatus() || expected.GetPayload() != actual.GetPayload() {
  1547. return errors.New("secret mismatch")
  1548. }
  1549. }
  1550. return nil
  1551. }
  1552. func compareUserFilterSubStructs(expected sdk.BaseUserFilters, actual sdk.BaseUserFilters) error {
  1553. for _, IPMask := range expected.AllowedIP {
  1554. if !util.Contains(actual.AllowedIP, IPMask) {
  1555. return errors.New("allowed IP contents mismatch")
  1556. }
  1557. }
  1558. for _, IPMask := range expected.DeniedIP {
  1559. if !util.Contains(actual.DeniedIP, IPMask) {
  1560. return errors.New("denied IP contents mismatch")
  1561. }
  1562. }
  1563. for _, method := range expected.DeniedLoginMethods {
  1564. if !util.Contains(actual.DeniedLoginMethods, method) {
  1565. return errors.New("denied login methods contents mismatch")
  1566. }
  1567. }
  1568. for _, protocol := range expected.DeniedProtocols {
  1569. if !util.Contains(actual.DeniedProtocols, protocol) {
  1570. return errors.New("denied protocols contents mismatch")
  1571. }
  1572. }
  1573. for _, options := range expected.WebClient {
  1574. if !util.Contains(actual.WebClient, options) {
  1575. return errors.New("web client options contents mismatch")
  1576. }
  1577. }
  1578. return compareUserFiltersEqualFields(expected, actual)
  1579. }
  1580. func compareUserFiltersEqualFields(expected sdk.BaseUserFilters, actual sdk.BaseUserFilters) error {
  1581. if expected.Hooks.ExternalAuthDisabled != actual.Hooks.ExternalAuthDisabled {
  1582. return errors.New("external_auth_disabled hook mismatch")
  1583. }
  1584. if expected.Hooks.PreLoginDisabled != actual.Hooks.PreLoginDisabled {
  1585. return errors.New("pre_login_disabled hook mismatch")
  1586. }
  1587. if expected.Hooks.CheckPasswordDisabled != actual.Hooks.CheckPasswordDisabled {
  1588. return errors.New("check_password_disabled hook mismatch")
  1589. }
  1590. if expected.DisableFsChecks != actual.DisableFsChecks {
  1591. return errors.New("disable_fs_checks mismatch")
  1592. }
  1593. if expected.StartDirectory != actual.StartDirectory {
  1594. return errors.New("start_directory mismatch")
  1595. }
  1596. return nil
  1597. }
  1598. func compareUserFilters(expected sdk.BaseUserFilters, actual sdk.BaseUserFilters) error {
  1599. if len(expected.AllowedIP) != len(actual.AllowedIP) {
  1600. return errors.New("allowed IP mismatch")
  1601. }
  1602. if len(expected.DeniedIP) != len(actual.DeniedIP) {
  1603. return errors.New("denied IP mismatch")
  1604. }
  1605. if len(expected.DeniedLoginMethods) != len(actual.DeniedLoginMethods) {
  1606. return errors.New("denied login methods mismatch")
  1607. }
  1608. if len(expected.DeniedProtocols) != len(actual.DeniedProtocols) {
  1609. return errors.New("denied protocols mismatch")
  1610. }
  1611. if expected.MaxUploadFileSize != actual.MaxUploadFileSize {
  1612. return errors.New("max upload file size mismatch")
  1613. }
  1614. if expected.TLSUsername != actual.TLSUsername {
  1615. return errors.New("TLSUsername mismatch")
  1616. }
  1617. if len(expected.WebClient) != len(actual.WebClient) {
  1618. return errors.New("WebClient filter mismatch")
  1619. }
  1620. if expected.AllowAPIKeyAuth != actual.AllowAPIKeyAuth {
  1621. return errors.New("allow_api_key_auth mismatch")
  1622. }
  1623. if expected.ExternalAuthCacheTime != actual.ExternalAuthCacheTime {
  1624. return errors.New("external_auth_cache_time mismatch")
  1625. }
  1626. if err := compareUserFilterSubStructs(expected, actual); err != nil {
  1627. return err
  1628. }
  1629. if err := compareUserBandwidthLimitFilters(expected, actual); err != nil {
  1630. return err
  1631. }
  1632. if err := compareUserDataTransferLimitFilters(expected, actual); err != nil {
  1633. return err
  1634. }
  1635. return compareUserFilePatternsFilters(expected, actual)
  1636. }
  1637. func checkFilterMatch(expected []string, actual []string) bool {
  1638. if len(expected) != len(actual) {
  1639. return false
  1640. }
  1641. for _, e := range expected {
  1642. if !util.Contains(actual, strings.ToLower(e)) {
  1643. return false
  1644. }
  1645. }
  1646. return true
  1647. }
  1648. func compareUserDataTransferLimitFilters(expected sdk.BaseUserFilters, actual sdk.BaseUserFilters) error {
  1649. if len(expected.DataTransferLimits) != len(actual.DataTransferLimits) {
  1650. return errors.New("data transfer limits filters mismatch")
  1651. }
  1652. for idx, l := range expected.DataTransferLimits {
  1653. if actual.DataTransferLimits[idx].UploadDataTransfer != l.UploadDataTransfer {
  1654. return errors.New("data transfer limit upload_data_transfer mismatch")
  1655. }
  1656. if actual.DataTransferLimits[idx].DownloadDataTransfer != l.DownloadDataTransfer {
  1657. return errors.New("data transfer limit download_data_transfer mismatch")
  1658. }
  1659. if actual.DataTransferLimits[idx].TotalDataTransfer != l.TotalDataTransfer {
  1660. return errors.New("data transfer limit total_data_transfer mismatch")
  1661. }
  1662. for _, source := range actual.DataTransferLimits[idx].Sources {
  1663. if !util.Contains(l.Sources, source) {
  1664. return errors.New("data transfer limit source mismatch")
  1665. }
  1666. }
  1667. }
  1668. return nil
  1669. }
  1670. func compareUserBandwidthLimitFilters(expected sdk.BaseUserFilters, actual sdk.BaseUserFilters) error {
  1671. if len(expected.BandwidthLimits) != len(actual.BandwidthLimits) {
  1672. return errors.New("bandwidth limits filters mismatch")
  1673. }
  1674. for idx, l := range expected.BandwidthLimits {
  1675. if actual.BandwidthLimits[idx].UploadBandwidth != l.UploadBandwidth {
  1676. return errors.New("bandwidth filters upload_bandwidth mismatch")
  1677. }
  1678. if actual.BandwidthLimits[idx].DownloadBandwidth != l.DownloadBandwidth {
  1679. return errors.New("bandwidth filters download_bandwidth mismatch")
  1680. }
  1681. if len(actual.BandwidthLimits[idx].Sources) != len(l.Sources) {
  1682. return errors.New("bandwidth filters sources mismatch")
  1683. }
  1684. for _, source := range actual.BandwidthLimits[idx].Sources {
  1685. if !util.Contains(l.Sources, source) {
  1686. return errors.New("bandwidth filters source mismatch")
  1687. }
  1688. }
  1689. }
  1690. return nil
  1691. }
  1692. func compareUserFilePatternsFilters(expected sdk.BaseUserFilters, actual sdk.BaseUserFilters) error {
  1693. if len(expected.FilePatterns) != len(actual.FilePatterns) {
  1694. return errors.New("file patterns mismatch")
  1695. }
  1696. for _, f := range expected.FilePatterns {
  1697. found := false
  1698. for _, f1 := range actual.FilePatterns {
  1699. if path.Clean(f.Path) == path.Clean(f1.Path) && f.DenyPolicy == f1.DenyPolicy {
  1700. if !checkFilterMatch(f.AllowedPatterns, f1.AllowedPatterns) ||
  1701. !checkFilterMatch(f.DeniedPatterns, f1.DeniedPatterns) {
  1702. return errors.New("file patterns contents mismatch")
  1703. }
  1704. found = true
  1705. }
  1706. }
  1707. if !found {
  1708. return errors.New("file patterns contents mismatch")
  1709. }
  1710. }
  1711. return nil
  1712. }
  1713. func compareEqualGroupSettingsFields(expected sdk.BaseGroupUserSettings, actual sdk.BaseGroupUserSettings) error {
  1714. if expected.HomeDir != actual.HomeDir {
  1715. return errors.New("home dir mismatch")
  1716. }
  1717. if expected.MaxSessions != actual.MaxSessions {
  1718. return errors.New("MaxSessions mismatch")
  1719. }
  1720. if expected.QuotaSize != actual.QuotaSize {
  1721. return errors.New("QuotaSize mismatch")
  1722. }
  1723. if expected.QuotaFiles != actual.QuotaFiles {
  1724. return errors.New("QuotaFiles mismatch")
  1725. }
  1726. if expected.UploadBandwidth != actual.UploadBandwidth {
  1727. return errors.New("UploadBandwidth mismatch")
  1728. }
  1729. if expected.DownloadBandwidth != actual.DownloadBandwidth {
  1730. return errors.New("DownloadBandwidth mismatch")
  1731. }
  1732. if expected.UploadDataTransfer != actual.UploadDataTransfer {
  1733. return errors.New("upload_data_transfer mismatch")
  1734. }
  1735. if expected.DownloadDataTransfer != actual.DownloadDataTransfer {
  1736. return errors.New("download_data_transfer mismatch")
  1737. }
  1738. if expected.TotalDataTransfer != actual.TotalDataTransfer {
  1739. return errors.New("total_data_transfer mismatch")
  1740. }
  1741. return compareUserPermissions(expected.Permissions, actual.Permissions)
  1742. }
  1743. func compareEqualsUserFields(expected *dataprovider.User, actual *dataprovider.User) error {
  1744. if dataprovider.ConvertName(expected.Username) != actual.Username {
  1745. return errors.New("username mismatch")
  1746. }
  1747. if expected.HomeDir != actual.HomeDir {
  1748. return errors.New("home dir mismatch")
  1749. }
  1750. if expected.UID != actual.UID {
  1751. return errors.New("UID mismatch")
  1752. }
  1753. if expected.GID != actual.GID {
  1754. return errors.New("GID mismatch")
  1755. }
  1756. if expected.MaxSessions != actual.MaxSessions {
  1757. return errors.New("MaxSessions mismatch")
  1758. }
  1759. if len(expected.Permissions) != len(actual.Permissions) {
  1760. return errors.New("permissions mismatch")
  1761. }
  1762. if expected.UploadBandwidth != actual.UploadBandwidth {
  1763. return errors.New("UploadBandwidth mismatch")
  1764. }
  1765. if expected.DownloadBandwidth != actual.DownloadBandwidth {
  1766. return errors.New("DownloadBandwidth mismatch")
  1767. }
  1768. if expected.Status != actual.Status {
  1769. return errors.New("status mismatch")
  1770. }
  1771. if expected.ExpirationDate != actual.ExpirationDate {
  1772. return errors.New("ExpirationDate mismatch")
  1773. }
  1774. if expected.AdditionalInfo != actual.AdditionalInfo {
  1775. return errors.New("AdditionalInfo mismatch")
  1776. }
  1777. if expected.Description != actual.Description {
  1778. return errors.New("description mismatch")
  1779. }
  1780. return compareQuotaUserFields(expected, actual)
  1781. }
  1782. func compareQuotaUserFields(expected *dataprovider.User, actual *dataprovider.User) error {
  1783. if expected.QuotaSize != actual.QuotaSize {
  1784. return errors.New("QuotaSize mismatch")
  1785. }
  1786. if expected.QuotaFiles != actual.QuotaFiles {
  1787. return errors.New("QuotaFiles mismatch")
  1788. }
  1789. if expected.UploadDataTransfer != actual.UploadDataTransfer {
  1790. return errors.New("upload_data_transfer mismatch")
  1791. }
  1792. if expected.DownloadDataTransfer != actual.DownloadDataTransfer {
  1793. return errors.New("download_data_transfer mismatch")
  1794. }
  1795. if expected.TotalDataTransfer != actual.TotalDataTransfer {
  1796. return errors.New("total_data_transfer mismatch")
  1797. }
  1798. return nil
  1799. }
  1800. func addLimitAndOffsetQueryParams(rawurl string, limit, offset int64) (*url.URL, error) {
  1801. url, err := url.Parse(rawurl)
  1802. if err != nil {
  1803. return nil, err
  1804. }
  1805. q := url.Query()
  1806. if limit > 0 {
  1807. q.Add("limit", strconv.FormatInt(limit, 10))
  1808. }
  1809. if offset > 0 {
  1810. q.Add("offset", strconv.FormatInt(offset, 10))
  1811. }
  1812. url.RawQuery = q.Encode()
  1813. return url, err
  1814. }
  1815. func addModeQueryParam(rawurl, mode string) (*url.URL, error) {
  1816. url, err := url.Parse(rawurl)
  1817. if err != nil {
  1818. return nil, err
  1819. }
  1820. q := url.Query()
  1821. if len(mode) > 0 {
  1822. q.Add("mode", mode)
  1823. }
  1824. url.RawQuery = q.Encode()
  1825. return url, err
  1826. }
  1827. func addDisconnectQueryParam(rawurl, disconnect string) (*url.URL, error) {
  1828. url, err := url.Parse(rawurl)
  1829. if err != nil {
  1830. return nil, err
  1831. }
  1832. q := url.Query()
  1833. if len(disconnect) > 0 {
  1834. q.Add("disconnect", disconnect)
  1835. }
  1836. url.RawQuery = q.Encode()
  1837. return url, err
  1838. }