@budibase/worker 1.1.32-alpha.4 → 1.1.32

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": "1.1.32-alpha.4",
4
+ "version": "1.1.32",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -35,10 +35,10 @@
35
35
  "author": "Budibase",
36
36
  "license": "GPL-3.0",
37
37
  "dependencies": {
38
- "@budibase/backend-core": "1.1.32-alpha.4",
39
- "@budibase/pro": "1.1.32-alpha.3",
40
- "@budibase/string-templates": "1.1.32-alpha.4",
41
- "@budibase/types": "1.1.32-alpha.4",
38
+ "@budibase/backend-core": "^1.1.32",
39
+ "@budibase/pro": "1.1.31",
40
+ "@budibase/string-templates": "^1.1.32",
41
+ "@budibase/types": "^1.1.32",
42
42
  "@koa/router": "8.0.8",
43
43
  "@sentry/node": "6.17.7",
44
44
  "@techpass/passport-openidconnect": "0.3.2",
@@ -101,5 +101,5 @@
101
101
  "./scripts/jestSetup.js"
102
102
  ]
103
103
  },
104
- "gitHead": "4b774539ea2654249886264a3284332c91e9b83c"
104
+ "gitHead": "900d5de0723c1b7acf218446507d52755fdf7d83"
105
105
  }
@@ -13,8 +13,6 @@ import {
13
13
  cache,
14
14
  } from "@budibase/backend-core"
15
15
  import { checkAnyUserExists } from "../../../utilities/users"
16
- import { groups as groupUtils } from "@budibase/pro"
17
- const MAX_USERS_UPLOAD_LIMIT = 1000
18
16
 
19
17
  export const save = async (ctx: any) => {
20
18
  try {
@@ -24,36 +22,6 @@ export const save = async (ctx: any) => {
24
22
  }
25
23
  }
26
24
 
27
- export const bulkCreate = async (ctx: any) => {
28
- let { users: newUsersRequested, groups } = ctx.request.body
29
-
30
- if (!env.SELF_HOSTED && newUsersRequested.length > MAX_USERS_UPLOAD_LIMIT) {
31
- ctx.throw(
32
- 400,
33
- "Max limit for upload is 1000 users. Please reduce file size and try again."
34
- )
35
- }
36
-
37
- const db = tenancy.getGlobalDB()
38
- let groupsToSave: any[] = []
39
-
40
- if (groups.length) {
41
- for (const groupId of groups) {
42
- let oldGroup = await db.get(groupId)
43
- groupsToSave.push(oldGroup)
44
- }
45
- }
46
-
47
- try {
48
- let response = await users.bulkCreate(newUsersRequested, groups)
49
- await groupUtils.bulkSaveGroupUsers(groupsToSave, response)
50
-
51
- ctx.body = response
52
- } catch (err: any) {
53
- ctx.throw(err.status || 400, err)
54
- }
55
- }
56
-
57
25
  const parseBooleanParam = (param: any) => {
58
26
  return !(param && param === "false")
59
27
  }
@@ -114,39 +82,14 @@ export const adminUser = async (ctx: any) => {
114
82
  })
115
83
  }
116
84
 
117
- export const countByApp = async (ctx: any) => {
118
- const appId = ctx.params.appId
119
- try {
120
- const response = await users.countUsersByApp(appId)
121
- ctx.body = response
122
- } catch (err: any) {
123
- ctx.throw(err.status || 400, err)
124
- }
125
- }
126
-
127
85
  export const destroy = async (ctx: any) => {
128
86
  const id = ctx.params.id
129
-
130
87
  await users.destroy(id, ctx.user)
131
-
132
88
  ctx.body = {
133
89
  message: `User ${id} deleted.`,
134
90
  }
135
91
  }
136
92
 
