@budibase/worker 1.2.58-alpha.6 → 1.2.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -7
- package/scripts/jestSetup.js +0 -6
- package/scripts/load/users.js +97 -0
- package/src/api/controllers/global/auth.ts +1 -14
- package/src/api/controllers/global/users.ts +55 -26
- package/src/api/{index.ts → index.js} +16 -12
- package/src/api/routes/global/configs.js +0 -1
- package/src/api/routes/index.js +0 -2
- package/src/api/routes/{global/tests/auth.spec.ts → tests/auth.spec.js} +56 -32
- package/src/api/routes/{global/tests/configs.spec.ts → tests/configs.spec.js} +47 -76
- package/src/api/routes/{global/tests/email.spec.ts → tests/email.spec.js} +15 -7
- package/src/api/routes/{global/tests/realEmail.spec.ts → tests/realEmail.spec.js} +21 -20
- package/src/api/routes/{global/tests/self.spec.ts → tests/self.spec.js} +18 -10
- package/src/api/routes/tests/users.spec.js +390 -0
- package/src/{environment.ts → environment.js} +9 -10
- package/src/index.ts +4 -6
- package/src/sdk/index.ts +0 -1
- package/src/sdk/users/users.ts +77 -271
- package/src/tests/TestConfiguration.js +231 -0
- package/src/tests/index.js +12 -0
- package/src/tests/mocks/index.js +5 -0
- package/src/tests/structures/index.js +14 -0
- package/src/tests/structures/users.ts +4 -12
- package/src/utilities/redis.js +0 -1
- package/src/api/controllers/system/accounts.ts +0 -21
- package/src/api/routes/global/tests/users.spec.ts +0 -512
- package/src/api/routes/system/accounts.ts +0 -19
- package/src/api/routes/system/tests/accounts.spec.ts +0 -57
- package/src/sdk/accounts/accounts.ts +0 -53
- package/src/sdk/accounts/index.ts +0 -1
- package/src/tests/TestConfiguration.ts +0 -273
- package/src/tests/api/accounts.ts +0 -28
- package/src/tests/api/auth.ts +0 -48
- package/src/tests/api/configs.ts +0 -40
- package/src/tests/api/email.ts +0 -24
- package/src/tests/api/index.ts +0 -25
- package/src/tests/api/self.ts +0 -21
- package/src/tests/api/users.ts +0 -111
- package/src/tests/index.ts +0 -14
- package/src/tests/mocks/index.ts +0 -7
- package/src/tests/structures/accounts.ts +0 -24
- package/src/tests/structures/index.ts +0 -20
package/src/sdk/users/users.ts
CHANGED
|
@@ -14,28 +14,9 @@ import {
|
|
|
14
14
|
HTTPError,
|
|
15
15
|
accounts,
|
|
16
16
|
migrations,
|
|
17
|
-
StaticDatabases,
|
|
18
|
-
ViewName,
|
|
19
|
-
events,
|
|
20
17
|
} from "@budibase/backend-core"
|
|
21
|
-
import {
|
|
22
|
-
MigrationType,
|
|
23
|
-
PlatformUserByEmail,
|
|
24
|
-
User,
|
|
25
|
-
BulkCreateUsersResponse,
|
|
26
|
-
CreateUserResponse,
|
|
27
|
-
BulkDeleteUsersResponse,
|
|
28
|
-
CloudAccount,
|
|
29
|
-
AllDocsResponse,
|
|
30
|
-
RowResponse,
|
|
31
|
-
BulkDocsResponse,
|
|
32
|
-
AccountMetadata,
|
|
33
|
-
InviteUsersRequest,
|
|
34
|
-
InviteUsersResponse,
|
|
35
|
-
} from "@budibase/types"
|
|
18
|
+
import { MigrationType, User } from "@budibase/types"
|
|
36
19
|
import { groups as groupUtils } from "@budibase/pro"
|
|
37
|
-
import { sendEmail } from "../../utilities/email"
|
|
38
|
-
import { EmailTemplatePurpose } from "../../constants"
|
|
39
20
|
|
|
40
21
|
const PAGE_LIMIT = 8
|
|
41
22
|
|
|
@@ -117,6 +98,7 @@ export const getUser = async (userId: string) => {
|
|
|
117
98
|
interface SaveUserOpts {
|
|
118
99
|
hashPassword?: boolean
|
|
119
100
|
requirePassword?: boolean
|
|
101
|
+
bulkCreate?: boolean
|
|
120
102
|
}
|
|
121
103
|
|
|
122
104
|
const buildUser = async (
|
|
@@ -127,7 +109,7 @@ const buildUser = async (
|
|
|
127
109
|
},
|
|
128
110
|
tenantId: string,
|
|
129
111
|
dbUser?: any
|
|
130
|
-
)
|
|
112
|
+
) => {
|
|
131
113
|
let { password, _id } = user
|
|
132
114
|
|
|
133
115
|
let hashedPassword
|
|
@@ -161,63 +143,62 @@ const buildUser = async (
|
|
|
161
143
|
return user
|
|
162
144
|
}
|
|
163
145
|
|
|
164
|
-
const validateUniqueUser = async (email: string, tenantId: string) => {
|
|
165
|
-
// check budibase users in other tenants
|
|
166
|
-
if (env.MULTI_TENANCY) {
|
|
167
|
-
const tenantUser = await tenancy.getTenantUser(email)
|
|
168
|
-
if (tenantUser != null && tenantUser.tenantId !== tenantId) {
|
|
169
|
-
throw `Unavailable`
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// check root account users in account portal
|
|
174
|
-
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
175
|
-
const account = await accounts.getAccount(email)
|
|
176
|
-
if (account && account.verified && account.tenantId !== tenantId) {
|
|
177
|
-
throw `Unavailable`
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
146
|
export const save = async (
|
|
183
|
-
user:
|
|
147
|
+
user: any,
|
|
184
148
|
opts: SaveUserOpts = {
|
|
185
149
|
hashPassword: true,
|
|
186
150
|
requirePassword: true,
|
|
151
|
+
bulkCreate: false,
|
|
187
152
|
}
|
|
188
|
-
)
|
|
153
|
+
) => {
|
|
189
154
|
const tenantId = tenancy.getTenantId()
|
|
190
155
|
const db = tenancy.getGlobalDB()
|
|
191
156
|
let { email, _id } = user
|
|
192
|
-
|
|
193
|
-
let dbUser:
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
dbUser = (await db.get(_id)) as User
|
|
197
|
-
if (email && dbUser.email !== email) {
|
|
198
|
-
throw "Email address cannot be changed"
|
|
199
|
-
}
|
|
200
|
-
email = dbUser.email
|
|
157
|
+
// make sure another user isn't using the same email
|
|
158
|
+
let dbUser: any
|
|
159
|
+
if (opts.bulkCreate) {
|
|
160
|
+
dbUser = null
|
|
201
161
|
} else if (email) {
|
|
202
|
-
//
|
|
162
|
+
// check budibase users inside the tenant
|
|
203
163
|
dbUser = await usersCore.getGlobalUserByEmail(email)
|
|
204
|
-
if (dbUser && dbUser._id !== _id) {
|
|
205
|
-
throw `
|
|
164
|
+
if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) {
|
|
165
|
+
throw `Email address ${email} already in use.`
|
|
206
166
|
}
|
|
207
|
-
} else {
|
|
208
|
-
throw new Error("_id or email is required")
|
|
209
|
-
}
|
|
210
167
|
|
|
211
|
-
|
|
168
|
+
// check budibase users in other tenants
|
|
169
|
+
if (env.MULTI_TENANCY) {
|
|
170
|
+
const tenantUser = await tenancy.getTenantUser(email)
|
|
171
|
+
if (tenantUser != null && tenantUser.tenantId !== tenantId) {
|
|
172
|
+
throw `Email address ${email} already in use.`
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// check root account users in account portal
|
|
177
|
+
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
178
|
+
const account = await accounts.getAccount(email)
|
|
179
|
+
if (account && account.verified && account.tenantId !== tenantId) {
|
|
180
|
+
throw `Email address ${email} already in use.`
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} else if (_id) {
|
|
184
|
+
dbUser = await db.get(_id)
|
|
185
|
+
}
|
|
212
186
|
|
|
213
187
|
let builtUser = await buildUser(user, opts, tenantId, dbUser)
|
|
214
188
|
|
|
215
189
|
// make sure we set the _id field for a new user
|
|
216
190
|
if (!_id) {
|
|
217
|
-
_id = builtUser._id
|
|
191
|
+
_id = builtUser._id
|
|
218
192
|
}
|
|
219
193
|
|
|
220
194
|
try {
|
|
195
|
+
const putOpts = {
|
|
196
|
+
password: builtUser.password,
|
|
197
|
+
...user,
|
|
198
|
+
}
|
|
199
|
+
if (opts.bulkCreate) {
|
|
200
|
+
return putOpts
|
|
201
|
+
}
|
|
221
202
|
// save the user to db
|
|
222
203
|
let response
|
|
223
204
|
const putUserFn = () => {
|
|
@@ -266,87 +247,29 @@ export const addTenant = async (
|
|
|
266
247
|
}
|
|
267
248
|
}
|
|
268
249
|
|
|
269
|
-
const getExistingTenantUsers = async (emails: string[]): Promise<User[]> => {
|
|
270
|
-
return dbUtils.queryGlobalView(ViewName.USER_BY_EMAIL, {
|
|
271
|
-
keys: emails,
|
|
272
|
-
include_docs: true,
|
|
273
|
-
arrayResponse: true,
|
|
274
|
-
})
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const getExistingPlatformUsers = async (
|
|
278
|
-
emails: string[]
|
|
279
|
-
): Promise<PlatformUserByEmail[]> => {
|
|
280
|
-
return dbUtils.doWithDB(
|
|
281
|
-
StaticDatabases.PLATFORM_INFO.name,
|
|
282
|
-
async (infoDb: any) => {
|
|
283
|
-
const response: AllDocsResponse<PlatformUserByEmail> =
|
|
284
|
-
await infoDb.allDocs({
|
|
285
|
-
keys: emails,
|
|
286
|
-
include_docs: true,
|
|
287
|
-
})
|
|
288
|
-
return response.rows
|
|
289
|
-
.filter(row => row.doc && (row.error !== "not_found") !== null)
|
|
290
|
-
.map((row: any) => row.doc)
|
|
291
|
-
}
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const getExistingAccounts = async (
|
|
296
|
-
emails: string[]
|
|
297
|
-
): Promise<AccountMetadata[]> => {
|
|
298
|
-
return dbUtils.queryPlatformView(ViewName.ACCOUNT_BY_EMAIL, {
|
|
299
|
-
keys: emails,
|
|
300
|
-
include_docs: true,
|
|
301
|
-
arrayResponse: true,
|
|
302
|
-
})
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Apply a system-wide search on emails:
|
|
307
|
-
* - in tenant
|
|
308
|
-
* - cross tenant
|
|
309
|
-
* - accounts
|
|
310
|
-
* return an array of emails that match the supplied emails.
|
|
311
|
-
*/
|
|
312
|
-
const searchExistingEmails = async (emails: string[]) => {
|
|
313
|
-
let matchedEmails: string[] = []
|
|
314
|
-
|
|
315
|
-
const existingTenantUsers = await getExistingTenantUsers(emails)
|
|
316
|
-
matchedEmails.push(...existingTenantUsers.map(user => user.email))
|
|
317
|
-
|
|
318
|
-
const existingPlatformUsers = await getExistingPlatformUsers(emails)
|
|
319
|
-
matchedEmails.push(...existingPlatformUsers.map(user => user._id!))
|
|
320
|
-
|
|
321
|
-
const existingAccounts = await getExistingAccounts(emails)
|
|
322
|
-
matchedEmails.push(...existingAccounts.map(account => account.email))
|
|
323
|
-
|
|
324
|
-
return [...new Set(matchedEmails)]
|
|
325
|
-
}
|
|
326
|
-
|
|
327
250
|
export const bulkCreate = async (
|
|
328
251
|
newUsersRequested: User[],
|
|
329
252
|
groups: string[]
|
|
330
|
-
)
|
|
253
|
+
) => {
|
|
331
254
|
const db = tenancy.getGlobalDB()
|
|
332
255
|
const tenantId = tenancy.getTenantId()
|
|
333
256
|
|
|
334
257
|
let usersToSave: any[] = []
|
|
335
258
|
let newUsers: any[] = []
|
|
336
259
|
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
260
|
+
const allUsers = await db.allDocs(
|
|
261
|
+
dbUtils.getGlobalUserParams(null, {
|
|
262
|
+
include_docs: true,
|
|
263
|
+
})
|
|
264
|
+
)
|
|
265
|
+
let mapped = allUsers.rows.map((row: any) => row.id)
|
|
340
266
|
|
|
267
|
+
const currentUserEmails = mapped.map((x: any) => x.email) || []
|
|
341
268
|
for (const newUser of newUsersRequested) {
|
|
342
269
|
if (
|
|
343
270
|
newUsers.find((x: any) => x.email === newUser.email) ||
|
|
344
|
-
|
|
271
|
+
currentUserEmails.includes(newUser.email)
|
|
345
272
|
) {
|
|
346
|
-
unsuccessful.push({
|
|
347
|
-
email: newUser.email,
|
|
348
|
-
reason: `Unavailable`,
|
|
349
|
-
})
|
|
350
273
|
continue
|
|
351
274
|
}
|
|
352
275
|
newUser.userGroups = groups
|
|
@@ -384,129 +307,62 @@ export const bulkCreate = async (
|
|
|
384
307
|
await apps.syncUserInApps(user._id)
|
|
385
308
|
}
|
|
386
309
|
|
|
387
|
-
|
|
310
|
+
return usersToBulkSave.map(user => {
|
|
388
311
|
return {
|
|
389
312
|
_id: user._id,
|
|
390
313
|
email: user.email,
|
|
391
314
|
}
|
|
392
315
|
})
|
|
393
|
-
|
|
394
|
-
return {
|
|
395
|
-
successful: saved,
|
|
396
|
-
unsuccessful,
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* For the given user id's, return the account holder if it is in the ids.
|
|
402
|
-
*/
|
|
403
|
-
const getAccountHolderFromUserIds = async (
|
|
404
|
-
userIds: string[]
|
|
405
|
-
): Promise<CloudAccount | undefined> => {
|
|
406
|
-
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
407
|
-
const tenantId = tenancy.getTenantId()
|
|
408
|
-
const account = await accounts.getAccountByTenantId(tenantId)
|
|
409
|
-
if (!account) {
|
|
410
|
-
throw new Error(`Account not found for tenantId=${tenantId}`)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const budibaseUserId = account.budibaseUserId
|
|
414
|
-
if (userIds.includes(budibaseUserId)) {
|
|
415
|
-
return account
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
316
|
}
|
|
419
317
|
|
|
420
|
-
export const bulkDelete = async (
|
|
421
|
-
userIds: string[]
|
|
422
|
-
): Promise<BulkDeleteUsersResponse> => {
|
|
318
|
+
export const bulkDelete = async (userIds: any) => {
|
|
423
319
|
const db = tenancy.getGlobalDB()
|
|
424
320
|
|
|
425
|
-
const response: BulkDeleteUsersResponse = {
|
|
426
|
-
successful: [],
|
|
427
|
-
unsuccessful: [],
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// remove the account holder from the delete request if present
|
|
431
|
-
const account = await getAccountHolderFromUserIds(userIds)
|
|
432
|
-
if (account) {
|
|
433
|
-
userIds = userIds.filter(u => u !== account.budibaseUserId)
|
|
434
|
-
// mark user as unsuccessful
|
|
435
|
-
response.unsuccessful.push({
|
|
436
|
-
_id: account.budibaseUserId,
|
|
437
|
-
email: account.email,
|
|
438
|
-
reason: "Account holder cannot be deleted",
|
|
439
|
-
})
|
|
440
|
-
}
|
|
441
|
-
|
|
442
321
|
let groupsToModify: any = {}
|
|
443
322
|
let builderCount = 0
|
|
444
|
-
|
|
445
323
|
// Get users and delete
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
324
|
+
let usersToDelete = (
|
|
325
|
+
await db.allDocs({
|
|
326
|
+
include_docs: true,
|
|
327
|
+
keys: userIds,
|
|
328
|
+
})
|
|
329
|
+
).rows.map((user: any) => {
|
|
330
|
+
// if we find a user that has an associated group, add it to
|
|
331
|
+
// an array so we can easily use allDocs on them later.
|
|
332
|
+
// This prevents us having to re-loop over all the users
|
|
333
|
+
if (user.doc.userGroups) {
|
|
334
|
+
for (let groupId of user.doc.userGroups) {
|
|
335
|
+
if (!Object.keys(groupsToModify).includes(groupId)) {
|
|
336
|
+
groupsToModify[groupId] = [user.id]
|
|
337
|
+
} else {
|
|
338
|
+
groupsToModify[groupId] = [...groupsToModify[groupId], user.id]
|
|
462
339
|
}
|
|
463
340
|
}
|
|
341
|
+
}
|
|
464
342
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return user.doc
|
|
343
|
+
// Also figure out how many builders are being deleted
|
|
344
|
+
if (eventHelpers.isAddingBuilder(user.doc, null)) {
|
|
345
|
+
builderCount++
|
|
471
346
|
}
|
|
472
|
-
)
|
|
473
347
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
348
|
+
return user.doc
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
const response = await db.bulkDocs(
|
|
352
|
+
usersToDelete.map((user: any) => ({
|
|
477
353
|
...user,
|
|
478
354
|
_deleted: true,
|
|
479
355
|
}))
|
|
480
356
|
)
|
|
481
357
|
|
|
482
|
-
// Deletion post processing
|
|
483
358
|
await groupUtils.bulkDeleteGroupUsers(groupsToModify)
|
|
359
|
+
|
|
360
|
+
//Deletion post processing
|
|
484
361
|
for (let user of usersToDelete) {
|
|
485
362
|
await bulkDeleteProcessing(user)
|
|
486
363
|
}
|
|
487
|
-
await quotas.removeDevelopers(builderCount)
|
|
488
364
|
|
|
489
|
-
|
|
490
|
-
// index users by id
|
|
491
|
-
const userIndex: { [key: string]: User } = {}
|
|
492
|
-
usersToDelete.reduce((prev, current) => {
|
|
493
|
-
prev[current._id!] = current
|
|
494
|
-
return prev
|
|
495
|
-
}, userIndex)
|
|
496
|
-
|
|
497
|
-
// add the successful and unsuccessful users to response
|
|
498
|
-
dbResponse.forEach(item => {
|
|
499
|
-
const email = userIndex[item.id].email
|
|
500
|
-
if (item.ok) {
|
|
501
|
-
response.successful.push({ _id: item.id, email })
|
|
502
|
-
} else {
|
|
503
|
-
response.unsuccessful.push({
|
|
504
|
-
_id: item.id,
|
|
505
|
-
email,
|
|
506
|
-
reason: "Database error",
|
|
507
|
-
})
|
|
508
|
-
}
|
|
509
|
-
})
|
|
365
|
+
await quotas.removeDevelopers(builderCount)
|
|
510
366
|
|
|
511
367
|
return response
|
|
512
368
|
}
|
|
@@ -555,53 +411,3 @@ const bulkDeleteProcessing = async (dbUser: User) => {
|
|
|
555
411
|
// let server know to sync user
|
|
556
412
|
await apps.syncUserInApps(userId)
|
|
557
413
|
}
|
|
558
|
-
|
|
559
|
-
export const invite = async (
|
|
560
|
-
users: InviteUsersRequest
|
|
561
|
-
): Promise<InviteUsersResponse> => {
|
|
562
|
-
const response: InviteUsersResponse = {
|
|
563
|
-
successful: [],
|
|
564
|
-
unsuccessful: [],
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
const matchedEmails = await searchExistingEmails(users.map(u => u.email))
|
|
568
|
-
const newUsers = []
|
|
569
|
-
|
|
570
|
-
// separate duplicates from new users
|
|
571
|
-
for (let user of users) {
|
|
572
|
-
if (matchedEmails.includes(user.email)) {
|
|
573
|
-
response.unsuccessful.push({ email: user.email, reason: "Unavailable" })
|
|
574
|
-
} else {
|
|
575
|
-
newUsers.push(user)
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
// overwrite users with new only
|
|
579
|
-
users = newUsers
|
|
580
|
-
|
|
581
|
-
// send the emails for new users
|
|
582
|
-
const tenantId = tenancy.getTenantId()
|
|
583
|
-
for (let user of users) {
|
|
584
|
-
try {
|
|
585
|
-
let userInfo = user.userInfo
|
|
586
|
-
if (!userInfo) {
|
|
587
|
-
userInfo = {}
|
|
588
|
-
}
|
|
589
|
-
userInfo.tenantId = tenantId
|
|
590
|
-
const opts: any = {
|
|
591
|
-
subject: "{{ company }} platform invitation",
|
|
592
|
-
info: userInfo,
|
|
593
|
-
}
|
|
594
|
-
await sendEmail(user.email, EmailTemplatePurpose.INVITATION, opts)
|
|
595
|
-
response.successful.push({ email: user.email })
|
|
596
|
-
await events.user.invited()
|
|
597
|
-
} catch (e) {
|
|
598
|
-
console.error(`Failed to send email invitation email=${user.email}`, e)
|
|
599
|
-
response.unsuccessful.push({
|
|
600
|
-
email: user.email,
|
|
601
|
-
reason: "Failed to send email",
|
|
602
|
-
})
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
return response
|
|
607
|
-
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
require("./mocks")
|
|
2
|
+
require("../db").init()
|
|
3
|
+
const env = require("../environment")
|
|
4
|
+
const controllers = require("./controllers")
|
|
5
|
+
const supertest = require("supertest")
|
|
6
|
+
const { jwt } = require("@budibase/backend-core/auth")
|
|
7
|
+
const { Cookies, Headers } = require("@budibase/backend-core/constants")
|
|
8
|
+
const { Configs } = require("../constants")
|
|
9
|
+
const { users } = require("@budibase/backend-core")
|
|
10
|
+
const { createASession } = require("@budibase/backend-core/sessions")
|
|
11
|
+
const { TENANT_ID, CSRF_TOKEN } = require("./structures")
|
|
12
|
+
const structures = require("./structures")
|
|
13
|
+
const { doInTenant } = require("@budibase/backend-core/tenancy")
|
|
14
|
+
const { groups } = require("@budibase/pro")
|
|
15
|
+
class TestConfiguration {
|
|
16
|
+
constructor(openServer = true) {
|
|
17
|
+
if (openServer) {
|
|
18
|
+
env.PORT = "0" // random port
|
|
19
|
+
this.server = require("../index")
|
|
20
|
+
// we need the request for logging in, involves cookies, hard to fake
|
|
21
|
+
this.request = supertest(this.server)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getRequest() {
|
|
26
|
+
return this.request
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// UTILS
|
|
30
|
+
|
|
31
|
+
async _req(config, params, controlFunc) {
|
|
32
|
+
const request = {}
|
|
33
|
+
// fake cookies, we don't need them
|
|
34
|
+
request.cookies = { set: () => {}, get: () => {} }
|
|
35
|
+
request.config = { jwtSecret: env.JWT_SECRET }
|
|
36
|
+
request.appId = this.appId
|
|
37
|
+
request.user = { appId: this.appId, tenantId: TENANT_ID }
|
|
38
|
+
request.query = {}
|
|
39
|
+
request.request = {
|
|
40
|
+
body: config,
|
|
41
|
+
}
|
|
42
|
+
request.throw = (status, err) => {
|
|
43
|
+
throw { status, message: err }
|
|
44
|
+
}
|
|
45
|
+
if (params) {
|
|
46
|
+
request.params = params
|
|
47
|
+
}
|
|
48
|
+
await doInTenant(TENANT_ID, () => {
|
|
49
|
+
return controlFunc(request)
|
|
50
|
+
})
|
|
51
|
+
return request.body
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// SETUP / TEARDOWN
|
|
55
|
+
|
|
56
|
+
async beforeAll() {
|
|
57
|
+
await this.login()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async afterAll() {
|
|
61
|
+
if (this.server) {
|
|
62
|
+
await this.server.close()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// USER / AUTH
|
|
67
|
+
|
|
68
|
+
async login() {
|
|
69
|
+
// create a test user
|
|
70
|
+
await this._req(
|
|
71
|
+
{
|
|
72
|
+
email: "test@test.com",
|
|
73
|
+
password: "test",
|
|
74
|
+
_id: "us_uuid1",
|
|
75
|
+
builder: {
|
|
76
|
+
global: true,
|
|
77
|
+
},
|
|
78
|
+
admin: {
|
|
79
|
+
global: true,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
null,
|
|
83
|
+
controllers.users.save
|
|
84
|
+
)
|
|
85
|
+
await createASession("us_uuid1", {
|
|
86
|
+
sessionId: "sessionid",
|
|
87
|
+
tenantId: TENANT_ID,
|
|
88
|
+
csrfToken: CSRF_TOKEN,
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
cookieHeader(cookies) {
|
|
93
|
+
return {
|
|
94
|
+
Cookie: [cookies],
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
defaultHeaders() {
|
|
99
|
+
const user = {
|
|
100
|
+
_id: "us_uuid1",
|
|
101
|
+
userId: "us_uuid1",
|
|
102
|
+
sessionId: "sessionid",
|
|
103
|
+
tenantId: TENANT_ID,
|
|
104
|
+
}
|
|
105
|
+
const authToken = jwt.sign(user, env.JWT_SECRET)
|
|
106
|
+
return {
|
|
107
|
+
Accept: "application/json",
|
|
108
|
+
...this.cookieHeader([`${Cookies.Auth}=${authToken}`]),
|
|
109
|
+
[Headers.CSRF_TOKEN]: CSRF_TOKEN,
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async getUser(email) {
|
|
114
|
+
return doInTenant(TENANT_ID, () => {
|
|
115
|
+
return users.getGlobalUserByEmail(email)
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async getGroup(id) {
|
|
120
|
+
return doInTenant(TENANT_ID, () => {
|
|
121
|
+
return groups.get(id)
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async saveGroup(group) {
|
|
126
|
+
const res = await this.getRequest()
|
|
127
|
+
.post(`/api/global/groups`)
|
|
128
|
+
.send(group)
|
|
129
|
+
.set(this.defaultHeaders())
|
|
130
|
+
.expect("Content-Type", /json/)
|
|
131
|
+
.expect(200)
|
|
132
|
+
return res.body
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async createUser(email, password) {
|
|
136
|
+
const user = await this.getUser(structures.users.email)
|
|
137
|
+
if (user) {
|
|
138
|
+
return user
|
|
139
|
+
}
|
|
140
|
+
await this._req(
|
|
141
|
+
structures.users.user({ email, password }),
|
|
142
|
+
null,
|
|
143
|
+
controllers.users.save
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async saveAdminUser() {
|
|
148
|
+
await this._req(
|
|
149
|
+
structures.users.user({ tenantId: TENANT_ID }),
|
|
150
|
+
null,
|
|
151
|
+
controllers.users.adminUser
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// CONFIGS
|
|
156
|
+
|
|
157
|
+
async deleteConfig(type) {
|
|
158
|
+
try {
|
|
159
|
+
const cfg = await this._req(
|
|
160
|
+
null,
|
|
161
|
+
{
|
|
162
|
+
type,
|
|
163
|
+
},
|
|
164
|
+
controllers.config.find
|
|
165
|
+
)
|
|
166
|
+
if (cfg) {
|
|
167
|
+
await this._req(
|
|
168
|
+
null,
|
|
169
|
+
{
|
|
170
|
+
id: cfg._id,
|
|
171
|
+
rev: cfg._rev,
|
|
172
|
+
},
|
|
173
|
+
controllers.config.destroy
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
} catch (err) {
|
|
177
|
+
// don't need to handle error
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// CONFIGS - SETTINGS
|
|
182
|
+
|
|
183
|
+
async saveSettingsConfig() {
|
|
184
|
+
await this.deleteConfig(Configs.SETTINGS)
|
|
185
|
+
await this._req(
|
|
186
|
+
structures.configs.settings(),
|
|
187
|
+
null,
|
|
188
|
+
controllers.config.save
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// CONFIGS - GOOGLE
|
|
193
|
+
|
|
194
|
+
async saveGoogleConfig() {
|
|
195
|
+
await this.deleteConfig(Configs.GOOGLE)
|
|
196
|
+
await this._req(structures.configs.google(), null, controllers.config.save)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// CONFIGS - OIDC
|
|
200
|
+
|
|
201
|
+
getOIDConfigCookie(configId) {
|
|
202
|
+
const token = jwt.sign(configId, env.JWT_SECRET)
|
|
203
|
+
return this.cookieHeader([[`${Cookies.OIDC_CONFIG}=${token}`]])
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async saveOIDCConfig() {
|
|
207
|
+
await this.deleteConfig(Configs.OIDC)
|
|
208
|
+
const config = structures.configs.oidc()
|
|
209
|
+
|
|
210
|
+
await this._req(config, null, controllers.config.save)
|
|
211
|
+
return config
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// CONFIGS - SMTP
|
|
215
|
+
|
|
216
|
+
async saveSmtpConfig() {
|
|
217
|
+
await this.deleteConfig(Configs.SMTP)
|
|
218
|
+
await this._req(structures.configs.smtp(), null, controllers.config.save)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async saveEtherealSmtpConfig() {
|
|
222
|
+
await this.deleteConfig(Configs.SMTP)
|
|
223
|
+
await this._req(
|
|
224
|
+
structures.configs.smtpEthereal(),
|
|
225
|
+
null,
|
|
226
|
+
controllers.config.save
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = TestConfiguration
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const TestConfiguration = require("./TestConfiguration")
|
|
2
|
+
const structures = require("./structures")
|
|
3
|
+
const mocks = require("./mocks")
|
|
4
|
+
const config = new TestConfiguration()
|
|
5
|
+
const request = config.getRequest()
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
structures,
|
|
9
|
+
mocks,
|
|
10
|
+
config,
|
|
11
|
+
request,
|
|
12
|
+
}
|