@budibase/worker 2.3.19 → 2.3.21-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__mocks__/node-fetch.ts +23 -0
- package/jest-testcontainers-config.js +8 -0
- package/jest.config.ts +14 -18
- package/package.json +12 -8
- package/scripts/dev/manage.js +1 -0
- package/scripts/test.sh +12 -0
- package/src/api/controllers/global/auth.ts +109 -111
- package/src/api/controllers/global/configs.ts +150 -142
- package/src/api/controllers/global/self.ts +28 -44
- package/src/api/controllers/global/users.ts +211 -32
- package/src/api/controllers/system/accounts.ts +7 -5
- package/src/api/controllers/system/tenants.ts +4 -8
- package/src/api/index.ts +4 -20
- package/src/api/routes/global/auth.ts +10 -7
- package/src/api/routes/global/configs.ts +6 -12
- package/src/api/routes/global/tests/auditLogs.spec.ts +111 -0
- package/src/api/routes/global/tests/auth.spec.ts +217 -18
- package/src/api/routes/global/tests/configs.spec.ts +93 -65
- package/src/api/routes/global/tests/realEmail.spec.ts +1 -0
- package/src/api/routes/global/tests/roles.spec.ts +20 -14
- package/src/api/routes/global/tests/self.spec.ts +3 -4
- package/src/api/routes/global/tests/users.spec.ts +29 -28
- package/src/api/routes/global/users.ts +19 -14
- package/src/api/routes/index.ts +3 -0
- 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/db/index.ts +8 -2
- package/src/ddApm.ts +7 -0
- package/src/elasticApm.ts +10 -0
- package/src/environment.ts +4 -0
- package/src/index.ts +29 -8
- package/src/middleware/tests/tenancy.spec.ts +4 -4
- package/src/migrations/functions/globalInfoSyncUsers.ts +4 -3
- package/src/sdk/accounts/index.ts +2 -1
- package/src/sdk/accounts/{accounts.ts → metadata.ts} +0 -1
- package/src/sdk/auth/auth.ts +86 -0
- package/src/sdk/auth/index.ts +1 -0
- package/src/sdk/tenants/index.ts +1 -0
- package/src/sdk/tenants/tenants.ts +76 -0
- package/src/sdk/users/events.ts +4 -0
- package/src/sdk/users/index.ts +1 -0
- package/src/sdk/users/tests/users.spec.ts +76 -0
- package/src/sdk/users/users.ts +86 -53
- package/src/tests/TestConfiguration.ts +41 -62
- package/src/tests/api/auditLogs.ts +26 -0
- package/src/tests/api/auth.ts +44 -14
- package/src/tests/api/base.ts +2 -1
- package/src/tests/api/configs.ts +9 -1
- package/src/tests/api/email.ts +1 -0
- package/src/tests/api/index.ts +3 -0
- package/src/tests/api/restore.ts +1 -0
- package/src/tests/api/users.ts +2 -2
- package/src/tests/jestEnv.ts +2 -1
- package/src/tests/jestSetup.ts +10 -6
- package/src/tests/logging.ts +34 -0
- package/src/tests/structures/configs.ts +20 -13
- package/src/tests/structures/index.ts +0 -4
- package/src/utilities/email.ts +22 -68
- package/src/utilities/redis.ts +42 -2
- package/src/utilities/templates.ts +4 -9
- package/coverage/clover.xml +0 -2199
- package/coverage/coverage-final.json +0 -84
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -431
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +0 -934
- package/coverage/lcov-report/src/api/controllers/global/configs.ts.html +0 -1348
- package/coverage/lcov-report/src/api/controllers/global/email.ts.html +0 -196
- package/coverage/lcov-report/src/api/controllers/global/events.ts.html +0 -136
- package/coverage/lcov-report/src/api/controllers/global/index.html +0 -251
- package/coverage/lcov-report/src/api/controllers/global/license.ts.html +0 -187
- package/coverage/lcov-report/src/api/controllers/global/roles.ts.html +0 -283
- package/coverage/lcov-report/src/api/controllers/global/self.ts.html +0 -577
- package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +0 -316
- package/coverage/lcov-report/src/api/controllers/global/users.ts.html +0 -838
- package/coverage/lcov-report/src/api/controllers/global/workspaces.ts.html +0 -244
- package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +0 -148
- package/coverage/lcov-report/src/api/controllers/system/environment.ts.html +0 -124
- package/coverage/lcov-report/src/api/controllers/system/index.html +0 -191
- package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +0 -124
- package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +0 -124
- package/coverage/lcov-report/src/api/controllers/system/status.ts.html +0 -133
- package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +0 -154
- package/coverage/lcov-report/src/api/index.html +0 -116
- package/coverage/lcov-report/src/api/index.ts.html +0 -595
- package/coverage/lcov-report/src/api/routes/global/auth.ts.html +0 -349
- package/coverage/lcov-report/src/api/routes/global/configs.ts.html +0 -457
- package/coverage/lcov-report/src/api/routes/global/email.ts.html +0 -193
- package/coverage/lcov-report/src/api/routes/global/events.ts.html +0 -109
- package/coverage/lcov-report/src/api/routes/global/index.html +0 -251
- package/coverage/lcov-report/src/api/routes/global/license.ts.html +0 -124
- package/coverage/lcov-report/src/api/routes/global/roles.ts.html +0 -133
- package/coverage/lcov-report/src/api/routes/global/self.ts.html +0 -139
- package/coverage/lcov-report/src/api/routes/global/templates.ts.html +0 -196
- package/coverage/lcov-report/src/api/routes/global/users.ts.html +0 -475
- package/coverage/lcov-report/src/api/routes/global/workspaces.ts.html +0 -196
- package/coverage/lcov-report/src/api/routes/index.html +0 -116
- package/coverage/lcov-report/src/api/routes/index.ts.html +0 -202
- package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +0 -142
- package/coverage/lcov-report/src/api/routes/system/environment.ts.html +0 -109
- package/coverage/lcov-report/src/api/routes/system/index.html +0 -191
- package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +0 -142
- package/coverage/lcov-report/src/api/routes/system/restore.ts.html +0 -109
- package/coverage/lcov-report/src/api/routes/system/status.ts.html +0 -109
- package/coverage/lcov-report/src/api/routes/system/tenants.ts.html +0 -124
- package/coverage/lcov-report/src/api/routes/validation/index.html +0 -131
- package/coverage/lcov-report/src/api/routes/validation/index.ts.html +0 -88
- package/coverage/lcov-report/src/api/routes/validation/users.ts.html +0 -235
- package/coverage/lcov-report/src/constants/index.html +0 -116
- package/coverage/lcov-report/src/constants/index.ts.html +0 -637
- package/coverage/lcov-report/src/constants/templates/index.html +0 -116
- package/coverage/lcov-report/src/constants/templates/index.ts.html +0 -316
- package/coverage/lcov-report/src/db/index.html +0 -116
- package/coverage/lcov-report/src/db/index.ts.html +0 -115
- package/coverage/lcov-report/src/environment.ts.html +0 -388
- package/coverage/lcov-report/src/index.html +0 -131
- package/coverage/lcov-report/src/index.ts.html +0 -394
- package/coverage/lcov-report/src/middleware/cloudRestricted.ts.html +0 -139
- package/coverage/lcov-report/src/middleware/index.html +0 -116
- package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +0 -145
- package/coverage/lcov-report/src/migrations/functions/index.html +0 -116
- package/coverage/lcov-report/src/migrations/index.html +0 -116
- package/coverage/lcov-report/src/migrations/index.ts.html +0 -271
- package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +0 -262
- package/coverage/lcov-report/src/sdk/accounts/index.html +0 -131
- package/coverage/lcov-report/src/sdk/accounts/index.ts.html +0 -88
- package/coverage/lcov-report/src/sdk/index.html +0 -116
- package/coverage/lcov-report/src/sdk/index.ts.html +0 -106
- package/coverage/lcov-report/src/sdk/users/events.ts.html +0 -601
- package/coverage/lcov-report/src/sdk/users/index.html +0 -146
- package/coverage/lcov-report/src/sdk/users/index.ts.html +0 -88
- package/coverage/lcov-report/src/sdk/users/users.ts.html +0 -1999
- package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +0 -1075
- package/coverage/lcov-report/src/tests/api/accounts.ts.html +0 -160
- package/coverage/lcov-report/src/tests/api/auth.ts.html +0 -220
- package/coverage/lcov-report/src/tests/api/base.ts.html +0 -133
- package/coverage/lcov-report/src/tests/api/configs.ts.html +0 -226
- package/coverage/lcov-report/src/tests/api/email.ts.html +0 -148
- package/coverage/lcov-report/src/tests/api/environment.ts.html +0 -130
- package/coverage/lcov-report/src/tests/api/groups.ts.html +0 -163
- package/coverage/lcov-report/src/tests/api/index.html +0 -356
- package/coverage/lcov-report/src/tests/api/index.ts.html +0 -238
- package/coverage/lcov-report/src/tests/api/license.ts.html +0 -136
- package/coverage/lcov-report/src/tests/api/migrations.ts.html +0 -151
- package/coverage/lcov-report/src/tests/api/restore.ts.html +0 -127
- package/coverage/lcov-report/src/tests/api/roles.ts.html +0 -181
- package/coverage/lcov-report/src/tests/api/self.ts.html +0 -163
- package/coverage/lcov-report/src/tests/api/status.ts.html +0 -121
- package/coverage/lcov-report/src/tests/api/templates.ts.html +0 -175
- package/coverage/lcov-report/src/tests/api/tenants.ts.html +0 -130
- package/coverage/lcov-report/src/tests/api/users.ts.html +0 -514
- package/coverage/lcov-report/src/tests/controllers.ts.html +0 -100
- package/coverage/lcov-report/src/tests/index.html +0 -146
- package/coverage/lcov-report/src/tests/index.ts.html +0 -103
- package/coverage/lcov-report/src/tests/mocks/email.ts.html +0 -115
- package/coverage/lcov-report/src/tests/mocks/index.html +0 -131
- package/coverage/lcov-report/src/tests/mocks/index.ts.html +0 -106
- package/coverage/lcov-report/src/tests/structures/configs.ts.html +0 -313
- package/coverage/lcov-report/src/tests/structures/groups.ts.html +0 -118
- package/coverage/lcov-report/src/tests/structures/index.html +0 -161
- package/coverage/lcov-report/src/tests/structures/index.ts.html +0 -151
- package/coverage/lcov-report/src/tests/structures/users.ts.html +0 -196
- package/coverage/lcov-report/src/utilities/appService.ts.html +0 -208
- package/coverage/lcov-report/src/utilities/email.ts.html +0 -850
- package/coverage/lcov-report/src/utilities/fileSystem.ts.html +0 -100
- package/coverage/lcov-report/src/utilities/index.html +0 -206
- package/coverage/lcov-report/src/utilities/index.ts.html +0 -112
- package/coverage/lcov-report/src/utilities/redis.ts.html +0 -424
- package/coverage/lcov-report/src/utilities/templates.ts.html +0 -232
- package/coverage/lcov-report/src/utilities/users.ts.html +0 -133
- package/coverage/lcov.info +0 -4187
- package/src/tests/structures/users.ts +0 -37
|
@@ -1,12 +1,27 @@
|
|
|
1
|
+
import { CloudAccount, SSOUser, User } from "@budibase/types"
|
|
2
|
+
|
|
1
3
|
jest.mock("nodemailer")
|
|
2
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
TestConfiguration,
|
|
6
|
+
mocks,
|
|
7
|
+
structures,
|
|
8
|
+
generator,
|
|
9
|
+
} from "../../../../tests"
|
|
3
10
|
const sendMailMock = mocks.email.mock()
|
|
4
|
-
import { events } from "@budibase/backend-core"
|
|
11
|
+
import { events, constants } from "@budibase/backend-core"
|
|
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
|
+
}
|
|
5
22
|
|
|
6
|
-
const expectSetAuthCookie = (
|
|
7
|
-
expect(
|
|
8
|
-
res.get("Set-Cookie").find((c: string) => c.startsWith("budibase:auth"))
|
|
9
|
-
).toBeDefined()
|
|
23
|
+
const expectSetAuthCookie = (response: Response) => {
|
|
24
|
+
expect(getAuthCookie(response).length > 1).toBe(true)
|
|
10
25
|
}
|
|
11
26
|
|
|
12
27
|
describe("/api/global/auth", () => {
|
|
@@ -24,53 +39,237 @@ describe("/api/global/auth", () => {
|
|
|
24
39
|
jest.clearAllMocks()
|
|
25
40
|
})
|
|
26
41
|
|
|
42
|
+
async function createSSOUser() {
|
|
43
|
+
return config.doInTenant(async () => {
|
|
44
|
+
return userSdk.save(structures.users.ssoUser(), {
|
|
45
|
+
requirePassword: false,
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
27
50
|
describe("password", () => {
|
|
28
51
|
describe("POST /api/global/auth/:tenantId/login", () => {
|
|
29
|
-
it("
|
|
52
|
+
it("logs in with correct credentials", async () => {
|
|
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: 403 }
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
expect(response.body).toEqual({
|
|
113
|
+
message: "Invalid credentials",
|
|
114
|
+
status: 403,
|
|
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
|
+
})
|
|
30
137
|
})
|
|
31
138
|
|
|
32
139
|
describe("POST /api/global/auth/logout", () => {
|
|
33
140
|
it("should logout", async () => {
|
|
34
|
-
await config.api.auth.logout()
|
|
141
|
+
const response = await config.api.auth.logout()
|
|
35
142
|
expect(events.auth.logout).toBeCalledTimes(1)
|
|
36
143
|
|
|
37
|
-
|
|
144
|
+
const authCookie = getAuthCookie(response)
|
|
145
|
+
expect(authCookie).toBe("")
|
|
38
146
|
})
|
|
39
147
|
})
|
|
40
148
|
|
|
41
149
|
describe("POST /api/global/auth/:tenantId/reset", () => {
|
|
42
150
|
it("should generate password reset email", async () => {
|
|
151
|
+
const user = await config.createUser()
|
|
152
|
+
|
|
43
153
|
const { res, code } = await config.api.auth.requestPasswordReset(
|
|
44
|
-
sendMailMock
|
|
154
|
+
sendMailMock,
|
|
155
|
+
user.email
|
|
45
156
|
)
|
|
46
|
-
const user = await config.getUser("test@test.com")
|
|
47
157
|
|
|
48
158
|
expect(res.body).toEqual({
|
|
49
159
|
message: "Please check your email for a reset link.",
|
|
50
160
|
})
|
|
51
161
|
expect(sendMailMock).toHaveBeenCalled()
|
|
52
|
-
|
|
53
162
|
expect(code).toBeDefined()
|
|
54
163
|
expect(events.user.passwordResetRequested).toBeCalledTimes(1)
|
|
55
164
|
expect(events.user.passwordResetRequested).toBeCalledWith(user)
|
|
56
165
|
})
|
|
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
|
+
)
|
|
175
|
+
expect(sendMailMock).not.toHaveBeenCalled()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
describe("budibase sso user", () => {
|
|
179
|
+
it("should prevent user from generating password reset email", async () => {
|
|
180
|
+
user = await createSSOUser()
|
|
181
|
+
await testSSOUser()
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
describe("root account sso user", () => {
|
|
186
|
+
it("should prevent user from generating password reset email", async () => {
|
|
187
|
+
user = await config.createUser(structures.users.user())
|
|
188
|
+
const account = structures.accounts.ssoAccount() as CloudAccount
|
|
189
|
+
mocks.accounts.getAccount.mockReturnValueOnce(
|
|
190
|
+
Promise.resolve(account)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
await testSSOUser()
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
})
|
|
57
197
|
})
|
|
58
198
|
|
|
59
199
|
describe("POST /api/global/auth/:tenantId/reset/update", () => {
|
|
60
200
|
it("should reset password", async () => {
|
|
201
|
+
let user = await config.createUser()
|
|
61
202
|
const { code } = await config.api.auth.requestPasswordReset(
|
|
62
|
-
sendMailMock
|
|
203
|
+
sendMailMock,
|
|
204
|
+
user.email
|
|
63
205
|
)
|
|
64
|
-
const user = await config.getUser("test@test.com")
|
|
65
206
|
delete user.password
|
|
66
207
|
|
|
67
|
-
const
|
|
208
|
+
const newPassword = "newpassword"
|
|
209
|
+
const res = await config.api.auth.updatePassword(code!, newPassword)
|
|
210
|
+
|
|
211
|
+
user = await config.getUser(user.email)
|
|
212
|
+
delete user.password
|
|
68
213
|
|
|
69
214
|
expect(res.body).toEqual({ message: "password reset successfully." })
|
|
70
215
|
expect(events.user.passwordReset).toBeCalledTimes(1)
|
|
71
216
|
expect(events.user.passwordReset).toBeCalledWith(user)
|
|
72
217
|
|
|
73
|
-
//
|
|
218
|
+
// login using new password
|
|
219
|
+
await config.api.auth.login(user.tenantId, user.email, newPassword)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
describe("sso user", () => {
|
|
223
|
+
let user: User | SSOUser
|
|
224
|
+
|
|
225
|
+
async function testSSOUser(code: string) {
|
|
226
|
+
const res = await config.api.auth.updatePassword(
|
|
227
|
+
code!,
|
|
228
|
+
generator.string(),
|
|
229
|
+
{ status: 400 }
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
expect(res.body).toEqual({
|
|
233
|
+
message: "Cannot reset password.",
|
|
234
|
+
status: 400,
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
describe("budibase sso user", () => {
|
|
239
|
+
it("should prevent user from generating password reset email", async () => {
|
|
240
|
+
user = await config.createUser()
|
|
241
|
+
const { code } = await config.api.auth.requestPasswordReset(
|
|
242
|
+
sendMailMock,
|
|
243
|
+
user.email
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
// convert to sso now that password reset has been requested
|
|
247
|
+
const ssoUser = user as SSOUser
|
|
248
|
+
ssoUser.providerType = structures.sso.providerType()
|
|
249
|
+
delete ssoUser.password
|
|
250
|
+
await config.doInTenant(() => userSdk.save(ssoUser))
|
|
251
|
+
|
|
252
|
+
await testSSOUser(code!)
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
describe("root account sso user", () => {
|
|
257
|
+
it("should prevent user from generating password reset email", async () => {
|
|
258
|
+
user = await config.createUser()
|
|
259
|
+
const { code } = await config.api.auth.requestPasswordReset(
|
|
260
|
+
sendMailMock,
|
|
261
|
+
user.email
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
// convert to account owner now that password has been requested
|
|
265
|
+
const account = structures.accounts.ssoAccount() as CloudAccount
|
|
266
|
+
mocks.accounts.getAccount.mockReturnValueOnce(
|
|
267
|
+
Promise.resolve(account)
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
await testSSOUser(code!)
|
|
271
|
+
})
|
|
272
|
+
})
|
|
74
273
|
})
|
|
75
274
|
})
|
|
76
275
|
})
|
|
@@ -145,7 +344,7 @@ describe("/api/global/auth", () => {
|
|
|
145
344
|
const location: string = res.get("location")
|
|
146
345
|
expect(
|
|
147
346
|
location.startsWith(
|
|
148
|
-
|
|
347
|
+
`http://localhost/auth?response_type=code&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A10000%2Fapi%2Fglobal%2Fauth%2F${config.tenantId}%2Foidc%2Fcallback&scope=openid%20profile%20email%20offline_access`
|
|
149
348
|
)
|
|
150
349
|
).toBe(true)
|
|
151
350
|
})
|
|
@@ -158,7 +357,7 @@ describe("/api/global/auth", () => {
|
|
|
158
357
|
|
|
159
358
|
const res = await config.api.configs.OIDCCallback(configId, preAuthRes)
|
|
160
359
|
|
|
161
|
-
expect(events.auth.login).toBeCalledWith("oidc")
|
|
360
|
+
expect(events.auth.login).toBeCalledWith("oidc", "oauth@example.com")
|
|
162
361
|
expect(events.auth.login).toBeCalledTimes(1)
|
|
163
362
|
expect(res.status).toBe(302)
|
|
164
363
|
const location: string = res.get("location")
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
jest.mock("nodemailer")
|
|
3
3
|
import { TestConfiguration, structures, mocks } from "../../../../tests"
|
|
4
4
|
mocks.email.mock()
|
|
5
|
-
import {
|
|
5
|
+
import { events } from "@budibase/backend-core"
|
|
6
|
+
import { GetPublicSettingsResponse, Config, ConfigType } from "@budibase/types"
|
|
6
7
|
|
|
7
8
|
describe("configs", () => {
|
|
8
9
|
const config = new TestConfiguration()
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
beforeEach(async () => {
|
|
11
12
|
await config.beforeAll()
|
|
12
13
|
})
|
|
13
14
|
|
|
@@ -19,22 +20,29 @@ describe("configs", () => {
|
|
|
19
20
|
await config.afterAll()
|
|
20
21
|
})
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
_rev,
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const res = await config.api.configs.saveConfig(data)
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
...data,
|
|
34
|
-
...res.body,
|
|
35
|
-
}
|
|
23
|
+
const saveConfig = async (conf: Config, _id?: string, _rev?: string) => {
|
|
24
|
+
const data = {
|
|
25
|
+
...conf,
|
|
26
|
+
_id,
|
|
27
|
+
_rev,
|
|
36
28
|
}
|
|
37
|
-
|
|
29
|
+
const res = await config.api.configs.saveConfig(data)
|
|
30
|
+
return {
|
|
31
|
+
...data,
|
|
32
|
+
...res.body,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const saveSettingsConfig = async (
|
|
37
|
+
conf?: any,
|
|
38
|
+
_id?: string,
|
|
39
|
+
_rev?: string
|
|
40
|
+
) => {
|
|
41
|
+
const settingsConfig = structures.configs.settings(conf)
|
|
42
|
+
return saveConfig(settingsConfig, _id, _rev)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("POST /api/global/configs", () => {
|
|
38
46
|
describe("google", () => {
|
|
39
47
|
const saveGoogleConfig = async (
|
|
40
48
|
conf?: any,
|
|
@@ -49,20 +57,20 @@ describe("configs", () => {
|
|
|
49
57
|
it("should create activated google config", async () => {
|
|
50
58
|
await saveGoogleConfig()
|
|
51
59
|
expect(events.auth.SSOCreated).toBeCalledTimes(1)
|
|
52
|
-
expect(events.auth.SSOCreated).toBeCalledWith(
|
|
60
|
+
expect(events.auth.SSOCreated).toBeCalledWith(ConfigType.GOOGLE)
|
|
53
61
|
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
54
62
|
expect(events.auth.SSOActivated).toBeCalledTimes(1)
|
|
55
|
-
expect(events.auth.SSOActivated).toBeCalledWith(
|
|
56
|
-
await config.deleteConfig(
|
|
63
|
+
expect(events.auth.SSOActivated).toBeCalledWith(ConfigType.GOOGLE)
|
|
64
|
+
await config.deleteConfig(ConfigType.GOOGLE)
|
|
57
65
|
})
|
|
58
66
|
|
|
59
67
|
it("should create deactivated google config", async () => {
|
|
60
68
|
await saveGoogleConfig({ activated: false })
|
|
61
69
|
expect(events.auth.SSOCreated).toBeCalledTimes(1)
|
|
62
|
-
expect(events.auth.SSOCreated).toBeCalledWith(
|
|
70
|
+
expect(events.auth.SSOCreated).toBeCalledWith(ConfigType.GOOGLE)
|
|
63
71
|
expect(events.auth.SSOActivated).not.toBeCalled()
|
|
64
72
|
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
65
|
-
await config.deleteConfig(
|
|
73
|
+
await config.deleteConfig(ConfigType.GOOGLE)
|
|
66
74
|
})
|
|
67
75
|
})
|
|
68
76
|
|
|
@@ -76,11 +84,11 @@ describe("configs", () => {
|
|
|
76
84
|
googleConf._rev
|
|
77
85
|
)
|
|
78
86
|
expect(events.auth.SSOUpdated).toBeCalledTimes(1)
|
|
79
|
-
expect(events.auth.SSOUpdated).toBeCalledWith(
|
|
87
|
+
expect(events.auth.SSOUpdated).toBeCalledWith(ConfigType.GOOGLE)
|
|
80
88
|
expect(events.auth.SSOActivated).not.toBeCalled()
|
|
81
89
|
expect(events.auth.SSODeactivated).toBeCalledTimes(1)
|
|
82
|
-
expect(events.auth.SSODeactivated).toBeCalledWith(
|
|
83
|
-
await config.deleteConfig(
|
|
90
|
+
expect(events.auth.SSODeactivated).toBeCalledWith(ConfigType.GOOGLE)
|
|
91
|
+
await config.deleteConfig(ConfigType.GOOGLE)
|
|
84
92
|
})
|
|
85
93
|
|
|
86
94
|
it("should update google config to activated", async () => {
|
|
@@ -92,11 +100,11 @@ describe("configs", () => {
|
|
|
92
100
|
googleConf._rev
|
|
93
101
|
)
|
|
94
102
|
expect(events.auth.SSOUpdated).toBeCalledTimes(1)
|
|
95
|
-
expect(events.auth.SSOUpdated).toBeCalledWith(
|
|
103
|
+
expect(events.auth.SSOUpdated).toBeCalledWith(ConfigType.GOOGLE)
|
|
96
104
|
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
97
105
|
expect(events.auth.SSOActivated).toBeCalledTimes(1)
|
|
98
|
-
expect(events.auth.SSOActivated).toBeCalledWith(
|
|
99
|
-
await config.deleteConfig(
|
|
106
|
+
expect(events.auth.SSOActivated).toBeCalledWith(ConfigType.GOOGLE)
|
|
107
|
+
await config.deleteConfig(ConfigType.GOOGLE)
|
|
100
108
|
})
|
|
101
109
|
})
|
|
102
110
|
})
|
|
@@ -115,20 +123,20 @@ describe("configs", () => {
|
|
|
115
123
|
it("should create activated OIDC config", async () => {
|
|
116
124
|
await saveOIDCConfig()
|
|
117
125
|
expect(events.auth.SSOCreated).toBeCalledTimes(1)
|
|
118
|
-
expect(events.auth.SSOCreated).toBeCalledWith(
|
|
126
|
+
expect(events.auth.SSOCreated).toBeCalledWith(ConfigType.OIDC)
|
|
119
127
|
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
120
128
|
expect(events.auth.SSOActivated).toBeCalledTimes(1)
|
|
121
|
-
expect(events.auth.SSOActivated).toBeCalledWith(
|
|
122
|
-
await config.deleteConfig(
|
|
129
|
+
expect(events.auth.SSOActivated).toBeCalledWith(ConfigType.OIDC)
|
|
130
|
+
await config.deleteConfig(ConfigType.OIDC)
|
|
123
131
|
})
|
|
124
132
|
|
|
125
133
|
it("should create deactivated OIDC config", async () => {
|
|
126
134
|
await saveOIDCConfig({ activated: false })
|
|
127
135
|
expect(events.auth.SSOCreated).toBeCalledTimes(1)
|
|
128
|
-
expect(events.auth.SSOCreated).toBeCalledWith(
|
|
136
|
+
expect(events.auth.SSOCreated).toBeCalledWith(ConfigType.OIDC)
|
|
129
137
|
expect(events.auth.SSOActivated).not.toBeCalled()
|
|
130
138
|
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
131
|
-
await config.deleteConfig(
|
|
139
|
+
await config.deleteConfig(ConfigType.OIDC)
|
|
132
140
|
})
|
|
133
141
|
})
|
|
134
142
|
|
|
@@ -142,11 +150,11 @@ describe("configs", () => {
|
|
|
142
150
|
oidcConf._rev
|
|
143
151
|
)
|
|
144
152
|
expect(events.auth.SSOUpdated).toBeCalledTimes(1)
|
|
145
|
-
expect(events.auth.SSOUpdated).toBeCalledWith(
|
|
153
|
+
expect(events.auth.SSOUpdated).toBeCalledWith(ConfigType.OIDC)
|
|
146
154
|
expect(events.auth.SSOActivated).not.toBeCalled()
|
|
147
155
|
expect(events.auth.SSODeactivated).toBeCalledTimes(1)
|
|
148
|
-
expect(events.auth.SSODeactivated).toBeCalledWith(
|
|
149
|
-
await config.deleteConfig(
|
|
156
|
+
expect(events.auth.SSODeactivated).toBeCalledWith(ConfigType.OIDC)
|
|
157
|
+
await config.deleteConfig(ConfigType.OIDC)
|
|
150
158
|
})
|
|
151
159
|
|
|
152
160
|
it("should update OIDC config to activated", async () => {
|
|
@@ -158,11 +166,11 @@ describe("configs", () => {
|
|
|
158
166
|
oidcConf._rev
|
|
159
167
|
)
|
|
160
168
|
expect(events.auth.SSOUpdated).toBeCalledTimes(1)
|
|
161
|
-
expect(events.auth.SSOUpdated).toBeCalledWith(
|
|
169
|
+
expect(events.auth.SSOUpdated).toBeCalledWith(ConfigType.OIDC)
|
|
162
170
|
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
163
171
|
expect(events.auth.SSOActivated).toBeCalledTimes(1)
|
|
164
|
-
expect(events.auth.SSOActivated).toBeCalledWith(
|
|
165
|
-
await config.deleteConfig(
|
|
172
|
+
expect(events.auth.SSOActivated).toBeCalledWith(ConfigType.OIDC)
|
|
173
|
+
await config.deleteConfig(ConfigType.OIDC)
|
|
166
174
|
})
|
|
167
175
|
})
|
|
168
176
|
})
|
|
@@ -179,11 +187,11 @@ describe("configs", () => {
|
|
|
179
187
|
|
|
180
188
|
describe("create", () => {
|
|
181
189
|
it("should create SMTP config", async () => {
|
|
182
|
-
await config.deleteConfig(
|
|
190
|
+
await config.deleteConfig(ConfigType.SMTP)
|
|
183
191
|
await saveSMTPConfig()
|
|
184
192
|
expect(events.email.SMTPUpdated).not.toBeCalled()
|
|
185
193
|
expect(events.email.SMTPCreated).toBeCalledTimes(1)
|
|
186
|
-
await config.deleteConfig(
|
|
194
|
+
await config.deleteConfig(ConfigType.SMTP)
|
|
187
195
|
})
|
|
188
196
|
})
|
|
189
197
|
|
|
@@ -194,24 +202,15 @@ describe("configs", () => {
|
|
|
194
202
|
await saveSMTPConfig(smtpConf.config, smtpConf._id, smtpConf._rev)
|
|
195
203
|
expect(events.email.SMTPCreated).not.toBeCalled()
|
|
196
204
|
expect(events.email.SMTPUpdated).toBeCalledTimes(1)
|
|
197
|
-
await config.deleteConfig(
|
|
205
|
+
await config.deleteConfig(ConfigType.SMTP)
|
|
198
206
|
})
|
|
199
207
|
})
|
|
200
208
|
})
|
|
201
209
|
|
|
202
210
|
describe("settings", () => {
|
|
203
|
-
const saveSettingsConfig = async (
|
|
204
|
-
conf?: any,
|
|
205
|
-
_id?: string,
|
|
206
|
-
_rev?: string
|
|
207
|
-
) => {
|
|
208
|
-
const settingsConfig = structures.configs.settings(conf)
|
|
209
|
-
return saveConfig(settingsConfig, _id, _rev)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
211
|
describe("create", () => {
|
|
213
212
|
it("should create settings config with default settings", async () => {
|
|
214
|
-
await config.deleteConfig(
|
|
213
|
+
await config.deleteConfig(ConfigType.SETTINGS)
|
|
215
214
|
|
|
216
215
|
await saveSettingsConfig()
|
|
217
216
|
|
|
@@ -221,8 +220,8 @@ describe("configs", () => {
|
|
|
221
220
|
})
|
|
222
221
|
|
|
223
222
|
it("should create settings config with non-default settings", async () => {
|
|
224
|
-
config.
|
|
225
|
-
await config.deleteConfig(
|
|
223
|
+
config.selfHosted()
|
|
224
|
+
await config.deleteConfig(ConfigType.SETTINGS)
|
|
226
225
|
const conf = {
|
|
227
226
|
company: "acme",
|
|
228
227
|
logoUrl: "http://example.com",
|
|
@@ -234,14 +233,14 @@ describe("configs", () => {
|
|
|
234
233
|
expect(events.org.nameUpdated).toBeCalledTimes(1)
|
|
235
234
|
expect(events.org.logoUpdated).toBeCalledTimes(1)
|
|
236
235
|
expect(events.org.platformURLUpdated).toBeCalledTimes(1)
|
|
237
|
-
config.
|
|
236
|
+
config.cloudHosted()
|
|
238
237
|
})
|
|
239
238
|
})
|
|
240
239
|
|
|
241
240
|
describe("update", () => {
|
|
242
241
|
it("should update settings config", async () => {
|
|
243
|
-
config.
|
|
244
|
-
await config.deleteConfig(
|
|
242
|
+
config.selfHosted()
|
|
243
|
+
await config.deleteConfig(ConfigType.SETTINGS)
|
|
245
244
|
const settingsConfig = await saveSettingsConfig()
|
|
246
245
|
settingsConfig.config.company = "acme"
|
|
247
246
|
settingsConfig.config.logoUrl = "http://example.com"
|
|
@@ -256,20 +255,49 @@ describe("configs", () => {
|
|
|
256
255
|
expect(events.org.nameUpdated).toBeCalledTimes(1)
|
|
257
256
|
expect(events.org.logoUpdated).toBeCalledTimes(1)
|
|
258
257
|
expect(events.org.platformURLUpdated).toBeCalledTimes(1)
|
|
259
|
-
config.
|
|
258
|
+
config.cloudHosted()
|
|
260
259
|
})
|
|
261
260
|
})
|
|
262
261
|
})
|
|
263
262
|
})
|
|
264
263
|
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
describe("GET /api/global/configs/checklist", () => {
|
|
265
|
+
it("should return the correct checklist", async () => {
|
|
266
|
+
await config.saveSmtpConfig()
|
|
267
|
+
|
|
268
|
+
const res = await config.api.configs.getConfigChecklist()
|
|
269
|
+
const checklist = res.body
|
|
267
270
|
|
|
268
|
-
|
|
269
|
-
|
|
271
|
+
expect(checklist.apps.checked).toBeFalsy()
|
|
272
|
+
expect(checklist.smtp.checked).toBeTruthy()
|
|
273
|
+
expect(checklist.adminUser.checked).toBeTruthy()
|
|
274
|
+
})
|
|
275
|
+
})
|
|
270
276
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
277
|
+
describe("GET /api/global/configs/public", () => {
|
|
278
|
+
it("should return the expected public settings", async () => {
|
|
279
|
+
await saveSettingsConfig()
|
|
280
|
+
|
|
281
|
+
const res = await config.api.configs.getPublicSettings()
|
|
282
|
+
const body = res.body as GetPublicSettingsResponse
|
|
283
|
+
|
|
284
|
+
const expected = {
|
|
285
|
+
_id: "config_settings",
|
|
286
|
+
type: "settings",
|
|
287
|
+
config: {
|
|
288
|
+
company: "Budibase",
|
|
289
|
+
logoUrl: "",
|
|
290
|
+
analyticsEnabled: false,
|
|
291
|
+
google: true,
|
|
292
|
+
googleCallbackUrl: `http://localhost:10000/api/global/auth/${config.tenantId}/google/callback`,
|
|
293
|
+
isSSOEnforced: false,
|
|
294
|
+
oidc: false,
|
|
295
|
+
oidcCallbackUrl: `http://localhost:10000/api/global/auth/${config.tenantId}/oidc/callback`,
|
|
296
|
+
platformUrl: "http://localhost:10000",
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
delete body._rev
|
|
300
|
+
expect(body).toEqual(expected)
|
|
301
|
+
})
|
|
274
302
|
})
|
|
275
303
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { structures, TestConfiguration } from "../../../../tests"
|
|
2
2
|
import { context, db, permissions, roles } from "@budibase/backend-core"
|
|
3
3
|
import { Mock } from "jest-mock"
|
|
4
|
+
import { Database } from "@budibase/types"
|
|
4
5
|
|
|
5
6
|
jest.mock("@budibase/backend-core", () => {
|
|
6
7
|
const core = jest.requireActual("@budibase/backend-core")
|
|
@@ -16,14 +17,13 @@ jest.mock("@budibase/backend-core", () => {
|
|
|
16
17
|
}
|
|
17
18
|
})
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
mockAppDB.mockReturnValue(appDb)
|
|
20
|
+
let appId: string
|
|
21
|
+
let appDb: Database
|
|
22
22
|
|
|
23
23
|
async function addAppMetadata() {
|
|
24
24
|
await appDb.put({
|
|
25
25
|
_id: "app_metadata",
|
|
26
|
-
appId:
|
|
26
|
+
appId: appId,
|
|
27
27
|
name: "New App",
|
|
28
28
|
version: "version",
|
|
29
29
|
url: "url",
|
|
@@ -32,6 +32,7 @@ async function addAppMetadata() {
|
|
|
32
32
|
|
|
33
33
|
describe("/api/global/roles", () => {
|
|
34
34
|
const config = new TestConfiguration()
|
|
35
|
+
|
|
35
36
|
const role = new roles.Role(
|
|
36
37
|
db.generateRoleID("newRole"),
|
|
37
38
|
roles.BUILTIN_ROLE_IDS.BASIC,
|
|
@@ -39,12 +40,19 @@ describe("/api/global/roles", () => {
|
|
|
39
40
|
)
|
|
40
41
|
|
|
41
42
|
beforeAll(async () => {
|
|
42
|
-
console.debug(role)
|
|
43
|
-
appDb.put(role)
|
|
44
|
-
await addAppMetadata()
|
|
45
43
|
await config.beforeAll()
|
|
46
44
|
})
|
|
47
45
|
|
|
46
|
+
beforeEach(async () => {
|
|
47
|
+
appId = db.generateAppID(config.tenantId)
|
|
48
|
+
appDb = db.getDB(appId)
|
|
49
|
+
const mockAppDB = context.getAppDB as Mock
|
|
50
|
+
mockAppDB.mockReturnValue(appDb)
|
|
51
|
+
|
|
52
|
+
await addAppMetadata()
|
|
53
|
+
await appDb.put(role)
|
|
54
|
+
})
|
|
55
|
+
|
|
48
56
|
afterAll(async () => {
|
|
49
57
|
await config.afterAll()
|
|
50
58
|
})
|
|
@@ -57,16 +65,14 @@ describe("/api/global/roles", () => {
|
|
|
57
65
|
it("retrieves roles", async () => {
|
|
58
66
|
const res = await config.api.roles.get()
|
|
59
67
|
expect(res.body).toBeDefined()
|
|
60
|
-
expect(res.body[
|
|
61
|
-
expect(res.body[
|
|
62
|
-
role._id
|
|
63
|
-
)
|
|
68
|
+
expect(res.body[appId].roles.length).toEqual(5)
|
|
69
|
+
expect(res.body[appId].roles.map((r: any) => r._id)).toContain(role._id)
|
|
64
70
|
})
|
|
65
71
|
})
|
|
66
72
|
|
|
67
73
|
describe("GET api/global/roles/:appId", () => {
|
|
68
74
|
it("finds a role by appId", async () => {
|
|
69
|
-
const res = await config.api.roles.find(
|
|
75
|
+
const res = await config.api.roles.find(appId)
|
|
70
76
|
expect(res.body).toBeDefined()
|
|
71
77
|
expect(res.body.name).toEqual("New App")
|
|
72
78
|
})
|
|
@@ -79,9 +85,9 @@ describe("/api/global/roles", () => {
|
|
|
79
85
|
app_test: "role1",
|
|
80
86
|
}
|
|
81
87
|
const userResponse = await config.createUser(user)
|
|
82
|
-
const res = await config.api.roles.remove(
|
|
88
|
+
const res = await config.api.roles.remove(appId)
|
|
83
89
|
const updatedUser = await config.api.users.getUser(userResponse._id!)
|
|
84
|
-
expect(updatedUser.body.roles).not.toHaveProperty(
|
|
90
|
+
expect(updatedUser.body.roles).not.toHaveProperty(appId)
|
|
85
91
|
expect(res.body.message).toEqual("App role removed from all users")
|
|
86
92
|
})
|
|
87
93
|
})
|