@budibase/worker 2.3.17 → 2.3.18-alpha.0

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 (166) hide show
  1. package/__mocks__/node-fetch.ts +23 -0
  2. package/jest-testcontainers-config.js +8 -0
  3. package/jest.config.ts +3 -2
  4. package/package.json +10 -7
  5. package/scripts/dev/manage.js +1 -0
  6. package/src/api/controllers/global/auth.ts +77 -70
  7. package/src/api/controllers/global/self.ts +28 -44
  8. package/src/api/controllers/global/users.ts +65 -25
  9. package/src/api/controllers/system/accounts.ts +7 -5
  10. package/src/api/controllers/system/tenants.ts +4 -8
  11. package/src/api/index.ts +4 -20
  12. package/src/api/routes/global/auth.ts +10 -7
  13. package/src/api/routes/global/tests/auth.spec.ts +226 -17
  14. package/src/api/routes/global/tests/configs.spec.ts +5 -5
  15. package/src/api/routes/global/tests/roles.spec.ts +20 -14
  16. package/src/api/routes/global/tests/self.spec.ts +3 -4
  17. package/src/api/routes/global/tests/users.spec.ts +24 -27
  18. package/src/api/routes/system/tenants.ts +1 -1
  19. package/src/api/routes/system/tests/accounts.spec.ts +4 -4
  20. package/src/api/routes/system/tests/migrations.spec.ts +2 -2
  21. package/src/api/routes/system/tests/restore.spec.ts +2 -2
  22. package/src/api/routes/system/tests/status.spec.ts +5 -5
  23. package/src/ddApm.ts +7 -0
  24. package/src/elasticApm.ts +10 -0
  25. package/src/environment.ts +15 -1
  26. package/src/index.ts +12 -7
  27. package/src/middleware/tests/tenancy.spec.ts +4 -4
  28. package/src/migrations/functions/globalInfoSyncUsers.ts +4 -3
  29. package/src/sdk/accounts/index.ts +2 -1
  30. package/src/sdk/accounts/{accounts.ts → metadata.ts} +0 -1
  31. package/src/sdk/auth/auth.ts +86 -0
  32. package/src/sdk/auth/index.ts +1 -0
  33. package/src/sdk/tenants/index.ts +1 -0
  34. package/src/sdk/tenants/tenants.ts +76 -0
  35. package/src/sdk/users/events.ts +4 -0
  36. package/src/sdk/users/index.ts +1 -0
  37. package/src/sdk/users/tests/users.spec.ts +52 -0
  38. package/src/sdk/users/users.ts +53 -46
  39. package/src/tests/TestConfiguration.ts +41 -62
  40. package/src/tests/api/auth.ts +42 -14
  41. package/src/tests/api/base.ts +2 -1
  42. package/src/tests/api/configs.ts +2 -2
  43. package/src/tests/api/restore.ts +1 -0
  44. package/src/tests/api/users.ts +2 -2
  45. package/src/tests/jestEnv.ts +2 -1
  46. package/src/tests/jestSetup.ts +10 -6
  47. package/src/tests/logging.ts +34 -0
  48. package/src/tests/structures/index.ts +0 -4
  49. package/src/utilities/email.ts +3 -3
  50. package/coverage/clover.xml +0 -2199
  51. package/coverage/coverage-final.json +0 -84
  52. package/coverage/lcov-report/base.css +0 -224
  53. package/coverage/lcov-report/block-navigation.js +0 -87
  54. package/coverage/lcov-report/favicon.png +0 -0
  55. package/coverage/lcov-report/index.html +0 -431
  56. package/coverage/lcov-report/prettify.css +0 -1
  57. package/coverage/lcov-report/prettify.js +0 -2
  58. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  59. package/coverage/lcov-report/sorter.js +0 -196
  60. package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +0 -934
  61. package/coverage/lcov-report/src/api/controllers/global/configs.ts.html +0 -1348
  62. package/coverage/lcov-report/src/api/controllers/global/email.ts.html +0 -196
  63. package/coverage/lcov-report/src/api/controllers/global/events.ts.html +0 -136
  64. package/coverage/lcov-report/src/api/controllers/global/index.html +0 -251
  65. package/coverage/lcov-report/src/api/controllers/global/license.ts.html +0 -187
  66. package/coverage/lcov-report/src/api/controllers/global/roles.ts.html +0 -283
  67. package/coverage/lcov-report/src/api/controllers/global/self.ts.html +0 -577
  68. package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +0 -316
  69. package/coverage/lcov-report/src/api/controllers/global/users.ts.html +0 -838
  70. package/coverage/lcov-report/src/api/controllers/global/workspaces.ts.html +0 -244
  71. package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +0 -148
  72. package/coverage/lcov-report/src/api/controllers/system/environment.ts.html +0 -124
  73. package/coverage/lcov-report/src/api/controllers/system/index.html +0 -191
  74. package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +0 -124
  75. package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +0 -124
  76. package/coverage/lcov-report/src/api/controllers/system/status.ts.html +0 -133
  77. package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +0 -154
  78. package/coverage/lcov-report/src/api/index.html +0 -116
  79. package/coverage/lcov-report/src/api/index.ts.html +0 -595
  80. package/coverage/lcov-report/src/api/routes/global/auth.ts.html +0 -349
  81. package/coverage/lcov-report/src/api/routes/global/configs.ts.html +0 -457
  82. package/coverage/lcov-report/src/api/routes/global/email.ts.html +0 -193
  83. package/coverage/lcov-report/src/api/routes/global/events.ts.html +0 -109
  84. package/coverage/lcov-report/src/api/routes/global/index.html +0 -251
  85. package/coverage/lcov-report/src/api/routes/global/license.ts.html +0 -124
  86. package/coverage/lcov-report/src/api/routes/global/roles.ts.html +0 -133
  87. package/coverage/lcov-report/src/api/routes/global/self.ts.html +0 -139
  88. package/coverage/lcov-report/src/api/routes/global/templates.ts.html +0 -196
  89. package/coverage/lcov-report/src/api/routes/global/users.ts.html +0 -475
  90. package/coverage/lcov-report/src/api/routes/global/workspaces.ts.html +0 -196
  91. package/coverage/lcov-report/src/api/routes/index.html +0 -116
  92. package/coverage/lcov-report/src/api/routes/index.ts.html +0 -202
  93. package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +0 -142
  94. package/coverage/lcov-report/src/api/routes/system/environment.ts.html +0 -109
  95. package/coverage/lcov-report/src/api/routes/system/index.html +0 -191
  96. package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +0 -142
  97. package/coverage/lcov-report/src/api/routes/system/restore.ts.html +0 -109
  98. package/coverage/lcov-report/src/api/routes/system/status.ts.html +0 -109
  99. package/coverage/lcov-report/src/api/routes/system/tenants.ts.html +0 -124
  100. package/coverage/lcov-report/src/api/routes/validation/index.html +0 -131
  101. package/coverage/lcov-report/src/api/routes/validation/index.ts.html +0 -88
  102. package/coverage/lcov-report/src/api/routes/validation/users.ts.html +0 -235
  103. package/coverage/lcov-report/src/constants/index.html +0 -116
  104. package/coverage/lcov-report/src/constants/index.ts.html +0 -637
  105. package/coverage/lcov-report/src/constants/templates/index.html +0 -116
  106. package/coverage/lcov-report/src/constants/templates/index.ts.html +0 -316
  107. package/coverage/lcov-report/src/db/index.html +0 -116
  108. package/coverage/lcov-report/src/db/index.ts.html +0 -115
  109. package/coverage/lcov-report/src/environment.ts.html +0 -388
  110. package/coverage/lcov-report/src/index.html +0 -131
  111. package/coverage/lcov-report/src/index.ts.html +0 -394
  112. package/coverage/lcov-report/src/middleware/cloudRestricted.ts.html +0 -139
  113. package/coverage/lcov-report/src/middleware/index.html +0 -116
  114. package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +0 -145
  115. package/coverage/lcov-report/src/migrations/functions/index.html +0 -116
  116. package/coverage/lcov-report/src/migrations/index.html +0 -116
  117. package/coverage/lcov-report/src/migrations/index.ts.html +0 -271
  118. package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +0 -262
  119. package/coverage/lcov-report/src/sdk/accounts/index.html +0 -131
  120. package/coverage/lcov-report/src/sdk/accounts/index.ts.html +0 -88
  121. package/coverage/lcov-report/src/sdk/index.html +0 -116
  122. package/coverage/lcov-report/src/sdk/index.ts.html +0 -106
  123. package/coverage/lcov-report/src/sdk/users/events.ts.html +0 -601
  124. package/coverage/lcov-report/src/sdk/users/index.html +0 -146
  125. package/coverage/lcov-report/src/sdk/users/index.ts.html +0 -88
  126. package/coverage/lcov-report/src/sdk/users/users.ts.html +0 -1999
  127. package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +0 -1075
  128. package/coverage/lcov-report/src/tests/api/accounts.ts.html +0 -160
  129. package/coverage/lcov-report/src/tests/api/auth.ts.html +0 -220
  130. package/coverage/lcov-report/src/tests/api/base.ts.html +0 -133
  131. package/coverage/lcov-report/src/tests/api/configs.ts.html +0 -226
  132. package/coverage/lcov-report/src/tests/api/email.ts.html +0 -148
  133. package/coverage/lcov-report/src/tests/api/environment.ts.html +0 -130
  134. package/coverage/lcov-report/src/tests/api/groups.ts.html +0 -163
  135. package/coverage/lcov-report/src/tests/api/index.html +0 -356
  136. package/coverage/lcov-report/src/tests/api/index.ts.html +0 -238
  137. package/coverage/lcov-report/src/tests/api/license.ts.html +0 -136
  138. package/coverage/lcov-report/src/tests/api/migrations.ts.html +0 -151
  139. package/coverage/lcov-report/src/tests/api/restore.ts.html +0 -127
  140. package/coverage/lcov-report/src/tests/api/roles.ts.html +0 -181
  141. package/coverage/lcov-report/src/tests/api/self.ts.html +0 -163
  142. package/coverage/lcov-report/src/tests/api/status.ts.html +0 -121
  143. package/coverage/lcov-report/src/tests/api/templates.ts.html +0 -175
  144. package/coverage/lcov-report/src/tests/api/tenants.ts.html +0 -130
  145. package/coverage/lcov-report/src/tests/api/users.ts.html +0 -514
  146. package/coverage/lcov-report/src/tests/controllers.ts.html +0 -100
  147. package/coverage/lcov-report/src/tests/index.html +0 -146
  148. package/coverage/lcov-report/src/tests/index.ts.html +0 -103
  149. package/coverage/lcov-report/src/tests/mocks/email.ts.html +0 -115
  150. package/coverage/lcov-report/src/tests/mocks/index.html +0 -131
  151. package/coverage/lcov-report/src/tests/mocks/index.ts.html +0 -106
  152. package/coverage/lcov-report/src/tests/structures/configs.ts.html +0 -313
  153. package/coverage/lcov-report/src/tests/structures/groups.ts.html +0 -118
  154. package/coverage/lcov-report/src/tests/structures/index.html +0 -161
  155. package/coverage/lcov-report/src/tests/structures/index.ts.html +0 -151
  156. package/coverage/lcov-report/src/tests/structures/users.ts.html +0 -196
  157. package/coverage/lcov-report/src/utilities/appService.ts.html +0 -208
  158. package/coverage/lcov-report/src/utilities/email.ts.html +0 -850
  159. package/coverage/lcov-report/src/utilities/fileSystem.ts.html +0 -100
  160. package/coverage/lcov-report/src/utilities/index.html +0 -206
  161. package/coverage/lcov-report/src/utilities/index.ts.html +0 -112
  162. package/coverage/lcov-report/src/utilities/redis.ts.html +0 -424
  163. package/coverage/lcov-report/src/utilities/templates.ts.html +0 -232
  164. package/coverage/lcov-report/src/utilities/users.ts.html +0 -133
  165. package/coverage/lcov.info +0 -4187
  166. package/src/tests/structures/users.ts +0 -37
