@budibase/worker 2.3.18-alpha.9 → 2.3.18
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/coverage/clover.xml +2199 -0
- package/coverage/coverage-final.json +84 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +431 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +934 -0
- package/coverage/lcov-report/src/api/controllers/global/configs.ts.html +1348 -0
- package/coverage/lcov-report/src/api/controllers/global/email.ts.html +196 -0
- package/coverage/lcov-report/src/api/controllers/global/events.ts.html +136 -0
- package/coverage/lcov-report/src/api/controllers/global/index.html +251 -0
- package/coverage/lcov-report/src/api/controllers/global/license.ts.html +187 -0
- package/coverage/lcov-report/src/api/controllers/global/roles.ts.html +283 -0
- package/coverage/lcov-report/src/api/controllers/global/self.ts.html +577 -0
- package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +316 -0
- package/coverage/lcov-report/src/api/controllers/global/users.ts.html +838 -0
- package/coverage/lcov-report/src/api/controllers/global/workspaces.ts.html +244 -0
- package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +148 -0
- package/coverage/lcov-report/src/api/controllers/system/environment.ts.html +124 -0
- package/coverage/lcov-report/src/api/controllers/system/index.html +191 -0
- package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +124 -0
- package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +124 -0
- package/coverage/lcov-report/src/api/controllers/system/status.ts.html +133 -0
- package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +154 -0
- package/coverage/lcov-report/src/api/index.html +116 -0
- package/coverage/lcov-report/src/api/index.ts.html +595 -0
- package/coverage/lcov-report/src/api/routes/global/auth.ts.html +349 -0
- package/coverage/lcov-report/src/api/routes/global/configs.ts.html +457 -0
- package/coverage/lcov-report/src/api/routes/global/email.ts.html +193 -0
- package/coverage/lcov-report/src/api/routes/global/events.ts.html +109 -0
- package/coverage/lcov-report/src/api/routes/global/index.html +251 -0
- package/coverage/lcov-report/src/api/routes/global/license.ts.html +124 -0
- package/coverage/lcov-report/src/api/routes/global/roles.ts.html +133 -0
- package/coverage/lcov-report/src/api/routes/global/self.ts.html +139 -0
- package/coverage/lcov-report/src/api/routes/global/templates.ts.html +196 -0
- package/coverage/lcov-report/src/api/routes/global/users.ts.html +475 -0
- package/coverage/lcov-report/src/api/routes/global/workspaces.ts.html +196 -0
- package/coverage/lcov-report/src/api/routes/index.html +116 -0
- package/coverage/lcov-report/src/api/routes/index.ts.html +202 -0
- package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +142 -0
- package/coverage/lcov-report/src/api/routes/system/environment.ts.html +109 -0
- package/coverage/lcov-report/src/api/routes/system/index.html +191 -0
- package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +142 -0
- package/coverage/lcov-report/src/api/routes/system/restore.ts.html +109 -0
- package/coverage/lcov-report/src/api/routes/system/status.ts.html +109 -0
- package/coverage/lcov-report/src/api/routes/system/tenants.ts.html +124 -0
- package/coverage/lcov-report/src/api/routes/validation/index.html +131 -0
- package/coverage/lcov-report/src/api/routes/validation/index.ts.html +88 -0
- package/coverage/lcov-report/src/api/routes/validation/users.ts.html +235 -0
- package/coverage/lcov-report/src/constants/index.html +116 -0
- package/coverage/lcov-report/src/constants/index.ts.html +637 -0
- package/coverage/lcov-report/src/constants/templates/index.html +116 -0
- package/coverage/lcov-report/src/constants/templates/index.ts.html +316 -0
- package/coverage/lcov-report/src/db/index.html +116 -0
- package/coverage/lcov-report/src/db/index.ts.html +115 -0
- package/coverage/lcov-report/src/environment.ts.html +388 -0
- package/coverage/lcov-report/src/index.html +131 -0
- package/coverage/lcov-report/src/index.ts.html +394 -0
- package/coverage/lcov-report/src/middleware/cloudRestricted.ts.html +139 -0
- package/coverage/lcov-report/src/middleware/index.html +116 -0
- package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +145 -0
- package/coverage/lcov-report/src/migrations/functions/index.html +116 -0
- package/coverage/lcov-report/src/migrations/index.html +116 -0
- package/coverage/lcov-report/src/migrations/index.ts.html +271 -0
- package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +262 -0
- package/coverage/lcov-report/src/sdk/accounts/index.html +131 -0
- package/coverage/lcov-report/src/sdk/accounts/index.ts.html +88 -0
- package/coverage/lcov-report/src/sdk/index.html +116 -0
- package/coverage/lcov-report/src/sdk/index.ts.html +106 -0
- package/coverage/lcov-report/src/sdk/users/events.ts.html +601 -0
- package/coverage/lcov-report/src/sdk/users/index.html +146 -0
- package/coverage/lcov-report/src/sdk/users/index.ts.html +88 -0
- package/coverage/lcov-report/src/sdk/users/users.ts.html +1999 -0
- package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +1075 -0
- package/coverage/lcov-report/src/tests/api/accounts.ts.html +160 -0
- package/coverage/lcov-report/src/tests/api/auth.ts.html +220 -0
- package/coverage/lcov-report/src/tests/api/base.ts.html +133 -0
- package/coverage/lcov-report/src/tests/api/configs.ts.html +226 -0
- package/coverage/lcov-report/src/tests/api/email.ts.html +148 -0
- package/coverage/lcov-report/src/tests/api/environment.ts.html +130 -0
- package/coverage/lcov-report/src/tests/api/groups.ts.html +163 -0
- package/coverage/lcov-report/src/tests/api/index.html +356 -0
- package/coverage/lcov-report/src/tests/api/index.ts.html +238 -0
- package/coverage/lcov-report/src/tests/api/license.ts.html +136 -0
- package/coverage/lcov-report/src/tests/api/migrations.ts.html +151 -0
- package/coverage/lcov-report/src/tests/api/restore.ts.html +127 -0
- package/coverage/lcov-report/src/tests/api/roles.ts.html +181 -0
- package/coverage/lcov-report/src/tests/api/self.ts.html +163 -0
- package/coverage/lcov-report/src/tests/api/status.ts.html +121 -0
- package/coverage/lcov-report/src/tests/api/templates.ts.html +175 -0
- package/coverage/lcov-report/src/tests/api/tenants.ts.html +130 -0
- package/coverage/lcov-report/src/tests/api/users.ts.html +514 -0
- package/coverage/lcov-report/src/tests/controllers.ts.html +100 -0
- package/coverage/lcov-report/src/tests/index.html +146 -0
- package/coverage/lcov-report/src/tests/index.ts.html +103 -0
- package/coverage/lcov-report/src/tests/mocks/email.ts.html +115 -0
- package/coverage/lcov-report/src/tests/mocks/index.html +131 -0
- package/coverage/lcov-report/src/tests/mocks/index.ts.html +106 -0
- package/coverage/lcov-report/src/tests/structures/configs.ts.html +313 -0
- package/coverage/lcov-report/src/tests/structures/groups.ts.html +118 -0
- package/coverage/lcov-report/src/tests/structures/index.html +161 -0
- package/coverage/lcov-report/src/tests/structures/index.ts.html +151 -0
- package/coverage/lcov-report/src/tests/structures/users.ts.html +196 -0
- package/coverage/lcov-report/src/utilities/appService.ts.html +208 -0
- package/coverage/lcov-report/src/utilities/email.ts.html +850 -0
- package/coverage/lcov-report/src/utilities/fileSystem.ts.html +100 -0
- package/coverage/lcov-report/src/utilities/index.html +206 -0
- package/coverage/lcov-report/src/utilities/index.ts.html +112 -0
- package/coverage/lcov-report/src/utilities/redis.ts.html +424 -0
- package/coverage/lcov-report/src/utilities/templates.ts.html +232 -0
- package/coverage/lcov-report/src/utilities/users.ts.html +133 -0
- package/coverage/lcov.info +4187 -0
- package/jest.config.ts +17 -13
- package/package.json +8 -11
- package/scripts/dev/manage.js +0 -1
- package/src/api/controllers/global/auth.ts +70 -77
- package/src/api/controllers/global/self.ts +44 -28
- package/src/api/controllers/global/users.ts +25 -65
- package/src/api/controllers/system/accounts.ts +5 -7
- package/src/api/controllers/system/tenants.ts +8 -4
- package/src/api/index.ts +20 -4
- package/src/api/routes/global/auth.ts +7 -10
- package/src/api/routes/global/tests/auth.spec.ts +17 -226
- package/src/api/routes/global/tests/configs.spec.ts +5 -5
- package/src/api/routes/global/tests/roles.spec.ts +14 -20
- package/src/api/routes/global/tests/self.spec.ts +4 -3
- package/src/api/routes/global/tests/users.spec.ts +27 -24
- package/src/api/routes/system/tenants.ts +1 -1
- package/src/api/routes/system/tests/accounts.spec.ts +4 -4
- package/src/api/routes/system/tests/migrations.spec.ts +2 -2
- package/src/api/routes/system/tests/restore.spec.ts +2 -2
- package/src/api/routes/system/tests/status.spec.ts +5 -5
- package/src/environment.ts +1 -15
- package/src/index.ts +7 -12
- package/src/middleware/tests/tenancy.spec.ts +4 -4
- package/src/migrations/functions/globalInfoSyncUsers.ts +3 -4
- package/src/sdk/accounts/{metadata.ts → accounts.ts} +1 -0
- package/src/sdk/accounts/index.ts +1 -2
- package/src/sdk/users/events.ts +0 -4
- package/src/sdk/users/index.ts +0 -1
- package/src/sdk/users/users.ts +46 -53
- package/src/tests/TestConfiguration.ts +62 -41
- package/src/tests/api/auth.ts +14 -42
- package/src/tests/api/base.ts +1 -2
- package/src/tests/api/configs.ts +2 -2
- package/src/tests/api/restore.ts +0 -1
- package/src/tests/api/users.ts +2 -2
- package/src/tests/jestEnv.ts +1 -2
- package/src/tests/jestSetup.ts +6 -10
- package/src/tests/structures/index.ts +4 -0
- package/src/tests/structures/users.ts +37 -0
- package/src/utilities/email.ts +3 -3
- package/src/utilities/redis.ts +0 -2
- package/__mocks__/node-fetch.ts +0 -23
- package/jest-testcontainers-config.js +0 -8
- package/scripts/test.sh +0 -12
- package/src/ddApm.ts +0 -7
- package/src/elasticApm.ts +0 -10
- package/src/sdk/auth/auth.ts +0 -86
- package/src/sdk/auth/index.ts +0 -1
- package/src/sdk/tenants/index.ts +0 -1
- package/src/sdk/tenants/tenants.ts +0 -76
- package/src/sdk/users/tests/users.spec.ts +0 -52
- package/src/tests/logging.ts +0 -34
|
@@ -1,48 +1,31 @@
|
|
|
1
1
|
import { checkInviteCode } from "../../../utilities/redis"
|
|
2
|
-
import
|
|
2
|
+
import sdk from "../../../sdk"
|
|
3
3
|
import env from "../../../environment"
|
|
4
4
|
import {
|
|
5
|
-
AcceptUserInviteRequest,
|
|
6
|
-
AcceptUserInviteResponse,
|
|
7
5
|
BulkUserRequest,
|
|
8
6
|
BulkUserResponse,
|
|
9
7
|
CloudAccount,
|
|
10
8
|
CreateAdminUserRequest,
|
|
11
|
-
CreateAdminUserResponse,
|
|
12
|
-
Ctx,
|
|
13
9
|
InviteUserRequest,
|
|
14
10
|
InviteUsersRequest,
|
|
15
|
-
MigrationType,
|
|
16
|
-
SaveUserResponse,
|
|
17
11
|
SearchUsersRequest,
|
|
18
12
|
User,
|
|
19
|
-
UserCtx,
|
|
20
13
|
} from "@budibase/types"
|
|
21
14
|
import {
|
|
22
15
|
accounts,
|
|
23
16
|
cache,
|
|
24
17
|
errors,
|
|
25
18
|
events,
|
|
26
|
-
migrations,
|
|
27
19
|
tenancy,
|
|
28
|
-
platform,
|
|
29
20
|
} from "@budibase/backend-core"
|
|
30
21
|
import { checkAnyUserExists } from "../../../utilities/users"
|
|
31
22
|
|
|
32
23
|
const MAX_USERS_UPLOAD_LIMIT = 1000
|
|
33
24
|
|
|
34
|
-
export const save = async (ctx:
|
|
25
|
+
export const save = async (ctx: any) => {
|
|
35
26
|
try {
|
|
36
27
|
const currentUserId = ctx.user._id
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const user = await userSdk.save(requestUser, { currentUserId })
|
|
40
|
-
|
|
41
|
-
ctx.body = {
|
|
42
|
-
_id: user._id!,
|
|
43
|
-
_rev: user._rev!,
|
|
44
|
-
email: user.email,
|
|
45
|
-
}
|
|
28
|
+
ctx.body = await sdk.users.save(ctx.request.body, { currentUserId })
|
|
46
29
|
} catch (err: any) {
|
|
47
30
|
ctx.throw(err.status || 400, err)
|
|
48
31
|
}
|
|
@@ -52,7 +35,7 @@ const bulkDelete = async (userIds: string[], currentUserId: string) => {
|
|
|
52
35
|
if (userIds?.indexOf(currentUserId) !== -1) {
|
|
53
36
|
throw new Error("Unable to delete self.")
|
|
54
37
|
}
|
|
55
|
-
return await
|
|
38
|
+
return await sdk.users.bulkDelete(userIds)
|
|
56
39
|
}
|
|
57
40
|
|
|
58
41
|
const bulkCreate = async (users: User[], groupIds: string[]) => {
|
|
@@ -61,7 +44,7 @@ const bulkCreate = async (users: User[], groupIds: string[]) => {
|
|
|
61
44
|
"Max limit for upload is 1000 users. Please reduce file size and try again."
|
|
62
45
|
)
|
|
63
46
|
}
|
|
64
|
-
return await
|
|
47
|
+
return await sdk.users.bulkCreate(users, groupIds)
|
|
65
48
|
}
|
|
66
49
|
|
|
67
50
|
export const bulkUpdate = async (ctx: any) => {
|
|
@@ -85,30 +68,19 @@ const parseBooleanParam = (param: any) => {
|
|
|
85
68
|
return !(param && param === "false")
|
|
86
69
|
}
|
|
87
70
|
|
|
88
|
-
export const adminUser = async (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const { email, password, tenantId } = ctx.request.body
|
|
92
|
-
|
|
93
|
-
if (await platform.tenants.exists(tenantId)) {
|
|
94
|
-
ctx.throw(403, "Organisation already exists.")
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (env.MULTI_TENANCY) {
|
|
98
|
-
// store the new tenant record in the platform db
|
|
99
|
-
await platform.tenants.addTenant(tenantId)
|
|
100
|
-
await migrations.backPopulateMigrations({
|
|
101
|
-
type: MigrationType.GLOBAL,
|
|
102
|
-
tenantId,
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
|
|
71
|
+
export const adminUser = async (ctx: any) => {
|
|
72
|
+
const { email, password, tenantId } = ctx.request
|
|
73
|
+
.body as CreateAdminUserRequest
|
|
106
74
|
await tenancy.doInTenant(tenantId, async () => {
|
|
107
75
|
// account portal sends a pre-hashed password - honour param to prevent double hashing
|
|
108
76
|
const hashPassword = parseBooleanParam(ctx.request.query.hashPassword)
|
|
109
77
|
// account portal sends no password for SSO users
|
|
110
78
|
const requirePassword = parseBooleanParam(ctx.request.query.requirePassword)
|
|
111
79
|
|
|
80
|
+
if (await tenancy.doesTenantExist(tenantId)) {
|
|
81
|
+
ctx.throw(403, "Organisation already exists.")
|
|
82
|
+
}
|
|
83
|
+
|
|
112
84
|
const userExists = await checkAnyUserExists()
|
|
113
85
|
if (userExists) {
|
|
114
86
|
ctx.throw(
|
|
@@ -134,7 +106,7 @@ export const adminUser = async (
|
|
|
134
106
|
// always bust checklist beforehand, if an error occurs but can proceed, don't get
|
|
135
107
|
// stuck in a cycle
|
|
136
108
|
await cache.bustCache(cache.CacheKey.CHECKLIST)
|
|
137
|
-
const finalUser = await
|
|
109
|
+
const finalUser = await sdk.users.save(user, {
|
|
138
110
|
hashPassword,
|
|
139
111
|
requirePassword,
|
|
140
112
|
})
|
|
@@ -146,11 +118,7 @@ export const adminUser = async (
|
|
|
146
118
|
}
|
|
147
119
|
await events.identification.identifyTenantGroup(tenantId, account)
|
|
148
120
|
|
|
149
|
-
ctx.body =
|
|
150
|
-
_id: finalUser._id!,
|
|
151
|
-
_rev: finalUser._rev!,
|
|
152
|
-
email: finalUser.email,
|
|
153
|
-
}
|
|
121
|
+
ctx.body = finalUser
|
|
154
122
|
} catch (err: any) {
|
|
155
123
|
ctx.throw(err.status || 400, err)
|
|
156
124
|
}
|
|
@@ -160,7 +128,7 @@ export const adminUser = async (
|
|
|
160
128
|
export const countByApp = async (ctx: any) => {
|
|
161
129
|
const appId = ctx.params.appId
|
|
162
130
|
try {
|
|
163
|
-
ctx.body = await
|
|
131
|
+
ctx.body = await sdk.users.countUsersByApp(appId)
|
|
164
132
|
} catch (err: any) {
|
|
165
133
|
ctx.throw(err.status || 400, err)
|
|
166
134
|
}
|
|
@@ -172,7 +140,7 @@ export const destroy = async (ctx: any) => {
|
|
|
172
140
|
ctx.throw(400, "Unable to delete self.")
|
|
173
141
|
}
|
|
174
142
|
|
|
175
|
-
await
|
|
143
|
+
await sdk.users.destroy(id, ctx.user)
|
|
176
144
|
|
|
177
145
|
ctx.body = {
|
|
178
146
|
message: `User ${id} deleted.`,
|
|
@@ -181,7 +149,7 @@ export const destroy = async (ctx: any) => {
|
|
|
181
149
|
|
|
182
150
|
export const search = async (ctx: any) => {
|
|
183
151
|
const body = ctx.request.body as SearchUsersRequest
|
|
184
|
-
const paginated = await
|
|
152
|
+
const paginated = await sdk.users.paginatedUsers(body)
|
|
185
153
|
// user hashed password shouldn't ever be returned
|
|
186
154
|
for (let user of paginated.data) {
|
|
187
155
|
if (user) {
|
|
@@ -193,7 +161,7 @@ export const search = async (ctx: any) => {
|
|
|
193
161
|
|
|
194
162
|
// called internally by app server user fetch
|
|
195
163
|
export const fetch = async (ctx: any) => {
|
|
196
|
-
const all = await
|
|
164
|
+
const all = await sdk.users.allUsers()
|
|
197
165
|
// user hashed password shouldn't ever be returned
|
|
198
166
|
for (let user of all) {
|
|
199
167
|
if (user) {
|
|
@@ -205,12 +173,12 @@ export const fetch = async (ctx: any) => {
|
|
|
205
173
|
|
|
206
174
|
// called internally by app server user find
|
|
207
175
|
export const find = async (ctx: any) => {
|
|
208
|
-
ctx.body = await
|
|
176
|
+
ctx.body = await sdk.users.getUser(ctx.params.id)
|
|
209
177
|
}
|
|
210
178
|
|
|
211
179
|
export const tenantUserLookup = async (ctx: any) => {
|
|
212
180
|
const id = ctx.params.id
|
|
213
|
-
const user = await
|
|
181
|
+
const user = await sdk.users.getPlatformUser(id)
|
|
214
182
|
if (user) {
|
|
215
183
|
ctx.body = user
|
|
216
184
|
} else {
|
|
@@ -220,7 +188,7 @@ export const tenantUserLookup = async (ctx: any) => {
|
|
|
220
188
|
|
|
221
189
|
export const invite = async (ctx: any) => {
|
|
222
190
|
const request = ctx.request.body as InviteUserRequest
|
|
223
|
-
const response = await
|
|
191
|
+
const response = await sdk.users.invite([request])
|
|
224
192
|
|
|
225
193
|
// explicitly throw for single user invite
|
|
226
194
|
if (response.unsuccessful.length) {
|
|
@@ -239,7 +207,7 @@ export const invite = async (ctx: any) => {
|
|
|
239
207
|
|
|
240
208
|
export const inviteMultiple = async (ctx: any) => {
|
|
241
209
|
const request = ctx.request.body as InviteUsersRequest
|
|
242
|
-
ctx.body = await
|
|
210
|
+
ctx.body = await sdk.users.invite(request)
|
|
243
211
|
}
|
|
244
212
|
|
|
245
213
|
export const checkInvite = async (ctx: any) => {
|
|
@@ -255,15 +223,13 @@ export const checkInvite = async (ctx: any) => {
|
|
|
255
223
|
}
|
|
256
224
|
}
|
|
257
225
|
|
|
258
|
-
export const inviteAccept = async (
|
|
259
|
-
ctx: Ctx<AcceptUserInviteRequest, AcceptUserInviteResponse>
|
|
260
|
-
) => {
|
|
226
|
+
export const inviteAccept = async (ctx: any) => {
|
|
261
227
|
const { inviteCode, password, firstName, lastName } = ctx.request.body
|
|
262
228
|
try {
|
|
263
229
|
// info is an extension of the user object that was stored by global
|
|
264
230
|
const { email, info }: any = await checkInviteCode(inviteCode)
|
|
265
|
-
|
|
266
|
-
const saved = await
|
|
231
|
+
ctx.body = await tenancy.doInTenant(info.tenantId, async () => {
|
|
232
|
+
const saved = await sdk.users.save({
|
|
267
233
|
firstName,
|
|
268
234
|
lastName,
|
|
269
235
|
password,
|
|
@@ -275,12 +241,6 @@ export const inviteAccept = async (
|
|
|
275
241
|
await events.user.inviteAccepted(user)
|
|
276
242
|
return saved
|
|
277
243
|
})
|
|
278
|
-
|
|
279
|
-
ctx.body = {
|
|
280
|
-
_id: user._id,
|
|
281
|
-
_rev: user._rev,
|
|
282
|
-
email: user.email,
|
|
283
|
-
}
|
|
284
244
|
} catch (err: any) {
|
|
285
245
|
if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
|
|
286
246
|
// explicitly re-throw limit exceeded errors
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { Account, AccountMetadata } from "@budibase/types"
|
|
2
|
-
import
|
|
2
|
+
import sdk from "../../../sdk"
|
|
3
3
|
|
|
4
4
|
export const save = async (ctx: any) => {
|
|
5
5
|
const account = ctx.request.body as Account
|
|
6
6
|
let metadata: AccountMetadata = {
|
|
7
|
-
_id: accounts.
|
|
7
|
+
_id: sdk.accounts.formatAccountMetadataId(account.accountId),
|
|
8
8
|
email: account.email,
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
metadata = await accounts.
|
|
11
|
+
metadata = await sdk.accounts.saveMetadata(metadata)
|
|
12
12
|
|
|
13
13
|
ctx.body = metadata
|
|
14
14
|
ctx.status = 200
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export const destroy = async (ctx: any) => {
|
|
18
|
-
const accountId = accounts.
|
|
19
|
-
|
|
20
|
-
)
|
|
21
|
-
await accounts.metadata.destroyMetadata(accountId)
|
|
18
|
+
const accountId = sdk.accounts.formatAccountMetadataId(ctx.params.accountId)
|
|
19
|
+
await sdk.accounts.destroyMetadata(accountId)
|
|
22
20
|
ctx.status = 204
|
|
23
21
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { BBContext } from "@budibase/types"
|
|
2
|
+
import { deprovisioning } from "@budibase/backend-core"
|
|
3
|
+
import { quotas } from "@budibase/pro"
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
const _delete = async (ctx: BBContext) => {
|
|
5
6
|
const user = ctx.user!
|
|
6
7
|
const tenantId = ctx.params.tenantId
|
|
7
8
|
|
|
@@ -10,10 +11,13 @@ export async function destroy(ctx: UserCtx) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
try {
|
|
13
|
-
await
|
|
14
|
+
await quotas.bustCache()
|
|
15
|
+
await deprovisioning.deleteTenant(tenantId)
|
|
14
16
|
ctx.status = 204
|
|
15
17
|
} catch (err) {
|
|
16
18
|
ctx.log.error(err)
|
|
17
19
|
throw err
|
|
18
20
|
}
|
|
19
21
|
}
|
|
22
|
+
|
|
23
|
+
export { _delete as delete }
|
package/src/api/index.ts
CHANGED
|
@@ -3,7 +3,8 @@ const compress = require("koa-compress")
|
|
|
3
3
|
const zlib = require("zlib")
|
|
4
4
|
import { routes } from "./routes"
|
|
5
5
|
import { middleware as pro } from "@budibase/pro"
|
|
6
|
-
import { auth, middleware } from "@budibase/backend-core"
|
|
6
|
+
import { errors, auth, middleware } from "@budibase/backend-core"
|
|
7
|
+
import { APIError } from "@budibase/types"
|
|
7
8
|
|
|
8
9
|
const PUBLIC_ENDPOINTS = [
|
|
9
10
|
// deprecated single tenant sso callback
|
|
@@ -108,9 +109,7 @@ const NO_TENANCY_ENDPOINTS = [
|
|
|
108
109
|
const NO_CSRF_ENDPOINTS = [...PUBLIC_ENDPOINTS]
|
|
109
110
|
|
|
110
111
|
const router: Router = new Router()
|
|
111
|
-
|
|
112
112
|
router
|
|
113
|
-
.use(middleware.errorHandling)
|
|
114
113
|
.use(
|
|
115
114
|
compress({
|
|
116
115
|
threshold: 2048,
|
|
@@ -137,12 +136,29 @@ router
|
|
|
137
136
|
(!ctx.isAuthenticated || (ctx.user && !ctx.user.budibaseAccess)) &&
|
|
138
137
|
!ctx.internal
|
|
139
138
|
) {
|
|
140
|
-
ctx.throw(403, "Unauthorized")
|
|
139
|
+
ctx.throw(403, "Unauthorized - no public worker access")
|
|
141
140
|
}
|
|
142
141
|
return next()
|
|
143
142
|
})
|
|
144
143
|
.use(middleware.auditLog)
|
|
145
144
|
|
|
145
|
+
// error handling middleware - TODO: This could be moved to backend-core
|
|
146
|
+
router.use(async (ctx, next) => {
|
|
147
|
+
try {
|
|
148
|
+
await next()
|
|
149
|
+
} catch (err: any) {
|
|
150
|
+
ctx.log.error(err)
|
|
151
|
+
ctx.status = err.status || err.statusCode || 500
|
|
152
|
+
const error = errors.getPublicError(err)
|
|
153
|
+
const body: APIError = {
|
|
154
|
+
message: err.message,
|
|
155
|
+
status: ctx.status,
|
|
156
|
+
error,
|
|
157
|
+
}
|
|
158
|
+
ctx.body = body
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
146
162
|
router.get("/health", ctx => (ctx.status = 200))
|
|
147
163
|
|
|
148
164
|
// authenticated routes
|
|
@@ -33,7 +33,7 @@ router
|
|
|
33
33
|
.post(
|
|
34
34
|
"/api/global/auth/:tenantId/login",
|
|
35
35
|
buildAuthValidation(),
|
|
36
|
-
authController.
|
|
36
|
+
authController.authenticate
|
|
37
37
|
)
|
|
38
38
|
.post("/api/global/auth/logout", authController.logout)
|
|
39
39
|
.post(
|
|
@@ -68,24 +68,21 @@ router
|
|
|
68
68
|
|
|
69
69
|
// GOOGLE - MULTI TENANT
|
|
70
70
|
.get("/api/global/auth/:tenantId/google", authController.googlePreAuth)
|
|
71
|
-
.get(
|
|
72
|
-
"/api/global/auth/:tenantId/google/callback",
|
|
73
|
-
authController.googleCallback
|
|
74
|
-
)
|
|
71
|
+
.get("/api/global/auth/:tenantId/google/callback", authController.googleAuth)
|
|
75
72
|
|
|
76
73
|
// GOOGLE - SINGLE TENANT - DEPRECATED
|
|
77
|
-
.get("/api/global/auth/google/callback", authController.
|
|
78
|
-
.get("/api/admin/auth/google/callback", authController.
|
|
74
|
+
.get("/api/global/auth/google/callback", authController.googleAuth)
|
|
75
|
+
.get("/api/admin/auth/google/callback", authController.googleAuth)
|
|
79
76
|
|
|
80
77
|
// OIDC - MULTI TENANT
|
|
81
78
|
.get(
|
|
82
79
|
"/api/global/auth/:tenantId/oidc/configs/:configId",
|
|
83
80
|
authController.oidcPreAuth
|
|
84
81
|
)
|
|
85
|
-
.get("/api/global/auth/:tenantId/oidc/callback", authController.
|
|
82
|
+
.get("/api/global/auth/:tenantId/oidc/callback", authController.oidcAuth)
|
|
86
83
|
|
|
87
84
|
// OIDC - SINGLE TENANT - DEPRECATED
|
|
88
|
-
.get("/api/global/auth/oidc/callback", authController.
|
|
89
|
-
.get("/api/admin/auth/oidc/callback", authController.
|
|
85
|
+
.get("/api/global/auth/oidc/callback", authController.oidcAuth)
|
|
86
|
+
.get("/api/admin/auth/oidc/callback", authController.oidcAuth)
|
|
90
87
|
|
|
91
88
|
export default router
|
|
@@ -1,27 +1,12 @@
|
|
|
1
|
-
import { CloudAccount, SSOUser, User } from "@budibase/types"
|
|
2
|
-
|
|
3
1
|
jest.mock("nodemailer")
|
|
4
|
-
import {
|
|
5
|
-
TestConfiguration,
|
|
6
|
-
mocks,
|
|
7
|
-
structures,
|
|
8
|
-
generator,
|
|
9
|
-
} from "../../../../tests"
|
|
2
|
+
import { TestConfiguration, mocks } from "../../../../tests"
|
|
10
3
|
const sendMailMock = mocks.email.mock()
|
|
11
|
-
import { events
|
|
12
|
-
import { Response } from "superagent"
|
|
13
|
-
|
|
14
|
-
import * as userSdk from "../../../../sdk/users"
|
|
15
|
-
|
|
16
|
-
function getAuthCookie(response: Response) {
|
|
17
|
-
return response.headers["set-cookie"]
|
|
18
|
-
.find((s: string) => s.startsWith(`${constants.Cookie.Auth}=`))
|
|
19
|
-
.split("=")[1]
|
|
20
|
-
.split(";")[0]
|
|
21
|
-
}
|
|
4
|
+
import { events } from "@budibase/backend-core"
|
|
22
5
|
|
|
23
|
-
const expectSetAuthCookie = (
|
|
24
|
-
expect(
|
|
6
|
+
const expectSetAuthCookie = (res: any) => {
|
|
7
|
+
expect(
|
|
8
|
+
res.get("Set-Cookie").find((c: string) => c.startsWith("budibase:auth"))
|
|
9
|
+
).toBeDefined()
|
|
25
10
|
}
|
|
26
11
|
|
|
27
12
|
describe("/api/global/auth", () => {
|
|
@@ -39,247 +24,53 @@ describe("/api/global/auth", () => {
|
|
|
39
24
|
jest.clearAllMocks()
|
|
40
25
|
})
|
|
41
26
|
|
|
42
|
-
async function createSSOUser() {
|
|
43
|
-
return config.doInTenant(async () => {
|
|
44
|
-
return userSdk.save(structures.users.ssoUser(), {
|
|
45
|
-
requirePassword: false,
|
|
46
|
-
})
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
27
|
describe("password", () => {
|
|
51
28
|
describe("POST /api/global/auth/:tenantId/login", () => {
|
|
52
|
-
it("
|
|
53
|
-
const tenantId = config.tenantId!
|
|
54
|
-
const email = config.user?.email!
|
|
55
|
-
const password = config.userPassword
|
|
56
|
-
|
|
57
|
-
const response = await config.api.auth.login(tenantId, email, password)
|
|
58
|
-
|
|
59
|
-
expectSetAuthCookie(response)
|
|
60
|
-
expect(events.auth.login).toBeCalledTimes(1)
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it("should return 403 with incorrect credentials", async () => {
|
|
64
|
-
const tenantId = config.tenantId!
|
|
65
|
-
const email = config.user?.email!
|
|
66
|
-
const password = "incorrect"
|
|
67
|
-
|
|
68
|
-
const response = await config.api.auth.login(
|
|
69
|
-
tenantId,
|
|
70
|
-
email,
|
|
71
|
-
password,
|
|
72
|
-
{ status: 403 }
|
|
73
|
-
)
|
|
74
|
-
expect(response.body).toEqual({
|
|
75
|
-
message: "Invalid credentials",
|
|
76
|
-
status: 403,
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it("should return 403 when user doesn't exist", async () => {
|
|
81
|
-
const tenantId = config.tenantId!
|
|
82
|
-
const email = "invaliduser@test.com"
|
|
83
|
-
const password = "password"
|
|
84
|
-
|
|
85
|
-
const response = await config.api.auth.login(
|
|
86
|
-
tenantId,
|
|
87
|
-
email,
|
|
88
|
-
password,
|
|
89
|
-
{ status: 403 }
|
|
90
|
-
)
|
|
91
|
-
expect(response.body).toEqual({
|
|
92
|
-
message: "Invalid credentials",
|
|
93
|
-
status: 403,
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
describe("sso user", () => {
|
|
98
|
-
let user: User
|
|
99
|
-
|
|
100
|
-
async function testSSOUser() {
|
|
101
|
-
const tenantId = user.tenantId!
|
|
102
|
-
const email = user.email
|
|
103
|
-
const password = "test"
|
|
104
|
-
|
|
105
|
-
const response = await config.api.auth.login(
|
|
106
|
-
tenantId,
|
|
107
|
-
email,
|
|
108
|
-
password,
|
|
109
|
-
{ status: 400 }
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
expect(response.body).toEqual({
|
|
113
|
-
message: "SSO user cannot login using password",
|
|
114
|
-
status: 400,
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
describe("budibase sso user", () => {
|
|
119
|
-
it("should prevent user from logging in", async () => {
|
|
120
|
-
user = await createSSOUser()
|
|
121
|
-
await testSSOUser()
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
describe("root account sso user", () => {
|
|
126
|
-
it("should prevent user from logging in", async () => {
|
|
127
|
-
user = await config.createUser()
|
|
128
|
-
const account = structures.accounts.ssoAccount() as CloudAccount
|
|
129
|
-
mocks.accounts.getAccount.mockReturnValueOnce(
|
|
130
|
-
Promise.resolve(account)
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
await testSSOUser()
|
|
134
|
-
})
|
|
135
|
-
})
|
|
136
|
-
})
|
|
29
|
+
it("should login", () => {})
|
|
137
30
|
})
|
|
138
31
|
|
|
139
32
|
describe("POST /api/global/auth/logout", () => {
|
|
140
33
|
it("should logout", async () => {
|
|
141
|
-
|
|
34
|
+
await config.api.auth.logout()
|
|
142
35
|
expect(events.auth.logout).toBeCalledTimes(1)
|
|
143
36
|
|
|
144
|
-
|
|
145
|
-
expect(authCookie).toBe("")
|
|
37
|
+
// TODO: Verify sessions deleted
|
|
146
38
|
})
|
|
147
39
|
})
|
|
148
40
|
|
|
149
41
|
describe("POST /api/global/auth/:tenantId/reset", () => {
|
|
150
42
|
it("should generate password reset email", async () => {
|
|
151
|
-
const user = await config.createUser()
|
|
152
|
-
|
|
153
43
|
const { res, code } = await config.api.auth.requestPasswordReset(
|
|
154
|
-
sendMailMock
|
|
155
|
-
user.email
|
|
44
|
+
sendMailMock
|
|
156
45
|
)
|
|
46
|
+
const user = await config.getUser("test@test.com")
|
|
157
47
|
|
|
158
48
|
expect(res.body).toEqual({
|
|
159
49
|
message: "Please check your email for a reset link.",
|
|
160
50
|
})
|
|
161
51
|
expect(sendMailMock).toHaveBeenCalled()
|
|
52
|
+
|
|
162
53
|
expect(code).toBeDefined()
|
|
163
54
|
expect(events.user.passwordResetRequested).toBeCalledTimes(1)
|
|
164
55
|
expect(events.user.passwordResetRequested).toBeCalledWith(user)
|
|
165
56
|
})
|
|
166
|
-
|
|
167
|
-
describe("sso user", () => {
|
|
168
|
-
let user: User
|
|
169
|
-
|
|
170
|
-
async function testSSOUser() {
|
|
171
|
-
const { res } = await config.api.auth.requestPasswordReset(
|
|
172
|
-
sendMailMock,
|
|
173
|
-
user.email,
|
|
174
|
-
{ status: 400 }
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
expect(res.body).toEqual({
|
|
178
|
-
message: "SSO user cannot reset password",
|
|
179
|
-
status: 400,
|
|
180
|
-
error: {
|
|
181
|
-
code: "http",
|
|
182
|
-
type: "generic",
|
|
183
|
-
},
|
|
184
|
-
})
|
|
185
|
-
expect(sendMailMock).not.toHaveBeenCalled()
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
describe("budibase sso user", () => {
|
|
189
|
-
it("should prevent user from generating password reset email", async () => {
|
|
190
|
-
user = await createSSOUser()
|
|
191
|
-
await testSSOUser()
|
|
192
|
-
})
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
describe("root account sso user", () => {
|
|
196
|
-
it("should prevent user from generating password reset email", async () => {
|
|
197
|
-
user = await config.createUser(structures.users.user())
|
|
198
|
-
const account = structures.accounts.ssoAccount() as CloudAccount
|
|
199
|
-
mocks.accounts.getAccount.mockReturnValueOnce(
|
|
200
|
-
Promise.resolve(account)
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
await testSSOUser()
|
|
204
|
-
})
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
57
|
})
|
|
208
58
|
|
|
209
59
|
describe("POST /api/global/auth/:tenantId/reset/update", () => {
|
|
210
60
|
it("should reset password", async () => {
|
|
211
|
-
let user = await config.createUser()
|
|
212
61
|
const { code } = await config.api.auth.requestPasswordReset(
|
|
213
|
-
sendMailMock
|
|
214
|
-
user.email
|
|
62
|
+
sendMailMock
|
|
215
63
|
)
|
|
64
|
+
const user = await config.getUser("test@test.com")
|
|
216
65
|
delete user.password
|
|
217
66
|
|
|
218
|
-
const
|
|
219
|
-
const res = await config.api.auth.updatePassword(code!, newPassword)
|
|
220
|
-
|
|
221
|
-
user = await config.getUser(user.email)
|
|
222
|
-
delete user.password
|
|
67
|
+
const res = await config.api.auth.updatePassword(code)
|
|
223
68
|
|
|
224
69
|
expect(res.body).toEqual({ message: "password reset successfully." })
|
|
225
70
|
expect(events.user.passwordReset).toBeCalledTimes(1)
|
|
226
71
|
expect(events.user.passwordReset).toBeCalledWith(user)
|
|
227
72
|
|
|
228
|
-
//
|
|
229
|
-
await config.api.auth.login(user.tenantId, user.email, newPassword)
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
describe("sso user", () => {
|
|
233
|
-
let user: User | SSOUser
|
|
234
|
-
|
|
235
|
-
async function testSSOUser(code: string) {
|
|
236
|
-
const res = await config.api.auth.updatePassword(
|
|
237
|
-
code!,
|
|
238
|
-
generator.string(),
|
|
239
|
-
{ status: 400 }
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
expect(res.body).toEqual({
|
|
243
|
-
message: "Cannot reset password.",
|
|
244
|
-
status: 400,
|
|
245
|
-
})
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
describe("budibase sso user", () => {
|
|
249
|
-
it("should prevent user from generating password reset email", async () => {
|
|
250
|
-
user = await config.createUser()
|
|
251
|
-
const { code } = await config.api.auth.requestPasswordReset(
|
|
252
|
-
sendMailMock,
|
|
253
|
-
user.email
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
// convert to sso now that password reset has been requested
|
|
257
|
-
const ssoUser = user as SSOUser
|
|
258
|
-
ssoUser.providerType = structures.sso.providerType()
|
|
259
|
-
delete ssoUser.password
|
|
260
|
-
await config.doInTenant(() => userSdk.save(ssoUser))
|
|
261
|
-
|
|
262
|
-
await testSSOUser(code!)
|
|
263
|
-
})
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
describe("root account sso user", () => {
|
|
267
|
-
it("should prevent user from generating password reset email", async () => {
|
|
268
|
-
user = await config.createUser()
|
|
269
|
-
const { code } = await config.api.auth.requestPasswordReset(
|
|
270
|
-
sendMailMock,
|
|
271
|
-
user.email
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
// convert to account owner now that password has been requested
|
|
275
|
-
const account = structures.accounts.ssoAccount() as CloudAccount
|
|
276
|
-
mocks.accounts.getAccount.mockReturnValueOnce(
|
|
277
|
-
Promise.resolve(account)
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
await testSSOUser(code!)
|
|
281
|
-
})
|
|
282
|
-
})
|
|
73
|
+
// TODO: Login using new password
|
|
283
74
|
})
|
|
284
75
|
})
|
|
285
76
|
})
|
|
@@ -354,7 +145,7 @@ describe("/api/global/auth", () => {
|
|
|
354
145
|
const location: string = res.get("location")
|
|
355
146
|
expect(
|
|
356
147
|
location.startsWith(
|
|
357
|
-
|
|
148
|
+
"http://localhost/auth?response_type=code&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A10000%2Fapi%2Fglobal%2Fauth%2Fdefault%2Foidc%2Fcallback&scope=openid%20profile%20email%20offline_access"
|
|
358
149
|
)
|
|
359
150
|
).toBe(true)
|
|
360
151
|
})
|