@budibase/worker 2.33.3 → 2.33.5

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.33.3",
4
+ "version": "2.33.5",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -37,10 +37,10 @@
37
37
  "author": "Budibase",
38
38
  "license": "GPL-3.0",
39
39
  "dependencies": {
40
- "@budibase/backend-core": "2.33.3",
41
- "@budibase/pro": "2.33.3",
42
- "@budibase/string-templates": "2.33.3",
43
- "@budibase/types": "2.33.3",
40
+ "@budibase/backend-core": "2.33.5",
41
+ "@budibase/pro": "2.33.5",
42
+ "@budibase/string-templates": "2.33.5",
43
+ "@budibase/types": "2.33.5",
44
44
  "@koa/router": "8.0.8",
45
45
  "@techpass/passport-openidconnect": "0.3.3",
46
46
  "@types/global-agent": "2.1.1",
@@ -107,5 +107,5 @@
107
107
  }
108
108
  }
109
109
  },
110
- "gitHead": "59d2531535e610f1ef961cb35e09bb1d477ff563"
110
+ "gitHead": "44bd5d95d92702fb1d7c01f32b56920523c11da2"
111
111
  }
@@ -23,9 +23,11 @@ import {
23
23
  SearchUsersRequest,
24
24
  User,
25
25
  UserCtx,
26
+ UserIdentifier,
26
27
  } from "@budibase/types"