@@ -0,0 +1,86 @@
1
+ import {
2
+ auth as authCore,
3
+ tenancy,
4
+ utils as coreUtils,
5
+ sessions,
6
+ events,
7
+ HTTPError,
8
+ } from "@budibase/backend-core"
9
+ import { PlatformLogoutOpts, User } from "@budibase/types"
10
+ import jwt from "jsonwebtoken"
11
+ import env from "../../environment"
12
+ import * as userSdk from "../users"
13
+ import * as emails from "../../utilities/email"
14
+ import * as redis from "../../utilities/redis"
15
+ import { EmailTemplatePurpose } from "../../constants"
16
+
17
+ // LOGIN / LOGOUT
18
+
19
+ export async function loginUser(user: User) {
20
+ const sessionId = coreUtils.newid()
21
+ const tenantId = tenancy.getTenantId()
22
+ await sessions.createASession(user._id!, { sessionId, tenantId })
23
+ const token = jwt.sign(
24
+ {
25
+ userId: user._id,
26
+ sessionId,
27
+ tenantId,
28
+ },
29
+ env.JWT_SECRET!
30
+ )
31
+ return token
32
+ }
33
+
34
+ export async function logout(opts: PlatformLogoutOpts) {
35
+ // TODO: This should be moved out of core and into worker only
36
+ // account-portal can call worker endpoint
37
+ return authCore.platformLogout(opts)
38
+ }
39
+
40
+ // PASSWORD MANAGEMENT
41
+
42
+ /**
43
+ * Reset the user password, used as part of a forgotten password flow.
44
+ */
45
+ export const reset = async (email: string) => {
46
+ const configured = await emails.isEmailConfigured()
47
+ if (!configured) {
48
+ throw new HTTPError(
49
+ "Please contact your platform administrator, SMTP is not configured.",
50
+ 400
51
+ )
52
+ }
53
+
54
+ const user = await userSdk.core.getGlobalUserByEmail(email)
55
+ // exit if user doesn't exist
56
+ if (!user) {
57
+ return
58
+ }
59
+
60
+ // exit if user has sso
61
+ if (await userSdk.isPreventSSOPasswords(user)) {
62
+ throw new HTTPError("SSO user cannot reset password", 400)
63
+ }
64
+
65
+ // send password reset
66
+ await emails.sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, {
67
+ user,
68
+ subject: "{{ company }} platform password reset",
69
+ })
70
+ await events.user.passwordResetRequested(user)
71
+ }
72
+
73
+ /**
74
+ * Perform the user password update if the provided reset code is valid.
75
+ */
76
+ export const resetUpdate = async (resetCode: string, password: string) => {
77
+ const { userId } = await redis.checkResetPasswordCode(resetCode)
78
+
79
+ let user = await userSdk.getUser(userId)
80
+ user.password = password
81
+ user = await userSdk.save(user)
82
+
83
+ // remove password from the user before sending events
84
+ delete user.password
85
+ await events.user.passwordReset(user)
86
+ }
@@ -0,0 +1 @@
1
+ export * from "./auth"
@@ -0,0 +1 @@
1
+ export * from "./tenants"
@@ -0,0 +1,76 @@
1
+ import { App } from "@budibase/types"
2
+ import { tenancy, db as dbCore, platform } from "@budibase/backend-core"
3
+ import { quotas } from "@budibase/pro"
4
+
5
+ export async function deleteTenant(tenantId: string) {
6
+ await quotas.bustCache()
7
+ await platform.tenants.removeTenant(tenantId)
8
+ await removeTenantUsers(tenantId)
9
+ await removeTenantApps(tenantId)
10
+ await removeGlobalDB(tenantId)
11
+ }
12
+
13
+ async function removeGlobalDB(tenantId: string) {
14
+ try {
15
+ const db = tenancy.getTenantDB(tenantId)
16
+ await db.destroy()
17
+ } catch (err) {
18
+ console.error(`Error removing tenant ${tenantId} users from info db`, err)
19
+ throw err
20
+ }
21
+ }
22
+
23
+ async function removeTenantApps(tenantId: string) {
24
+ try {
25
+ const apps = (await dbCore.getAllApps({ all: true })) as App[]
26
+ const destroyPromises = apps.map(app => {
27
+ const db = dbCore.getDB(app.appId)
28
+ return db.destroy()
29
+ })
30
+ await Promise.allSettled(destroyPromises)
31
+ } catch (err) {
32
+ console.error(`Error removing tenant ${tenantId} apps`, err)
33
+ throw err
34
+ }
35
+ }
36
+
37
+ function getTenantUsers(tenantId: string) {
38
+ const db = tenancy.getTenantDB(tenantId)
39
+
40
+ return db.allDocs(
41
+ dbCore.getGlobalUserParams(null, {
42
+ include_docs: true,
43
+ })
44
+ )
45
+ }
46
+
47
+ async function removeTenantUsers(tenantId: string) {
48
+ try {
49
+ const allUsers = await getTenantUsers(tenantId)
50
+ const allEmails = allUsers.rows.map((row: any) => row.doc.email)
51
+
52
+ // get the id and email doc ids
53
+ let keys = allUsers.rows.map((row: any) => row.id)
54
+ keys = keys.concat(allEmails)
55
+
56
+ const platformDb = platform.getPlatformDB()
57
+
58
+ // retrieve the docs
59
+ const userDocs = await platformDb.allDocs({
60
+ keys,
61
+ include_docs: true,
62
+ })
63
+
64
+ // delete the docs
65
+ const toDelete = userDocs.rows.map((row: any) => {
66
+ return {
67
+ ...row.doc,
68
+ _deleted: true,
69
+ }
70
+ })
71
+ await platformDb.bulkDocs(toDelete)
72
+ } catch (err) {
73
+ console.error(`Error removing tenant ${tenantId} users from info db`, err)
74
+ throw err
75
+ }
76
+ }
@@ -84,6 +84,10 @@ export const handleSaveEvents = async (
84
84
  ) {
85
85
  await events.user.passwordForceReset(user)
86
86
  }