137
- export const bulkDelete = async (ctx: any) => {
138
- const { userIds } = ctx.request.body
139
- try {
140
- let usersResponse = await users.bulkDelete(userIds)
141
-
142
- ctx.body = {
143
- message: `${usersResponse.length} user(s) deleted`,
144
- }
145
- } catch (err) {
146
- ctx.throw(err)
147
- }
148
- }
149
-
150
93
  export const search = async (ctx: any) => {
151
94
  const paginated = await users.paginatedUsers(ctx.request.body)
152
95
  // user hashed password shouldn't ever be returned
@@ -206,39 +149,6 @@ export const invite = async (ctx: any) => {
206
149
  await events.user.invited()
207
150
  }
208
151
 
209
- export const inviteMultiple = async (ctx: any) => {
210
- let { emails, userInfo } = ctx.request.body
211
- let existing = false
212
- let existingEmail
213
- for (let email of emails) {
214
- if (await usersCore.getGlobalUserByEmail(email)) {
215
- existing = true
216
- existingEmail = email
217
- break
218
- }
219
- }
220
-
221
- if (existing) {
222
- ctx.throw(400, `${existingEmail} already exists`)
223
- }
224
- if (!userInfo) {
225
- userInfo = {}
226
- }
227
- userInfo.tenantId = tenancy.getTenantId()
228
- const opts: any = {
229
- subject: "{{ company }} platform invitation",
230
- info: userInfo,
231
- }
232
-
233
- for (let i = 0; i < emails.length; i++) {
234
- await sendEmail(emails[i], EmailTemplatePurpose.INVITATION, opts)
235
- }
236
-
237
- ctx.body = {
238
- message: "Invitations have been sent.",
239
- }
240
- }
241
-
242
152
  export const inviteAccept = async (ctx: any) => {
243
153
  const { inviteCode, password, firstName, lastName } = ctx.request.body
244
154
  try {
@@ -1,6 +1,6 @@
1
1
  const Router = require("@koa/router")
2
2
  const authController = require("../../controllers/global/auth")
3
- const { joiValidator } = require("@budibase/backend-core/auth")
3
+ const joiValidator = require("../../../middleware/joi-validator")
4
4
  const Joi = require("joi")
5
5
  const { updateTenantId } = require("@budibase/backend-core/tenancy")
6
6
 
@@ -1,7 +1,7 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/global/configs")
3
- const { joiValidator } = require("@budibase/backend-core/auth")
4
- const { adminOnly } = require("@budibase/backend-core/auth")
3
+ const joiValidator = require("../../../middleware/joi-validator")
4
+ const adminOnly = require("../../../middleware/adminOnly")
5
5
  const Joi = require("joi")
6
6
  const { Configs } = require("../../../constants")
7
7
 
@@ -65,8 +65,6 @@ function buildConfigSaveValidation() {
65
65
  _rev: Joi.string().optional(),
66
66
  workspace: Joi.string().optional(),
67
67
  type: Joi.string().valid(...Object.values(Configs)).required(),
68
- createdAt: Joi.string().optional(),
69
- updatedAt: Joi.string().optional(),
70
68
  config: Joi.alternatives()
71
69
  .conditional("type", {
72
70
  switch: [
@@ -77,7 +75,7 @@ function buildConfigSaveValidation() {
77
75
  { is: Configs.OIDC, then: oidcValidation() }
78
76
  ],
79
77
  }),
80
- }).required().unknown(true),
78
+ }).required().unknown(true),
81
79
  )
82
80
  }
83
81
 
@@ -1,8 +1,8 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/global/email")
3
3
  const { EmailTemplatePurpose } = require("../../../constants")
4
- const { joiValidator } = require("@budibase/backend-core/auth")
5
- const { adminOnly } = require("@budibase/backend-core/auth")
4
+ const joiValidator = require("../../../middleware/joi-validator")
5
+ const adminOnly = require("../../../middleware/adminOnly")
6
6
  const Joi = require("joi")
7
7
 
8
8
  const router = Router()
@@ -1,6 +1,6 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/global/roles")
3
- const { adminOnly } = require("@budibase/backend-core/auth")
3
+ const adminOnly = require("../../../middleware/adminOnly")
4
4
 
5
5
  const router = Router()
6
6
 
@@ -1,6 +1,6 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/global/sessions")
3
- const { adminOnly } = require("@budibase/backend-core/auth")
3
+ const adminOnly = require("../../../middleware/adminOnly")
4
4
 
5
5
  const router = Router()
6
6
 
@@ -1,9 +1,9 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/global/templates")
3
- const { joiValidator } = require("@budibase/backend-core/auth")
3
+ const joiValidator = require("../../../middleware/joi-validator")
4
4
  const Joi = require("joi")
5
5
  const { TemplatePurpose, TemplateTypes } = require("../../../constants")
6
- const { adminOnly } = require("@budibase/backend-core/auth")
6
+ const adminOnly = require("../../../middleware/adminOnly")
7
7
 
8
8
  const router = Router()
9
9
 
@@ -1,7 +1,7 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/global/users")
3
- const { joiValidator } = require("@budibase/backend-core/auth")
4
- const { adminOnly } = require("@budibase/backend-core/auth")
3
+ const joiValidator = require("../../../middleware/joi-validator")
4
+ const adminOnly = require("../../../middleware/adminOnly")
5
5
  const Joi = require("joi")
6
6
  const cloudRestricted = require("../../../middleware/cloudRestricted")
