@budibase/worker 1.0.219 → 1.0.220-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.
package/.yarnrc ADDED
@@ -0,0 +1 @@
1
+ network-timeout 100000
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.0.219",
4
+ "version": "1.0.220-alpha.2",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -34,9 +34,9 @@
34
34
  "author": "Budibase",
35
35
  "license": "GPL-3.0",
36
36
  "dependencies": {
37
- "@budibase/backend-core": "^1.0.219",
38
- "@budibase/pro": "1.0.218",
39
- "@budibase/string-templates": "^1.0.219",
37
+ "@budibase/backend-core": "^1.0.220-alpha.2",
38
+ "@budibase/pro": "1.0.220-alpha.1",
39
+ "@budibase/string-templates": "^1.0.220-alpha.2",
40
40
  "@koa/router": "8.0.8",
41
41
  "@sentry/node": "6.17.7",
42
42
  "@techpass/passport-openidconnect": "0.3.2",
@@ -66,7 +66,7 @@
66
66
  "server-destroy": "1.0.1"
67
67
  },
68
68
  "devDependencies": {
69
- "@budibase/types": "^1.0.219",
69
+ "@budibase/types": "^1.0.220-alpha.2",
70
70
  "@types/jest": "26.0.23",
71
71
  "@types/koa": "2.13.4",
72
72
  "@types/koa-router": "7.4.4",
@@ -100,5 +100,5 @@
100
100
  "./scripts/jestSetup.js"
101
101
  ]
102
102
  },
103
- "gitHead": "231b038a3c99f730dd27a595f870663dd84b7837"
103
+ "gitHead": "80b7a2da2fba39ff25622873c95598fa931ea6ed"
104
104
  }
@@ -1,49 +1,22 @@
1
1
  const core = require("@budibase/backend-core")
2
- const { getScopedConfig } = require("@budibase/backend-core/db")
3
- const { google } = require("@budibase/backend-core/middleware")
4
- const { oidc } = require("@budibase/backend-core/middleware")
5
2
  const { Configs, EmailTemplatePurpose } = require("../../../constants")
6
3
  const { sendEmail, isEmailConfigured } = require("../../../utilities/email")
7
4
  const { setCookie, getCookie, clearCookie, hash, platformLogout } = core.utils
8
5
  const { Cookies, Headers } = core.constants
9
- const { passport } = core.auth
6
+ const { passport, ssoCallbackUrl, google, oidc } = core.auth
10
7
  const { checkResetPasswordCode } = require("../../../utilities/redis")
11
- const {
12
- getGlobalDB,
13
- getTenantId,
14
- isMultiTenant,
15
- } = require("@budibase/backend-core/tenancy")
8
+ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
16
9
  const env = require("../../../environment")
17
10
  import { events, users as usersCore, context } from "@budibase/backend-core"
18
11
  import { users } from "../../../sdk"
19
12
  import { User } from "@budibase/types"
20
13
 
21
- const ssoCallbackUrl = async (config: any, type: any) => {
22
- // incase there is a callback URL from before
23
- if (config && config.callbackURL) {
24
- return config.callbackURL
25
- }
26
-
27
- const db = getGlobalDB()
28
- const publicConfig = await getScopedConfig(db, {
29
- type: Configs.SETTINGS,
30
- })
31
-
32
- let callbackUrl = `/api/global/auth`
33
- if (isMultiTenant()) {
34
- callbackUrl += `/${getTenantId()}`
35
- }
36
- callbackUrl += `/${type}/callback`
37
-
38
- return `${publicConfig.platformUrl}${callbackUrl}`
39
- }
40
-
41
14
  export const googleCallbackUrl = async (config: any) => {
42
- return ssoCallbackUrl(config, "google")
15
+ return ssoCallbackUrl(getGlobalDB(), config, "google")
43
16
  }
44
17
 
45
18
  export const oidcCallbackUrl = async (config: any) => {
46
- return ssoCallbackUrl(config, "oidc")
19
+ return ssoCallbackUrl(getGlobalDB(), config, "oidc")
47
20
  }
48
21
 