87
+
88
+ if (user.password !== existingUser.password) {
89
+ await events.user.passwordUpdated(user)
90
+ }
87
91
  } else {
88
92
  await events.user.created(user)
89
93
  }
@@ -1 +1,2 @@
1
1
  export * from "./users"
2
+ export { users as core } from "@budibase/backend-core"
@@ -0,0 +1,52 @@
1
+ import { structures } from "../../../tests"
2
+ import * as users from "../users"
3
+ import env from "../../../environment"
4
+ import { mocks } from "@budibase/backend-core/tests"
5
+ import { CloudAccount } from "@budibase/types"
6
+
7
+ describe("users", () => {
8
+ describe("isPreventSSOPasswords", () => {
9
+ it("returns true for sso account user", async () => {
10
+ const user = structures.users.user()
11
+ mocks.accounts.getAccount.mockReturnValue(
12
+ Promise.resolve(structures.accounts.ssoAccount() as CloudAccount)
13
+ )
14
+ const result = await users.isPreventSSOPasswords(user)
15
+ expect(result).toBe(true)
16
+ })
17
+
18
+ it("returns true for sso user", async () => {
19
+ const user = structures.users.ssoUser()
20
+ const result = await users.isPreventSSOPasswords(user)
21
+ expect(result).toBe(true)
22
+ })
23
+
24
+ describe("sso maintenance mode", () => {
25
+ beforeEach(() => {
26
+ env._set("ENABLE_SSO_MAINTENANCE_MODE", true)
27
+ })
28
+
29
+ afterEach(() => {
30
+ env._set("ENABLE_SSO_MAINTENANCE_MODE", false)
31
+ })
32
+
33
+ describe("non-admin user", () => {
34
+ it("returns true", async () => {
35
+ const user = structures.users.ssoUser()
36
+ const result = await users.isPreventSSOPasswords(user)
37
+ expect(result).toBe(true)
38
+ })
39
+ })
40
+
41
+ describe("admin user", () => {
42
+ it("returns false", async () => {
43
+ const user = structures.users.ssoUser({
44
+ user: structures.users.adminUser(),
45
+ })
46
+ const result = await users.isPreventSSOPasswords(user)
47
+ expect(result).toBe(false)
48
+ })
49
+ })
50
+ })
51
+ })
52
+ })
@@ -6,12 +6,11 @@ import {
6
6
  cache,
7
7
  constants,
8
8
  db as dbUtils,
9
- deprovisioning,
10
9
  events,
11
10
  HTTPError,
12
- migrations,
13
11
  sessions,
14
12
  tenancy,
13
+ platform,
15
14
  users as usersCore,
16
15
  utils,
17
16
  ViewName,
@@ -21,21 +20,22 @@ import {
21
20
  AllDocsResponse,
22
21
  BulkUserResponse,
23
22
  CloudAccount,
24
- CreateUserResponse,
25
23
  InviteUsersRequest,
26
24
  InviteUsersResponse,
27
- MigrationType,
25
+ isSSOAccount,
26
+ isSSOUser,
28
27
  PlatformUser,
29
28
  PlatformUserByEmail,
30
29
  RowResponse,
31
30
  SearchUsersRequest,
31
+ UpdateSelf,
32
32
  User,
33
- ThirdPartyUser,
34
- isUser,
33
+ SaveUserOpts,
35
34
  } from "@budibase/types"
36
35
  import { sendEmail } from "../../utilities/email"
37
36
  import { EmailTemplatePurpose } from "../../constants"
38
37
  import { groups as groupsSdk } from "@budibase/pro"
38
+ import * as accountSdk from "../accounts"
39
39
 
40
40
  const PAGE_LIMIT = 8
41
41
 
@@ -94,26 +94,23 @@ export const paginatedUsers = async ({
94
94
  })
95
95
  }