7
7
  const { users } = require("../validation")
@@ -30,14 +30,6 @@ function buildInviteValidation() {
30
30
  }).required())
31
31
  }
32
32
 
33
- function buildInviteMultipleValidation() {
34
- // prettier-ignore
35
- return joiValidator.body(Joi.object({
36
- emails: Joi.array().required(),
37
- userInfo: Joi.object().optional(),
38
- }).required())
39
- }
40
-
41
33
  function buildInviteAcceptValidation() {
42
34
  // prettier-ignore
43
35
  return joiValidator.body(Joi.object({
@@ -53,18 +45,9 @@ router
53
45
  users.buildUserSaveValidation(),
54
46
  controller.save
55
47
  )
56
- .post(
57
- "/api/global/users/bulkCreate",
58
- adminOnly,
59
- users.buildUserBulkSaveValidation(),
60
- controller.bulkCreate
61
- )
62
-
63
48
  .get("/api/global/users", builderOrAdmin, controller.fetch)
64
49
  .post("/api/global/users/search", builderOrAdmin, controller.search)
65
50
  .delete("/api/global/users/:id", adminOnly, controller.destroy)
66
- .post("/api/global/users/bulkDelete", adminOnly, controller.bulkDelete)
67
- .get("/api/global/users/count/:appId", adminOnly, controller.countByApp)
68
51
  .get("/api/global/roles/:appId")
69
52
  .post(
70
53
  "/api/global/users/invite",
@@ -72,19 +55,6 @@ router
72
55
  buildInviteValidation(),
73
56
  controller.invite
74
57
  )
75
- .post(
76
- "/api/global/users/invite",
77
- adminOnly,
78
- buildInviteValidation(),
79
- controller.invite
80
- )
81
- .post(
82
- "/api/global/users/inviteMultiple",
83
- adminOnly,
84
- buildInviteMultipleValidation(),
85
- controller.inviteMultiple
86
- )
87
-
88
58
  // non-global endpoints
89
59
  .post(
90
60
  "/api/global/users/invite/accept",
@@ -1,7 +1,7 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/global/workspaces")
3
- const { joiValidator } = require("@budibase/backend-core/auth")
4
- const { adminOnly } = require("@budibase/backend-core/auth")
3
+ const joiValidator = require("../../../middleware/joi-validator")
4
+ const adminOnly = require("../../../middleware/adminOnly")
5
5
  const Joi = require("joi")
6
6
 
7
7
  const router = Router()
@@ -17,9 +17,9 @@ function buildWorkspaceSaveValidation() {
17
17
  roles: Joi.object({
18
18
  default: Joi.string().optional(),
19
19
  app: Joi.object()
20
- .pattern(/.*/, Joi.string())
21
- .required()
22
- .unknown(true),
20
+ .pattern(/.*/, Joi.string())
21
+ .required()
22
+ .unknown(true),
23
23
  }).unknown(true).optional(),
24
24
  }).required().unknown(true))
25
25
  }
@@ -1,4 +1,3 @@
1
- const { api } = require("@budibase/pro")
2
1
  const userRoutes = require("./global/users")
3
2
  const configRoutes = require("./global/configs")
4
3
  const workspaceRoutes = require("./global/workspaces")
@@ -13,7 +12,6 @@ const statusRoutes = require("./system/status")
13
12
  const selfRoutes = require("./global/self")
14
13
  const licenseRoutes = require("./global/license")
15
14
 
16
- let userGroupRoutes = api.groups
17
15
  exports.routes = [
18
16
  configRoutes,
19
17
  userRoutes,
@@ -28,5 +26,4 @@ exports.routes = [
28
26
  statusRoutes,
29
27
  selfRoutes,
30
28
  licenseRoutes,
31
- userGroupRoutes,
32
29
  ]
@@ -1,6 +1,6 @@
1
1
  const Router = require("@koa/router")
2
2
  const controller = require("../../controllers/system/tenants")
3
- const { adminOnly } = require("@budibase/backend-core/auth")
3
+ const adminOnly = require("../../../middleware/adminOnly")
4
4
 
5
5
  const router = Router()
6
6
 
@@ -2,6 +2,7 @@ jest.mock("nodemailer")
2
2
  const { config, request, mocks, structures } = require("../../../tests")
3
3
  const sendMailMock = mocks.email.mock()
4
4
  const { events } = require("@budibase/backend-core")
5
+
5
6
  describe("/api/global/users", () => {
6
7
 
7
8
  beforeAll(async () => {
@@ -23,9 +24,9 @@ describe("/api/global/users", () => {
23
24
  .set(config.defaultHeaders())
24
25
  .expect("Content-Type", /json/)
25
26
  .expect(200)
26
-
27
- const emailCall = sendMailMock.mock.calls[0][0]
28
- // after this URL there should be a code
27
+
28
+ const emailCall = sendMailMock.mock.calls[0][0]
29
+ // after this URL there should be a code
29
30
  const parts = emailCall.html.split("http://localhost:10000/builder/invite?code=")
30
31
  const code = parts[1].split("\"")[0].split("&")[0]
31
32
  return { code, res }
@@ -59,7 +60,7 @@ describe("/api/global/users", () => {
59
60
  expect(events.user.inviteAccepted).toBeCalledWith(user)
60
61
  })
61
62
 
62
- const createUser = async (user) => {
63
+ const createUser = async (user) => {
63
64
  const existing = await config.getUser(user.email)
64
65
  if (existing) {
65
66
  await deleteUser(existing._id)
@@ -83,37 +84,14 @@ describe("/api/global/users", () => {
83
84
  return res.body
84
85
  }
85
86
 
86
-
87
- const bulkCreateUsers = async (users) => {
88
- const res = await request
89
- .post(`/api/global/users/bulkCreate`)
90
- .send(users)
91
- .set(config.defaultHeaders())
92
- .expect("Content-Type", /json/)
93
- .expect(200)
94
- return res.body
95
- }
96
-
97
- const bulkDeleteUsers = async (users) => {
98
- const res = await request
99
- .post(`/api/global/users/bulkDelete`)
100
- .send(users)
101
- .set(config.defaultHeaders())
102
- .expect("Content-Type", /json/)
103
- .expect(200)
104
- return res.body
105
- }
106
-
107
-
108
-
109
87
  const deleteUser = async (email) => {
110
88
  const user = await config.getUser(email)
111
89
  if (user) {
112
90
  await request
113
- .delete(`/api/global/users/${user._id}`)
114
- .set(config.defaultHeaders())
115
- .expect("Content-Type", /json/)
116
- .expect(200)
91
+ .delete(`/api/global/users/${user._id}`)
92
+ .set(config.defaultHeaders())
93
+ .expect("Content-Type", /json/)
94
+ .expect(200)
117
95
  }
118
96
  }
119
97
 
@@ -129,25 +107,10 @@ describe("/api/global/users", () => {
129
107
  expect(events.user.permissionAdminAssigned).not.toBeCalled()
130
108
  })
131
109
 
132
- it("should be able to bulkCreate users with different permissions", async () => {
133
- jest.clearAllMocks()
134
- const builder = structures.users.builderUser({ email: "bulkbasic@test.com" })
135
- const admin = structures.users.adminUser({ email: "bulkadmin@test.com" })
136
- const user = structures.users.user({ email: "bulkuser@test.com" })
137
-
138
- let toCreate = { users: [builder, admin, user], groups: [] }
139
- await bulkCreateUsers(toCreate)
140
-
141
- expect(events.user.created).toBeCalledTimes(3)
142
- expect(events.user.permissionAdminAssigned).toBeCalledTimes(1)
143
- expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1)
144
- })
145
-
146
-
147
110
  it("should be able to create an admin user", async () => {
148
111
  jest.clearAllMocks()
149
112
  const user = structures.users.adminUser({ email: "admin@test.com" })
150
- await createUser(user)
113
+ await createUser(user)
151
114
 
152
115
  expect(events.user.created).toBeCalledTimes(1)
153
116
  expect(events.user.updated).not.toBeCalled()
@@ -370,21 +333,5 @@ describe("/api/global/users", () => {
370
333
  expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1)
371
334
  expect(events.user.permissionAdminRemoved).not.toBeCalled()
372
335
  })
373
-
374
- it("should be able to bulk delete users with different permissions", async () => {
375
- jest.clearAllMocks()
376
- const builder = structures.users.builderUser({ email: "basic@test.com" })
377
- const admin = structures.users.adminUser({ email: "admin@test.com" })
378
- const user = structures.users.user({ email: "user@test.com" })
379
-
380
- let toCreate = { users: [builder, admin, user], groups: [] }
381
- let createdUsers = await bulkCreateUsers(toCreate)
382
- await bulkDeleteUsers({ userIds: [createdUsers[0]._id, createdUsers[1]._id, createdUsers[2]._id] })
383
- expect(events.user.deleted).toBeCalledTimes(3)
384
- expect(events.user.permissionAdminRemoved).toBeCalledTimes(1)
385
- expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1)
386
-
387
- })
388
-
389
336
  })
390
337
  })
@@ -1,34 +1,22 @@
1
1
  import joiValidator from "../../../middleware/joi-validator"
2
2
  import Joi from "joi"
3
3
 
4
- let schema: any = {
5
- email: Joi.string().allow(null, ""),
6
- password: Joi.string().allow(null, ""),
7
- forceResetPassword: Joi.boolean().optional(),
8
- firstName: Joi.string().allow(null, ""),
9
- lastName: Joi.string().allow(null, ""),
10
- builder: Joi.object({
11
- global: Joi.boolean().optional(),
12
- apps: Joi.array().optional(),
13
- })
14
- .unknown(true)
15
- .optional(),
16
- // maps appId -> roleId for the user
17
- roles: Joi.object().pattern(/.*/, Joi.string()).required().unknown(true),
18
- }
19
-
20
4
  export const buildUserSaveValidation = (isSelf = false) => {
21
- if (!isSelf) {
22
- schema = {
23
- ...schema,
24
- _id: Joi.string(),
25
- _rev: Joi.string(),
26
- }
5
+ let schema: any = {
6
+ email: Joi.string().allow(null, ""),
7
+ password: Joi.string().allow(null, ""),
8
+ forceResetPassword: Joi.boolean().optional(),
9
+ firstName: Joi.string().allow(null, ""),
10
+ lastName: Joi.string().allow(null, ""),
11
+ builder: Joi.object({
12
+ global: Joi.boolean().optional(),
13
+ apps: Joi.array().optional(),
14
+ })
15
+ .unknown(true)
16
+ .optional(),
17
+ // maps appId -> roleId for the user
18
+ roles: Joi.object().pattern(/.*/, Joi.string()).required().unknown(true),
27
19
  }
28
- return joiValidator.body(Joi.object(schema).required().unknown(true))
29
- }
30
-
31
- export const buildUserBulkSaveValidation = (isSelf = false) => {
32
20
  if (!isSelf) {
33
21
  schema = {
34
22
  ...schema,
@@ -36,10 +24,5 @@ export const buildUserBulkSaveValidation = (isSelf = false) => {
36
24
  _rev: Joi.string(),
37
25
  }
38
26
  }
39
- let bulkSaveSchema = {
40
- groups: Joi.array().optional(),
41
- users: Joi.array().items(Joi.object(schema).required().unknown(true)),
42
- }
43
-
44
- return joiValidator.body(Joi.object(bulkSaveSchema).required().unknown(true))
27
+ return joiValidator.body(Joi.object(schema).required().unknown(true))
45
28
  }
@@ -15,8 +15,7 @@ import {
15
15
  accounts,
16
16
  migrations,
17
17
  } from "@budibase/backend-core"
18
- import { MigrationType, User } from "@budibase/types"
19
- import { groups as groupUtils } from "@budibase/pro"
18
+ import { MigrationType } from "@budibase/types"
20
19
 
21
20
  const PAGE_LIMIT = 8
22
21
 
@@ -30,18 +29,10 @@ export const allUsers = async () => {
30
29
  return response.rows.map((row: any) => row.doc)
31
30
  }
32
31
 
33
- export const countUsersByApp = async (appId: string) => {
34
- let response: any = await usersCore.searchGlobalUsersByApp(appId, {})
35
- return {
36
- userCount: response.length,
37
- }
38
- }
39
-
40
32
  export const paginatedUsers = async ({
41
33
  page,
42
- email,
43
- appId,
44
- }: { page?: string; email?: string; appId?: string } = {}) => {
34
+ search,
35
+ }: { page?: string; search?: string } = {}) => {
45
36
  const db = tenancy.getGlobalDB()
46
37
  // get one extra document, to have the next page
47
38
  const opts: any = {
@@ -53,24 +44,19 @@ export const paginatedUsers = async ({
53
44
  opts.startkey = page
54
45
  }
55
46
  // property specifies what to use for the page/anchor
56
- let userList,
57
- property = "_id",
58
- getKey
59
- if (appId) {
60
- userList = await usersCore.searchGlobalUsersByApp(appId, opts)
61
- getKey = (doc: any) => usersCore.getGlobalUserByAppPage(appId, doc)
62
- } else if (email) {
63
- userList = await usersCore.searchGlobalUsersByEmail(email, opts)
64
- property = "email"
65
- } else {
66
- // no search, query allDocs
47
+ let userList, property
48
+ // no search, query allDocs
49
+ if (!search) {
67
50
  const response = await db.allDocs(dbUtils.getGlobalUserParams(null, opts))
68
51
  userList = response.rows.map((row: any) => row.doc)
52
+ property = "_id"
53
+ } else {
54
+ userList = await usersCore.searchGlobalUsersByEmail(search, opts)
55
+ property = "email"
69
56
  }
70
57
  return dbUtils.pagination(userList, PAGE_LIMIT, {
71
58
  paginate: true,
72
59
  property,
73
- getKey,
74
60
  })
75
61
  }
76
62
 
@@ -101,49 +87,6 @@ interface SaveUserOpts {
101
87
  bulkCreate?: boolean
102
88
  }
103
89
 
104
- export const buildUser = async (
105
- user: any,
106
- opts: SaveUserOpts = {
107
- hashPassword: true,
108
- requirePassword: true,
109
- bulkCreate: false,
110
- },
111
- tenantId: string,
112
- dbUser?: any
113
- ) => {
114
- let { password, _id } = user
115
-
116
- let hashedPassword
117
- if (password) {
118
- hashedPassword = opts.hashPassword ? await utils.hash(password) : password
119
- } else if (dbUser) {
120
- hashedPassword = dbUser.password
121
- } else if (opts.requirePassword) {
122
- throw "Password must be specified."
123
- }
124
-
125
- _id = _id || dbUtils.generateGlobalUserID()
126
-
127
- user = {
128
- createdAt: Date.now(),
129
- ...dbUser,
130
- ...user,
131
- _id,
132
- password: hashedPassword,
133
- tenantId,
134
- }
135
- // make sure the roles object is always present
136
- if (!user.roles) {
137
- user.roles = {}
138
- }
139
- // add the active status to a user if its not provided
140
- if (user.status == null) {
141
- user.status = constants.UserStatus.ACTIVE
142
- }
143
-
144
- return user
145
- }
146
-
147
90
  export const save = async (
148
91
  user: any,
149
92
  opts: SaveUserOpts = {
@@ -154,7 +97,7 @@ export const save = async (
154
97
  ) => {
155
98
  const tenantId = tenancy.getTenantId()
156
99
  const db = tenancy.getGlobalDB()
157
- let { email, _id } = user
100
+ let { email, password, _id } = user
158
101
  // make sure another user isn't using the same email
159
102
  let dbUser: any
160
103
  if (opts.bulkCreate) {
@@ -185,19 +128,36 @@ export const save = async (
185
128
  dbUser = await db.get(_id)
186
129
  }
187
130
 
188
- let builtUser = await buildUser(
189
- user,
190
- {
191
- hashPassword: true,
192
- requirePassword: user.requirePassword,
193
- },
194
- tenantId,
195
- dbUser
196
- )
131
+ // get the password, make sure one is defined
132
+ let hashedPassword
133
+ if (password) {
134
+ hashedPassword = opts.hashPassword ? await utils.hash(password) : password
135
+ } else if (dbUser) {
136
+ hashedPassword = dbUser.password
137
+ } else if (opts.requirePassword) {
138
+ throw "Password must be specified."
139
+ }
197
140
 
141
+ _id = _id || dbUtils.generateGlobalUserID()
142
+ user = {
143
+ createdAt: Date.now(),
144
+ ...dbUser,
145
+ ...user,
146
+ _id,
147
+ password: hashedPassword,
148
+ tenantId,
149
+ }
150
+ // make sure the roles object is always present
151
+ if (!user.roles) {
152
+ user.roles = {}
153
+ }
154
+ // add the active status to a user if its not provided
155
+ if (user.status == null) {
156
+ user.status = constants.UserStatus.ACTIVE
157
+ }
198
158
  try {
199
159
  const putOpts = {
200
- password: builtUser.password,
160
+ password: hashedPassword,
201
161
  ...user,
202
162
  }
203
163
  if (opts.bulkCreate) {
@@ -206,21 +166,28 @@ export const save = async (
206
166
  // save the user to db
207
167
  let response
208
168
  const putUserFn = () => {
209
- return db.put(builtUser)
169
+ return db.put(user)
210
170
  }
211
-
212
- if (eventHelpers.isAddingBuilder(builtUser, dbUser)) {
171
+ if (eventHelpers.isAddingBuilder(user, dbUser)) {
213
172
  response = await quotas.addDeveloper(putUserFn)
214
173
  } else {
215
174
  response = await putUserFn()
216
175
  }
217
- builtUser._rev = response.rev
176
+ user._rev = response.rev
177
+
178
+ await eventHelpers.handleSaveEvents(user, dbUser)
218
179
 
219
- await eventHelpers.handleSaveEvents(builtUser, dbUser)
220
- await addTenant(tenantId, _id, email)
180
+ if (env.MULTI_TENANCY) {
181
+ const afterCreateTenant = () =>
182
+ migrations.backPopulateMigrations({
183
+ type: MigrationType.GLOBAL,
184
+ tenantId,
185
+ })
186
+ await tenancy.tryAddTenant(tenantId, _id, email, afterCreateTenant)
187
+ }
221
188
  await cache.user.invalidateUser(response.id)
222
189
  // let server know to sync user
223
- await apps.syncUserInApps(builtUser._id)
190
+ await apps.syncUserInApps(user._id)
224
191
 
225
192
  return {
226
193
  _id: response.id,
@@ -236,143 +203,9 @@ export const save = async (
236
203
  }
237
204
  }
238
205
 
239
- export const addTenant = async (
240
- tenantId: string,
241
- _id: string,
242
- email: string
243
- ) => {
244
- if (env.MULTI_TENANCY) {
245
- const afterCreateTenant = () =>
246
- migrations.backPopulateMigrations({
247
- type: MigrationType.GLOBAL,
248
- tenantId,
249
- })
250
- await tenancy.tryAddTenant(tenantId, _id, email, afterCreateTenant)
251
- }
252
- }
253
-
254
- export const bulkCreate = async (
255
- newUsersRequested: User[],
256
- groups: string[]
257
- ) => {
258
- const db = tenancy.getGlobalDB()
259
- const tenantId = tenancy.getTenantId()
260
-
261
- let usersToSave: any[] = []
262
- let newUsers: any[] = []
263
-
264
- const allUsers = await db.allDocs(
265
- dbUtils.getGlobalUserParams(null, {
266
- include_docs: true,
267
- })
268
- )
269
- let mapped = allUsers.rows.map((row: any) => row.id)
270
-
271
- const currentUserEmails = mapped.map((x: any) => x.email) || []
272
- for (const newUser of newUsersRequested) {
273
- if (
274
- newUsers.find((x: any) => x.email === newUser.email) ||
275
- currentUserEmails.includes(newUser.email)
276
- ) {
277
- continue
278
- }
279
- newUser.userGroups = groups
280
- newUsers.push(newUser)
281
- }
282
-
283
- // Figure out how many builders we are adding and create the promises
284
- // array that will be called by bulkDocs
285
- let builderCount = 0
286
- newUsers.forEach((user: any) => {
287
- if (eventHelpers.isAddingBuilder(user, null)) {
288
- builderCount++
289
- }
290
- usersToSave.push(
291
- buildUser(
292
- user,
293
- {
294
- hashPassword: true,
295
- requirePassword: user.requirePassword,
296
- bulkCreate: false,
297
- },
298
- tenantId
299
- )
300
- )
301
- })
302
-
303
- const usersToBulkSave = await Promise.all(usersToSave)
304
- await quotas.addDevelopers(() => db.bulkDocs(usersToBulkSave), builderCount)
305
-
306
- // Post processing of bulk added users, i.e events and cache operations
307
- for (const user of usersToBulkSave) {
308
- await eventHelpers.handleSaveEvents(user, null)
309
- await apps.syncUserInApps(user._id)
310
- }
311
-
312
- return usersToBulkSave.map(user => {
313
- return {
314
- _id: user._id,
315
- email: user.email,
316
- }
317
- })
318
- }
319
-
320
- export const bulkDelete = async (userIds: any) => {
321
- const db = tenancy.getGlobalDB()
322
-
323
- let groupsToModify: any = {}
324
- let builderCount = 0
325
- // Get users and delete
326
- let usersToDelete = (
327
- await db.allDocs({
328
- include_docs: true,
329
- keys: userIds,
330
- })
331
- ).rows.map((user: any) => {
332
- // if we find a user that has an associated group, add it to
333
- // an array so we can easily use allDocs on them later.
334
- // This prevents us having to re-loop over all the users
335
- if (user.doc.userGroups) {
336
- for (let groupId of user.doc.userGroups) {
337
- if (!Object.keys(groupsToModify).includes(groupId)) {
338
- groupsToModify[groupId] = [user.id]
339
- } else {
340
- groupsToModify[groupId] = [...groupsToModify[groupId], user.id]
341
- }
342
- }
343
- }
344
-
345
- // Also figure out how many builders are being deleted
346
- if (eventHelpers.isAddingBuilder(user.doc, null)) {
347
- builderCount++
348
- }
349
-
350
- return user.doc
351
- })
352
-
353
- const response = await db.bulkDocs(
354
- usersToDelete.map((user: any) => ({
355
- ...user,
356
- _deleted: true,
357
- }))
358
- )
359
-
360
- await groupUtils.bulkDeleteGroupUsers(groupsToModify)
361
-
362
- //Deletion post processing
363
- for (let user of usersToDelete) {
364
- await bulkDeleteProcessing(user)
365
- }
366
-
367
- await quotas.removeDevelopers(builderCount)
368
-
369
- return response
370
- }
371
-
372
206
  export const destroy = async (id: string, currentUser: any) => {
373
207
  const db = tenancy.getGlobalDB()
374
208
  const dbUser = await db.get(id)
375
- let groups = dbUser.userGroups
376
209
 
377
210
  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
378
211
  // root account holder can't be deleted from inside budibase
@@ -388,13 +221,7 @@ export const destroy = async (id: string, currentUser: any) => {
388
221
  }
389
222
 
390
223
  await deprovisioning.removeUserFromInfoDB(dbUser)
391
-
392
224
  await db.remove(dbUser._id, dbUser._rev)
393
-
394
- if (groups) {
395
- await groupUtils.deleteGroupUsers(groups, dbUser)
396
- }
397
-
398
225
  await eventHelpers.handleDeleteEvents(dbUser)
399
226
  await quotas.removeUser(dbUser)
400
227
  await cache.user.invalidateUser(dbUser._id)
@@ -402,12 +229,3 @@ export const destroy = async (id: string, currentUser: any) => {
402
229
  // let server know to sync user
403
230
  await apps.syncUserInApps(dbUser._id)
404
231
  }
405
-
406
- const bulkDeleteProcessing = async (dbUser: User) => {
407
- await deprovisioning.removeUserFromInfoDB(dbUser)
408
- await eventHelpers.handleDeleteEvents(dbUser)
409
- await cache.user.invalidateUser(dbUser._id)
410
- await sessions.invalidateSessions(dbUser._id)
411
- // let server know to sync user
412
- await apps.syncUserInApps(dbUser._id)
413
- }
@@ -11,7 +11,7 @@ const { createASession } = require("@budibase/backend-core/sessions")
11
11
  const { TENANT_ID, CSRF_TOKEN } = require("./structures")
12
12
  const structures = require("./structures")
13
13
  const { doInTenant } = require("@budibase/backend-core/tenancy")
14
- const { groups } = require("@budibase/pro")
14
+
15
15
  class TestConfiguration {
16
16
  constructor(openServer = true) {
17
17
  if (openServer) {
@@ -116,22 +116,6 @@ class TestConfiguration {
116
116
  })
117
117
  }
118
118
 
119
- async getGroup(id) {
120
- return doInTenant(TENANT_ID, () => {
121
- return groups.get(id)
122
- })
123
- }
124
-
125
- async saveGroup(group) {
126
- const res = await this.getRequest()
127
- .post(`/api/global/groups`)
128
- .send(group)
129
- .set(this.defaultHeaders())
130
- .expect("Content-Type", /json/)
131
- .expect(200)
132
- return res.body
133
- }
134
-
135
119
  async createUser(email, password) {
136
120
  const user = await this.getUser(structures.users.email)
137
121
  if (user) {
@@ -1,6 +1,5 @@
1
1
  const configs = require("./configs")
2
2
  const users = require("./users")
3
- const groups = require("./groups")
4
3
 
5
4
  const TENANT_ID = "default"
6
5
  const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306"
@@ -10,5 +9,4 @@ module.exports = {
10
9
  users,
11
10
  TENANT_ID,
12
11
  CSRF_TOKEN,
13
- groups,
14
12
  }
@@ -185,20 +185,14 @@ exports.sendEmail = async (
185
185
  // if there is a link code needed this will retrieve it
186
186
  const code = await getLinkCode(purpose, email, user, info)
187
187
  const context = await getSettingsTemplateContext(purpose, code)
188
-
189
- let message = {
188
+ const message = {
190
189
  from: from || config.from,
190
+ to: email,
191
191
  html: await buildEmail(purpose, email, context, {
192
192
  user,
193
193
  contents,
194
194
  }),
195
195
  }
196
-
197
- message = {
198
- ...message,
199
- to: email,
200
- }
201
-
202
196
  if (subject || config.subject) {
203
197
  message.subject = await processString(subject || config.subject, context)
204
198
  }
@@ -1,11 +0,0 @@
1
- export const UserGroup = () => {
2
- let group = {
3
- apps: [],
4
- color: "var(--spectrum-global-color-blue-600)",
5
- icon: "UserGroup",
6
- name: "New group",
7
- roles: {},
8
- users: [],
9
- }
10
- return group
11
- }