@budibase/worker 2.30.1 → 2.30.3
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 -6
- package/src/api/controllers/global/self.ts +1 -1
- package/src/api/controllers/global/users.ts +9 -1
- package/src/api/controllers/system/environment.ts +1 -0
- package/src/api/routes/global/tests/auth.spec.ts +17 -18
- package/src/api/routes/global/tests/realEmail.spec.ts +29 -13
- package/src/api/routes/global/tests/self.spec.ts +1 -1
- package/src/api/routes/global/tests/tenant.spec.ts +1 -1
- package/src/environment.ts +1 -0
- package/src/index.ts +2 -0
- package/src/tests/TestConfiguration.ts +1 -1
- package/src/tests/api/configs.ts +1 -1
- package/src/tests/api/users.ts +2 -2
- package/src/tests/jestSetup.ts +12 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.30.
|
|
4
|
+
"version": "2.30.3",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"author": "Budibase",
|
|
38
38
|
"license": "GPL-3.0",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@budibase/backend-core": "2.30.
|
|
41
|
-
"@budibase/pro": "2.30.
|
|
42
|
-
"@budibase/string-templates": "2.30.
|
|
43
|
-
"@budibase/types": "2.30.
|
|
40
|
+
"@budibase/backend-core": "2.30.3",
|
|
41
|
+
"@budibase/pro": "2.30.3",
|
|
42
|
+
"@budibase/string-templates": "2.30.3",
|
|
43
|
+
"@budibase/types": "2.30.3",
|
|
44
44
|
"@koa/router": "8.0.8",
|
|
45
45
|
"@techpass/passport-openidconnect": "0.3.3",
|
|
46
46
|
"@types/global-agent": "2.1.1",
|
|
@@ -107,5 +107,5 @@
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "bb6092e58b80d4343bf3b720881f2dc01a30d1c9"
|
|
111
111
|
}
|
|
@@ -104,7 +104,7 @@ export async function getSelf(ctx: any) {
|
|
|
104
104
|
ctx.body = await groups.enrichUserRolesFromGroups(user)
|
|
105
105
|
|
|
106
106
|
// add the feature flags for this tenant
|
|
107
|
-
const flags = await features.fetch()
|
|
107
|
+
const flags = await features.flags.fetch()
|
|
108
108
|
ctx.body.flags = flags
|
|
109
109
|
|
|
110
110
|
addSessionAttributesToUser(ctx)
|
|
@@ -41,6 +41,14 @@ import { BpmStatusKey, BpmStatusValue } from "@budibase/shared-core"
|
|
|
41
41
|
|
|
42
42
|
const MAX_USERS_UPLOAD_LIMIT = 1000
|
|
43
43
|
|
|
44
|
+
const generatePassword = (length: number) => {
|
|
45
|
+
const array = new Uint8Array(length)
|
|
46
|
+
crypto.getRandomValues(array)
|
|
47
|
+
return Array.from(array, byte => byte.toString(36).padStart(2, "0"))
|
|
48
|
+
.join("")
|
|
49
|
+
.slice(0, length)
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
export const save = async (ctx: UserCtx<User, SaveUserResponse>) => {
|
|
45
53
|
try {
|
|
46
54
|
const currentUserId = ctx.user?._id
|
|
@@ -296,7 +304,7 @@ export const onboardUsers = async (
|
|
|
296
304
|
|
|
297
305
|
let createdPasswords: Record<string, string> = {}
|
|
298
306
|
const users: User[] = ctx.request.body.map(invite => {
|
|
299
|
-
|
|
307
|
+
const password = generatePassword(12)
|
|
300
308
|
createdPasswords[invite.email] = password
|
|
301
309
|
|
|
302
310
|
return {
|
|
@@ -66,7 +66,7 @@ describe("/api/global/auth", () => {
|
|
|
66
66
|
it("should return 403 with incorrect credentials", async () => {
|
|
67
67
|
const tenantId = config.tenantId!
|
|
68
68
|
const email = config.user?.email!
|
|
69
|
-
const password = "
|
|
69
|
+
const password = "incorrect123"
|
|
70
70
|
|
|
71
71
|
const response = await config.api.auth.login(
|
|
72
72
|
tenantId,
|
|
@@ -83,7 +83,7 @@ describe("/api/global/auth", () => {
|
|
|
83
83
|
it("should return 403 when user doesn't exist", async () => {
|
|
84
84
|
const tenantId = config.tenantId!
|
|
85
85
|
const email = "invaliduser@example.com"
|
|
86
|
-
const password = "
|
|
86
|
+
const password = "password123!"
|
|
87
87
|
|
|
88
88
|
const response = await config.api.auth.login(
|
|
89
89
|
tenantId,
|
|
@@ -203,7 +203,7 @@ describe("/api/global/auth", () => {
|
|
|
203
203
|
)
|
|
204
204
|
delete user.password
|
|
205
205
|
|
|
206
|
-
const newPassword = "
|
|
206
|
+
const newPassword = "newpassword1"
|
|
207
207
|
const res = await config.api.auth.updatePassword(code!, newPassword)
|
|
208
208
|
|
|
209
209
|
user = (await config.getUser(user.email))!
|
|
@@ -292,9 +292,9 @@ describe("/api/global/auth", () => {
|
|
|
292
292
|
it("redirects to auth provider", async () => {
|
|
293
293
|
nock("http://someconfigurl").get("/").times(1).reply(200, {
|
|
294
294
|
issuer: "test",
|
|
295
|
-
authorization_endpoint: "http://
|
|
296
|
-
token_endpoint: "http://
|
|
297
|
-
userinfo_endpoint: "http://
|
|
295
|
+
authorization_endpoint: "http://example.com/auth",
|
|
296
|
+
token_endpoint: "http://example.com/token",
|
|
297
|
+
userinfo_endpoint: "http://example.com/userinfo",
|
|
298
298
|
})
|
|
299
299
|
|
|
300
300
|
const configId = await generateOidcConfig()
|
|
@@ -305,7 +305,7 @@ describe("/api/global/auth", () => {
|
|
|
305
305
|
const location: string = res.get("location")
|
|
306
306
|
expect(
|
|
307
307
|
location.startsWith(
|
|
308
|
-
`http://
|
|
308
|
+
`http://example.com/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`
|
|
309
309
|
)
|
|
310
310
|
).toBe(true)
|
|
311
311
|
})
|
|
@@ -313,11 +313,13 @@ describe("/api/global/auth", () => {
|
|
|
313
313
|
|
|
314
314
|
describe("GET /api/global/auth/:tenantId/oidc/callback", () => {
|
|
315
315
|
it("logs in", async () => {
|
|
316
|
+
const email = `${generator.guid()}@example.com`
|
|
317
|
+
|
|
316
318
|
nock("http://someconfigurl").get("/").times(2).reply(200, {
|
|
317
319
|
issuer: "test",
|
|
318
|
-
authorization_endpoint: "http://
|
|
319
|
-
token_endpoint: "http://
|
|
320
|
-
userinfo_endpoint: "http://
|
|
320
|
+
authorization_endpoint: "http://example.com/auth",
|
|
321
|
+
token_endpoint: "http://example.com/token",
|
|
322
|
+
userinfo_endpoint: "http://example.com/userinfo",
|
|
321
323
|
})
|
|
322
324
|
|
|
323
325
|
const token = jwt.sign(
|
|
@@ -326,20 +328,20 @@ describe("/api/global/auth", () => {
|
|
|
326
328
|
sub: "sub",
|
|
327
329
|
aud: "clientId",
|
|
328
330
|
exp: Math.floor(Date.now() / 1000) + 60 * 60,
|
|
329
|
-
email
|
|
331
|
+
email,
|
|
330
332
|
},
|
|
331
333
|
"secret"
|
|
332
334
|
)
|
|
333
335
|
|
|
334
|
-
nock("http://
|
|
336
|
+
nock("http://example.com").post("/token").reply(200, {
|
|
335
337
|
access_token: "access",
|
|
336
338
|
refresh_token: "refresh",
|
|
337
339
|
id_token: token,
|
|
338
340
|
})
|
|
339
341
|
|
|
340
|
-
nock("http://
|
|
342
|
+
nock("http://example.com").get("/userinfo?schema=openid").reply(200, {
|
|
341
343
|
sub: "sub",
|
|
342
|
-
email
|
|
344
|
+
email,
|
|
343
345
|
})
|
|
344
346
|
|
|
345
347
|
const configId = await generateOidcConfig()
|
|
@@ -351,10 +353,7 @@ describe("/api/global/auth", () => {
|
|
|
351
353
|
)
|
|
352
354
|
}
|
|
353
355
|
|
|
354
|
-
expect(events.auth.login).toHaveBeenCalledWith(
|
|
355
|
-
"oidc",
|
|
356
|
-
"oauth@example.com"
|
|
357
|
-
)
|
|
356
|
+
expect(events.auth.login).toHaveBeenCalledWith("oidc", email)
|
|
358
357
|
expect(events.auth.login).toHaveBeenCalledTimes(1)
|
|
359
358
|
expect(res.status).toBe(302)
|
|
360
359
|
const location: string = res.get("location")
|
|
@@ -12,6 +12,33 @@ const nodemailer = require("nodemailer")
|
|
|
12
12
|
// for the real email tests give them a long time to try complete/fail
|
|
13
13
|
jest.setTimeout(30000)
|
|
14
14
|
|
|
15
|
+
function cancelableTimeout(timeout: number): [Promise<unknown>, () => void] {
|
|
16
|
+
let timeoutId: NodeJS.Timeout
|
|
17
|
+
return [
|
|
18
|
+
new Promise((resolve, reject) => {
|
|
19
|
+
timeoutId = setTimeout(() => {
|
|
20
|
+
reject({
|
|
21
|
+
status: 301,
|
|
22
|
+
errno: "ETIME",
|
|
23
|
+
})
|
|
24
|
+
}, timeout)
|
|
25
|
+
}),
|
|
26
|
+
() => {
|
|
27
|
+
clearTimeout(timeoutId)
|
|
28
|
+
},
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function withTimeout<T>(
|
|
33
|
+
timeout: number,
|
|
34
|
+
promise: Promise<T>
|
|
35
|
+
): Promise<T> {
|
|
36
|
+
const [timeoutPromise, cancel] = cancelableTimeout(timeout)
|
|
37
|
+
const result = (await Promise.race([promise, timeoutPromise])) as T
|
|
38
|
+
cancel()
|
|
39
|
+
return result
|
|
40
|
+
}
|
|
41
|
+
|
|
15
42
|
describe("/api/global/email", () => {
|
|
16
43
|
const config = new TestConfiguration()
|
|
17
44
|
|
|
@@ -30,19 +57,8 @@ describe("/api/global/email", () => {
|
|
|
30
57
|
) {
|
|
31
58
|
let response, text
|
|
32
59
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
setTimeout(
|
|
36
|
-
() =>
|
|
37
|
-
reject({
|
|
38
|
-
status: 301,
|
|
39
|
-
errno: "ETIME",
|
|
40
|
-
}),
|
|
41
|
-
20000
|
|
42
|
-
)
|
|
43
|
-
)
|
|
44
|
-
await Promise.race([config.saveEtherealSmtpConfig(), timeout()])
|
|
45
|
-
await Promise.race([config.saveSettingsConfig(), timeout()])
|
|
60
|
+
await withTimeout(20000, config.saveEtherealSmtpConfig())
|
|
61
|
+
await withTimeout(20000, config.saveSettingsConfig())
|
|
46
62
|
let res
|
|
47
63
|
if (attachments) {
|
|
48
64
|
res = await config.api.emails
|
package/src/environment.ts
CHANGED
|
@@ -26,6 +26,7 @@ const environment = {
|
|
|
26
26
|
SALT_ROUNDS: process.env.SALT_ROUNDS,
|
|
27
27
|
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
|
28
28
|
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
|
|
29
|
+
PASSWORD_MIN_LENGTH: process.env.PASSWORD_MIN_LENGTH,
|
|
29
30
|
// urls
|
|
30
31
|
MINIO_URL: process.env.MINIO_URL,
|
|
31
32
|
COUCH_DB_URL: process.env.COUCH_DB_URL,
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
timers,
|
|
19
19
|
redis,
|
|
20
20
|
cache,
|
|
21
|
+
features,
|
|
21
22
|
} from "@budibase/backend-core"
|
|
22
23
|
|
|
23
24
|
db.init()
|
|
@@ -95,6 +96,7 @@ export default server.listen(parseInt(env.PORT || "4002"), async () => {
|
|
|
95
96
|
console.log(startupLog)
|
|
96
97
|
await initPro()
|
|
97
98
|
await redis.clients.init()
|
|
99
|
+
features.init()
|
|
98
100
|
cache.docWritethrough.init()
|
|
99
101
|
// configure events to use the pro audit log write
|
|
100
102
|
// can't integrate directly into backend-core due to cyclic issues
|
package/src/tests/api/configs.ts
CHANGED
|
@@ -40,7 +40,7 @@ export class ConfigAPI extends TestAPI {
|
|
|
40
40
|
const sessionContent = JSON.parse(
|
|
41
41
|
Buffer.from(koaSession, "base64").toString("utf-8")
|
|
42
42
|
)
|
|
43
|
-
const handle = sessionContent["openidconnect:
|
|
43
|
+
const handle = sessionContent["openidconnect:example.com"].state.handle
|
|
44
44
|
return this.request
|
|
45
45
|
.get(`/api/global/auth/${this.config.getTenantId()}/oidc/callback`)
|
|
46
46
|
.query({ code: "test", state: handle })
|
package/src/tests/api/users.ts
CHANGED
|
@@ -48,7 +48,7 @@ export class UserAPI extends TestAPI {
|
|
|
48
48
|
return this.request
|
|
49
49
|
.post(`/api/global/users/invite/accept`)
|
|
50
50
|
.send({
|
|
51
|
-
password: "
|
|
51
|
+
password: "newpassword1",
|
|
52
52
|
inviteCode: code,
|
|
53
53
|
firstName: "Ted",
|
|
54
54
|
})
|
|
@@ -101,7 +101,7 @@ export class UserAPI extends TestAPI {
|
|
|
101
101
|
if (!request) {
|
|
102
102
|
request = {
|
|
103
103
|
email: structures.email(),
|
|
104
|
-
password: generator.string({ length:
|
|
104
|
+
password: generator.string({ length: 12 }),
|
|
105
105
|
tenantId: structures.tenant.id(),
|
|
106
106
|
}
|
|
107
107
|
}
|
package/src/tests/jestSetup.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { mocks, testContainerUtils } from "@budibase/backend-core/tests"
|
|
2
2
|
import env from "../environment"
|
|
3
3
|
import { env as coreEnv, timers } from "@budibase/backend-core"
|
|
4
|
-
|
|
5
|
-
// must explicitly enable fetch mock
|
|
6
|
-
mocks.fetch.enable()
|
|
4
|
+
import nock from "nock"
|
|
7
5
|
|
|
8
6
|
// mock all dates to 2020-01-01T00:00:00.000Z
|
|
9
7
|
// use tk.reset() to use real dates in individual tests
|
|
10
|
-
|
|
8
|
+
import tk from "timekeeper"
|
|
9
|
+
|
|
10
|
+
nock.disableNetConnect()
|
|
11
|
+
nock.enableNetConnect(host => {
|
|
12
|
+
return (
|
|
13
|
+
host.includes("localhost") ||
|
|
14
|
+
host.includes("127.0.0.1") ||
|
|
15
|
+
host.includes("::1") ||
|
|
16
|
+
host.includes("ethereal.email") // used in realEmail.spec.ts
|
|
17
|
+
)
|
|
18
|
+
})
|
|
11
19
|
|
|
12
20
|
tk.freeze(mocks.date.MOCK_DATE)
|
|
13
21
|
|