@budibase/worker 2.3.17-alpha.4 → 2.3.17-alpha.5
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 +8 -7
- package/scripts/dev/manage.js +1 -0
- package/src/api/controllers/global/auth.ts +77 -70
- package/src/api/controllers/global/self.ts +28 -44
- package/src/api/controllers/global/users.ts +65 -25
- 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/tests/auth.spec.ts +234 -33
- package/src/api/routes/global/tests/configs.spec.ts +93 -113
- package/src/api/routes/global/tests/roles.spec.ts +3 -2
- package/src/api/routes/global/tests/self.spec.ts +3 -4
- package/src/api/routes/global/tests/users.spec.ts +44 -46
- package/src/api/routes/system/tenants.ts +1 -1
- package/src/api/routes/system/tests/accounts.spec.ts +4 -4
- package/src/api/routes/system/tests/migrations.spec.ts +2 -2
- package/src/api/routes/system/tests/restore.spec.ts +2 -2
- package/src/api/routes/system/tests/status.spec.ts +5 -5
- package/src/environment.ts +15 -1
- package/src/index.ts +6 -0
- 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 +52 -0
- package/src/sdk/users/users.ts +53 -46
- package/src/tests/TestConfiguration.ts +38 -89
- package/src/tests/api/auth.ts +42 -18
- package/src/tests/api/base.ts +2 -1
- package/src/tests/api/restore.ts +1 -0
- package/src/tests/jestEnv.ts +2 -1
- package/src/tests/jestSetup.ts +2 -5
- package/src/tests/logging.ts +34 -0
- package/src/tests/structures/index.ts +0 -2
- package/src/utilities/email.ts +3 -3
- package/src/tests/structures/users.ts +0 -37
|
@@ -15,61 +15,29 @@ const supertest = require("supertest")
|
|
|
15
15
|
import { Config } from "../constants"
|
|
16
16
|
import {
|
|
17
17
|
users,
|
|
18
|
-
|
|
18
|
+
context,
|
|
19
19
|
sessions,
|
|
20
20
|
auth,
|
|
21
21
|
constants,
|
|
22
22
|
env as coreEnv,
|
|
23
|
-
DEFAULT_TENANT_ID,
|
|
24
23
|
} from "@budibase/backend-core"
|
|
25
|
-
import structures, {
|
|
26
|
-
import {
|
|
24
|
+
import structures, { CSRF_TOKEN } from "./structures"
|
|
25
|
+
import { SaveUserResponse, User, AuthToken } from "@budibase/types"
|
|
27
26
|
import API from "./api"
|
|
28
|
-
import sdk from "../sdk"
|
|
29
|
-
|
|
30
|
-
enum Mode {
|
|
31
|
-
CLOUD = "cloud",
|
|
32
|
-
SELF = "self",
|
|
33
|
-
}
|
|
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
27
|
|
|
54
28
|
class TestConfiguration {
|
|
55
29
|
server: any
|
|
56
30
|
request: any
|
|
57
31
|
api: API
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
constructor(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
) {
|
|
68
|
-
if (opts.mode === Mode.CLOUD) {
|
|
69
|
-
this.modeCloud()
|
|
70
|
-
} else if (opts.mode === Mode.SELF) {
|
|
71
|
-
this.modeSelf()
|
|
72
|
-
}
|
|
32
|
+
tenantId: string
|
|
33
|
+
user?: User
|
|
34
|
+
userPassword = "test"
|
|
35
|
+
|
|
36
|
+
constructor(opts: { openServer: boolean } = { openServer: true }) {
|
|
37
|
+
// default to cloud hosting
|
|
38
|
+
this.cloudHosted()
|
|
39
|
+
|
|
40
|
+
this.tenantId = structures.tenant.id()
|
|
73
41
|
|
|
74
42
|
if (opts.openServer) {
|
|
75
43
|
env.PORT = "0" // random port
|
|
@@ -85,26 +53,19 @@ class TestConfiguration {
|
|
|
85
53
|
return this.request
|
|
86
54
|
}
|
|
87
55
|
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
setMultiTenancy = (value: boolean) => {
|
|
91
|
-
env._set("MULTI_TENANCY", value)
|
|
92
|
-
coreEnv._set("MULTI_TENANCY", value)
|
|
93
|
-
}
|
|
56
|
+
// HOSTING
|
|
94
57
|
|
|
95
58
|
setSelfHosted = (value: boolean) => {
|
|
96
59
|
env._set("SELF_HOSTED", value)
|
|
97
60
|
coreEnv._set("SELF_HOSTED", value)
|
|
98
61
|
}
|
|
99
62
|
|
|
100
|
-
|
|
63
|
+
cloudHosted = () => {
|
|
101
64
|
this.setSelfHosted(false)
|
|
102
|
-
this.setMultiTenancy(true)
|
|
103
65
|
}
|
|
104
66
|
|
|
105
|
-
|
|
67
|
+
selfHosted = () => {
|
|
106
68
|
this.setSelfHosted(true)
|
|
107
|
-
this.setMultiTenancy(false)
|
|
108
69
|
}
|
|
109
70
|
|
|
110
71
|
// UTILS
|
|
@@ -125,7 +86,7 @@ class TestConfiguration {
|
|
|
125
86
|
if (params) {
|
|
126
87
|
request.params = params
|
|
127
88
|
}
|
|
128
|
-
await
|
|
89
|
+
await context.doInTenant(this.getTenantId(), () => {
|
|
129
90
|
return controlFunc(request)
|
|
130
91
|
})
|
|
131
92
|
return request.body
|
|
@@ -135,18 +96,10 @@ class TestConfiguration {
|
|
|
135
96
|
|
|
136
97
|
async beforeAll() {
|
|
137
98
|
try {
|
|
138
|
-
this
|
|
139
|
-
|
|
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
|
-
})
|
|
99
|
+
await this.createDefaultUser()
|
|
100
|
+
await this.createSession(this.user!)
|
|
148
101
|
} catch (e: any) {
|
|
149
|
-
console.
|
|
102
|
+
console.error(e)
|
|
150
103
|
throw new Error(e.message)
|
|
151
104
|
}
|
|
152
105
|
}
|
|
@@ -159,12 +112,16 @@ class TestConfiguration {
|
|
|
159
112
|
|
|
160
113
|
// TENANCY
|
|
161
114
|
|
|
115
|
+
doInTenant(task: any) {
|
|
116
|
+
return context.doInTenant(this.tenantId, () => {
|
|
117
|
+
return task()
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
162
121
|
createTenant = async (): Promise<User> => {
|
|
163
122
|
// create user / new tenant
|
|
164
123
|
const res = await this.api.users.createAdminUser()
|
|
165
124
|
|
|
166
|
-
await sdk.users.addTenant(res.tenantId, res.userId, res.email)
|
|
167
|
-
|
|
168
125
|
// return the created user
|
|
169
126
|
const userRes = await this.api.users.getUser(res.userId, {
|
|
170
127
|
headers: {
|
|
@@ -182,9 +139,9 @@ class TestConfiguration {
|
|
|
182
139
|
|
|
183
140
|
getTenantId() {
|
|
184
141
|
try {
|
|
185
|
-
return
|
|
186
|
-
} catch (e
|
|
187
|
-
return
|
|
142
|
+
return context.getTenantId()
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return this.tenantId!
|
|
188
145
|
}
|
|
189
146
|
}
|
|
190
147
|
|
|
@@ -232,14 +189,11 @@ class TestConfiguration {
|
|
|
232
189
|
}
|
|
233
190
|
|
|
234
191
|
defaultHeaders() {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
} else {
|
|
241
|
-
throw new Error("could not determine auth headers to use")
|
|
242
|
-
}
|
|
192
|
+
return this.authHeaders(this.user!)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
tenantIdHeaders() {
|
|
196
|
+
return { [constants.Header.TENANT_ID]: this.tenantId }
|
|
243
197
|
}
|
|
244
198
|
|
|
245
199
|
internalAPIHeaders() {
|
|
@@ -254,20 +208,15 @@ class TestConfiguration {
|
|
|
254
208
|
|
|
255
209
|
async createDefaultUser() {
|
|
256
210
|
const user = structures.users.adminUser({
|
|
257
|
-
password:
|
|
211
|
+
password: this.userPassword,
|
|
258
212
|
})
|
|
259
|
-
this.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
async createTenant1User() {
|
|
263
|
-
const user = structures.users.adminUser({
|
|
264
|
-
password: "test",
|
|
213
|
+
await context.doInTenant(this.tenantId!, async () => {
|
|
214
|
+
this.user = await this.createUser(user)
|
|
265
215
|
})
|
|
266
|
-
this.tenant1User = await this.createUser(user)
|
|
267
216
|
}
|
|
268
217
|
|
|
269
218
|
async getUser(email: string): Promise<User> {
|
|
270
|
-
return
|
|
219
|
+
return context.doInTenant(this.getTenantId(), () => {
|
|
271
220
|
return users.getGlobalUserByEmail(email)
|
|
272
221
|
})
|
|
273
222
|
}
|
|
@@ -277,7 +226,7 @@ class TestConfiguration {
|
|
|
277
226
|
user = structures.users.user()
|
|
278
227
|
}
|
|
279
228
|
const response = await this._req(user, null, controllers.users.save)
|
|
280
|
-
const body = response as
|
|
229
|
+
const body = response as SaveUserResponse
|
|
281
230
|
return this.getUser(body.email)
|
|
282
231
|
}
|
|
283
232
|
|
package/src/tests/api/auth.ts
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
1
|
-
import structures from "../structures"
|
|
2
1
|
import TestConfiguration from "../TestConfiguration"
|
|
3
|
-
import { TestAPI } from "./base"
|
|
2
|
+
import { TestAPI, TestAPIOpts } from "./base"
|
|
4
3
|
|
|
5
4
|
export class AuthAPI extends TestAPI {
|
|
6
5
|
constructor(config: TestConfiguration) {
|
|
7
6
|
super(config)
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
updatePassword = (
|
|
9
|
+
updatePassword = (
|
|
10
|
+
resetCode: string,
|
|
11
|
+
password: string,
|
|
12
|
+
opts?: TestAPIOpts
|
|
13
|
+
) => {
|
|
11
14
|
return this.request
|
|
12
15
|
.post(`/api/global/auth/${this.config.getTenantId()}/reset/update`)
|
|
13
16
|
.send({
|
|
14
|
-
password
|
|
15
|
-
resetCode
|
|
17
|
+
password,
|
|
18
|
+
resetCode,
|
|
16
19
|
})
|
|
17
20
|
.expect("Content-Type", /json/)
|
|
18
|
-
.expect(200)
|
|
21
|
+
.expect(opts?.status ? opts.status : 200)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
login = (
|
|
25
|
+
tenantId: string,
|
|
26
|
+
email: string,
|
|
27
|
+
password: string,
|
|
28
|
+
opts?: TestAPIOpts
|
|
29
|
+
) => {
|
|
30
|
+
return this.request
|
|
31
|
+
.post(`/api/global/auth/${tenantId}/login`)
|
|
32
|
+
.send({
|
|
33
|
+
username: email,
|
|
34
|
+
password: password,
|
|
35
|
+
})
|
|
36
|
+
.expect(opts?.status ? opts.status : 200)
|
|
19
37
|
}
|
|
20
38
|
|
|
21
39
|
logout = () => {
|
|
@@ -25,25 +43,31 @@ export class AuthAPI extends TestAPI {
|
|
|
25
43
|
.expect(200)
|
|
26
44
|
}
|
|
27
45
|
|
|
28
|
-
requestPasswordReset = async (
|
|
46
|
+
requestPasswordReset = async (
|
|
47
|
+
sendMailMock: any,
|
|
48
|
+
email: string,
|
|
49
|
+
opts?: TestAPIOpts
|
|
50
|
+
) => {
|
|
29
51
|
await this.config.saveSmtpConfig()
|
|
30
52
|
await this.config.saveSettingsConfig()
|
|
31
|
-
|
|
32
|
-
...structures.users.user(),
|
|
33
|
-
email: userEmail,
|
|
34
|
-
})
|
|
53
|
+
|
|
35
54
|
const res = await this.request
|
|
36
55
|
.post(`/api/global/auth/${this.config.getTenantId()}/reset`)
|
|
37
56
|
.send({
|
|
38
|
-
email:
|
|
57
|
+
email: email,
|
|
39
58
|
})
|
|
40
59
|
.expect("Content-Type", /json/)
|
|
41
|
-
.expect(200)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
.expect(opts?.status ? opts.status : 200)
|
|
61
|
+
|
|
62
|
+
let code: string | undefined
|
|
63
|
+
if (res.status === 200) {
|
|
64
|
+
const emailCall = sendMailMock.mock.calls[0][0]
|
|
65
|
+
const parts = emailCall.html.split(
|
|
66
|
+
`http://localhost:10000/builder/auth/reset?code=`
|
|
67
|
+
)
|
|
68
|
+
code = parts[1].split('"')[0].split("&")[0]
|
|
69
|
+
}
|
|
70
|
+
|
|
47
71
|
return { code, res }
|
|
48
72
|
}
|
|
49
73
|
}
|
package/src/tests/api/base.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import TestConfiguration from "../TestConfiguration"
|
|
2
|
+
import { SuperTest, Test } from "supertest"
|
|
2
3
|
|
|
3
4
|
export interface TestAPIOpts {
|
|
4
5
|
headers?: any
|
|
@@ -7,7 +8,7 @@ export interface TestAPIOpts {
|
|
|
7
8
|
|
|
8
9
|
export abstract class TestAPI {
|
|
9
10
|
config: TestConfiguration
|
|
10
|
-
request:
|
|
11
|
+
request: SuperTest<Test>
|
|
11
12
|
|
|
12
13
|
protected constructor(config: TestConfiguration) {
|
|
13
14
|
this.config = config
|
package/src/tests/api/restore.ts
CHANGED
package/src/tests/jestEnv.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
process.env.SELF_HOSTED = "0"
|
|
2
2
|
process.env.NODE_ENV = "jest"
|
|
3
3
|
process.env.JWT_SECRET = "test-jwtsecret"
|
|
4
|
-
process.env.LOG_LEVEL = "
|
|
4
|
+
process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
|
|
5
|
+
process.env.ENABLE_4XX_HTTP_LOGGING = "0"
|
|
5
6
|
process.env.MULTI_TENANCY = "1"
|
|
6
7
|
process.env.MINIO_URL = "http://localhost"
|
|
7
8
|
process.env.MINIO_ACCESS_KEY = "test"
|
package/src/tests/jestSetup.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./logging"
|
|
2
2
|
|
|
3
|
+
import { mocks, testContainerUtils } from "@budibase/backend-core/tests"
|
|
3
4
|
import env from "../environment"
|
|
4
5
|
import { env as coreEnv } from "@budibase/backend-core"
|
|
5
6
|
|
|
@@ -11,10 +12,6 @@ mocks.fetch.enable()
|
|
|
11
12
|
const tk = require("timekeeper")
|
|
12
13
|
tk.freeze(mocks.date.MOCK_DATE)
|
|
13
14
|
|
|
14
|
-
if (!process.env.DEBUG) {
|
|
15
|
-
global.console.log = jest.fn() // console.log are ignored in tests
|
|
16
|
-
}
|
|
17
|
-
|
|
18
15
|
if (!process.env.CI) {
|
|
19
16
|
// set a longer timeout in dev for debugging
|
|
20
17
|
// 100 seconds
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export enum LogLevel {
|
|
2
|
+
TRACE = "trace",
|
|
3
|
+
DEBUG = "debug",
|
|
4
|
+
INFO = "info",
|
|
5
|
+
WARN = "warn",
|
|
6
|
+
ERROR = "error",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const LOG_INDEX: { [key in LogLevel]: number } = {
|
|
10
|
+
[LogLevel.TRACE]: 1,
|
|
11
|
+
[LogLevel.DEBUG]: 2,
|
|
12
|
+
[LogLevel.INFO]: 3,
|
|
13
|
+
[LogLevel.WARN]: 4,
|
|
14
|
+
[LogLevel.ERROR]: 5,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const setIndex = LOG_INDEX[process.env.LOG_LEVEL as LogLevel]
|
|
18
|
+
|
|
19
|
+
if (setIndex > LOG_INDEX.trace) {
|
|
20
|
+
global.console.trace = jest.fn()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (setIndex > LOG_INDEX.debug) {
|
|
24
|
+
global.console.debug = jest.fn()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (setIndex > LOG_INDEX.info) {
|
|
28
|
+
global.console.info = jest.fn()
|
|
29
|
+
global.console.log = jest.fn()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (setIndex > LOG_INDEX.warn) {
|
|
33
|
+
global.console.warn = jest.fn()
|
|
34
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { structures } from "@budibase/backend-core/tests"
|
|
2
2
|
import * as configs from "./configs"
|
|
3
|
-
import * as users from "./users"
|
|
4
3
|
import * as groups from "./groups"
|
|
5
4
|
import { v4 as uuid } from "uuid"
|
|
6
5
|
|
|
@@ -11,7 +10,6 @@ const pkg = {
|
|
|
11
10
|
...structures,
|
|
12
11
|
uuid,
|
|
13
12
|
configs,
|
|
14
|
-
users,
|
|
15
13
|
TENANT_ID,
|
|
16
14
|
CSRF_TOKEN,
|
|
17
15
|
groups,
|
package/src/utilities/email.ts
CHANGED
|
@@ -26,7 +26,7 @@ type SendEmailOpts = {
|
|
|
26
26
|
automation?: boolean
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const TEST_MODE =
|
|
29
|
+
const TEST_MODE = env.ENABLE_EMAIL_TEST_MODE && env.isDev()
|
|
30
30
|
const TYPE = TemplateType.EMAIL
|
|
31
31
|
|
|
32
32
|
const FULL_EMAIL_PURPOSES = [
|
|
@@ -62,8 +62,8 @@ function createSMTPTransport(config: any) {
|
|
|
62
62
|
host: "smtp.ethereal.email",
|
|
63
63
|
secure: false,
|
|
64
64
|
auth: {
|
|
65
|
-
user: "
|
|
66
|
-
pass: "
|
|
65
|
+
user: "wyatt.zulauf29@ethereal.email",
|
|
66
|
+
pass: "tEwDtHBWWxusVWAPfa",
|
|
67
67
|
},
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export const email = "test@test.com"
|
|
2
|
-
import { AdminUser, BuilderUser, User } from "@budibase/types"
|
|
3
|
-
import { v4 as uuid } from "uuid"
|
|
4
|
-
|
|
5
|
-
export const newEmail = () => {
|
|
6
|
-
return `${uuid()}@test.com`
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const user = (userProps?: any): User => {
|
|
10
|
-
return {
|
|
11
|
-
email: newEmail(),
|
|
12
|
-
password: "test",
|
|
13
|
-
roles: { app_test: "admin" },
|
|
14
|
-
...userProps,
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const adminUser = (userProps?: any): AdminUser => {
|
|
19
|
-
return {
|
|
20
|
-
...user(userProps),
|
|
21
|
-
admin: {
|
|
22
|
-
global: true,
|
|
23
|
-
},
|
|
24
|
-
builder: {
|
|
25
|
-
global: true,
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const builderUser = (userProps?: any): BuilderUser => {
|
|
31
|
-
return {
|
|
32
|
-
...user(userProps),
|
|
33
|
-
builder: {
|
|
34
|
-
global: true,
|
|
35
|
-
},
|
|
36
|
-
}
|
|
37
|
-
}
|