49
22
  async function authInternal(ctx: any, user: any, err = null, info = null) {
@@ -198,6 +171,8 @@ export const googlePreAuth = async (ctx: any, next: any) => {
198
171
 
199
172
  return passport.authenticate(strategy, {
200
173
  scope: ["profile", "email"],
174
+ accessType: "offline",
175
+ prompt: "consent",
201
176
  })(ctx, next)
202
177
  }
203
178
 
@@ -224,7 +199,7 @@ export const googleAuth = async (ctx: any, next: any) => {
224
199
  )(ctx, next)
225
200
  }
226
201
 
227
- async function oidcStrategyFactory(ctx: any, configId: any) {
202
+ export const oidcStrategyFactory = async (ctx: any, configId: any) => {
228
203
  const db = getGlobalDB()
229
204
  const config = await core.db.getScopedConfig(db, {
230
205
  type: Configs.OIDC,
@@ -234,7 +209,12 @@ async function oidcStrategyFactory(ctx: any, configId: any) {
234
209
  const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
235
210
  let callbackUrl = await exports.oidcCallbackUrl(chosenConfig)
236
211
 
237
- return oidc.strategyFactory(chosenConfig, callbackUrl, users.save)
212
+ //Remote Config
213
+ const enrichedConfig = await oidc.fetchStrategyConfig(
214
+ chosenConfig,
215
+ callbackUrl
216
+ )
217
+ return oidc.strategyFactory(enrichedConfig, users.save)
238
218
  }
239
219
 
240
220
  /**
@@ -249,7 +229,7 @@ export const oidcPreAuth = async (ctx: any, next: any) => {
249
229
 
250
230
  return passport.authenticate(strategy, {
251
231
  // required 'openid' scope is added by oidc strategy factory
252
- scope: ["profile", "email"],
232
+ scope: ["profile", "email", "offline_access"], //auth0 offline_access scope required for the refresh token behaviour.
253
233
  })(ctx, next)
254
234
  }
255
235
 
@@ -1,7 +1,6 @@
1
1
  const {
2
2
  generateConfigID,
3
3
  getConfigParams,
4
- getGlobalUserParams,
5
4
  getScopedFullConfig,
6
5
  getAllApps,
7
6
  } = require("@budibase/backend-core/db")
@@ -18,8 +17,10 @@ const {
18
17
  withCache,
19
18
  CacheKeys,
20
19
  bustCache,
20
+ cache,
21
21
  } = require("@budibase/backend-core/cache")
22
22
  const { events } = require("@budibase/backend-core")
23
+ const { checkAnyUserExists } = require("../../../utilities/users")
23
24
 
24
25
  const BB_TENANT_CDN = "https://tenants.cdn.budi.live"
25
26
 
@@ -365,9 +366,9 @@ exports.upload = async function (ctx) {
365
366
  exports.destroy = async function (ctx) {
366
367
  const db = getGlobalDB()
367
368
  const { id, rev } = ctx.params
368
-
369
369
  try {
370
370
  await db.remove(id, rev)
371
+ await cache.delete(CacheKeys.CHECKLIST)
371
372
  ctx.body = { message: "Config deleted successfully" }
372
373
  } catch (err) {
373
374
  ctx.throw(err.status, err)
@@ -405,12 +406,7 @@ exports.configChecklist = async function (ctx) {
405
406
  })
406
407
 
407
408
  // They have set up an global user
408
- const users = await db.allDocs(
409
- getGlobalUserParams(null, {
410
- include_docs: true,
411
- limit: 1,
412
- })
413
- )
409
+ const userExists = await checkAnyUserExists()
414
410
  return {
415
411
  apps: {
416
412
  checked: apps.length > 0,
@@ -423,7 +419,7 @@ exports.configChecklist = async function (ctx) {
423
419
  link: "/builder/portal/manage/email",
424
420
  },
425
421
  adminUser: {
426
- checked: users && users.rows.length >= 1,
422
+ checked: userExists,
427
423
  label: "Create your first user",
428
424
  link: "/builder/portal/manage/users",
429
425
  },
@@ -7,7 +7,7 @@ const {
7
7
  const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
8
8
  const { user: userCache } = require("@budibase/backend-core/cache")
9
9
  const { getGlobalDB } = require("@budibase/backend-core/tenancy")
10
- const { users } = require("../../../sdk")
10
+ const { allUsers } = require("../../../sdk/users")
11
11
 
12
12
  exports.fetch = async ctx => {
13
13
  const tenantId = ctx.user.tenantId
@@ -49,10 +49,10 @@ exports.find = async ctx => {
49
49
  exports.removeAppRole = async ctx => {
50
50
  const { appId } = ctx.params
51
51
  const db = getGlobalDB()
52
- const allUsers = await users.allUsers(ctx)
52
+ const users = await allUsers(ctx)
53
53
  const bulk = []
54
54
  const cacheInvalidations = []
55
- for (let user of allUsers) {
55
+ for (let user of users) {
56
56
  if (user.roles[appId]) {
57
57
  cacheInvalidations.push(userCache.invalidateUser(user._id))
58
58
  delete user.roles[appId]
@@ -8,16 +8,15 @@ import {
8
8
  events,
9
9
  errors,
10
10
  accounts,
11
- db as dbUtils,
12
11
  users as usersCore,
13
12
  tenancy,
14
13
  cache,
15
14
  } from "@budibase/backend-core"
15
+ import { checkAnyUserExists } from "../../../utilities/users"
16
16
 
17
17
  export const save = async (ctx: any) => {
18
18
  try {
19
- const user = await users.save(ctx.request.body)
20
- ctx.body = user
19
+ ctx.body = await users.save(ctx.request.body)
21
20
  } catch (err: any) {
22
21
  ctx.throw(err.status || 400, err)
23
22
  }
@@ -39,15 +38,8 @@ export const adminUser = async (ctx: any) => {
39
38
  ctx.throw(403, "Organisation already exists.")
40
39
  }
41
40
 
42
- const response = await tenancy.doWithGlobalDB(tenantId, async (db: any) => {
43
- return db.allDocs(
44
- dbUtils.getGlobalUserParams(null, {
45
- include_docs: true,
46
- })
47
- )
48
- })
49
-
50
- if (response.rows.some((row: any) => row.doc.admin)) {
41
+ const userExists = await checkAnyUserExists()
42
+ if (userExists) {
51
43
  ctx.throw(
52
44
  403,
53
45
  "You cannot initialise once an global user has been created."
@@ -68,11 +60,13 @@ export const adminUser = async (ctx: any) => {
68
60
  tenantId,
69
61
  }
70
62
  try {
63
+ // always bust checklist beforehand, if an error occurs but can proceed, don't get
64
+ // stuck in a cycle
65
+ await cache.bustCache(cache.CacheKeys.CHECKLIST)
71
66
  const finalUser = await users.save(user, {
72
67
  hashPassword,
73
68
  requirePassword,
74
69
  })
75
- await cache.bustCache(cache.CacheKeys.CHECKLIST)
76
70
 
77
71
  // events
78
72
  let account: CloudAccount | undefined
@@ -96,6 +90,17 @@ export const destroy = async (ctx: any) => {
96
90
  }
97
91
  }
98
92
 
93
+ export const search = async (ctx: any) => {
94
+ const paginated = await users.paginatedUsers(ctx.request.body)
95
+ // user hashed password shouldn't ever be returned
96
+ for (let user of paginated.data) {
97
+ if (user) {
98
+ delete user.password
99
+ }
100
+ }
101
+ ctx.body = paginated
102
+ }
103
+
99
104
  // called internally by app server user fetch
100
105
  export const fetch = async (ctx: any) => {
101
106
  const all = await users.allUsers()
@@ -46,6 +46,7 @@ router
46
46
  controller.save
47
47
  )
48
48
  .get("/api/global/users", builderOrAdmin, controller.fetch)
49
+ .post("/api/global/users/search", builderOrAdmin, controller.search)
49
50
  .delete("/api/global/users/:id", adminOnly, controller.destroy)
50
51
  .get("/api/global/roles/:appId")
51
52
  .post(
@@ -77,27 +77,30 @@ describe("/api/global/auth", () => {
77
77
  describe("oidc", () => {
78
78
  const auth = require("@budibase/backend-core/auth")
79
79
 
80
+ const passportSpy = jest.spyOn(auth.passport, "authenticate")
81
+ let oidcConf
82
+ let chosenConfig
83
+ let configId
84
+
80
85
  // mock the oidc strategy implementation and return value
81
86
  let strategyFactory = jest.fn()
82
87
  let mockStrategyReturn = jest.fn()
88
+ let mockStrategyConfig = jest.fn()
89
+ auth.oidc.fetchStrategyConfig = mockStrategyConfig
90
+
83
91
  strategyFactory.mockReturnValue(mockStrategyReturn)
84
92
  auth.oidc.strategyFactory = strategyFactory
85
93
 
86
- const passportSpy = jest.spyOn(auth.passport, "authenticate")
87
- let oidcConf
88
- let chosenConfig
89
- let configId
90
-
91
94
  beforeEach(async () => {
92
95
  oidcConf = await config.saveOIDCConfig()
93
96
  chosenConfig = oidcConf.config.configs[0]
94
97
  configId = chosenConfig.uuid
98
+ mockStrategyConfig.mockReturnValue(chosenConfig)
95
99
  })
96
100
 
97
101
  afterEach(() => {
98
102
  expect(strategyFactory).toBeCalledWith(
99
103
  chosenConfig,
100
- `http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback`,
101
104
  expect.any(Function)
102
105
  )
103
106
  })
@@ -107,7 +110,7 @@ describe("/api/global/auth", () => {
107
110
  await request.get(`/api/global/auth/${TENANT_ID}/oidc/configs/${configId}`)
108
111
 
109
112
  expect(passportSpy).toBeCalledWith(mockStrategyReturn, {
110
- scope: ["profile", "email"],
113
+ scope: ["profile", "email", "offline_access"]
111
114
  })
112
115
  expect(passportSpy.mock.calls.length).toBe(1);
113
116
  })
@@ -17,9 +17,8 @@ import {
17
17
  } from "@budibase/backend-core"
18
18
  import { MigrationType } from "@budibase/types"
19
19
 
20
- /**
21
- * Retrieves all users from the current tenancy.
22
- */
20
+ const PAGE_LIMIT = 8
21
+
23
22
  export const allUsers = async () => {
24
23
  const db = tenancy.getGlobalDB()
25
24
  const response = await db.allDocs(
@@ -30,6 +29,37 @@ export const allUsers = async () => {
30
29
  return response.rows.map((row: any) => row.doc)
31
30
  }
32
31
 
32
+ export const paginatedUsers = async ({
33
+ page,
34
+ search,
35
+ }: { page?: string; search?: string } = {}) => {
36
+ const db = tenancy.getGlobalDB()
37
+ // get one extra document, to have the next page
38
+ const opts: any = {
39
+ include_docs: true,
40
+ limit: PAGE_LIMIT + 1,
41
+ }
42
+ // add a startkey if the page was specified (anchor)
43
+ if (page) {
44
+ opts.startkey = page
45
+ }
46
+ // property specifies what to use for the page/anchor
47
+ let userList, property
48
+ // no search, query allDocs
49
+ if (!search) {
50
+ const response = await db.allDocs(dbUtils.getGlobalUserParams(null, opts))
51
+ userList = response.rows.map((row: any) => row.doc)
52
+ property = "_id"
53
+ } else {
54
+ userList = await usersCore.searchGlobalUsersByEmail(search, opts)
55
+ property = "email"
56
+ }
57
+ return dbUtils.pagination(userList, PAGE_LIMIT, {
58
+ paginate: true,
59
+ property,
60
+ })
61
+ }
62
+
33
63
  /**
34
64
  * Gets a user by ID from the global database, based on the current tenancy.
35
65
  */
@@ -0,0 +1,17 @@
1
+ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
2
+ const { getGlobalUserParams } = require("@budibase/backend-core/db")
3
+
4
+ exports.checkAnyUserExists = async () => {
5
+ try {
6
+ const db = getGlobalDB()
7
+ const users = await db.allDocs(
8
+ getGlobalUserParams(null, {
9
+ include_docs: true,
10
+ limit: 1,
11
+ })
12
+ )
13
+ return users && users.rows.length >= 1
14
+ } catch (err) {
15
+ throw new Error("Unable to retrieve user list")
16
+ }
17
+ }