@budibase/server 1.0.47 → 1.0.49-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/builder/assets/{index.937b2767.js → index.82e12602.js} +234 -231
  2. package/builder/assets/index.83c0944f.css +1 -0
  3. package/builder/assets/{vendor.f9faf6ab.js → vendor.d526bfe8.js} +12 -12
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +2334 -2013
  6. package/coverage/coverage-final.json +100 -95
  7. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/application.js.html +210 -126
  10. package/coverage/lcov-report/api/controllers/auth.js.html +18 -6
  11. package/coverage/lcov-report/api/controllers/automation.js.html +21 -21
  12. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +72 -36
  16. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/index.html +53 -53
  18. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/layout.js.html +6 -6
  20. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/migrations.js.html +119 -0
  22. package/coverage/lcov-report/api/controllers/permission.js.html +15 -15
  23. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  24. package/coverage/lcov-report/api/controllers/query/index.js.html +19 -19
  25. package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -6
  26. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  27. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/external.js.html +16 -16
  29. package/coverage/lcov-report/api/controllers/row/index.html +48 -33
  30. package/coverage/lcov-report/api/controllers/row/index.js.html +26 -26
  31. package/coverage/lcov-report/api/controllers/row/internal.js.html +95 -158
  32. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +41 -17
  33. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +551 -0
  34. package/coverage/lcov-report/api/controllers/row/utils.js.html +39 -57
  35. package/coverage/lcov-report/api/controllers/screen.js.html +7 -7
  36. package/coverage/lcov-report/api/controllers/script.js.html +4 -4
  37. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +629 -0
  38. package/coverage/lcov-report/api/controllers/table/external.js.html +12 -12
  39. package/coverage/lcov-report/api/controllers/table/index.html +49 -34
  40. package/coverage/lcov-report/api/controllers/table/index.js.html +53 -77
  41. package/coverage/lcov-report/api/controllers/table/internal.js.html +59 -35
  42. package/coverage/lcov-report/api/controllers/table/utils.js.html +283 -76
  43. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/user.js.html +30 -24
  45. package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
  46. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  47. package/coverage/lcov-report/api/controllers/view/index.js.html +14 -14
  48. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  49. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
  50. package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
  51. package/coverage/lcov-report/api/index.html +1 -1
  52. package/coverage/lcov-report/api/index.js.html +8 -8
  53. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/index.html +18 -18
  65. package/coverage/lcov-report/api/routes/index.js.html +3 -3
  66. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/{hosting.js.html → migrations.js.html} +17 -14
  70. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/static.js.html +17 -2
  78. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  82. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  83. package/coverage/lcov-report/automations/actions.js.html +24 -24
  84. package/coverage/lcov-report/automations/automationUtils.js.html +5 -5
  85. package/coverage/lcov-report/automations/bullboard.js.html +16 -16
  86. package/coverage/lcov-report/automations/index.html +1 -1
  87. package/coverage/lcov-report/automations/index.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
  89. package/coverage/lcov-report/automations/steps/createRow.js.html +13 -19
  90. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  91. package/coverage/lcov-report/automations/steps/deleteRow.js.html +11 -20
  92. package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
  93. package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
  94. package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
  95. package/coverage/lcov-report/automations/steps/filter.js.html +7 -7
  96. package/coverage/lcov-report/automations/steps/index.html +13 -13
  97. package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
  98. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
  99. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -10
  100. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
  101. package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
  102. package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
  103. package/coverage/lcov-report/automations/steps/updateRow.js.html +6 -6
  104. package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
  105. package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
  106. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  107. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  108. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  110. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  113. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggers.js.html +18 -18
  115. package/coverage/lcov-report/automations/utils.js.html +32 -32
  116. package/coverage/lcov-report/constants/index.html +11 -11
  117. package/coverage/lcov-report/constants/index.js.html +68 -32
  118. package/coverage/lcov-report/constants/layouts.js.html +5 -5
  119. package/coverage/lcov-report/constants/screens.js.html +4 -4
  120. package/coverage/lcov-report/db/client.js.html +18 -18
  121. package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
  122. package/coverage/lcov-report/db/index.html +15 -15
  123. package/coverage/lcov-report/db/index.js.html +3 -3
  124. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +18 -18
  125. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  126. package/coverage/lcov-report/db/linkedRows/index.js.html +38 -41
  127. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
  128. package/coverage/lcov-report/db/newid.js.html +4 -4
  129. package/coverage/lcov-report/db/utils.js.html +86 -86
  130. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  131. package/coverage/lcov-report/events/index.html +1 -1
  132. package/coverage/lcov-report/events/index.js.html +5 -5
  133. package/coverage/lcov-report/events/utils.js.html +3 -3
  134. package/coverage/lcov-report/index.html +117 -102
  135. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  136. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  137. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  138. package/coverage/lcov-report/middleware/authorized.js.html +166 -64
  139. package/coverage/lcov-report/middleware/builder.js.html +8 -8
  140. package/coverage/lcov-report/middleware/currentapp.js.html +29 -29
  141. package/coverage/lcov-report/middleware/index.html +30 -30
  142. package/coverage/lcov-report/middleware/joi-validator.js.html +3 -3
  143. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  144. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  145. package/coverage/lcov-report/middleware/usageQuota.js.html +270 -42
  146. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  147. package/coverage/lcov-report/threads/automation.js.html +1 -1
  148. package/coverage/lcov-report/threads/index.html +15 -15
  149. package/coverage/lcov-report/threads/index.js.html +10 -10
  150. package/coverage/lcov-report/threads/query.js.html +23 -8
  151. package/coverage/lcov-report/threads/utils.js.html +14 -14
  152. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  153. package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
  154. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  155. package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
  156. package/coverage/lcov-report/utilities/global.js.html +26 -26
  157. package/coverage/lcov-report/utilities/index.html +19 -19
  158. package/coverage/lcov-report/utilities/index.js.html +25 -25
  159. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +8 -8
  160. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  161. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  162. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  163. package/coverage/lcov-report/utilities/rowProcessor/index.html +27 -27
  164. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +115 -79
  165. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +75 -27
  166. package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
  167. package/coverage/lcov-report/utilities/security.js.html +8 -8
  168. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  169. package/coverage/lcov-report/utilities/usageQuota/index.html +141 -0
  170. package/coverage/lcov-report/utilities/usageQuota/index.js.html +356 -0
  171. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +302 -0
  172. package/coverage/lcov-report/{api/controllers/hosting.js.html → utilities/usageQuota/usageQuoteReset.js.html} +36 -48
  173. package/coverage/lcov-report/utilities/users.js.html +5 -5
  174. package/coverage/lcov-report/utilities/workerRequests.js.html +39 -108
  175. package/coverage/lcov.info +3665 -3040
  176. package/dist/api/controllers/application.js +61 -34
  177. package/dist/api/controllers/auth.js +4 -0
  178. package/dist/api/controllers/datasource.js +11 -0
  179. package/dist/api/controllers/migrations.js +21 -0
  180. package/dist/api/controllers/row/ExternalRequest.js +2 -1
  181. package/dist/api/controllers/row/internal.js +30 -53
  182. package/dist/api/controllers/row/internalSearch.js +12 -4
  183. package/dist/api/controllers/row/staticFormula.js +147 -0
  184. package/dist/api/controllers/row/utils.js +6 -8
  185. package/dist/api/controllers/static/index.js +59 -10
  186. package/dist/api/controllers/table/bulkFormula.js +160 -0
  187. package/dist/api/controllers/table/index.js +12 -9
  188. package/dist/api/controllers/table/internal.js +9 -0
  189. package/dist/api/controllers/table/utils.js +56 -14
  190. package/dist/api/controllers/user.js +2 -0
  191. package/dist/api/routes/index.js +2 -2
  192. package/dist/api/routes/migrations.js +9 -0
  193. package/dist/api/routes/static.js +2 -1
  194. package/dist/app.js +35 -5
  195. package/dist/automations/steps/createRow.js +2 -4
  196. package/dist/automations/steps/deleteRow.js +1 -4
  197. package/dist/constants/index.js +12 -5
  198. package/dist/db/linkedRows/index.js +4 -5
  199. package/dist/definitions/datasource.js +1 -0
  200. package/dist/environment.js +2 -0
  201. package/dist/integrations/base/sql.js +13 -6
  202. package/dist/integrations/googlesheets.js +340 -0
  203. package/dist/integrations/index.js +3 -0
  204. package/dist/integrations/utils.js +57 -8
  205. package/dist/middleware/authorized.js +55 -30
  206. package/dist/middleware/usageQuota.js +80 -13
  207. package/dist/migrations/functions/appUrls.js +37 -0
  208. package/dist/migrations/functions/quotas1.js +50 -0
  209. package/dist/migrations/functions/usageQuotas/index.js +20 -0
  210. package/dist/migrations/functions/usageQuotas/syncApps.js +32 -0
  211. package/dist/migrations/functions/usageQuotas/syncRows.js +35 -0
  212. package/dist/migrations/functions/userEmailViewCasing.js +24 -0
  213. package/dist/migrations/index.js +62 -0
  214. package/dist/package.json +8 -5
  215. package/dist/tests/utilities/TestConfiguration.js +13 -6
  216. package/dist/threads/query.js +4 -0
  217. package/dist/tsconfig.tsbuildinfo +1 -1
  218. package/dist/utilities/rowProcessor/index.js +18 -11
  219. package/dist/utilities/rowProcessor/utils.js +14 -4
  220. package/dist/utilities/{usageQuota.js → usageQuota/index.js} +44 -31
  221. package/dist/utilities/usageQuota/rows.js +77 -0
  222. package/dist/utilities/usageQuota/usageQuoteReset.js +16 -0
  223. package/dist/utilities/workerRequests.js +0 -20
  224. package/nodemon.json +1 -1
  225. package/package.json +9 -6
  226. package/src/api/controllers/application.js +52 -24
  227. package/src/api/controllers/auth.js +4 -0
  228. package/src/api/controllers/datasource.js +12 -0
  229. package/src/api/controllers/migrations.js +13 -0
  230. package/src/api/controllers/row/ExternalRequest.ts +1 -1
  231. package/src/api/controllers/row/internal.js +29 -50
  232. package/src/api/controllers/row/internalSearch.js +12 -4
  233. package/src/api/controllers/row/staticFormula.js +157 -0
  234. package/src/api/controllers/row/utils.js +6 -12
  235. package/src/api/controllers/static/index.js +63 -10
  236. package/src/api/controllers/table/bulkFormula.js +183 -0
  237. package/src/api/controllers/table/index.js +16 -24
  238. package/src/api/controllers/table/internal.js +9 -1
  239. package/src/api/controllers/table/utils.js +80 -11
  240. package/src/api/controllers/user.js +2 -0
  241. package/src/api/routes/index.js +2 -2
  242. package/src/api/routes/migrations.js +14 -0
  243. package/src/api/routes/static.js +5 -0
  244. package/src/api/routes/tests/application.spec.js +2 -2
  245. package/src/api/routes/tests/auth.spec.js +1 -2
  246. package/src/api/routes/tests/static.spec.js +98 -0
  247. package/src/app.ts +18 -5
  248. package/src/automations/steps/createRow.js +2 -4
  249. package/src/automations/steps/deleteRow.js +1 -4
  250. package/src/constants/index.js +17 -5
  251. package/src/db/linkedRows/index.js +4 -5
  252. package/src/definitions/common.ts +3 -0
  253. package/src/definitions/datasource.ts +3 -0
  254. package/src/environment.js +2 -0
  255. package/src/integrations/base/sql.ts +14 -7
  256. package/src/integrations/googlesheets.ts +353 -0
  257. package/src/integrations/index.ts +3 -0
  258. package/src/integrations/tests/sql.spec.js +16 -0
  259. package/src/integrations/utils.ts +71 -10
  260. package/src/middleware/authorized.js +64 -30
  261. package/src/middleware/tests/authorized.spec.js +6 -4
  262. package/src/middleware/tests/usageQuota.spec.js +18 -24
  263. package/src/middleware/usageQuota.js +91 -15
  264. package/src/migrations/functions/appUrls.ts +25 -0
  265. package/src/migrations/functions/quotas1.ts +20 -0
  266. package/src/migrations/functions/tests/appUrls.spec.js +29 -0
  267. package/src/migrations/functions/tests/quotas1.spec.js +27 -0
  268. package/src/migrations/functions/tests/userEmailViewCasing.spec.js +25 -0
  269. package/src/migrations/functions/usageQuotas/index.ts +8 -0
  270. package/src/migrations/functions/usageQuotas/syncApps.ts +18 -0
  271. package/src/migrations/functions/usageQuotas/syncRows.ts +21 -0
  272. package/src/migrations/functions/usageQuotas/tests/syncApps.spec.js +37 -0
  273. package/src/migrations/functions/usageQuotas/tests/syncRows.spec.js +43 -0
  274. package/src/migrations/functions/userEmailViewCasing.ts +13 -0
  275. package/src/migrations/index.ts +56 -0
  276. package/src/tests/utilities/TestConfiguration.js +14 -6
  277. package/src/threads/query.js +5 -0
  278. package/src/utilities/rowProcessor/index.js +24 -12
  279. package/src/utilities/rowProcessor/utils.js +23 -7
  280. package/src/utilities/tests/usageQuota/usageQuota.spec.js +72 -0
  281. package/src/utilities/usageQuota/index.js +92 -0
  282. package/src/utilities/usageQuota/rows.js +74 -0
  283. package/src/utilities/usageQuota/usageQuoteReset.js +18 -0
  284. package/src/utilities/workerRequests.js +0 -23
  285. package/builder/assets/index.39e01193.css +0 -1
  286. package/dist/api/controllers/hosting.js +0 -30
  287. package/dist/api/routes/hosting.js +0 -11
  288. package/src/api/controllers/hosting.js +0 -22
  289. package/src/api/routes/hosting.js +0 -13
  290. package/src/api/routes/tests/hosting.spec.js +0 -36
  291. package/src/utilities/usageQuota.js +0 -73
