@budibase/worker 2.2.26 → 2.2.27-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.
- package/Dockerfile +1 -1
- package/__mocks__/node-fetch.ts +23 -0
- package/coverage/clover.xml +1423 -1379
- package/coverage/coverage-final.json +83 -77
- package/coverage/lcov-report/index.html +140 -140
- package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +52 -52
- package/coverage/lcov-report/src/api/controllers/global/configs.ts.html +167 -224
- package/coverage/lcov-report/src/api/controllers/global/email.ts.html +4 -4
- package/coverage/lcov-report/src/api/controllers/global/events.ts.html +136 -0
- package/coverage/lcov-report/src/api/controllers/global/index.html +83 -68
- package/coverage/lcov-report/src/api/controllers/global/license.ts.html +16 -16
- package/coverage/lcov-report/src/api/controllers/global/roles.ts.html +69 -69
- package/coverage/lcov-report/src/api/controllers/global/self.ts.html +20 -20
- package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +54 -54
- package/coverage/lcov-report/src/api/controllers/global/users.ts.html +83 -44
- package/coverage/lcov-report/src/api/controllers/global/workspaces.ts.html +9 -9
- package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +5 -5
- package/coverage/lcov-report/src/api/controllers/system/environment.ts.html +4 -4
- package/coverage/lcov-report/src/api/controllers/system/index.html +11 -11
- package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +5 -5
- package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +4 -4
- package/coverage/lcov-report/src/api/controllers/system/status.ts.html +5 -5
- package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +7 -7
- package/coverage/lcov-report/src/api/index.html +1 -1
- package/coverage/lcov-report/src/api/index.ts.html +42 -30
- package/coverage/lcov-report/src/api/routes/global/auth.ts.html +14 -14
- package/coverage/lcov-report/src/api/routes/global/configs.ts.html +25 -25
- package/coverage/lcov-report/src/api/routes/global/email.ts.html +13 -13
- package/coverage/lcov-report/src/api/routes/global/events.ts.html +109 -0
- package/coverage/lcov-report/src/api/routes/global/index.html +37 -22
- package/coverage/lcov-report/src/api/routes/global/license.ts.html +9 -9
- package/coverage/lcov-report/src/api/routes/global/roles.ts.html +10 -10
- package/coverage/lcov-report/src/api/routes/global/self.ts.html +11 -11
- package/coverage/lcov-report/src/api/routes/global/templates.ts.html +14 -14
- package/coverage/lcov-report/src/api/routes/global/users.ts.html +62 -20
- package/coverage/lcov-report/src/api/routes/global/workspaces.ts.html +12 -12
- package/coverage/lcov-report/src/api/routes/index.html +7 -7
- package/coverage/lcov-report/src/api/routes/index.ts.html +29 -23
- package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +10 -10
- package/coverage/lcov-report/src/api/routes/system/environment.ts.html +9 -9
- package/coverage/lcov-report/src/api/routes/system/index.html +15 -15
- package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +10 -10
- package/coverage/lcov-report/src/api/routes/system/restore.ts.html +9 -9
- package/coverage/lcov-report/src/api/routes/system/status.ts.html +9 -9
- package/coverage/lcov-report/src/api/routes/system/tenants.ts.html +10 -10
- package/coverage/lcov-report/src/api/routes/validation/index.html +3 -3
- package/coverage/lcov-report/src/api/routes/validation/index.ts.html +2 -2
- package/coverage/lcov-report/src/api/routes/validation/users.ts.html +14 -14
- package/coverage/lcov-report/src/constants/index.html +17 -17
- package/coverage/lcov-report/src/constants/index.ts.html +77 -53
- package/coverage/lcov-report/src/constants/templates/index.html +18 -18
- package/coverage/lcov-report/src/constants/templates/index.ts.html +42 -42
- package/coverage/lcov-report/src/db/index.html +17 -17
- package/coverage/lcov-report/src/db/index.ts.html +18 -18
- package/coverage/lcov-report/src/environment.ts.html +45 -30
- package/coverage/lcov-report/src/index.html +27 -27
- package/coverage/lcov-report/src/index.ts.html +55 -55
- package/coverage/lcov-report/src/middleware/cloudRestricted.ts.html +5 -5
- package/coverage/lcov-report/src/middleware/index.html +1 -1
- package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +9 -9
- package/coverage/lcov-report/src/migrations/functions/index.html +11 -11
- package/coverage/lcov-report/src/migrations/index.html +15 -15
- package/coverage/lcov-report/src/migrations/index.ts.html +27 -27
- package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +13 -13
- package/coverage/lcov-report/src/sdk/accounts/index.html +13 -13
- package/coverage/lcov-report/src/sdk/accounts/index.ts.html +2 -2
- package/coverage/lcov-report/src/sdk/index.html +5 -5
- package/coverage/lcov-report/src/sdk/index.ts.html +6 -6
- package/coverage/lcov-report/src/sdk/users/events.ts.html +82 -58
- package/coverage/lcov-report/src/sdk/users/index.html +28 -28
- package/coverage/lcov-report/src/sdk/users/index.ts.html +2 -2
- package/coverage/lcov-report/src/sdk/users/users.ts.html +174 -120
- package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +221 -107
- package/coverage/lcov-report/src/tests/api/accounts.ts.html +7 -7
- package/coverage/lcov-report/src/tests/api/auth.ts.html +25 -13
- package/coverage/lcov-report/src/tests/api/base.ts.html +5 -5
- package/coverage/lcov-report/src/tests/api/configs.ts.html +11 -11
- package/coverage/lcov-report/src/tests/api/email.ts.html +6 -6
- package/coverage/lcov-report/src/tests/api/environment.ts.html +6 -6
- package/coverage/lcov-report/src/tests/api/groups.ts.html +163 -0
- package/coverage/lcov-report/src/tests/api/index.html +91 -31
- package/coverage/lcov-report/src/tests/api/index.ts.html +62 -29
- package/coverage/lcov-report/src/tests/api/license.ts.html +136 -0
- package/coverage/lcov-report/src/tests/api/migrations.ts.html +8 -8
- package/coverage/lcov-report/src/tests/api/restore.ts.html +7 -7
- package/coverage/lcov-report/src/tests/api/roles.ts.html +181 -0
- package/coverage/lcov-report/src/tests/api/self.ts.html +7 -7
- package/coverage/lcov-report/src/tests/api/status.ts.html +6 -6
- package/coverage/lcov-report/src/tests/api/templates.ts.html +175 -0
- package/coverage/lcov-report/src/tests/api/tenants.ts.html +10 -10
- package/coverage/lcov-report/src/tests/api/users.ts.html +30 -27
- package/coverage/lcov-report/src/tests/controllers.ts.html +14 -14
- package/coverage/lcov-report/src/tests/index.html +34 -34
- package/coverage/lcov-report/src/tests/index.ts.html +19 -55
- package/coverage/lcov-report/src/tests/mocks/{email.js.html → email.ts.html} +5 -5
- package/coverage/lcov-report/src/tests/mocks/index.html +6 -6
- package/coverage/lcov-report/src/tests/mocks/index.ts.html +10 -10
- package/coverage/lcov-report/src/tests/structures/configs.ts.html +11 -11
- package/coverage/lcov-report/src/tests/structures/groups.ts.html +15 -15
- package/coverage/lcov-report/src/tests/structures/index.html +26 -26
- package/coverage/lcov-report/src/tests/structures/index.ts.html +16 -22
- package/coverage/lcov-report/src/tests/structures/users.ts.html +16 -16
- package/coverage/lcov-report/src/utilities/appService.ts.html +45 -18
- package/coverage/lcov-report/src/utilities/email.ts.html +28 -28
- package/coverage/lcov-report/src/utilities/fileSystem.ts.html +4 -4
- package/coverage/lcov-report/src/utilities/index.html +27 -27
- package/coverage/lcov-report/src/utilities/index.ts.html +2 -2
- package/coverage/lcov-report/src/utilities/redis.ts.html +20 -20
- package/coverage/lcov-report/src/utilities/templates.ts.html +6 -6
- package/coverage/lcov-report/src/utilities/users.ts.html +7 -7
- package/coverage/lcov.info +2826 -2609
- package/jest-testcontainers-config.js +8 -0
- package/jest.config.ts +10 -4
- package/package.json +11 -7
- package/scripts/dev/manage.js +2 -2
- package/src/api/controllers/global/auth.ts +1 -1
- package/src/api/controllers/global/configs.ts +112 -131
- package/src/api/controllers/global/events.ts +17 -0
- package/src/api/controllers/global/users.ts +14 -1
- package/src/api/index.ts +4 -0
- package/src/api/routes/global/auth.ts +1 -1
- package/src/api/routes/global/configs.ts +7 -7
- package/src/api/routes/global/email.ts +1 -1
- package/src/api/routes/global/events.ts +8 -0
- package/src/api/routes/global/license.ts +1 -1
- package/src/api/routes/global/roles.ts +1 -1
- package/src/api/routes/global/self.ts +1 -1
- package/src/api/routes/global/templates.ts +1 -1
- package/src/api/routes/global/tests/auth.spec.ts +30 -22
- package/src/api/routes/global/tests/configs.spec.ts +114 -94
- package/src/api/routes/global/tests/groups.spec.ts +58 -0
- package/src/api/routes/global/tests/license.spec.ts +0 -2
- package/src/api/routes/global/tests/roles.spec.ts +71 -5
- package/src/api/routes/global/tests/templates.spec.ts +81 -9
- package/src/api/routes/global/tests/users.spec.ts +35 -36
- package/src/api/routes/global/users.ts +15 -1
- package/src/api/routes/global/workspaces.ts +1 -1
- package/src/api/routes/index.ts +2 -0
- package/src/api/routes/system/accounts.ts +1 -1
- package/src/api/routes/system/environment.ts +1 -1
- package/src/api/routes/system/migrations.ts +1 -1
- package/src/api/routes/system/restore.ts +1 -1
- package/src/api/routes/system/status.ts +1 -1
- package/src/api/routes/system/tenants.ts +1 -1
- package/src/api/routes/system/tests/environment.spec.ts +1 -1
- package/src/constants/index.ts +13 -5
- package/src/db/index.ts +1 -1
- package/src/environment.ts +6 -1
- package/src/index.ts +5 -5
- package/src/middleware/cloudRestricted.ts +1 -1
- package/src/sdk/users/events.ts +8 -0
- package/src/sdk/users/users.ts +23 -5
- package/src/tests/TestConfiguration.ts +52 -14
- package/src/tests/api/auth.ts +7 -3
- package/src/tests/api/configs.ts +2 -2
- package/src/tests/api/groups.ts +26 -0
- package/src/tests/api/index.ts +12 -1
- package/src/tests/api/license.ts +17 -0
- package/src/tests/api/roles.ts +32 -0
- package/src/tests/api/templates.ts +30 -0
- package/src/tests/api/users.ts +4 -3
- package/src/tests/index.ts +6 -18
- package/src/tests/jestEnv.ts +11 -0
- package/src/tests/jestSetup.ts +8 -15
- package/src/tests/mocks/{email.js → email.ts} +1 -1
- package/src/tests/mocks/index.ts +4 -4
- package/src/tests/structures/configs.ts +2 -2
- package/src/tests/structures/groups.ts +1 -1
- package/src/tests/structures/index.ts +3 -5
- package/src/tests/structures/users.ts +1 -1
- package/src/utilities/appService.ts +12 -3
- package/tsconfig.json +2 -2
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { InviteUsersResponse, User } from "@budibase/types"
|
|
2
2
|
|
|
3
3
|
jest.mock("nodemailer")
|
|
4
|
-
import {
|
|
5
|
-
TestConfiguration,
|
|
6
|
-
mocks,
|
|
7
|
-
structures,
|
|
8
|
-
TENANT_1,
|
|
9
|
-
} from "../../../../tests"
|
|
4
|
+
import { TestConfiguration, mocks, structures } from "../../../../tests"
|
|
10
5
|
const sendMailMock = mocks.email.mock()
|
|
11
|
-
import { events, tenancy } from "@budibase/backend-core"
|
|
6
|
+
import { context, events, tenancy } from "@budibase/backend-core"
|
|
12
7
|
|
|
13
8
|
describe("/api/global/users", () => {
|
|
14
9
|
const config = new TestConfiguration()
|
|
@@ -27,16 +22,18 @@ describe("/api/global/users", () => {
|
|
|
27
22
|
|
|
28
23
|
describe("invite", () => {
|
|
29
24
|
it("should be able to generate an invitation", async () => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
await context.doInTenant(config.tenant1User!.tenantId, async () => {
|
|
26
|
+
const email = structures.users.newEmail()
|
|
27
|
+
const { code, res } = await config.api.users.sendUserInvite(
|
|
28
|
+
sendMailMock,
|
|
29
|
+
email
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
expect(res.body).toEqual({ message: "Invitation has been sent." })
|
|
33
|
+
expect(sendMailMock).toHaveBeenCalled()
|
|
34
|
+
expect(code).toBeDefined()
|
|
35
|
+
expect(events.user.invited).toBeCalledTimes(1)
|
|
36
|
+
})
|
|
40
37
|
})
|
|
41
38
|
|
|
42
39
|
it("should not be able to generate an invitation for existing user", async () => {
|
|
@@ -53,20 +50,22 @@ describe("/api/global/users", () => {
|
|
|
53
50
|
})
|
|
54
51
|
|
|
55
52
|
it("should be able to create new user from invite", async () => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
await context.doInTenant(config.tenant1User!.tenantId, async () => {
|
|
54
|
+
const email = structures.users.newEmail()
|
|
55
|
+
const { code } = await config.api.users.sendUserInvite(
|
|
56
|
+
sendMailMock,
|
|
57
|
+
email
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const res = await config.api.users.acceptInvite(code)
|
|
61
|
+
|
|
62
|
+
expect(res.body._id).toBeDefined()
|
|
63
|
+
const user = await config.getUser(email)
|
|
64
|
+
expect(user).toBeDefined()
|
|
65
|
+
expect(user._id).toEqual(res.body._id)
|
|
66
|
+
expect(events.user.inviteAccepted).toBeCalledTimes(1)
|
|
67
|
+
expect(events.user.inviteAccepted).toBeCalledWith(user)
|
|
68
|
+
})
|
|
70
69
|
})
|
|
71
70
|
})
|
|
72
71
|
|
|
@@ -116,9 +115,9 @@ describe("/api/global/users", () => {
|
|
|
116
115
|
|
|
117
116
|
it("should ignore users existing in other tenants", async () => {
|
|
118
117
|
const user = await config.createUser()
|
|
119
|
-
jest.
|
|
118
|
+
jest.clearAllMocks()
|
|
120
119
|
|
|
121
|
-
await tenancy.doInTenant(
|
|
120
|
+
await tenancy.doInTenant(config.getTenantId(), async () => {
|
|
122
121
|
const response = await config.api.users.bulkCreateUsers([user])
|
|
123
122
|
|
|
124
123
|
expect(response.created?.successful.length).toBe(0)
|
|
@@ -229,9 +228,9 @@ describe("/api/global/users", () => {
|
|
|
229
228
|
|
|
230
229
|
it("should not be able to create user that exists in other tenant", async () => {
|
|
231
230
|
const user = await config.createUser()
|
|
232
|
-
jest.
|
|
231
|
+
jest.clearAllMocks()
|
|
233
232
|
|
|
234
|
-
await tenancy.doInTenant(
|
|
233
|
+
await tenancy.doInTenant(config.getTenantId(), async () => {
|
|
235
234
|
delete user._id
|
|
236
235
|
const response = await config.api.users.saveUser(user, 400)
|
|
237
236
|
|
|
@@ -444,7 +443,7 @@ describe("/api/global/users", () => {
|
|
|
444
443
|
})
|
|
445
444
|
|
|
446
445
|
it("should not be able to update email address", async () => {
|
|
447
|
-
const email =
|
|
446
|
+
const email = structures.email()
|
|
448
447
|
const user = await config.createUser(structures.users.user({ email }))
|
|
449
448
|
user.email = "new@test.com"
|
|
450
449
|
|
|
@@ -38,6 +38,13 @@ function buildInviteMultipleValidation() {
|
|
|
38
38
|
))
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function buildInviteLookupValidation() {
|
|
42
|
+
// prettier-ignore
|
|
43
|
+
return auth.joiValidator.params(Joi.object({
|
|
44
|
+
code: Joi.string().required()
|
|
45
|
+
}).unknown(true))
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
const createUserAdminOnly = (ctx: any, next: any) => {
|
|
42
49
|
if (!ctx.request.body._id) {
|
|
43
50
|
return auth.adminOnly(ctx, next)
|
|
@@ -51,6 +58,8 @@ function buildInviteAcceptValidation() {
|
|
|
51
58
|
return auth.joiValidator.body(Joi.object({
|
|
52
59
|
inviteCode: Joi.string().required(),
|
|
53
60
|
password: Joi.string().required(),
|
|
61
|
+
firstName: Joi.string().required(),
|
|
62
|
+
lastName: Joi.string().optional(),
|
|
54
63
|
}).required().unknown(true))
|
|
55
64
|
}
|
|
56
65
|
|
|
@@ -91,6 +100,11 @@ router
|
|
|
91
100
|
)
|
|
92
101
|
|
|
93
102
|
// non-global endpoints
|
|
103
|
+
.get(
|
|
104
|
+
"/api/global/users/invite/:code",
|
|
105
|
+
buildInviteLookupValidation(),
|
|
106
|
+
controller.checkInvite
|
|
107
|
+
)
|
|
94
108
|
.post(
|
|
95
109
|
"/api/global/users/invite/accept",
|
|
96
110
|
buildInviteAcceptValidation(),
|
|
@@ -113,4 +127,4 @@ router
|
|
|
113
127
|
selfController.updateSelf
|
|
114
128
|
)
|
|
115
129
|
|
|
116
|
-
export
|
|
130
|
+
export default router
|
package/src/api/routes/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import templateRoutes from "./global/templates"
|
|
|
7
7
|
import emailRoutes from "./global/email"
|
|
8
8
|
import authRoutes from "./global/auth"
|
|
9
9
|
import roleRoutes from "./global/roles"
|
|
10
|
+
import eventRoutes from "./global/events"
|
|
10
11
|
import environmentRoutes from "./system/environment"
|
|
11
12
|
import tenantsRoutes from "./system/tenants"
|
|
12
13
|
import statusRoutes from "./system/status"
|
|
@@ -34,4 +35,5 @@ export const routes: Router[] = [
|
|
|
34
35
|
migrationRoutes,
|
|
35
36
|
accountRoutes,
|
|
36
37
|
restoreRoutes,
|
|
38
|
+
eventRoutes,
|
|
37
39
|
]
|
package/src/constants/index.ts
CHANGED
|
@@ -27,6 +27,14 @@ export enum EmailTemplatePurpose {
|
|
|
27
27
|
CUSTOM = "custom",
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export enum TemplateMetadataNames {
|
|
31
|
+
BASE = "Base format",
|
|
32
|
+
PASSWORD_RECOVERY = "Password recovery",
|
|
33
|
+
WELCOME = "User welcome",
|
|
34
|
+
INVITATION = "User invitation",
|
|
35
|
+
CUSTOM = "Custom",
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
export enum InternalTemplateBinding {
|
|
31
39
|
PLATFORM_URL = "platformUrl",
|
|
32
40
|
COMPANY = "company",
|
|
@@ -93,7 +101,7 @@ export const TemplateBindings = {
|
|
|
93
101
|
export const TemplateMetadata = {
|
|
94
102
|
[TemplateType.EMAIL]: [
|
|
95
103
|
{
|
|
96
|
-
name:
|
|
104
|
+
name: TemplateMetadataNames.BASE,
|
|
97
105
|
description:
|
|
98
106
|
"This is the base template, all others are based on it. The {{ body }} will be replaced with another email template.",
|
|
99
107
|
category: "miscellaneous",
|
|
@@ -110,7 +118,7 @@ export const TemplateMetadata = {
|
|
|
110
118
|
],
|
|
111
119
|
},
|
|
112
120
|
{
|
|
113
|
-
name:
|
|
121
|
+
name: TemplateMetadataNames.PASSWORD_RECOVERY,
|
|
114
122
|
description:
|
|
115
123
|
"When a user requests a password reset they will receive an email built with this template.",
|
|
116
124
|
category: "user management",
|
|
@@ -129,7 +137,7 @@ export const TemplateMetadata = {
|
|
|
129
137
|
],
|
|
130
138
|
},
|
|
131
139
|
{
|
|
132
|
-
name:
|
|
140
|
+
name: TemplateMetadataNames.WELCOME,
|
|
133
141
|
description:
|
|
134
142
|
"When a new user is added they will be sent a welcome email using this template.",
|
|
135
143
|
category: "user management",
|
|
@@ -137,7 +145,7 @@ export const TemplateMetadata = {
|
|
|
137
145
|
bindings: [],
|
|
138
146
|
},
|
|
139
147
|
{
|
|
140
|
-
name:
|
|
148
|
+
name: TemplateMetadataNames.INVITATION,
|
|
141
149
|
description:
|
|
142
150
|
"When inviting a user via the email on-boarding this template will be used.",
|
|
143
151
|
category: "user management",
|
|
@@ -156,7 +164,7 @@ export const TemplateMetadata = {
|
|
|
156
164
|
],
|
|
157
165
|
},
|
|
158
166
|
{
|
|
159
|
-
name:
|
|
167
|
+
name: TemplateMetadataNames.CUSTOM,
|
|
160
168
|
description:
|
|
161
169
|
"A custom template, this is currently used for SMTP email actions in automations.",
|
|
162
170
|
category: "automations",
|
package/src/db/index.ts
CHANGED
package/src/environment.ts
CHANGED
|
@@ -91,6 +91,11 @@ for (let [key, value] of Object.entries(environment)) {
|
|
|
91
91
|
// @ts-ignore
|
|
92
92
|
environment[key] = 0
|
|
93
93
|
}
|
|
94
|
+
// handle the edge case of "false" to disable an environment variable
|
|
95
|
+
if (value === "false") {
|
|
96
|
+
// @ts-ignore
|
|
97
|
+
environment[key] = 0
|
|
98
|
+
}
|
|
94
99
|
}
|
|
95
100
|
|
|
96
|
-
export
|
|
101
|
+
export default environment
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { Event } from "@sentry/types/dist/event"
|
|
|
14
14
|
import Application from "koa"
|
|
15
15
|
import { bootstrap } from "global-agent"
|
|
16
16
|
import * as db from "./db"
|
|
17
|
-
import { auth, logging, events,
|
|
17
|
+
import { auth, logging, events, middleware } from "@budibase/backend-core"
|
|
18
18
|
db.init()
|
|
19
19
|
import Koa from "koa"
|
|
20
20
|
import koaBody from "koa-body"
|
|
@@ -24,7 +24,7 @@ import * as redis from "./utilities/redis"
|
|
|
24
24
|
const Sentry = require("@sentry/node")
|
|
25
25
|
const koaSession = require("koa-session")
|
|
26
26
|
const logger = require("koa-pino-logger")
|
|
27
|
-
|
|
27
|
+
import destroyable from "server-destroy"
|
|
28
28
|
|
|
29
29
|
// this will setup http and https proxies form env variables
|
|
30
30
|
bootstrap()
|
|
@@ -36,7 +36,8 @@ app.keys = ["secret", "key"]
|
|
|
36
36
|
// set up top level koa middleware
|
|
37
37
|
app.use(koaBody({ multipart: true }))
|
|
38
38
|
app.use(koaSession(app))
|
|
39
|
-
app.use(
|
|
39
|
+
app.use(middleware.logging)
|
|
40
|
+
app.use(logger(logging.pinoSettings()))
|
|
40
41
|
|
|
41
42
|
// authentication
|
|
42
43
|
app.use(auth.passport.initialize())
|
|
@@ -79,11 +80,10 @@ server.on("close", async () => {
|
|
|
79
80
|
|
|
80
81
|
const shutdown = () => {
|
|
81
82
|
server.close()
|
|
82
|
-
// @ts-ignore
|
|
83
83
|
server.destroy()
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
export
|
|
86
|
+
export default server.listen(parseInt(env.PORT || "4002"), async () => {
|
|
87
87
|
console.log(`Worker running on ${JSON.stringify(server.address())}`)
|
|
88
88
|
await redis.init()
|
|
89
89
|
})
|
|
@@ -6,7 +6,7 @@ import { BBContext } from "@budibase/types"
|
|
|
6
6
|
* This is a restricted endpoint in the cloud.
|
|
7
7
|
* Ensure that the correct API key has been supplied.
|
|
8
8
|
*/
|
|
9
|
-
export
|
|
9
|
+
export default async (ctx: BBContext, next: any) => {
|
|
10
10
|
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
11
11
|
const apiKey = ctx.request.headers[constants.Header.API_KEY]
|
|
12
12
|
if (apiKey !== env.INTERNAL_API_KEY) {
|
package/src/sdk/users/events.ts
CHANGED
|
@@ -73,6 +73,10 @@ export const handleSaveEvents = async (
|
|
|
73
73
|
await events.user.permissionAdminRemoved(user)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
if (isOnboardingComplete(user, existingUser)) {
|
|
77
|
+
await events.user.onboardingComplete(user)
|
|
78
|
+
}
|
|
79
|
+
|
|
76
80
|
if (
|
|
77
81
|
!existingUser.forceResetPassword &&
|
|
78
82
|
user.forceResetPassword &&
|
|
@@ -114,6 +118,10 @@ const isRemovingAdmin = (user: any, existingUser: any) => {
|
|
|
114
118
|
return isRemovingPermission(user, existingUser, isAdmin)
|
|
115
119
|
}
|
|
116
120
|
|
|
121
|
+
const isOnboardingComplete = (user: any, existingUser: any) => {
|
|
122
|
+
return !existingUser?.onboardedAt && typeof user.onboardedAt === "string"
|
|
123
|
+
}
|
|
124
|
+
|
|
117
125
|
/**
|
|
118
126
|
* Check if a permission is being added to a new or existing user.
|
|
119
127
|
*/
|
package/src/sdk/users/users.ts
CHANGED
|
@@ -25,11 +25,13 @@ import {
|
|
|
25
25
|
InviteUsersRequest,
|
|
26
26
|
InviteUsersResponse,
|
|
27
27
|
MigrationType,
|
|
28
|
+
PlatformUser,
|
|
28
29
|
PlatformUserByEmail,
|
|
29
30
|
RowResponse,
|
|
30
31
|
SearchUsersRequest,
|
|
31
32
|
User,
|
|
32
33
|
ThirdPartyUser,
|
|
34
|
+
isUser,
|
|
33
35
|
} from "@budibase/types"
|
|
34
36
|
import { sendEmail } from "../../utilities/email"
|
|
35
37
|
import { EmailTemplatePurpose } from "../../constants"
|
|
@@ -153,10 +155,26 @@ const buildUser = async (
|
|
|
153
155
|
return fullUser
|
|
154
156
|
}
|
|
155
157
|
|
|
158
|
+
// lookup, could be email or userId, either will return a doc
|
|
159
|
+
export const getPlatformUser = async (
|
|
160
|
+
identifier: string
|
|
161
|
+
): Promise<PlatformUser | null> => {
|
|
162
|
+
// use the view here and allow to find anyone regardless of casing
|
|
163
|
+
// Use lowercase to ensure email login is case insensitive
|
|
164
|
+
const response = dbUtils.queryPlatformView(
|
|
165
|
+
ViewName.PLATFORM_USERS_LOWERCASE,
|
|
166
|
+
{
|
|
167
|
+
keys: [identifier.toLowerCase()],
|
|
168
|
+
include_docs: true,
|
|
169
|
+
}
|
|
170
|
+
) as Promise<PlatformUser>
|
|
171
|
+
return response
|
|
172
|
+
}
|
|
173
|
+
|
|
156
174
|
const validateUniqueUser = async (email: string, tenantId: string) => {
|
|
157
175
|
// check budibase users in other tenants
|
|
158
176
|
if (env.MULTI_TENANCY) {
|
|
159
|
-
const tenantUser = await
|
|
177
|
+
const tenantUser = await getPlatformUser(email)
|
|
160
178
|
if (tenantUser != null && tenantUser.tenantId !== tenantId) {
|
|
161
179
|
throw `Unavailable`
|
|
162
180
|
}
|
|
@@ -246,10 +264,10 @@ export const save = async (
|
|
|
246
264
|
builtUser._rev = response.rev
|
|
247
265
|
|
|
248
266
|
await eventHelpers.handleSaveEvents(builtUser, dbUser)
|
|
249
|
-
await addTenant(tenantId, _id, email)
|
|
250
267
|
await cache.user.invalidateUser(response.id)
|
|
268
|
+
|
|
251
269
|
// let server know to sync user
|
|
252
|
-
await apps.syncUserInApps(_id)
|
|
270
|
+
await apps.syncUserInApps(_id, dbUser)
|
|
253
271
|
|
|
254
272
|
await Promise.all(groupPromises)
|
|
255
273
|
|
|
@@ -555,7 +573,7 @@ export const destroy = async (id: string, currentUser: any) => {
|
|
|
555
573
|
await cache.user.invalidateUser(userId)
|
|
556
574
|
await sessions.invalidateSessions(userId, { reason: "deletion" })
|
|
557
575
|
// let server know to sync user
|
|
558
|
-
await apps.syncUserInApps(userId)
|
|
576
|
+
await apps.syncUserInApps(userId, dbUser)
|
|
559
577
|
}
|
|
560
578
|
|
|
561
579
|
const bulkDeleteProcessing = async (dbUser: User) => {
|
|
@@ -565,7 +583,7 @@ const bulkDeleteProcessing = async (dbUser: User) => {
|
|
|
565
583
|
await cache.user.invalidateUser(userId)
|
|
566
584
|
await sessions.invalidateSessions(userId, { reason: "bulk-deletion" })
|
|
567
585
|
// let server know to sync user
|
|
568
|
-
await apps.syncUserInApps(userId)
|
|
586
|
+
await apps.syncUserInApps(userId, dbUser)
|
|
569
587
|
}
|
|
570
588
|
|
|
571
589
|
export const invite = async (
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import "./mocks"
|
|
1
|
+
import mocks from "./mocks"
|
|
2
|
+
|
|
3
|
+
// init the licensing mock
|
|
4
|
+
import * as pro from "@budibase/pro"
|
|
5
|
+
mocks.licenses.init(pro)
|
|
6
|
+
|
|
7
|
+
// use unlimited license by default
|
|
8
|
+
mocks.licenses.useUnlimited()
|
|
9
|
+
|
|
2
10
|
import * as dbConfig from "../db"
|
|
3
11
|
dbConfig.init()
|
|
4
12
|
import env from "../environment"
|
|
@@ -12,22 +20,44 @@ import {
|
|
|
12
20
|
auth,
|
|
13
21
|
constants,
|
|
14
22
|
env as coreEnv,
|
|
23
|
+
DEFAULT_TENANT_ID,
|
|
15
24
|
} from "@budibase/backend-core"
|
|
16
|
-
import structures, { TENANT_ID,
|
|
25
|
+
import structures, { TENANT_ID, CSRF_TOKEN } from "./structures"
|
|
17
26
|
import { CreateUserResponse, User, AuthToken } from "@budibase/types"
|
|
18
27
|
import API from "./api"
|
|
28
|
+
import sdk from "../sdk"
|
|
19
29
|
|
|
20
30
|
enum Mode {
|
|
21
31
|
CLOUD = "cloud",
|
|
22
32
|
SELF = "self",
|
|
23
33
|
}
|
|
24
34
|
|
|
35
|
+
async function retry<T extends (...arg0: any[]) => any>(
|
|
36
|
+
fn: T,
|
|
37
|
+
maxTry: number = 5,
|
|
38
|
+
retryCount = 1
|
|
39
|
+
): Promise<Awaited<ReturnType<T>>> {
|
|
40
|
+
const currRetry = typeof retryCount === "number" ? retryCount : 1
|
|
41
|
+
try {
|
|
42
|
+
const result = await fn()
|
|
43
|
+
return result
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.log(`Retry ${currRetry} failed.`)
|
|
46
|
+
if (currRetry > maxTry) {
|
|
47
|
+
console.log(`All ${maxTry} retry attempts exhausted`)
|
|
48
|
+
throw e
|
|
49
|
+
}
|
|
50
|
+
return retry(fn, maxTry, currRetry + 1)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
25
54
|
class TestConfiguration {
|
|
26
55
|
server: any
|
|
27
56
|
request: any
|
|
28
57
|
api: API
|
|
29
58
|
defaultUser?: User
|
|
30
59
|
tenant1User?: User
|
|
60
|
+
#tenantId?: string
|
|
31
61
|
|
|
32
62
|
constructor(
|
|
33
63
|
opts: { openServer: boolean; mode: Mode } = {
|
|
@@ -43,7 +73,7 @@ class TestConfiguration {
|
|
|
43
73
|
|
|
44
74
|
if (opts.openServer) {
|
|
45
75
|
env.PORT = "0" // random port
|
|
46
|
-
this.server = require("../index")
|
|
76
|
+
this.server = require("../index").default
|
|
47
77
|
// we need the request for logging in, involves cookies, hard to fake
|
|
48
78
|
this.request = supertest(this.server)
|
|
49
79
|
}
|
|
@@ -104,13 +134,21 @@ class TestConfiguration {
|
|
|
104
134
|
// SETUP / TEARDOWN
|
|
105
135
|
|
|
106
136
|
async beforeAll() {
|
|
107
|
-
|
|
108
|
-
|
|
137
|
+
try {
|
|
138
|
+
this.#tenantId = structures.tenant.id()
|
|
109
139
|
|
|
110
|
-
|
|
111
|
-
await this.
|
|
112
|
-
await this.createSession(this.
|
|
113
|
-
|
|
140
|
+
// Running tests in parallel causes issues creating the globaldb twice. This ensures the db is properly created before starting
|
|
141
|
+
await retry(async () => await this.createDefaultUser())
|
|
142
|
+
await this.createSession(this.defaultUser!)
|
|
143
|
+
|
|
144
|
+
await tenancy.doInTenant(this.#tenantId, async () => {
|
|
145
|
+
await this.createTenant1User()
|
|
146
|
+
await this.createSession(this.tenant1User!)
|
|
147
|
+
})
|
|
148
|
+
} catch (e: any) {
|
|
149
|
+
console.log(e)
|
|
150
|
+
throw new Error(e.message)
|
|
151
|
+
}
|
|
114
152
|
}
|
|
115
153
|
|
|
116
154
|
async afterAll() {
|
|
@@ -125,6 +163,8 @@ class TestConfiguration {
|
|
|
125
163
|
// create user / new tenant
|
|
126
164
|
const res = await this.api.users.createAdminUser()
|
|
127
165
|
|
|
166
|
+
await sdk.users.addTenant(res.tenantId, res.userId, res.email)
|
|
167
|
+
|
|
128
168
|
// return the created user
|
|
129
169
|
const userRes = await this.api.users.getUser(res.userId, {
|
|
130
170
|
headers: {
|
|
@@ -144,7 +184,7 @@ class TestConfiguration {
|
|
|
144
184
|
try {
|
|
145
185
|
return tenancy.getTenantId()
|
|
146
186
|
} catch (e: any) {
|
|
147
|
-
return
|
|
187
|
+
return DEFAULT_TENANT_ID
|
|
148
188
|
}
|
|
149
189
|
}
|
|
150
190
|
|
|
@@ -195,7 +235,7 @@ class TestConfiguration {
|
|
|
195
235
|
const tenantId = this.getTenantId()
|
|
196
236
|
if (tenantId === TENANT_ID) {
|
|
197
237
|
return this.authHeaders(this.defaultUser!)
|
|
198
|
-
} else if (tenantId ===
|
|
238
|
+
} else if (tenantId === this.getTenantId()) {
|
|
199
239
|
return this.authHeaders(this.tenant1User!)
|
|
200
240
|
} else {
|
|
201
241
|
throw new Error("could not determine auth headers to use")
|
|
@@ -214,7 +254,6 @@ class TestConfiguration {
|
|
|
214
254
|
|
|
215
255
|
async createDefaultUser() {
|
|
216
256
|
const user = structures.users.adminUser({
|
|
217
|
-
email: "test@test.com",
|
|
218
257
|
password: "test",
|
|
219
258
|
})
|
|
220
259
|
this.defaultUser = await this.createUser(user)
|
|
@@ -222,7 +261,6 @@ class TestConfiguration {
|
|
|
222
261
|
|
|
223
262
|
async createTenant1User() {
|
|
224
263
|
const user = structures.users.adminUser({
|
|
225
|
-
email: "tenant1@test.com",
|
|
226
264
|
password: "test",
|
|
227
265
|
})
|
|
228
266
|
this.tenant1User = await this.createUser(user)
|
|
@@ -319,4 +357,4 @@ class TestConfiguration {
|
|
|
319
357
|
}
|
|
320
358
|
}
|
|
321
359
|
|
|
322
|
-
export
|
|
360
|
+
export default TestConfiguration
|
package/src/tests/api/auth.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import structures from "../structures"
|
|
1
2
|
import TestConfiguration from "../TestConfiguration"
|
|
2
3
|
import { TestAPI } from "./base"
|
|
3
4
|
|
|
@@ -24,14 +25,17 @@ export class AuthAPI extends TestAPI {
|
|
|
24
25
|
.expect(200)
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
requestPasswordReset = async (sendMailMock: any) => {
|
|
28
|
+
requestPasswordReset = async (sendMailMock: any, userEmail: string) => {
|
|
28
29
|
await this.config.saveSmtpConfig()
|
|
29
30
|
await this.config.saveSettingsConfig()
|
|
30
|
-
await this.config.createUser(
|
|
31
|
+
await this.config.createUser({
|
|
32
|
+
...structures.users.user(),
|
|
33
|
+
email: userEmail,
|
|
34
|
+
})
|
|
31
35
|
const res = await this.request
|
|
32
36
|
.post(`/api/global/auth/${this.config.getTenantId()}/reset`)
|
|
33
37
|
.send({
|
|
34
|
-
email:
|
|
38
|
+
email: userEmail,
|
|
35
39
|
})
|
|
36
40
|
.expect("Content-Type", /json/)
|
|
37
41
|
.expect(200)
|
package/src/tests/api/configs.ts
CHANGED
|
@@ -10,8 +10,8 @@ export class ConfigAPI extends TestAPI {
|
|
|
10
10
|
return this.request
|
|
11
11
|
.get(`/api/global/configs/checklist`)
|
|
12
12
|
.set(this.config.defaultHeaders())
|
|
13
|
-
.expect("Content-Type", /json/)
|
|
14
13
|
.expect(200)
|
|
14
|
+
.expect("Content-Type", /json/)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
saveConfig = (data: any) => {
|
|
@@ -19,8 +19,8 @@ export class ConfigAPI extends TestAPI {
|
|
|
19
19
|
.post(`/api/global/configs`)
|
|
20
20
|
.send(data)
|
|
21
21
|
.set(this.config.defaultHeaders())
|
|
22
|
-
.expect("Content-Type", /json/)
|
|
23
22
|
.expect(200)
|
|
23
|
+
.expect("Content-Type", /json/)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
OIDCCallback = (configId: string, preAuthRes: any) => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { UserGroup } from "@budibase/types"
|
|
2
|
+
import TestConfiguration from "../TestConfiguration"
|
|
3
|
+
import { TestAPI } from "./base"
|
|
4
|
+
|
|
5
|
+
export class GroupsAPI extends TestAPI {
|
|
6
|
+
constructor(config: TestConfiguration) {
|
|
7
|
+
super(config)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
saveGroup = (group: UserGroup) => {
|
|
11
|
+
return this.request
|
|
12
|
+
.post(`/api/global/groups`)
|
|
13
|
+
.send(group)
|
|
14
|
+
.set(this.config.defaultHeaders())
|
|
15
|
+
.expect("Content-Type", /json/)
|
|
16
|
+
.expect(200)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
deleteGroup = (id: string, rev: string) => {
|
|
20
|
+
return this.request
|
|
21
|
+
.delete(`/api/global/groups/${id}/${rev}`)
|
|
22
|
+
.set(this.config.defaultHeaders())
|
|
23
|
+
.expect("Content-Type", /json/)
|
|
24
|
+
.expect(200)
|
|
25
|
+
}
|
|
26
|
+
}
|