@budibase/worker 1.1.29-alpha.1 → 1.1.29

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.29-alpha.1",
4
+ "version": "1.1.29",
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.29-alpha.1",
39
- "@budibase/pro": "1.1.29-alpha.0",
40
- "@budibase/string-templates": "^1.1.29-alpha.1",
41
- "@budibase/types": "^1.1.29-alpha.1",
38
+ "@budibase/backend-core": "^1.1.29",
39
+ "@budibase/pro": "1.1.28",
40
+ "@budibase/string-templates": "^1.1.29",
41
+ "@budibase/types": "^1.1.29",
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": "f00000b82e47fffa6341c813cfce59f76384b3a5"
104
+ "gitHead": "17638b7cf6b850d6cf472537f9334b9721875bc4"
105
105
  }
@@ -3,7 +3,7 @@ import { checkInviteCode } from "../../../utilities/redis"
3
3
  import { sendEmail } from "../../../utilities/email"
4
4
  import { users } from "../../../sdk"
5
5
  import env from "../../../environment"
6
- import { User, CloudAccount, UserGroup } from "@budibase/types"
6
+ import { User, CloudAccount } from "@budibase/types"
7
7
  import {
8
8
  events,
9
9
  errors,
@@ -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
  }
@@ -116,27 +84,12 @@ export const adminUser = async (ctx: any) => {
116
84
 
117
85
  export const destroy = async (ctx: any) => {
118
86
  const id = ctx.params.id
119
-
120
87
  await users.destroy(id, ctx.user)
121
-
122
88
  ctx.body = {
123
89
  message: `User ${id} deleted.`,
124
90
  }
125
91
  }
126
92
 
127
- export const bulkDelete = async (ctx: any) => {
128
- const { userIds } = ctx.request.body
129
- try {
130
- let usersResponse = await users.bulkDelete(userIds)
131
-
132
- ctx.body = {
133
- message: `${usersResponse.length} user(s) deleted`,
134
- }
135
- } catch (err) {
136
- ctx.throw(err)
137
- }
138
- }
139
-
140
93
  export const search = async (ctx: any) => {
141
94
  const paginated = await users.paginatedUsers(ctx.request.body)
142
95
  // user hashed password shouldn't ever be returned
@@ -196,39 +149,6 @@ export const invite = async (ctx: any) => {
196
149
  await events.user.invited()
197
150
  }
198
151
 
199
- export const inviteMultiple = async (ctx: any) => {
200
- let { emails, userInfo } = ctx.request.body
201
- let existing = false
202
- let existingEmail
203
- for (let email of emails) {
204
- if (await usersCore.getGlobalUserByEmail(email)) {
205
- existing = true
206
- existingEmail = email
207
- break
208
- }
209
- }
210
-
211
- if (existing) {
212
- ctx.throw(400, `${existingEmail} already exists`)
213
- }
214
- if (!userInfo) {
215
- userInfo = {}
216
- }
217
- userInfo.tenantId = tenancy.getTenantId()
218
- const opts: any = {
219
- subject: "{{ company }} platform invitation",
220
- info: userInfo,
221
- }
222
-
223
- for (let i = 0; i < emails.length; i++) {
224
- await sendEmail(emails[i], EmailTemplatePurpose.INVITATION, opts)
225
- }
226
-
227
- ctx.body = {
228
- message: "Invitations have been sent.",
229
- }
230
- }
231
-
232
152
  export const inviteAccept = async (ctx: any) => {
233
153
  const { inviteCode, password, firstName, lastName } = ctx.request.body
234
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,17 +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
51
  .get("/api/global/roles/:appId")
68
52
  .post(
69
53
  "/api/global/users/invite",
@@ -71,19 +55,6 @@ router
71
55
  buildInviteValidation(),
72
56
  controller.invite
73
57
  )
74
- .post(
75
- "/api/global/users/invite",
76
- adminOnly,
77
- buildInviteValidation(),
78
- controller.invite
79
- )
80
- .post(
81
- "/api/global/users/inviteMultiple",
82
- adminOnly,
83
- buildInviteMultipleValidation(),
84
- controller.inviteMultiple
85
- )
86
-
87
58
  // non-global endpoints
88
59
  .post(
89
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,12 +15,11 @@ 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
 
23
- export const allUsers = async (newDb?: any) => {
22
+ export const allUsers = async () => {
24
23
  const db = tenancy.getGlobalDB()
25
24
  const response = await db.allDocs(
26
25
  dbUtils.getGlobalUserParams(null, {
@@ -32,9 +31,8 @@ export const allUsers = async (newDb?: any) => {
32
31
 
33
32
  export const paginatedUsers = async ({
34
33
  page,
35
- email,
36
- appId,
37
- }: { page?: string; email?: string; appId?: string } = {}) => {
34
+ search,
35
+ }: { page?: string; search?: string } = {}) => {
38
36
  const db = tenancy.getGlobalDB()
39
37
  // get one extra document, to have the next page
40
38
  const opts: any = {
@@ -46,24 +44,19 @@ export const paginatedUsers = async ({
46
44
  opts.startkey = page
47
45
  }
48
46
  // property specifies what to use for the page/anchor
49
- let userList,
50
- property = "_id",
51
- getKey
52
- if (appId) {
53
- userList = await usersCore.searchGlobalUsersByApp(appId, opts)
54
- getKey = (doc: any) => usersCore.getGlobalUserByAppPage(appId, doc)
55
- } else if (email) {
56
- userList = await usersCore.searchGlobalUsersByEmail(email, opts)
57
- property = "email"
58
- } else {
59
- // no search, query allDocs
47
+ let userList, property
48
+ // no search, query allDocs
49
+ if (!search) {
60
50
  const response = await db.allDocs(dbUtils.getGlobalUserParams(null, opts))
61
51
  userList = response.rows.map((row: any) => row.doc)
52
+ property = "_id"
53
+ } else {
54
+ userList = await usersCore.searchGlobalUsersByEmail(search, opts)
55
+ property = "email"
62
56
  }
63
57
  return dbUtils.pagination(userList, PAGE_LIMIT, {
64
58
  paginate: true,
65
59
  property,
66
- getKey,
67
60
  })
68
61
  }
69
62
 
@@ -94,49 +87,6 @@ interface SaveUserOpts {
94
87
  bulkCreate?: boolean
95
88
  }
96
89
 
97
- export const buildUser = async (
98
- user: any,
99
- opts: SaveUserOpts = {
100
- hashPassword: true,
101
- requirePassword: true,
102
- bulkCreate: false,
103
- },
104
- tenantId: string,
105
- dbUser?: any
106
- ) => {
107
- let { password, _id } = user
108
-
109
- let hashedPassword
110
- if (password) {
111
- hashedPassword = opts.hashPassword ? await utils.hash(password) : password
112
- } else if (dbUser) {
113
- hashedPassword = dbUser.password
114
- } else if (opts.requirePassword) {
115
- throw "Password must be specified."
116
- }
117
-
118
- _id = _id || dbUtils.generateGlobalUserID()
119
-
120
- user = {
121
- createdAt: Date.now(),
122
- ...dbUser,
123
- ...user,
124
- _id,
125
- password: hashedPassword,
126
- tenantId,
127
- }
128
- // make sure the roles object is always present
129
- if (!user.roles) {
130
- user.roles = {}
131
- }
132
- // add the active status to a user if its not provided
133
- if (user.status == null) {
134
- user.status = constants.UserStatus.ACTIVE
135
- }
136
-
137
- return user
138
- }
139
-
140
90
  export const save = async (
141
91
  user: any,
142
92
  opts: SaveUserOpts = {
@@ -147,7 +97,7 @@ export const save = async (
147
97
  ) => {
148
98
  const tenantId = tenancy.getTenantId()
149
99
  const db = tenancy.getGlobalDB()
150
- let { email, _id } = user
100
+ let { email, password, _id } = user
151
101
  // make sure another user isn't using the same email
152
102
  let dbUser: any
153
103
  if (opts.bulkCreate) {
@@ -178,19 +128,36 @@ export const save = async (
178
128
  dbUser = await db.get(_id)
179
129
  }
180
130
 
181
- let builtUser = await buildUser(
182
- user,
183
- {
184
- hashPassword: true,
185
- requirePassword: user.requirePassword,
186
- },
187
- tenantId,
188
- dbUser
189
- )
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
+ }
190
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
+ }
191
158
  try {
192
159
  const putOpts = {
193
- password: builtUser.password,
160
+ password: hashedPassword,
194
161
  ...user,
195
162
  }
196
163
  if (opts.bulkCreate) {
@@ -199,21 +166,28 @@ export const save = async (
199
166
  // save the user to db
200
167
  let response
201
168
  const putUserFn = () => {
202
- return db.put(builtUser)
169
+ return db.put(user)
203
170
  }
204
-
205
- if (eventHelpers.isAddingBuilder(builtUser, dbUser)) {
171
+ if (eventHelpers.isAddingBuilder(user, dbUser)) {
206
172
  response = await quotas.addDeveloper(putUserFn)
207
173
  } else {
208
174
  response = await putUserFn()
209
175
  }
210
- builtUser._rev = response.rev
176
+ user._rev = response.rev
211
177
 
212
- await eventHelpers.handleSaveEvents(builtUser, dbUser)
213
- await addTenant(tenantId, _id, email)
178
+ await eventHelpers.handleSaveEvents(user, dbUser)
179
+
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
+ }
214
188
  await cache.user.invalidateUser(response.id)
215
189
  // let server know to sync user
216
- await apps.syncUserInApps(builtUser._id)
190
+ await apps.syncUserInApps(user._id)
217
191
 
218
192
  return {
219
193
  _id: response.id,
@@ -229,143 +203,9 @@ export const save = async (
229
203
  }
230
204
  }
231
205
 
232
- export const addTenant = async (
233
- tenantId: string,
234
- _id: string,
235
- email: string
236
- ) => {
237
- if (env.MULTI_TENANCY) {
238
- const afterCreateTenant = () =>
239
- migrations.backPopulateMigrations({
240
- type: MigrationType.GLOBAL,
241
- tenantId,
242
- })
243
- await tenancy.tryAddTenant(tenantId, _id, email, afterCreateTenant)
244
- }
245
- }
246
-
247
- export const bulkCreate = async (
248
- newUsersRequested: User[],
249
- groups: string[]
250
- ) => {
251
- const db = tenancy.getGlobalDB()
252
- const tenantId = tenancy.getTenantId()
253
-
254
- let usersToSave: any[] = []
255
- let newUsers: any[] = []
256
-
257
- const allUsers = await db.allDocs(
258
- dbUtils.getGlobalUserParams(null, {
259
- include_docs: true,
260
- })
261
- )
262
- let mapped = allUsers.rows.map((row: any) => row.id)
263
-
264
- const currentUserEmails = mapped.map((x: any) => x.email) || []
265
- for (const newUser of newUsersRequested) {
266
- if (
267
- newUsers.find((x: any) => x.email === newUser.email) ||
268
- currentUserEmails.includes(newUser.email)
269
- ) {
270
- continue
271
- }
272
- newUser.userGroups = groups
273
- newUsers.push(newUser)
274
- }
275
-
276
- // Figure out how many builders we are adding and create the promises
277
- // array that will be called by bulkDocs
278
- let builderCount = 0
279
- newUsers.forEach((user: any) => {
280
- if (eventHelpers.isAddingBuilder(user, null)) {
281
- builderCount++
282
- }
283
- usersToSave.push(
284
- buildUser(
285
- user,
286
- {
287
- hashPassword: true,
288
- requirePassword: user.requirePassword,
289
- bulkCreate: false,
290
- },
291
- tenantId
292
- )
293
- )
294
- })
295
-
296
- const usersToBulkSave = await Promise.all(usersToSave)
297
- await quotas.addDevelopers(() => db.bulkDocs(usersToBulkSave), builderCount)
298
-
299
- // Post processing of bulk added users, i.e events and cache operations
300
- for (const user of usersToBulkSave) {
301
- await eventHelpers.handleSaveEvents(user, null)
302
- await apps.syncUserInApps(user._id)
303
- }
304
-
305
- return usersToBulkSave.map(user => {
306
- return {
307
- _id: user._id,
308
- email: user.email,
309
- }
310
- })
311
- }
312
-
313
- export const bulkDelete = async (userIds: any) => {
314
- const db = tenancy.getGlobalDB()
315
-
316
- let groupsToModify: any = {}
317
- let builderCount = 0
318
- // Get users and delete
319
- let usersToDelete = (
320
- await db.allDocs({
321
- include_docs: true,
322
- keys: userIds,
323
- })
324
- ).rows.map((user: any) => {
325
- // if we find a user that has an associated group, add it to
326
- // an array so we can easily use allDocs on them later.
327
- // This prevents us having to re-loop over all the users
328
- if (user.doc.userGroups) {
329
- for (let groupId of user.doc.userGroups) {
330
- if (!Object.keys(groupsToModify).includes(groupId)) {
331
- groupsToModify[groupId] = [user.id]
332
- } else {
333
- groupsToModify[groupId] = [...groupsToModify[groupId], user.id]
334
- }
335
- }
336
- }
337
-
338
- // Also figure out how many builders are being deleted
339
- if (eventHelpers.isAddingBuilder(user.doc, null)) {
340
- builderCount++
341
- }
342
-
343
- return user.doc
344
- })
345
-
346
- const response = await db.bulkDocs(
347
- usersToDelete.map((user: any) => ({
348
- ...user,
349
- _deleted: true,
350
- }))
351
- )
352
-
353
- await groupUtils.bulkDeleteGroupUsers(groupsToModify)
354
-
355
- //Deletion post processing
356
- for (let user of usersToDelete) {
357
- await bulkDeleteProcessing(user)
358
- }
359
-
360
- await quotas.removeDevelopers(builderCount)
361
-
362
- return response
363
- }
364
-
365
206
  export const destroy = async (id: string, currentUser: any) => {
366
207
  const db = tenancy.getGlobalDB()
367
208
  const dbUser = await db.get(id)
368
- let groups = dbUser.userGroups
369
209
 
370
210
  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
371
211
  // root account holder can't be deleted from inside budibase
@@ -381,13 +221,7 @@ export const destroy = async (id: string, currentUser: any) => {
381
221
  }
382
222
 
383
223
  await deprovisioning.removeUserFromInfoDB(dbUser)
384
-
385
224
  await db.remove(dbUser._id, dbUser._rev)
386
-
387
- if (groups) {
388
- await groupUtils.deleteGroupUsers(groups, dbUser)
389
- }
390
-
391
225
  await eventHelpers.handleDeleteEvents(dbUser)
392
226
  await quotas.removeUser(dbUser)
393
227
  await cache.user.invalidateUser(dbUser._id)
@@ -395,12 +229,3 @@ export const destroy = async (id: string, currentUser: any) => {
395
229
  // let server know to sync user
396
230
  await apps.syncUserInApps(dbUser._id)
397
231
  }
398
-
399
- const bulkDeleteProcessing = async (dbUser: User) => {
400
- await deprovisioning.removeUserFromInfoDB(dbUser)
401
- await eventHelpers.handleDeleteEvents(dbUser)
402
- await cache.user.invalidateUser(dbUser._id)
403
- await sessions.invalidateSessions(dbUser._id)
404
- // let server know to sync user
405
- await apps.syncUserInApps(dbUser._id)
406
- }
@@ -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
- }