@@ -9,11 +9,59 @@ const {
9
9
  } = require("@budibase/backend-core/permissions")
10
10
  const builderMiddleware = require("./builder")
11
11
  const { isWebhookEndpoint } = require("./utils")
12
+ const { buildCsrfMiddleware } = require("@budibase/backend-core/auth")
12
13
 
13
14
  function hasResource(ctx) {
14
15
  return ctx.resourceId != null
15
16
  }
16
17
 
18
+ const csrf = buildCsrfMiddleware()
19
+
20
+ /**
21
+ * Apply authorization to the requested resource:
22
+ * - If this is a builder resource the user must be a builder.
23
+ * - Builders can access all resources.
24
+ * - Otherwise the user must have the required role.
25
+ */
26
+ const checkAuthorized = async (ctx, resourceRoles, permType, permLevel) => {
27
+ // check if this is a builder api and the user is not a builder
28
+ const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
29
+ const isBuilderApi = permType === PermissionTypes.BUILDER
30
+ if (isBuilderApi && !isBuilder) {
31
+ return ctx.throw(403, "Not Authorized")
32
+ }
33
+
34
+ // check for resource authorization
35
+ if (!isBuilder) {
36
+ await checkAuthorizedResource(ctx, resourceRoles, permType, permLevel)
37
+ }
38
+ }
39
+
40
+ const checkAuthorizedResource = async (
41
+ ctx,
42
+ resourceRoles,
43
+ permType,
44
+ permLevel
45
+ ) => {
46
+ // get the user's roles
47
+ const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
48
+ const userRoles = await getUserRoleHierarchy(ctx.appId, roleId, {
49
+ idOnly: false,
50
+ })
51
+ const permError = "User does not have permission"
52
+ // check if the user has the required role
53
+ if (resourceRoles.length > 0) {
54
+ // deny access if the user doesn't have the required resource role
55
+ const found = userRoles.find(role => resourceRoles.indexOf(role._id) !== -1)
56
+ if (!found) {
57
+ ctx.throw(403, permError)
58
+ }
59
+ // fallback to the base permissions when no resource roles are found
60
+ } else if (!doesHaveBasePermission(permType, permLevel, userRoles)) {
61
+ ctx.throw(403, permError)
62
+ }
63
+ }
64
+
17
65
  module.exports =