96
96
 
97
+ export async function getUserByEmail(email: string) {
98
+ return usersCore.getGlobalUserByEmail(email)
99
+ }
100
+
97
101
  /**
98
102
  * Gets a user by ID from the global database, based on the current tenancy.
99
103
  */
100
104
  export const getUser = async (userId: string) => {
101
- const db = tenancy.getGlobalDB()
102
- let user = await db.get(userId)
105
+ const user = await usersCore.getById(userId)
103
106
  if (user) {
104
107
  delete user.password
105
108
  }
106
109
  return user
107
110
  }
108
111
 
109
- export interface SaveUserOpts {
110
- hashPassword?: boolean
111
- requirePassword?: boolean
112
- currentUserId?: string
113
- }
114
-
115
112
  const buildUser = async (
116
- user: User | ThirdPartyUser,
113
+ user: User,
117
114
  opts: SaveUserOpts = {
118
115
  hashPassword: true,
119
116
  requirePassword: true,
@@ -121,11 +118,13 @@ const buildUser = async (
121
118
  tenantId: string,
122
119
  dbUser?: any
123
120
  ): Promise<User> => {
124
- let fullUser = user as User
125
- let { password, _id } = fullUser
121
+ let { password, _id } = user
126
122
 
127
123
  let hashedPassword
128
124
  if (password) {
125
+ if (await isPreventSSOPasswords(user)) {
126
+ throw new HTTPError("SSO user cannot set password", 400)
127
+ }
129
128
  hashedPassword = opts.hashPassword ? await utils.hash(password) : password
130
129
  } else if (dbUser) {
131
130
  hashedPassword = dbUser.password
@@ -135,10 +134,10 @@ const buildUser = async (
135
134
 
136
135
  _id = _id || dbUtils.generateGlobalUserID()
137
136
 
138
- fullUser = {
137
+ const fullUser = {
139
138
  createdAt: Date.now(),
140
139
  ...dbUser,
141
- ...fullUser,
140
+ ...user,
142
141
  _id,
143
142
  password: hashedPassword,
144
143
  tenantId,
@@ -189,10 +188,36 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
189
188
  }
190
189
  }
191
190
 
191
+ export async function isPreventSSOPasswords(user: User) {
192
+ // when in maintenance mode we allow sso users with the admin role
193
+ // to perform any password action - this prevents lockout
194
+ if (env.ENABLE_SSO_MAINTENANCE_MODE && user.admin?.global) {
195
+ return false
196
+ }
197
+
198
+ // Check local sso
199
+ if (isSSOUser(user)) {
200
+ return true
201
+ }
202
+
203
+ // Check account sso
204
+ const account = await accountSdk.api.getAccount(user.email)
205
+ return !!(account && isSSOAccount(account))
206
+ }
207
+
208
+ export async function updateSelf(id: string, data: UpdateSelf) {
209
+ let user = await getUser(id)
210
+ user = {
211
+ ...user,
212
+ ...data,
213
+ }
214
+ return save(user)
215
+ }
216
+
192
217
  export const save = async (
193
- user: User | ThirdPartyUser,
218
+ user: User,
194
219
  opts: SaveUserOpts = {}
195
- ): Promise<CreateUserResponse> => {
220
+ ): Promise<User> => {
196
221
  // default booleans to true
197
222
  if (opts.hashPassword == null) {
198
223
  opts.hashPassword = true
@@ -264,7 +289,7 @@ export const save = async (
264
289
  builtUser._rev = response.rev
265
290
 
266
291
  await eventHelpers.handleSaveEvents(builtUser, dbUser)
267
- await addTenant(tenantId, _id, email)
292
+ await platform.users.addUser(tenantId, builtUser._id!, builtUser.email)
268
293
  await cache.user.invalidateUser(response.id)
269
294
 
270
295
  // let server know to sync user
@@ -272,11 +297,8 @@ export const save = async (
272
297
 
273
298
  await Promise.all(groupPromises)
274
299
 
275
- return {
276
- _id: response.id,
277
- _rev: response.rev,
278
- email,
279
- }
300
+ // finally returned the saved user from the db
301
+ return db.get(builtUser._id!)
280
302
  } catch (err: any) {
281
303
  if (err.status === 409) {
282
304
  throw "User exists already"
@@ -286,21 +308,6 @@ export const save = async (
286
308
  }
287
309
  }
288
310
 
289
- export const addTenant = async (
290
- tenantId: string,
291
- _id: string,
292
- email: string
293
- ) => {
294
- if (env.MULTI_TENANCY) {
295
- const afterCreateTenant = () =>
296
- migrations.backPopulateMigrations({
297
- type: MigrationType.GLOBAL,
298
- tenantId,
299
- })
300
- await tenancy.tryAddTenant(tenantId, _id, email, afterCreateTenant)
301
- }
302
- }
303
-
304
311
  const getExistingTenantUsers = async (emails: string[]): Promise<User[]> => {
305
312
  const lcEmails = emails.map(email => email.toLowerCase())
306
313
  const params = {
@@ -432,7 +439,7 @@ export const bulkCreate = async (
432
439
  for (const user of usersToBulkSave) {
433
440
  // TODO: Refactor to bulk insert users into the info db
434
441
  // instead of relying on looping tenant creation
435
- await addTenant(tenantId, user._id, user.email)
442
+ await platform.users.addUser(tenantId, user._id, user.email)
436
443
  await eventHelpers.handleSaveEvents(user, undefined)
437
444
  await apps.syncUserInApps(user._id)
438
445
  }
@@ -550,7 +557,7 @@ export const bulkDelete = async (
550
557
 
551
558
  export const destroy = async (id: string, currentUser: any) => {
552
559
  const db = tenancy.getGlobalDB()
553
- const dbUser = await db.get(id)
560
+ const dbUser = (await db.get(id)) as User
554
561
  const userId = dbUser._id as string
555
562
 
556
563
  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
@@ -566,7 +573,7 @@ export const destroy = async (id: string, currentUser: any) => {
566
573
  }
567
574
  }
568
575
 
569
- await deprovisioning.removeUserFromInfoDB(dbUser)
576
+ await platform.users.removeUser(dbUser)
570
577
 
571
578
  await db.remove(userId, dbUser._rev)
572
579
 
@@ -579,7 +586,7 @@ export const destroy = async (id: string, currentUser: any) => {
579
586
 
580
587
  const bulkDeleteProcessing = async (dbUser: User) => {
581
588
  const userId = dbUser._id as string
582
- await deprovisioning.removeUserFromInfoDB(dbUser)
589
+ await platform.users.removeUser(dbUser)
583
590
  await eventHelpers.handleDeleteEvents(dbUser)
584
591
  await cache.user.invalidateUser(userId)
585
592
  await sessions.invalidateSessions(userId, { reason: "bulk-deletion" })
@@ -15,39 +15,29 @@ const supertest = require("supertest")
15
15
  import { Config } from "../constants"
16
16
  import {
17
17
  users,
18
- tenancy,
18
+ context,
19
19
  sessions,
20
20
  auth,
21
21
  constants,
22
22
  env as coreEnv,
23
23
  } from "@budibase/backend-core"
24
- import structures, { TENANT_ID, TENANT_1, CSRF_TOKEN } from "./structures"
25
- import { CreateUserResponse, User, AuthToken } from "@budibase/types"
24
+ import structures, { CSRF_TOKEN } from "./structures"
25
+ import { SaveUserResponse, User, AuthToken } from "@budibase/types"
26
26
  import API from "./api"
27
27
 
28
- enum Mode {
29
- CLOUD = "cloud",
30
- SELF = "self",
31
- }
32
-
33
28
  class TestConfiguration {
34
29
  server: any
35
30
  request: any
36
31
  api: API
37
- defaultUser?: User
38
- tenant1User?: User
32
+ tenantId: string
33
+ user?: User
34
+ userPassword = "test"
39
35
 
40
- constructor(
41
- opts: { openServer: boolean; mode: Mode } = {
42
- openServer: true,
43
- mode: Mode.CLOUD,
44
- }
45
- ) {
46
- if (opts.mode === Mode.CLOUD) {
47
- this.modeCloud()
48
- } else if (opts.mode === Mode.SELF) {
49
- this.modeSelf()
50
- }
36
+ constructor(opts: { openServer: boolean } = { openServer: true }) {
37
+ // default to cloud hosting
38
+ this.cloudHosted()
39
+
40
+ this.tenantId = structures.tenant.id()
51
41
 
52
42
  if (opts.openServer) {
53
43
  env.PORT = "0" // random port
@@ -63,26 +53,19 @@ class TestConfiguration {
63
53
  return this.request
64
54
  }
65
55
 
66
- // MODES
67
-
68
- setMultiTenancy = (value: boolean) => {
69
- env._set("MULTI_TENANCY", value)
70
- coreEnv._set("MULTI_TENANCY", value)
71
- }
56
+ // HOSTING
72
57
 
73
58
  setSelfHosted = (value: boolean) => {
74
59
  env._set("SELF_HOSTED", value)
75
60
  coreEnv._set("SELF_HOSTED", value)
76
61
  }
77
62
 
78
- modeCloud = () => {
63
+ cloudHosted = () => {
79
64
  this.setSelfHosted(false)
80
- this.setMultiTenancy(true)
81
65
  }
82
66
 
83
- modeSelf = () => {
67
+ selfHosted = () => {
84
68
  this.setSelfHosted(true)
85
- this.setMultiTenancy(false)
86
69
  }
87
70
 
88
71
  // UTILS
@@ -103,7 +86,7 @@ class TestConfiguration {
103
86
  if (params) {
104
87
  request.params = params
105
88
  }
106
- await tenancy.doInTenant(this.getTenantId(), () => {
89
+ await context.doInTenant(this.getTenantId(), () => {
107
90
  return controlFunc(request)
108
91
  })
109
92
  return request.body
@@ -112,13 +95,13 @@ class TestConfiguration {
112
95
  // SETUP / TEARDOWN
113
96
 
114
97
  async beforeAll() {
115
- await this.createDefaultUser()
116
- await this.createSession(this.defaultUser!)
117
-
118
- await tenancy.doInTenant(TENANT_1, async () => {
119
- await this.createTenant1User()
120
- await this.createSession(this.tenant1User!)
121
- })
98
+ try {
99
+ await this.createDefaultUser()
100
+ await this.createSession(this.user!)
101
+ } catch (e: any) {
102
+ console.error(e)
103
+ throw new Error(e.message)
104
+ }
122
105
  }
123
106
 
124
107
  async afterAll() {
@@ -129,6 +112,12 @@ class TestConfiguration {
129
112
 
130
113
  // TENANCY
131
114
 
115
+ doInTenant(task: any) {
116
+ return context.doInTenant(this.tenantId, () => {
117
+ return task()
118
+ })
119
+ }
120
+
132
121
  createTenant = async (): Promise<User> => {
133
122
  // create user / new tenant
134
123
  const res = await this.api.users.createAdminUser()
@@ -150,9 +139,9 @@ class TestConfiguration {
150
139
 
151
140
  getTenantId() {
152
141
  try {
153
- return tenancy.getTenantId()
154
- } catch (e: any) {
155
- return TENANT_ID
142
+ return context.getTenantId()
143
+ } catch (e) {
144
+ return this.tenantId!
156
145
  }
157
146
  }
158
147
 
@@ -200,14 +189,11 @@ class TestConfiguration {
200
189
  }
201
190
 
202
191
  defaultHeaders() {
203
- const tenantId = this.getTenantId()
204
- if (tenantId === TENANT_ID) {
205
- return this.authHeaders(this.defaultUser!)
206
- } else if (tenantId === TENANT_1) {
207
- return this.authHeaders(this.tenant1User!)
208
- } else {
209
- throw new Error("could not determine auth headers to use")
210
- }
192
+ return this.authHeaders(this.user!)
193
+ }
194
+
195
+ tenantIdHeaders() {
196
+ return { [constants.Header.TENANT_ID]: this.tenantId }
211
197
  }
212
198
 
213
199
  internalAPIHeaders() {
@@ -222,22 +208,15 @@ class TestConfiguration {
222
208
 
223
209
  async createDefaultUser() {
224
210
  const user = structures.users.adminUser({
225
- email: "test@test.com",
226
- password: "test",
211
+ password: this.userPassword,
227
212
  })
228
- this.defaultUser = await this.createUser(user)
229
- }
230
-
231
- async createTenant1User() {
232
- const user = structures.users.adminUser({
233
- email: "tenant1@test.com",
234
- password: "test",
213
+ await context.doInTenant(this.tenantId!, async () => {
214
+ this.user = await this.createUser(user)
235
215
  })
236
- this.tenant1User = await this.createUser(user)
237
216
  }
238
217
 
239
218
  async getUser(email: string): Promise<User> {
240
- return tenancy.doInTenant(this.getTenantId(), () => {
219
+ return context.doInTenant(this.getTenantId(), () => {
241
220
  return users.getGlobalUserByEmail(email)
242
221
  })
243
222
  }
@@ -247,7 +226,7 @@ class TestConfiguration {
247
226
  user = structures.users.user()
248
227
  }
249
228
  const response = await this._req(user, null, controllers.users.save)
250
- const body = response as CreateUserResponse
229
+ const body = response as SaveUserResponse
251
230
  return this.getUser(body.email)
252
231
  }
253
232