@budibase/worker 2.3.17 → 2.3.18-alpha.1

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 (167) hide show
  1. package/__mocks__/node-fetch.ts +23 -0
  2. package/jest-testcontainers-config.js +8 -0
  3. package/jest.config.ts +13 -17
  4. package/package.json +11 -8
  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/src/utilities/redis.ts +2 -0
  51. package/coverage/clover.xml +0 -2199
  52. package/coverage/coverage-final.json +0 -84
  53. package/coverage/lcov-report/base.css +0 -224
  54. package/coverage/lcov-report/block-navigation.js +0 -87
  55. package/coverage/lcov-report/favicon.png +0 -0
  56. package/coverage/lcov-report/index.html +0 -431
  57. package/coverage/lcov-report/prettify.css +0 -1
  58. package/coverage/lcov-report/prettify.js +0 -2
  59. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  60. package/coverage/lcov-report/sorter.js +0 -196
  61. package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +0 -934
  62. package/coverage/lcov-report/src/api/controllers/global/configs.ts.html +0 -1348
  63. package/coverage/lcov-report/src/api/controllers/global/email.ts.html +0 -196
  64. package/coverage/lcov-report/src/api/controllers/global/events.ts.html +0 -136
  65. package/coverage/lcov-report/src/api/controllers/global/index.html +0 -251
  66. package/coverage/lcov-report/src/api/controllers/global/license.ts.html +0 -187
  67. package/coverage/lcov-report/src/api/controllers/global/roles.ts.html +0 -283
  68. package/coverage/lcov-report/src/api/controllers/global/self.ts.html +0 -577
  69. package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +0 -316
  70. package/coverage/lcov-report/src/api/controllers/global/users.ts.html +0 -838
  71. package/coverage/lcov-report/src/api/controllers/global/workspaces.ts.html +0 -244
  72. package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +0 -148
  73. package/coverage/lcov-report/src/api/controllers/system/environment.ts.html +0 -124
  74. package/coverage/lcov-report/src/api/controllers/system/index.html +0 -191
  75. package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +0 -124
  76. package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +0 -124
  77. package/coverage/lcov-report/src/api/controllers/system/status.ts.html +0 -133
  78. package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +0 -154
  79. package/coverage/lcov-report/src/api/index.html +0 -116
  80. package/coverage/lcov-report/src/api/index.ts.html +0 -595
  81. package/coverage/lcov-report/src/api/routes/global/auth.ts.html +0 -349
  82. package/coverage/lcov-report/src/api/routes/global/configs.ts.html +0 -457
  83. package/coverage/lcov-report/src/api/routes/global/email.ts.html +0 -193
  84. package/coverage/lcov-report/src/api/routes/global/events.ts.html +0 -109
  85. package/coverage/lcov-report/src/api/routes/global/index.html +0 -251
  86. package/coverage/lcov-report/src/api/routes/global/license.ts.html +0 -124
  87. package/coverage/lcov-report/src/api/routes/global/roles.ts.html +0 -133
  88. package/coverage/lcov-report/src/api/routes/global/self.ts.html +0 -139
  89. package/coverage/lcov-report/src/api/routes/global/templates.ts.html +0 -196
  90. package/coverage/lcov-report/src/api/routes/global/users.ts.html +0 -475
  91. package/coverage/lcov-report/src/api/routes/global/workspaces.ts.html +0 -196
  92. package/coverage/lcov-report/src/api/routes/index.html +0 -116
  93. package/coverage/lcov-report/src/api/routes/index.ts.html +0 -202
  94. package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +0 -142
  95. package/coverage/lcov-report/src/api/routes/system/environment.ts.html +0 -109
  96. package/coverage/lcov-report/src/api/routes/system/index.html +0 -191
  97. package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +0 -142
  98. package/coverage/lcov-report/src/api/routes/system/restore.ts.html +0 -109
  99. package/coverage/lcov-report/src/api/routes/system/status.ts.html +0 -109
  100. package/coverage/lcov-report/src/api/routes/system/tenants.ts.html +0 -124
  101. package/coverage/lcov-report/src/api/routes/validation/index.html +0 -131
  102. package/coverage/lcov-report/src/api/routes/validation/index.ts.html +0 -88
  103. package/coverage/lcov-report/src/api/routes/validation/users.ts.html +0 -235
  104. package/coverage/lcov-report/src/constants/index.html +0 -116
  105. package/coverage/lcov-report/src/constants/index.ts.html +0 -637
  106. package/coverage/lcov-report/src/constants/templates/index.html +0 -116
  107. package/coverage/lcov-report/src/constants/templates/index.ts.html +0 -316
  108. package/coverage/lcov-report/src/db/index.html +0 -116
  109. package/coverage/lcov-report/src/db/index.ts.html +0 -115
  110. package/coverage/lcov-report/src/environment.ts.html +0 -388
  111. package/coverage/lcov-report/src/index.html +0 -131
  112. package/coverage/lcov-report/src/index.ts.html +0 -394
  113. package/coverage/lcov-report/src/middleware/cloudRestricted.ts.html +0 -139
  114. package/coverage/lcov-report/src/middleware/index.html +0 -116
  115. package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +0 -145
  116. package/coverage/lcov-report/src/migrations/functions/index.html +0 -116
  117. package/coverage/lcov-report/src/migrations/index.html +0 -116
  118. package/coverage/lcov-report/src/migrations/index.ts.html +0 -271
  119. package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +0 -262
  120. package/coverage/lcov-report/src/sdk/accounts/index.html +0 -131
  121. package/coverage/lcov-report/src/sdk/accounts/index.ts.html +0 -88
  122. package/coverage/lcov-report/src/sdk/index.html +0 -116
  123. package/coverage/lcov-report/src/sdk/index.ts.html +0 -106
  124. package/coverage/lcov-report/src/sdk/users/events.ts.html +0 -601
  125. package/coverage/lcov-report/src/sdk/users/index.html +0 -146
  126. package/coverage/lcov-report/src/sdk/users/index.ts.html +0 -88
  127. package/coverage/lcov-report/src/sdk/users/users.ts.html +0 -1999
  128. package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +0 -1075
  129. package/coverage/lcov-report/src/tests/api/accounts.ts.html +0 -160
  130. package/coverage/lcov-report/src/tests/api/auth.ts.html +0 -220
  131. package/coverage/lcov-report/src/tests/api/base.ts.html +0 -133
  132. package/coverage/lcov-report/src/tests/api/configs.ts.html +0 -226
  133. package/coverage/lcov-report/src/tests/api/email.ts.html +0 -148
  134. package/coverage/lcov-report/src/tests/api/environment.ts.html +0 -130
  135. package/coverage/lcov-report/src/tests/api/groups.ts.html +0 -163
  136. package/coverage/lcov-report/src/tests/api/index.html +0 -356
  137. package/coverage/lcov-report/src/tests/api/index.ts.html +0 -238
  138. package/coverage/lcov-report/src/tests/api/license.ts.html +0 -136
  139. package/coverage/lcov-report/src/tests/api/migrations.ts.html +0 -151
  140. package/coverage/lcov-report/src/tests/api/restore.ts.html +0 -127
  141. package/coverage/lcov-report/src/tests/api/roles.ts.html +0 -181
  142. package/coverage/lcov-report/src/tests/api/self.ts.html +0 -163
  143. package/coverage/lcov-report/src/tests/api/status.ts.html +0 -121
  144. package/coverage/lcov-report/src/tests/api/templates.ts.html +0 -175
  145. package/coverage/lcov-report/src/tests/api/tenants.ts.html +0 -130
  146. package/coverage/lcov-report/src/tests/api/users.ts.html +0 -514
  147. package/coverage/lcov-report/src/tests/controllers.ts.html +0 -100
  148. package/coverage/lcov-report/src/tests/index.html +0 -146
  149. package/coverage/lcov-report/src/tests/index.ts.html +0 -103
  150. package/coverage/lcov-report/src/tests/mocks/email.ts.html +0 -115
  151. package/coverage/lcov-report/src/tests/mocks/index.html +0 -131
  152. package/coverage/lcov-report/src/tests/mocks/index.ts.html +0 -106
  153. package/coverage/lcov-report/src/tests/structures/configs.ts.html +0 -313
  154. package/coverage/lcov-report/src/tests/structures/groups.ts.html +0 -118
  155. package/coverage/lcov-report/src/tests/structures/index.html +0 -161
  156. package/coverage/lcov-report/src/tests/structures/index.ts.html +0 -151
  157. package/coverage/lcov-report/src/tests/structures/users.ts.html +0 -196
  158. package/coverage/lcov-report/src/utilities/appService.ts.html +0 -208
  159. package/coverage/lcov-report/src/utilities/email.ts.html +0 -850
  160. package/coverage/lcov-report/src/utilities/fileSystem.ts.html +0 -100
  161. package/coverage/lcov-report/src/utilities/index.html +0 -206
  162. package/coverage/lcov-report/src/utilities/index.ts.html +0 -112
  163. package/coverage/lcov-report/src/utilities/redis.ts.html +0 -424
  164. package/coverage/lcov-report/src/utilities/templates.ts.html +0 -232
  165. package/coverage/lcov-report/src/utilities/users.ts.html +0 -133
  166. package/coverage/lcov.info +0 -4187
  167. 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