18
66
  (permType, permLevel = null) =>
19
67
  async (ctx, next) => {
@@ -31,40 +79,26 @@ module.exports =
31
79
  // to find API endpoints which are builder focused
32
80
  await builderMiddleware(ctx, permType)
33
81
 
34
- const isAuthed = ctx.isAuthenticated
35
- // builders for now have permission to do anything
36
- let isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
37
- const isBuilderApi = permType === PermissionTypes.BUILDER
38
- if (isBuilder) {
39
- return next()
40
- } else if (isBuilderApi && !isBuilder) {
41
- return ctx.throw(403, "Not Authorized")
82
+ // get the resource roles
83
+ let resourceRoles = []
84
+ if (ctx.appId && hasResource(ctx)) {
85
+ resourceRoles = await getRequiredResourceRole(ctx.appId, permLevel, ctx)
42
86
  }
43
87
 
44
- // need to check this first, in-case public access, don't check authed until last
45
- const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
46
- const hierarchy = await getUserRoleHierarchy(ctx.appId, roleId, {
47
- idOnly: false,
48
- })
49
- const permError = "User does not have permission"
50
- let possibleRoleIds = []
51
- if (hasResource(ctx)) {
52
- possibleRoleIds = await getRequiredResourceRole(ctx.appId, permLevel, ctx)
53
- }
54
- // check if we found a role, if not fallback to base permissions
55
- if (possibleRoleIds.length > 0) {
56
- const found = hierarchy.find(
57
- role => possibleRoleIds.indexOf(role._id) !== -1
58
- )
59
- return found ? next() : ctx.throw(403, permError)
60
- } else if (!doesHaveBasePermission(permType, permLevel, hierarchy)) {
61
- ctx.throw(403, permError)
88
+ // if the resource is public, proceed
89
+ const isPublicResource = resourceRoles.includes(BUILTIN_ROLE_IDS.PUBLIC)
90
+ if (isPublicResource) {
91
+ return next()
62
92
  }
63
93
 
64
- // if they are not authed, then anything using the authorized middleware will fail
65
- if (!isAuthed) {
66
- ctx.throw(403, "Session not authenticated")
94
+ // check authenticated
95
+ if (!ctx.isAuthenticated) {
96
+ return ctx.throw(403, "Session not authenticated")
67
97
  }
68
98
 
69
- return next()
99
+ // check authorized
100
+ await checkAuthorized(ctx, resourceRoles, permType, permLevel)
101
+
102
+ // csrf protection
103
+ return csrf(ctx, next)
70
104
  }
@@ -17,6 +17,7 @@ class TestConfiguration {
17
17
  this.middleware = authorizedMiddleware(role)
18
18
  this.next = jest.fn()
19
19
  this.throw = jest.fn()
20
+ this.headers = {}
20
21
  this.ctx = {
21
22
  headers: {},
22
23
  request: {
@@ -25,7 +26,8 @@ class TestConfiguration {
25
26
  appId: "",
26
27
  auth: {},
27
28
  next: this.next,
28
- throw: this.throw
29
+ throw: this.throw,
30
+ get: (name) => this.headers[name],
29
31
  }
30
32
  }
31
33
 
@@ -46,7 +48,7 @@ class TestConfiguration {
46
48
  }
47
49
 
48
50
  setAuthenticated(isAuthed) {
49
- this.ctx.auth = { authenticated: isAuthed }
51
+ this.ctx.isAuthenticated = isAuthed
50
52
  }
51
53
 
52
54
  setRequestUrl(url) {
@@ -107,7 +109,7 @@ describe("Authorization middleware", () => {
107
109
  expect(config.next).toHaveBeenCalled()
108
110
  })
109
111
 
110
- it("throws if the user has only builder permissions", async () => {
112
+ it("throws if the user does not have builder permissions", async () => {
111
113
  config.setEnvironment(false)
112
114
  config.setMiddlewareRequiredPermission(PermissionTypes.BUILDER)
113
115
  config.setUser({
@@ -133,7 +135,7 @@ describe("Authorization middleware", () => {
133
135
  expect(config.next).toHaveBeenCalled()
134
136
  })
135
137
 
136
- it("throws if the user session is not authenticated after permission checks", async () => {
138
+ it("throws if the user session is not authenticated", async () => {
137
139
  config.setUser({
138
140
  role: {
139
141
  _id: ""
@@ -1,11 +1,5 @@
1
1
  jest.mock("../../db")
2
2
  jest.mock("../../utilities/usageQuota")
3
- jest.mock("../../environment", () => ({
4
- isTest: () => true,
5
- isProd: () => false,
6
- isDev: () => true,
7
- _set: () => {},
8
- }))
9
3
  jest.mock("@budibase/backend-core/tenancy", () => ({
10
4
  getTenantId: () => "testing123"
11
5
  }))
@@ -29,9 +23,10 @@ class TestConfiguration {
29
23
  },
30
24
  req: {
31
25
  method: "POST",
32
- url: "/rows"
26
+ url: "/applications"
33
27
  }
34
28
  }
29
+ usageQuota.useQuotas = () => true
35
30
  }
36
31
 
37
32
  executeMiddleware() {
@@ -113,7 +108,6 @@ describe("usageQuota middleware", () => {
113
108
 
114
109
  it("calculates and persists the correct usage quota for the relevant action", async () => {
115
110
  config.setUrl("/rows")
116
- config.setProd(true)
117
111
 
118
112
  await config.executeMiddleware()
119
113
 
@@ -121,20 +115,20 @@ describe("usageQuota middleware", () => {
121
115
  expect(config.next).toHaveBeenCalled()
122
116
  })
123
117
 
124
- it("calculates the correct file size from a file upload call and adds it to quota", async () => {
125
- config.setUrl("/upload")
126
- config.setProd(true)
127
- config.setFiles([
128
- {
129
- size: 100
130
- },
131
- {
132
- size: 10000
133
- },
134
- ])
135
- await config.executeMiddleware()
136
-
137
- expect(usageQuota.update).toHaveBeenCalledWith("storage", 10100)
138
- expect(config.next).toHaveBeenCalled()
139
- })
118
+ // it("calculates the correct file size from a file upload call and adds it to quota", async () => {
119
+ // config.setUrl("/upload")
120
+ // config.setProd(true)
121
+ // config.setFiles([
122
+ // {
123
+ // size: 100
124
+ // },
125
+ // {
126
+ // size: 10000
127
+ // },
128
+ // ])
129
+ // await config.executeMiddleware()
130
+
131
+ // expect(usageQuota.update).toHaveBeenCalledWith("storage", 10100)
132
+ // expect(config.next).toHaveBeenCalled()
133
+ // })
140
134
  })
@@ -1,15 +1,11 @@
1
1
  const CouchDB = require("../db")
2
2
  const usageQuota = require("../utilities/usageQuota")
3
- const env = require("../environment")
4
- const { getTenantId } = require("@budibase/backend-core/tenancy")
3
+ const { getUniqueRows } = require("../utilities/usageQuota/rows")
5
4
  const {
6
5
  isExternalTable,
7
6
  isRowId: isExternalRowId,
8
7
  } = require("../integrations/utils")
9
8
 
10
- // tenants without limits
11
- const EXCLUDED_TENANTS = ["bb", "default", "bbtest", "bbstaging"]
12
-
13
9
  // currently only counting new writes and deletes
14
10
  const METHOD_MAP = {
15
11
  POST: 1,
@@ -18,13 +14,13 @@ const METHOD_MAP = {
18
14
 
19
15
  const DOMAIN_MAP = {
20
16
  rows: usageQuota.Properties.ROW,
21
- upload: usageQuota.Properties.UPLOAD,
22
- views: usageQuota.Properties.VIEW,
23
- users: usageQuota.Properties.USER,
17
+ // upload: usageQuota.Properties.UPLOAD, // doesn't work yet
18
+ // views: usageQuota.Properties.VIEW, // doesn't work yet
19
+ // users: usageQuota.Properties.USER, // doesn't work yet
24
20
  applications: usageQuota.Properties.APPS,
25
21
  // this will not be updated by endpoint calls
26
22
  // instead it will be updated by triggerInfo
27
- automationRuns: usageQuota.Properties.AUTOMATION,
23
+ // automationRuns: usageQuota.Properties.AUTOMATION, // doesn't work yet
28
24
  }
29
25
 
30
26
  function getProperty(url) {
@@ -36,10 +32,7 @@ function getProperty(url) {
36
32
  }
37
33
 
38
34
  module.exports = async (ctx, next) => {
39
- const tenantId = getTenantId()
40
-
41
- // if in development or a self hosted cloud usage quotas should not be executed
42
- if (env.isDev() || env.SELF_HOSTED || EXCLUDED_TENANTS.includes(tenantId)) {
35
+ if (!usageQuota.useQuotas()) {
43
36
  return next()
44
37
  }
45
38
 
@@ -80,9 +73,92 @@ module.exports = async (ctx, next) => {
80
73
  usage = files.map(file => file.size).reduce((total, size) => total + size)
81
74
  }
82
75
  try {
83
- await usageQuota.update(property, usage)
84
- return next()
76
+ await performRequest(ctx, next, property, usage)
85
77
  } catch (err) {
86
78
  ctx.throw(400, err)
87
79
  }
88
80
  }
81
+
82
+ const performRequest = async (ctx, next, property, usage) => {
83
+ const usageContext = {
84
+ skipNext: false,
85
+ skipUsage: false,
86
+ [usageQuota.Properties.APPS]: {},
87
+ }
88
+
89
+ if (usage === -1) {
90
+ if (PRE_DELETE[property]) {
91
+ await PRE_DELETE[property](ctx, usageContext)
92
+ }
93
+ } else {
94
+ if (PRE_CREATE[property]) {
95
+ await PRE_CREATE[property](ctx, usageContext)
96
+ }
97
+ }
98
+
99
+ // run the request
100
+ if (!usageContext.skipNext) {
101
+ await usageQuota.update(property, usage, { dryRun: true })
102
+ await next()
103
+ }
104
+
105
+ if (usage === -1) {
106
+ if (POST_DELETE[property]) {
107
+ await POST_DELETE[property](ctx, usageContext)
108
+ }
109
+ } else {
110
+ if (POST_CREATE[property]) {
111
+ await POST_CREATE[property](ctx, usageContext)
112
+ }
113
+ }
114
+
115
+ // update the usage
116
+ if (!usageContext.skipUsage) {
117
+ await usageQuota.update(property, usage)
118
+ }
119
+ }
120
+
121
+ const appPreDelete = async (ctx, usageContext) => {
122
+ if (ctx.query.unpublish) {
123
+ // don't run usage decrement for unpublish
124
+ usageContext.skipUsage = true
125
+ return
126
+ }
127
+
128
+ // store the row count to delete
129
+ const rows = await getUniqueRows([ctx.appId])
130
+ if (rows.length) {
131
+ usageContext[usageQuota.Properties.APPS] = { rowCount: rows.length }
132
+ }
133
+ }
134
+
135
+ const appPostDelete = async (ctx, usageContext) => {
136
+ // delete the app rows from usage
137
+ const rowCount = usageContext[usageQuota.Properties.APPS].rowCount
138
+ if (rowCount) {
139
+ await usageQuota.update(usageQuota.Properties.ROW, -rowCount)
140
+ }
141
+ }
142
+
143
+ const appPostCreate = async ctx => {
144
+ // app import & template creation
145
+ if (ctx.request.body.useTemplate === "true") {
146
+ const rows = await getUniqueRows([ctx.response.body.appId])
147
+ const rowCount = rows ? rows.length : 0
148
+ await usageQuota.update(usageQuota.Properties.ROW, rowCount)
149
+ }
150
+ }
151
+
152
+ const PRE_DELETE = {
153
+ [usageQuota.Properties.APPS]: appPreDelete,
154
+ }
155
+
156
+ const POST_DELETE = {
157
+ [usageQuota.Properties.APPS]: appPostDelete,
158
+ }
159
+
160
+ const PRE_CREATE = {}
161
+
162
+ const POST_CREATE = {
163
+ [usageQuota.Properties.APPS]: appPostCreate,
164
+ }
@@ -0,0 +1,25 @@
1
+ const { DocumentTypes } = require("@budibase/backend-core/db")
2
+ import { getAppUrl } from "../../api/controllers/application"
3
+
4
+ /**
5
+ * Date:
6
+ * January 2022
7
+ *
8
+ * Description:
9
+ * Add the url to the app metadata if it doesn't exist
10
+ */
11
+ export const run = async (appDb: any) => {
12
+ const metadata = await appDb.get(DocumentTypes.APP_METADATA)
13
+ if (!metadata.url) {
14
+ const context = {
15
+ request: {
16
+ body: {
17
+ name: metadata.name,
18
+ },
19
+ },
20
+ }
21
+ metadata.url = getAppUrl(context)
22
+ console.log(`Adding url to app: ${metadata.url}`)
23
+ }
24
+ await appDb.put(metadata)
25
+ }
@@ -0,0 +1,20 @@
1
+ import { runQuotaMigration } from "./usageQuotas"
2
+ import * as syncApps from "./usageQuotas/syncApps"
3
+ import * as syncRows from "./usageQuotas/syncRows"
4
+
5
+ /**
6
+ * Date:
7
+ * January 2022
8
+ *
9
+ * Description:
10
+ * Synchronise the app and row quotas to the state of the db after it was
11
+ * discovered that the quota resets were still in place and the row quotas
12
+ * weren't being decremented correctly.
13
+ */
14
+
15
+ export const run = async () => {
16
+ await runQuotaMigration(async () => {
17
+ await syncApps.run()
18
+ await syncRows.run()
19
+ })
20
+ }
@@ -0,0 +1,29 @@
1
+ const { DocumentTypes } = require("@budibase/backend-core/db")
2
+ const env = require("../../../environment")
3
+ const TestConfig = require("../../../tests/utilities/TestConfiguration")
4
+
5
+ const migration = require("../appUrls")
6
+
7
+ describe("run", () => {
8
+ let config = new TestConfig(false)
9
+ const CouchDB = config.getCouch()
10
+
11
+ beforeEach(async () => {
12
+ await config.init()
13
+ })
14
+
15
+ afterAll(config.end)
16
+
17
+ it("runs successfully", async () => {
18
+ const app = await config.createApp("testApp")
19
+ const appDb = new CouchDB(app.appId)
20
+ let metadata = await appDb.get(DocumentTypes.APP_METADATA)
21
+ delete metadata.url
22
+ await appDb.put(metadata)
23
+
24
+ await migration.run(appDb)
25
+
26
+ metadata = await appDb.get(DocumentTypes.APP_METADATA)
27
+ expect(metadata.url).toEqual("/testapp")
28
+ })
29
+ })
@@ -0,0 +1,27 @@
1
+ const env = require("../../../environment")
2
+ const TestConfig = require("../../../tests/utilities/TestConfiguration")
3
+
4
+ const syncApps = jest.fn()
5
+ const syncRows = jest.fn()
6
+
7
+ jest.mock("../usageQuotas/syncApps", () => ({ run: syncApps }) )
8
+ jest.mock("../usageQuotas/syncRows", () => ({ run: syncRows }) )
9
+
10
+ const migration = require("../quotas1")
11
+
12
+ describe("run", () => {
13
+ let config = new TestConfig(false)
14
+
15
+ beforeEach(async () => {
16
+ await config.init()
17
+ env._set("USE_QUOTAS", 1)
18
+ })
19
+
20
+ afterAll(config.end)
21
+
22
+ it("runs ", async () => {
23
+ await migration.run()
24
+ expect(syncApps).toHaveBeenCalledTimes(1)
25
+ expect(syncRows).toHaveBeenCalledTimes(1)
26
+ })
27
+ })
@@ -0,0 +1,25 @@
1
+ const TestConfig = require("../../../tests/utilities/TestConfiguration")
2
+ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
3
+
4
+ // mock email view creation
5
+ const coreDb = require("@budibase/backend-core/db")
6
+ const createUserEmailView = jest.fn()
7
+ coreDb.createUserEmailView = createUserEmailView
8
+
9
+ const migration = require("../userEmailViewCasing")
10
+
11
+ describe("run", () => {
12
+ let config = new TestConfig(false)
13
+ const globalDb = getGlobalDB()
14
+
15
+ beforeEach(async () => {
16
+ await config.init()
17
+ })
18
+
19
+ afterAll(config.end)
20
+
21
+ it("runs successfully", async () => {
22
+ await migration.run(globalDb)
23
+ expect(createUserEmailView).toHaveBeenCalledTimes(1)
24
+ })
25
+ })
@@ -0,0 +1,8 @@
1
+ const { useQuotas } = require("../../../utilities/usageQuota")
2
+
3
+ export const runQuotaMigration = async (migration: Function) => {
4
+ if (!useQuotas()) {
5
+ return
6
+ }
7
+ await migration()
8
+ }
@@ -0,0 +1,18 @@
1
+ const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
2
+ const { getAllApps } = require("@budibase/backend-core/db")
3
+ import CouchDB from "../../../db"
4
+ import { getUsageQuotaDoc } from "../../../utilities/usageQuota"
5
+
6
+ export const run = async () => {
7
+ const db = getGlobalDB()
8
+ // get app count
9
+ const devApps = await getAllApps(CouchDB, { dev: true })
10
+ const appCount = devApps ? devApps.length : 0
11
+
12
+ // sync app count
13
+ const tenantId = getTenantId()
14
+ console.log(`[Tenant: ${tenantId}] Syncing app count: ${appCount}`)
15
+ const usageDoc = await getUsageQuotaDoc(db)
16
+ usageDoc.usageQuota.apps = appCount
17
+ await db.put(usageDoc)
18
+ }
@@ -0,0 +1,21 @@
1
+ const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
2
+ const { getAllApps } = require("@budibase/backend-core/db")
3
+ import CouchDB from "../../../db"
4
+ import { getUsageQuotaDoc } from "../../../utilities/usageQuota"
5
+ import { getUniqueRows } from "../../../utilities/usageQuota/rows"
6
+
7
+ export const run = async () => {
8
+ const db = getGlobalDB()
9
+ // get all rows in all apps
10
+ const allApps = await getAllApps(CouchDB, { all: true })
11
+ const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : []
12
+ const rows = await getUniqueRows(appIds)
13
+ const rowCount = rows ? rows.length : 0
14
+
15
+ // sync row count
16
+ const tenantId = getTenantId()
17
+ console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`)
18
+ const usageDoc = await getUsageQuotaDoc(db)
19
+ usageDoc.usageQuota.rows = rowCount
20
+ await db.put(usageDoc)
21
+ }
@@ -0,0 +1,37 @@
1
+ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
2
+ const TestConfig = require("../../../../tests/utilities/TestConfiguration")
3
+ const { getUsageQuotaDoc, update, Properties } = require("../../../../utilities/usageQuota")
4
+ const syncApps = require("../syncApps")
5
+ const env = require("../../../../environment")
6
+
7
+ describe("syncApps", () => {
8
+ let config = new TestConfig(false)
9
+
10
+ beforeEach(async () => {
11
+ await config.init()
12
+ env._set("USE_QUOTAS", 1)
13
+ })
14
+
15
+ afterAll(config.end)
16
+
17
+ it("runs successfully", async () => {
18
+ // create the usage quota doc and mock usages
19
+ const db = getGlobalDB()
20
+ await getUsageQuotaDoc(db)
21
+ await update(Properties.APPS, 3)
22
+
23
+ let usageDoc = await getUsageQuotaDoc(db)
24
+ expect(usageDoc.usageQuota.apps).toEqual(3)
25
+
26
+ // create an extra app to test the migration
27
+ await config.createApp("quota-test")
28
+
29
+ // migrate
30
+ await syncApps.run()
31
+
32
+ // assert the migration worked
33
+ usageDoc = await getUsageQuotaDoc(db)
34
+ expect(usageDoc.usageQuota.apps).toEqual(2)
35
+ })
36
+ })
37
+
@@ -0,0 +1,43 @@
1
+ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
2
+ const TestConfig = require("../../../../tests/utilities/TestConfiguration")
3
+ const { getUsageQuotaDoc, update, Properties } = require("../../../../utilities/usageQuota")
4
+ const syncRows = require("../syncRows")
5
+ const env = require("../../../../environment")
6
+
7
+ describe("syncRows", () => {
8
+ let config = new TestConfig(false)
9
+
10
+ beforeEach(async () => {
11
+ await config.init()
12
+ env._set("USE_QUOTAS", 1)
13
+ })
14
+
15
+ afterAll(config.end)
16
+
17
+ it("runs successfully", async () => {
18
+ // create the usage quota doc and mock usages
19
+ const db = getGlobalDB()
20
+ await getUsageQuotaDoc(db)
21
+ await update(Properties.ROW, 300)
22
+
23
+ let usageDoc = await getUsageQuotaDoc(db)
24
+ expect(usageDoc.usageQuota.rows).toEqual(300)
25
+
26
+ // app 1
27
+ await config.createTable()
28
+ await config.createRow()
29
+ // app 2
30
+ await config.createApp("second-app")
31
+ await config.createTable()
32
+ await config.createRow()
33
+ await config.createRow()
34
+
35
+ // migrate
36
+ await syncRows.run()
37
+
38
+ // assert the migration worked
39
+ usageDoc = await getUsageQuotaDoc(db)
40
+ expect(usageDoc.usageQuota.rows).toEqual(3)
41
+ })
42
+ })
43
+
@@ -0,0 +1,13 @@
1
+ const { createUserEmailView } = require("@budibase/backend-core/db")
2
+
3
+ /**
4
+ * Date:
5
+ * October 2021
6
+ *
7
+ * Description:
8
+ * Recreate the user email view to include latest changes i.e. lower casing the email address
9
+ */
10
+
11
+ export const run = async (db: any) => {
12
+ await createUserEmailView(db)
13
+ }