27
28
  import {
28
29
  accounts,
30
+ users,
29
31
  cache,
30
32
  ErrorCode,
31
33
  events,
@@ -55,8 +57,8 @@ export const save = async (ctx: UserCtx<User, SaveUserResponse>) => {
55
57
  const requestUser = ctx.request.body
56
58
 
57
59
  // Do not allow the account holder role to be changed
58
- const tenantInfo = await tenancy.getTenantInfo(requestUser.tenantId)
59
- if (tenantInfo?.owner.email === requestUser.email) {
60
+ const accountMetadata = await users.getExistingAccounts([requestUser.email])
61
+ if (accountMetadata?.length > 0) {
60
62
  if (
61
63
  requestUser.admin?.global !== true ||
62
64
  requestUser.builder?.global !== true
@@ -103,11 +105,14 @@ export const addSsoSupport = async (ctx: Ctx<AddSSoUserRequest>) => {
103
105
  }
104
106
  }
105
107
 
106
- const bulkDelete = async (userIds: string[], currentUserId: string) => {
107
- if (userIds?.indexOf(currentUserId) !== -1) {
108
+ const bulkDelete = async (
109
+ users: Array<UserIdentifier>,
110
+ currentUserId: string
111
+ ) => {
112
+ if (users.find(u => u.userId === currentUserId)) {
108
113
  throw new Error("Unable to delete self.")
109
114
  }
110
- return await userSdk.db.bulkDelete(userIds)
115
+ return await userSdk.db.bulkDelete(users)
111
116
  }
112
117
 
113
118
  const bulkCreate = async (users: User[], groupIds: string[]) => {
@@ -130,7 +135,7 @@ export const bulkUpdate = async (
130
135
  created = await bulkCreate(input.create.users, input.create.groups)
131
136
  }
132
137
  if (input.delete) {
133
- deleted = await bulkDelete(input.delete.userIds, currentUserId)
138
+ deleted = await bulkDelete(input.delete.users, currentUserId)
134
139
  }
135
140
  } catch (err: any) {
136
141
  ctx.throw(err.status || 400, err?.message || err)
@@ -302,6 +307,23 @@ export const tenantUserLookup = async (ctx: any) => {
302
307
  }
303
308
  }
304
309
 
310
+ /**
311
+ * This will be paginated to a default of the first 50 users,
312
+ * So the account holder may not be found until further pagination has occurred
313
+ */
314
+ export const accountHolderLookup = async (ctx: Ctx) => {
315
+ const users = await userSdk.core.getAllUsers()
316
+ const response = await userSdk.core.getExistingAccounts(
317
+ users.map(u => u.email)
318
+ )
319
+ const holder = response[0]
320
+ if (!holder) {
321
+ return
322
+ }
323
+ holder._id = users.find(u => u.email === holder.email)?._id
324
+ ctx.body = holder
325
+ }
326
+
305
327
  /*
306
328
  Encapsulate the app user onboarding flows here.
307
329
  */
package/src/api/index.ts CHANGED
@@ -71,10 +71,6 @@ const PUBLIC_ENDPOINTS = [
71
71
  route: "/api/global/users/invite",
72
72
  method: "GET",
73
73
  },
74
- {
75
- route: "/api/global/tenant",
76
- method: "POST",
77
- },
78
74
  ]
79
75
 
80
76
  const NO_TENANCY_ENDPOINTS = [
@@ -121,11 +117,7 @@ const NO_TENANCY_ENDPOINTS = [
121
117
  method: "GET",
122
118
  },
123
119
  {
124
- route: "/api/global/tenant",
125
- method: "POST",
126
- },
127
- {
128
- route: "/api/global/tenant/:id",
120
+ route: "/api/global/users/accountholder",
129
121
  method: "GET",
130
122
  },
131
123
  ]
@@ -412,28 +412,6 @@ describe("/api/global/users", () => {
412
412
  expect(events.user.permissionBuilderRemoved).toHaveBeenCalledTimes(1)
413
413
  })
414
414
 
415
- it("should not be able to update an account holder user to a basic user", async () => {
416
- const accountHolderUser = await config.createUser(
417
- structures.users.adminUser()
418
- )
419
- jest.clearAllMocks()
420
- tenancy.getTenantInfo = jest.fn().mockImplementation(() => ({
421
- owner: {
422
- email: accountHolderUser.email,
423
- },
424
- }))
425
-
426
- accountHolderUser.admin!.global = false
427
- accountHolderUser.builder!.global = false
428
-
429
- await config.api.users.saveUser(accountHolderUser, 400)
430
-
431
- expect(events.user.created).not.toHaveBeenCalled()
432
- expect(events.user.updated).not.toHaveBeenCalled()
433
- expect(events.user.permissionAdminRemoved).not.toHaveBeenCalled()
434
- expect(events.user.permissionBuilderRemoved).not.toHaveBeenCalled()
435
- })
436
-
437
415
  it("should be able to update an builder user to a basic user", async () => {
438
416
  const user = await config.createUser(structures.users.builderUser())
439
417
  jest.clearAllMocks()
@@ -592,55 +570,21 @@ describe("/api/global/users", () => {
592
570
 
593
571
  describe("POST /api/global/users/bulk (delete)", () => {
594
572
  it("should not be able to bulk delete current user", async () => {
595
- const user = await config.user!
573
+ const user = config.user!
596
574
 
597
- const response = await config.api.users.bulkDeleteUsers([user._id!], 400)
575
+ const response = await config.api.users.bulkDeleteUsers(
576
+ [
577
+ {
578
+ userId: user._id!,
579
+ email: "test@example.com",
580
+ },
581
+ ],
582
+ 400
583
+ )
598
584
 
599
585
  expect(response.message).toBe("Unable to delete self.")
600
586
  expect(events.user.deleted).not.toHaveBeenCalled()
601
587
  })
602
-
603
- it("should not be able to bulk delete account owner", async () => {
604
- const user = await config.createUser()
605
- const account = structures.accounts.cloudAccount()
606
- account.budibaseUserId = user._id!
607
- accounts.getAccountByTenantId.mockReturnValue(Promise.resolve(account))
608
-
609
- const response = await config.api.users.bulkDeleteUsers([user._id!])
610
-
611
- expect(response.deleted?.successful.length).toBe(0)
612
- expect(response.deleted?.unsuccessful.length).toBe(1)
613
- expect(response.deleted?.unsuccessful[0].reason).toBe(
614
- "Account holder cannot be deleted"
615
- )
616
- expect(response.deleted?.unsuccessful[0]._id).toBe(user._id)
617
- expect(events.user.deleted).not.toHaveBeenCalled()
618
- })
619
-
620
- it("should be able to bulk delete users", async () => {
621
- const account = structures.accounts.cloudAccount()
622
- accounts.getAccountByTenantId.mockReturnValue(Promise.resolve(account))
623
-
624
- const builder = structures.users.builderUser()
625
- const admin = structures.users.adminUser()
626
- const user = structures.users.user()
627
- const createdUsers = await config.api.users.bulkCreateUsers([
628
- builder,
629
- admin,
630
- user,
631
- ])
632
-
633
- const toDelete = createdUsers.created?.successful.map(
634
- u => u._id!
635
- ) as string[]
636
- const response = await config.api.users.bulkDeleteUsers(toDelete)
637
-
638
- expect(response.deleted?.successful.length).toBe(3)
639
- expect(response.deleted?.unsuccessful.length).toBe(0)
640
- expect(events.user.deleted).toHaveBeenCalledTimes(3)
641
- expect(events.user.permissionAdminRemoved).toHaveBeenCalledTimes(1)
642
- expect(events.user.permissionBuilderRemoved).toHaveBeenCalledTimes(2)
643
- })
644
588
  })
645
589
 
646
590
  describe("POST /api/global/users/search", () => {
@@ -136,6 +136,7 @@ router
136
136
  buildAdminInitValidation(),
137
137
  controller.adminUser
138
138
  )
139
+ .get("/api/global/users/accountholder", controller.accountHolderLookup)
139
140
  .get("/api/global/users/tenant/:id", controller.tenantUserLookup)
140
141
  // global endpoint but needs to come at end (blocks other endpoints otherwise)
141
142
  .get("/api/global/users/:id", auth.builderOrAdmin, controller.find)
@@ -1,7 +1,6 @@
1
1
  import Router from "@koa/router"
2
2
  import { api as pro } from "@budibase/pro"
3
3
  import userRoutes from "./global/users"
4
- import tenantRoutes from "./global/tenant"
5
4
  import configRoutes from "./global/configs"
6
5
  import workspaceRoutes from "./global/workspaces"
7
6
  import templateRoutes from "./global/templates"
@@ -41,7 +40,6 @@ export const routes: Router[] = [
41
40
  accountRoutes,
42
41
  restoreRoutes,
43
42
  eventRoutes,
44
- tenantRoutes,
45
43
  pro.scim,
46
44
  ]
47
45
 
@@ -66,7 +66,14 @@ export const buildUserBulkUserValidation = (isSelf = false) => {
66
66
  users: Joi.array().items(Joi.object(schema).required().unknown(true)),
67
67
  }),
68
68
  delete: Joi.object({
69
- userIds: Joi.array().items(Joi.string()),
69
+ users: Joi.array().items(
70
+ Joi.object({
71
+ email: Joi.string(),
72
+ userId: Joi.string(),
73
+ })
74
+ .required()
75
+ .unknown(true)
76
+ ),
70
77
  }),
71
78
  }
72
79
 
@@ -1,4 +1,3 @@
1
- import { TenantInfo } from "@budibase/types"
2
1
  import TestConfiguration from "../TestConfiguration"
3
2
  import { TestAPI, TestAPIOpts } from "./base"
4
3
 
@@ -15,12 +14,4 @@ export class TenantAPI extends TestAPI {
15
14
  .set(opts?.headers)
16
15
  .expect(opts?.status ? opts.status : 204)
17
16
  }
18
-
19
- saveTenantInfo = (tenantInfo: TenantInfo) => {
20
- return this.request
21
- .post("/api/global/tenant")
22
- .set(this.config.internalAPIHeaders())
23
- .send(tenantInfo)
24
- .expect(200)
25
- }
26
17
  }
@@ -81,8 +81,14 @@ export class UserAPI extends TestAPI {
81
81
  return res.body as BulkUserResponse
82
82
  }
83
83
 
84
- bulkDeleteUsers = async (userIds: string[], status?: number) => {
85
- const body: BulkUserRequest = { delete: { userIds } }
84
+ bulkDeleteUsers = async (
85
+ users: Array<{
86
+ userId: string
87
+ email: string
88
+ }>,
89
+ status?: number
90
+ ) => {
91
+ const body: BulkUserRequest = { delete: { users } }
86
92
  const res = await this.request
87
93
  .post(`/api/global/users/bulk`)
88
94
  .send(body)
@@ -1,14 +0,0 @@
1
- import { tenancy } from "@budibase/backend-core"
2
- import { TenantInfo, Ctx } from "@budibase/types"
3
-
4
- export const save = async (ctx: Ctx<TenantInfo>) => {
5
- const response = await tenancy.saveTenantInfo(ctx.request.body)
6
- ctx.body = {
7
- _id: response.id,
8
- _rev: response.rev,
9
- }
10
- }
11
-
12
- export const get = async (ctx: Ctx) => {
13
- ctx.body = await tenancy.getTenantInfo(ctx.params.id)
14
- }
@@ -1,11 +0,0 @@
1
- import Router from "@koa/router"
2
- import * as controller from "../../controllers/global/tenant"
3
- import cloudRestricted from "../../../middleware/cloudRestricted"
4
-
5
- const router: Router = new Router()
6
-
7
- router
8
- .post("/api/global/tenant", cloudRestricted, controller.save)
9
- .get("/api/global/tenant/:id", controller.get)
10
-
11
- export default router
@@ -1,48 +0,0 @@
1
- import { Hosting, TenantInfo } from "@budibase/types"
2
- import { TestConfiguration } from "../../../../tests"
3
- import { tenancy as _tenancy } from "@budibase/backend-core"
4
-
5
- const tenancy = jest.mocked(_tenancy)
6
-
7
- describe("/api/global/tenant", () => {
8
- const config = new TestConfiguration()
9
-
10
- beforeAll(async () => {
11
- await config.beforeAll()
12
- })
13
-
14
- afterAll(async () => {
15
- await config.afterAll()
16
- })
17
-
18
- beforeEach(() => {
19
- jest.clearAllMocks()
20
- })
21
-
22
- describe("POST /api/global/tenant", () => {
23
- it("should save the tenantInfo", async () => {
24
- tenancy.saveTenantInfo = jest.fn().mockImplementation(async () => ({
25
- id: "DOC_ID",
26
- ok: true,
27
- rev: "DOC_REV",
28
- }))
29
- const tenantInfo: TenantInfo = {
30
- owner: {
31
- email: "test@example.com",
32
- password: "PASSWORD123!",
33
- ssoId: "SSO_ID",
34
- givenName: "Jane",
35
- familyName: "Doe",
36
- budibaseUserId: "USER_ID",
37
- },
38
- tenantId: "tenant123",
39
- hosting: Hosting.CLOUD,
40
- }
41
- const response = await config.api.tenants.saveTenantInfo(tenantInfo)
42
-
43
- expect(_tenancy.saveTenantInfo).toHaveBeenCalledTimes(1)
44
- expect(_tenancy.saveTenantInfo).toHaveBeenCalledWith(tenantInfo)
45
- expect(response.text).toEqual('{"_id":"DOC_ID","_rev":"DOC_REV"}')
46
- })
47
- })
48
- })