@budibase/worker 2.1.22-alpha.4 → 2.1.22-alpha.6
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__/oauth.ts +57 -0
- package/coverage/clover.xml +1093 -1033
- package/coverage/coverage-final.json +77 -72
- package/coverage/lcov-report/index.html +83 -83
- package/coverage/lcov-report/src/api/controllers/global/auth.ts.html +70 -70
- package/coverage/lcov-report/src/api/controllers/global/configs.js.html +35 -35
- package/coverage/lcov-report/src/api/controllers/global/email.js.html +4 -4
- package/coverage/lcov-report/src/api/controllers/global/index.html +38 -38
- package/coverage/lcov-report/src/api/controllers/global/license.ts.html +7 -7
- package/coverage/lcov-report/src/api/controllers/global/roles.js.html +10 -10
- package/coverage/lcov-report/src/api/controllers/global/self.ts.html +58 -58
- package/coverage/lcov-report/src/api/controllers/global/templates.ts.html +11 -11
- package/coverage/lcov-report/src/api/controllers/global/users.ts.html +75 -69
- package/coverage/lcov-report/src/api/controllers/global/workspaces.js.html +7 -7
- package/coverage/lcov-report/src/api/controllers/system/accounts.ts.html +4 -4
- package/coverage/lcov-report/src/api/controllers/system/{environment.js.html → environment.ts.html} +21 -18
- package/coverage/lcov-report/src/api/controllers/system/index.html +63 -63
- package/coverage/lcov-report/src/api/controllers/system/migrations.ts.html +23 -23
- package/coverage/lcov-report/src/api/controllers/system/restore.ts.html +22 -22
- package/coverage/lcov-report/src/api/controllers/system/{status.js.html → status.ts.html} +29 -26
- package/coverage/lcov-report/src/api/controllers/system/tenants.ts.html +34 -163
- package/coverage/lcov-report/src/api/index.html +11 -11
- package/coverage/lcov-report/src/api/index.ts.html +113 -44
- package/coverage/lcov-report/src/api/routes/global/auth.js.html +38 -89
- package/coverage/lcov-report/src/api/routes/global/configs.js.html +17 -17
- package/coverage/lcov-report/src/api/routes/global/email.js.html +11 -11
- package/coverage/lcov-report/src/api/routes/global/index.html +11 -11
- package/coverage/lcov-report/src/api/routes/global/license.ts.html +6 -6
- package/coverage/lcov-report/src/api/routes/global/roles.js.html +7 -7
- package/coverage/lcov-report/src/api/routes/global/self.ts.html +8 -8
- package/coverage/lcov-report/src/api/routes/global/templates.ts.html +11 -11
- package/coverage/lcov-report/src/api/routes/global/users.js.html +17 -17
- package/coverage/lcov-report/src/api/routes/global/workspaces.js.html +10 -10
- package/coverage/lcov-report/src/api/routes/index.html +1 -1
- package/coverage/lcov-report/src/api/routes/index.ts.html +19 -19
- package/coverage/lcov-report/src/api/routes/system/accounts.ts.html +7 -7
- package/coverage/lcov-report/src/api/routes/system/{environment.js.html → environment.ts.html} +11 -11
- package/coverage/lcov-report/src/api/routes/system/index.html +4 -4
- package/coverage/lcov-report/src/api/routes/system/migrations.ts.html +7 -7
- package/coverage/lcov-report/src/api/routes/system/restore.ts.html +6 -6
- package/coverage/lcov-report/src/api/routes/system/{status.js.html → status.ts.html} +11 -11
- package/coverage/lcov-report/src/api/routes/system/{tenants.js.html → tenants.ts.html} +20 -17
- package/coverage/lcov-report/src/api/routes/validation/index.html +1 -1
- package/coverage/lcov-report/src/api/routes/validation/index.ts.html +2 -2
- package/coverage/lcov-report/src/api/routes/validation/users.ts.html +13 -13
- package/coverage/lcov-report/src/constants/index.html +1 -1
- package/coverage/lcov-report/src/constants/index.js.html +18 -18
- package/coverage/lcov-report/src/constants/templates/index.html +1 -1
- package/coverage/lcov-report/src/constants/templates/index.js.html +10 -10
- package/coverage/lcov-report/src/db/index.html +1 -1
- package/coverage/lcov-report/src/db/index.ts.html +8 -8
- package/coverage/lcov-report/src/environment.ts.html +15 -15
- package/coverage/lcov-report/src/index.html +1 -1
- package/coverage/lcov-report/src/index.ts.html +40 -40
- package/coverage/lcov-report/src/middleware/cloudRestricted.js.html +22 -22
- package/coverage/lcov-report/src/middleware/index.html +21 -21
- package/coverage/lcov-report/src/migrations/functions/globalInfoSyncUsers.ts.html +3 -3
- package/coverage/lcov-report/src/migrations/functions/index.html +1 -1
- package/coverage/lcov-report/src/migrations/index.html +1 -1
- package/coverage/lcov-report/src/migrations/index.ts.html +17 -17
- package/coverage/lcov-report/src/sdk/accounts/accounts.ts.html +6 -6
- package/coverage/lcov-report/src/sdk/accounts/index.html +1 -1
- package/coverage/lcov-report/src/sdk/accounts/index.ts.html +2 -2
- package/coverage/lcov-report/src/sdk/index.html +1 -1
- package/coverage/lcov-report/src/sdk/index.ts.html +4 -4
- package/coverage/lcov-report/src/sdk/users/events.ts.html +43 -43
- package/coverage/lcov-report/src/sdk/users/index.html +19 -19
- package/coverage/lcov-report/src/sdk/users/index.ts.html +2 -2
- package/coverage/lcov-report/src/sdk/users/users.ts.html +99 -99
- package/coverage/lcov-report/src/tests/TestConfiguration.ts.html +261 -123
- package/coverage/lcov-report/src/tests/api/accounts.ts.html +12 -21
- package/coverage/lcov-report/src/tests/api/auth.ts.html +11 -20
- package/coverage/lcov-report/src/tests/api/base.ts.html +133 -0
- package/coverage/lcov-report/src/tests/api/configs.ts.html +44 -23
- package/coverage/lcov-report/src/tests/api/email.ts.html +9 -18
- package/coverage/lcov-report/src/tests/api/environment.ts.html +130 -0
- package/coverage/lcov-report/src/tests/api/index.html +106 -16
- package/coverage/lcov-report/src/tests/api/index.ts.html +62 -17
- package/coverage/lcov-report/src/tests/{structures/accounts.ts.html → api/migrations.ts.html} +31 -37
- package/coverage/lcov-report/src/tests/api/restore.ts.html +127 -0
- package/coverage/lcov-report/src/tests/api/self.ts.html +31 -16
- package/coverage/lcov-report/src/tests/api/status.ts.html +121 -0
- package/coverage/lcov-report/src/tests/api/tenants.ts.html +130 -0
- package/coverage/lcov-report/src/tests/api/users.ts.html +121 -25
- package/coverage/lcov-report/src/tests/controllers.js.html +2 -2
- package/coverage/lcov-report/src/tests/index.html +20 -20
- package/coverage/lcov-report/src/tests/index.ts.html +21 -9
- package/coverage/lcov-report/src/tests/mocks/email.js.html +2 -2
- package/coverage/lcov-report/src/tests/mocks/index.html +1 -1
- package/coverage/lcov-report/src/tests/mocks/index.ts.html +4 -4
- package/coverage/lcov-report/src/tests/structures/configs.js.html +8 -8
- package/coverage/lcov-report/src/tests/structures/groups.ts.html +2 -2
- package/coverage/lcov-report/src/tests/structures/index.html +9 -24
- package/coverage/lcov-report/src/tests/structures/index.ts.html +21 -15
- package/coverage/lcov-report/src/tests/structures/users.ts.html +13 -10
- package/coverage/lcov-report/src/utilities/appService.js.html +11 -11
- package/coverage/lcov-report/src/utilities/email.js.html +16 -16
- package/coverage/lcov-report/src/utilities/fileSystem.js.html +4 -4
- package/coverage/lcov-report/src/utilities/index.html +1 -1
- package/coverage/lcov-report/src/utilities/index.js.html +2 -2
- package/coverage/lcov-report/src/utilities/redis.js.html +13 -13
- package/coverage/lcov-report/src/utilities/templates.js.html +7 -7
- package/coverage/lcov-report/src/utilities/users.js.html +8 -8
- package/coverage/lcov.info +1685 -1511
- package/package.json +8 -6
- package/scripts/localdomain.js +33 -7
- package/src/api/controllers/global/auth.ts +1 -1
- package/src/api/controllers/global/users.ts +3 -1
- package/src/api/controllers/system/{environment.js → environment.ts} +3 -2
- package/src/api/controllers/system/{status.js → status.ts} +4 -3
- package/src/api/controllers/system/tenants.ts +8 -51
- package/src/api/index.ts +29 -6
- package/src/api/routes/global/auth.js +22 -39
- package/src/api/routes/global/tests/auth.spec.ts +134 -64
- package/src/api/routes/global/tests/configs.spec.ts +5 -6
- package/src/api/routes/global/tests/email.spec.ts +4 -3
- package/src/api/routes/global/tests/license.spec.ts +31 -0
- package/src/api/routes/global/tests/realEmail.spec.ts +2 -3
- package/src/api/routes/global/tests/roles.spec.ts +27 -0
- package/src/api/routes/global/tests/self.spec.ts +3 -4
- package/src/api/routes/global/tests/templates.spec.ts +35 -0
- package/src/api/routes/global/tests/users.spec.ts +51 -43
- package/src/api/routes/global/tests/workspaces.spec.ts +29 -0
- package/src/api/routes/system/environment.ts +8 -0
- package/src/api/routes/system/status.ts +8 -0
- package/src/api/routes/system/tenants.ts +13 -0
- package/src/api/routes/system/tests/accounts.spec.ts +5 -6
- package/src/api/routes/system/tests/environment.spec.ts +29 -0
- package/src/api/routes/system/tests/migrations.spec.ts +63 -0
- package/src/api/routes/system/tests/restore.spec.ts +36 -0
- package/src/api/routes/system/tests/status.spec.ts +48 -0
- package/src/api/routes/system/tests/tenants.spec.ts +61 -0
- package/src/middleware/tests/tenancy.spec.ts +73 -0
- package/src/tests/TestConfiguration.ts +88 -42
- package/src/tests/api/accounts.ts +5 -8
- package/src/tests/api/auth.ts +3 -6
- package/src/tests/api/base.ts +16 -0
- package/src/tests/api/configs.ts +15 -8
- package/src/tests/api/email.ts +3 -6
- package/src/tests/api/environment.ts +15 -0
- package/src/tests/api/index.ts +15 -0
- package/src/tests/api/migrations.ts +22 -0
- package/src/tests/api/restore.ts +14 -0
- package/src/tests/api/self.ts +11 -6
- package/src/tests/api/status.ts +12 -0
- package/src/tests/api/tenants.ts +15 -0
- package/src/tests/api/users.ts +38 -6
- package/src/tests/index.ts +4 -0
- package/src/tests/jestSetup.ts +7 -1
- package/src/tests/structures/index.ts +4 -2
- package/src/tests/structures/users.ts +1 -0
- package/__mocks__/node-fetch.ts +0 -1
- package/src/api/routes/system/environment.js +0 -8
- package/src/api/routes/system/status.js +0 -8
- package/src/api/routes/system/tenants.js +0 -12
- package/src/tests/structures/accounts.ts +0 -24
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import sdk from "../../../../sdk"
|
|
2
|
-
import { TestConfiguration, structures
|
|
2
|
+
import { TestConfiguration, structures } from "../../../../tests"
|
|
3
3
|
import { v4 as uuid } from "uuid"
|
|
4
4
|
|
|
5
5
|
describe("accounts", () => {
|
|
6
6
|
const config = new TestConfiguration()
|
|
7
|
-
const api = new API(config)
|
|
8
7
|
|
|
9
8
|
beforeAll(async () => {
|
|
10
9
|
await config.beforeAll()
|
|
@@ -23,7 +22,7 @@ describe("accounts", () => {
|
|
|
23
22
|
it("saves account metadata", async () => {
|
|
24
23
|
let account = structures.accounts.account()
|
|
25
24
|
|
|
26
|
-
const response = await api.accounts.saveMetadata(account)
|
|
25
|
+
const response = await config.api.accounts.saveMetadata(account)
|
|
27
26
|
|
|
28
27
|
const id = sdk.accounts.formatAccountMetadataId(account.accountId)
|
|
29
28
|
const metadata = await sdk.accounts.getMetadata(id)
|
|
@@ -34,9 +33,9 @@ describe("accounts", () => {
|
|
|
34
33
|
describe("destroyMetadata", () => {
|
|
35
34
|
it("destroys account metadata", async () => {
|
|
36
35
|
const account = structures.accounts.account()
|
|
37
|
-
await api.accounts.saveMetadata(account)
|
|
36
|
+
await config.api.accounts.saveMetadata(account)
|
|
38
37
|
|
|
39
|
-
await api.accounts.destroyMetadata(account.accountId)
|
|
38
|
+
await config.api.accounts.destroyMetadata(account.accountId)
|
|
40
39
|
|
|
41
40
|
const deleted = await sdk.accounts.getMetadata(account.accountId)
|
|
42
41
|
expect(deleted).toBe(undefined)
|
|
@@ -45,7 +44,7 @@ describe("accounts", () => {
|
|
|
45
44
|
it("destroys account metadata that does not exist", async () => {
|
|
46
45
|
const id = uuid()
|
|
47
46
|
|
|
48
|
-
const response = await api.accounts.destroyMetadata(id)
|
|
47
|
+
const response = await config.api.accounts.destroyMetadata(id)
|
|
49
48
|
|
|
50
49
|
expect(response.status).toBe(204)
|
|
51
50
|
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { TestConfiguration } from "../../../../tests"
|
|
2
|
+
|
|
3
|
+
describe("/api/system/environment", () => {
|
|
4
|
+
const config = new TestConfiguration()
|
|
5
|
+
|
|
6
|
+
beforeAll(async () => {
|
|
7
|
+
await config.beforeAll()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
afterAll(async () => {
|
|
11
|
+
await config.afterAll()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
jest.clearAllMocks()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe("GET /api/system/environment", () => {
|
|
19
|
+
it("returns the expected environment", async () => {
|
|
20
|
+
const env = await config.api.environment.getEnvironment()
|
|
21
|
+
expect(env.body).toEqual({
|
|
22
|
+
cloud: true,
|
|
23
|
+
disableAccountPortal: false,
|
|
24
|
+
isDev: false,
|
|
25
|
+
multiTenancy: true,
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const migrateFn = jest.fn()
|
|
2
|
+
|
|
3
|
+
import { TestConfiguration } from "../../../../tests"
|
|
4
|
+
|
|
5
|
+
jest.mock("../../../../migrations", () => {
|
|
6
|
+
return {
|
|
7
|
+
...jest.requireActual("../../../../migrations"),
|
|
8
|
+
migrate: migrateFn,
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
describe("/api/system/migrations", () => {
|
|
13
|
+
const config = new TestConfiguration()
|
|
14
|
+
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
await config.beforeAll()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
afterAll(async () => {
|
|
20
|
+
await config.afterAll()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
jest.clearAllMocks()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe("POST /api/system/migrations/run", () => {
|
|
28
|
+
it("fails with no internal api key", async () => {
|
|
29
|
+
const res = await config.api.migrations.runMigrations({
|
|
30
|
+
headers: {},
|
|
31
|
+
status: 403,
|
|
32
|
+
})
|
|
33
|
+
expect(res.text).toBe("Unauthorized - no public worker access")
|
|
34
|
+
expect(migrateFn).toBeCalledTimes(0)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("runs migrations", async () => {
|
|
38
|
+
const res = await config.api.migrations.runMigrations()
|
|
39
|
+
expect(res.text).toBe("OK")
|
|
40
|
+
expect(migrateFn).toBeCalledTimes(1)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe("DELETE /api/system/migrations/definitions", () => {
|
|
45
|
+
it("fails with no internal api key", async () => {
|
|
46
|
+
const res = await config.api.migrations.getMigrationDefinitions({
|
|
47
|
+
headers: {},
|
|
48
|
+
status: 403,
|
|
49
|
+
})
|
|
50
|
+
expect(res.text).toBe("Unauthorized - no public worker access")
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it("returns definitions", async () => {
|
|
54
|
+
const res = await config.api.migrations.getMigrationDefinitions()
|
|
55
|
+
expect(res.body).toEqual([
|
|
56
|
+
{
|
|
57
|
+
name: "global_info_sync_users",
|
|
58
|
+
type: "global",
|
|
59
|
+
},
|
|
60
|
+
])
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TestConfiguration } from "../../../../tests"
|
|
2
|
+
|
|
3
|
+
describe("/api/system/restore", () => {
|
|
4
|
+
const config = new TestConfiguration()
|
|
5
|
+
|
|
6
|
+
beforeAll(async () => {
|
|
7
|
+
await config.beforeAll()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
afterAll(async () => {
|
|
11
|
+
await config.afterAll()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
jest.clearAllMocks()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe("POST /api/global/restore", () => {
|
|
19
|
+
it("doesn't allow restore in cloud", async () => {
|
|
20
|
+
const res = await config.api.restore.restored({ status: 405 })
|
|
21
|
+
expect(res.body).toEqual({
|
|
22
|
+
message: "This operation is not allowed in cloud.",
|
|
23
|
+
status: 405,
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it("restores in self host", async () => {
|
|
28
|
+
config.modeSelf()
|
|
29
|
+
const res = await config.api.restore.restored()
|
|
30
|
+
expect(res.body).toEqual({
|
|
31
|
+
message: "System prepared after restore.",
|
|
32
|
+
})
|
|
33
|
+
config.modeCloud()
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { TestConfiguration } from "../../../../tests"
|
|
2
|
+
import { accounts } from "@budibase/backend-core"
|
|
3
|
+
import { mocks } from "@budibase/backend-core/tests"
|
|
4
|
+
|
|
5
|
+
describe("/api/system/status", () => {
|
|
6
|
+
const config = new TestConfiguration()
|
|
7
|
+
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
await config.beforeAll()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
afterAll(async () => {
|
|
13
|
+
await config.afterAll()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
jest.clearAllMocks()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe("GET /api/system/status", () => {
|
|
21
|
+
it("returns status in self host", async () => {
|
|
22
|
+
config.modeSelf()
|
|
23
|
+
const res = await config.api.status.getStatus()
|
|
24
|
+
expect(res.body).toEqual({
|
|
25
|
+
health: {
|
|
26
|
+
passing: true,
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
expect(accounts.getStatus).toBeCalledTimes(0)
|
|
30
|
+
config.modeCloud()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("returns status in cloud", async () => {
|
|
34
|
+
const value = {
|
|
35
|
+
health: {
|
|
36
|
+
passing: false,
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
mocks.accounts.getStatus.mockReturnValueOnce(value)
|
|
41
|
+
|
|
42
|
+
const res = await config.api.status.getStatus()
|
|
43
|
+
|
|
44
|
+
expect(accounts.getStatus).toBeCalledTimes(1)
|
|
45
|
+
expect(res.body).toEqual(value)
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TestConfiguration } from "../../../../tests"
|
|
2
|
+
import { tenancy } from "@budibase/backend-core"
|
|
3
|
+
|
|
4
|
+
describe("/api/global/tenants", () => {
|
|
5
|
+
const config = new TestConfiguration()
|
|
6
|
+
|
|
7
|
+
beforeAll(async () => {
|
|
8
|
+
await config.beforeAll()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterAll(async () => {
|
|
12
|
+
await config.afterAll()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
jest.clearAllMocks()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe("DELETE /api/system/tenants/:tenantId", () => {
|
|
20
|
+
it("allows deleting the current tenant", async () => {
|
|
21
|
+
const user = await config.createTenant()
|
|
22
|
+
|
|
23
|
+
await config.api.tenants.delete(user.tenantId, {
|
|
24
|
+
headers: config.authHeaders(user),
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it("rejects deleting another tenant", async () => {
|
|
29
|
+
const user1 = await config.createTenant()
|
|
30
|
+
// create a second user in another tenant
|
|
31
|
+
const user2 = await config.createTenant()
|
|
32
|
+
|
|
33
|
+
const status = 403
|
|
34
|
+
const res = await config.api.tenants.delete(user1.tenantId, {
|
|
35
|
+
status,
|
|
36
|
+
headers: config.authHeaders(user2),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
expect(res.body).toEqual({
|
|
40
|
+
message: "Tenant ID does not match current user",
|
|
41
|
+
status,
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it("rejects non-admin", async () => {
|
|
46
|
+
const user1 = await config.createTenant()
|
|
47
|
+
// create an internal non-admin user
|
|
48
|
+
const user2 = await tenancy.doInTenant(user1.tenantId, () => {
|
|
49
|
+
return config.createUser()
|
|
50
|
+
})
|
|
51
|
+
await config.createSession(user2)
|
|
52
|
+
|
|
53
|
+
const res = await config.api.tenants.delete(user1.tenantId, {
|
|
54
|
+
status: 403,
|
|
55
|
+
headers: config.authHeaders(user2),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
expect(res.body).toEqual(config.adminOnlyResponse())
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TestConfiguration, structures } from "../../tests"
|
|
2
|
+
import { constants } from "@budibase/backend-core"
|
|
3
|
+
|
|
4
|
+
describe("tenancy middleware", () => {
|
|
5
|
+
const config = new TestConfiguration()
|
|
6
|
+
|
|
7
|
+
beforeAll(async () => {
|
|
8
|
+
await config.beforeAll()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterAll(async () => {
|
|
12
|
+
await config.afterAll()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
jest.clearAllMocks()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it("should get tenant id from user", async () => {
|
|
20
|
+
const user = await config.createTenant()
|
|
21
|
+
await config.createSession(user)
|
|
22
|
+
const res = await config.api.self.getSelf(user)
|
|
23
|
+
expect(res.headers[constants.Headers.TENANT_ID]).toBe(user.tenantId)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it("should get tenant id from header", async () => {
|
|
27
|
+
const tenantId = structures.uuid()
|
|
28
|
+
const headers = {
|
|
29
|
+
[constants.Headers.TENANT_ID]: tenantId,
|
|
30
|
+
}
|
|
31
|
+
const res = await config.request
|
|
32
|
+
.get(`/api/global/configs/checklist`)
|
|
33
|
+
.set(headers)
|
|
34
|
+
expect(res.headers[constants.Headers.TENANT_ID]).toBe(tenantId)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("should get tenant id from query param", async () => {
|
|
38
|
+
const tenantId = structures.uuid()
|
|
39
|
+
const res = await config.request.get(
|
|
40
|
+
`/api/global/configs/checklist?tenantId=${tenantId}`
|
|
41
|
+
)
|
|
42
|
+
expect(res.headers[constants.Headers.TENANT_ID]).toBe(tenantId)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it("should get tenant id from subdomain", async () => {
|
|
46
|
+
const tenantId = structures.uuid()
|
|
47
|
+
const headers = {
|
|
48
|
+
host: `${tenantId}.localhost:10000`,
|
|
49
|
+
}
|
|
50
|
+
const res = await config.request
|
|
51
|
+
.get(`/api/global/configs/checklist`)
|
|
52
|
+
.set(headers)
|
|
53
|
+
expect(res.headers[constants.Headers.TENANT_ID]).toBe(tenantId)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("should get tenant id from path variable", async () => {
|
|
57
|
+
const user = await config.createTenant()
|
|
58
|
+
const res = await config.request
|
|
59
|
+
.post(`/api/global/auth/${user.tenantId}/login`)
|
|
60
|
+
.send({
|
|
61
|
+
username: user.email,
|
|
62
|
+
password: user.password,
|
|
63
|
+
})
|
|
64
|
+
expect(res.headers[constants.Headers.TENANT_ID]).toBe(user.tenantId)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it("should throw when no tenant id is found", async () => {
|
|
68
|
+
const res = await config.request.get(`/api/global/configs/checklist`)
|
|
69
|
+
expect(res.status).toBe(403)
|
|
70
|
+
expect(res.text).toBe("Tenant id not set")
|
|
71
|
+
expect(res.headers[constants.Headers.TENANT_ID]).toBe(undefined)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -12,30 +12,33 @@ import {
|
|
|
12
12
|
Headers,
|
|
13
13
|
sessions,
|
|
14
14
|
auth,
|
|
15
|
+
constants,
|
|
16
|
+
env as coreEnv,
|
|
15
17
|
} from "@budibase/backend-core"
|
|
16
|
-
import { TENANT_ID, TENANT_1, CSRF_TOKEN } from "./structures"
|
|
17
|
-
import structures from "./structures"
|
|
18
|
+
import structures, { TENANT_ID, TENANT_1, CSRF_TOKEN } from "./structures"
|
|
18
19
|
import { CreateUserResponse, User, AuthToken } from "@budibase/types"
|
|
20
|
+
import API from "./api"
|
|
19
21
|
|
|
20
22
|
enum Mode {
|
|
21
|
-
|
|
23
|
+
CLOUD = "cloud",
|
|
22
24
|
SELF = "self",
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
class TestConfiguration {
|
|
26
28
|
server: any
|
|
27
29
|
request: any
|
|
30
|
+
api: API
|
|
28
31
|
defaultUser?: User
|
|
29
32
|
tenant1User?: User
|
|
30
33
|
|
|
31
34
|
constructor(
|
|
32
35
|
opts: { openServer: boolean; mode: Mode } = {
|
|
33
36
|
openServer: true,
|
|
34
|
-
mode: Mode.
|
|
37
|
+
mode: Mode.CLOUD,
|
|
35
38
|
}
|
|
36
39
|
) {
|
|
37
|
-
if (opts.mode === Mode.
|
|
38
|
-
this.
|
|
40
|
+
if (opts.mode === Mode.CLOUD) {
|
|
41
|
+
this.modeCloud()
|
|
39
42
|
} else if (opts.mode === Mode.SELF) {
|
|
40
43
|
this.modeSelf()
|
|
41
44
|
}
|
|
@@ -46,6 +49,8 @@ class TestConfiguration {
|
|
|
46
49
|
// we need the request for logging in, involves cookies, hard to fake
|
|
47
50
|
this.request = supertest(this.server)
|
|
48
51
|
}
|
|
52
|
+
|
|
53
|
+
this.api = new API(this)
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
getRequest() {
|
|
@@ -54,20 +59,24 @@ class TestConfiguration {
|
|
|
54
59
|
|
|
55
60
|
// MODES
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
env.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
setMultiTenancy = (value: boolean) => {
|
|
63
|
+
env._set("MULTI_TENANCY", value)
|
|
64
|
+
coreEnv._set("MULTI_TENANCY", value)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setSelfHosted = (value: boolean) => {
|
|
68
|
+
env._set("SELF_HOSTED", value)
|
|
69
|
+
coreEnv._set("SELF_HOSTED", value)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
modeCloud = () => {
|
|
73
|
+
this.setSelfHosted(false)
|
|
74
|
+
this.setMultiTenancy(true)
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
modeSelf = () => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
env.MULTI_TENANCY = false
|
|
69
|
-
// @ts-ignore
|
|
70
|
-
env.DISABLE_ACCOUNT_PORTAL = true
|
|
78
|
+
this.setSelfHosted(true)
|
|
79
|
+
this.setMultiTenancy(false)
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
// UTILS
|
|
@@ -114,6 +123,25 @@ class TestConfiguration {
|
|
|
114
123
|
|
|
115
124
|
// TENANCY
|
|
116
125
|
|
|
126
|
+
createTenant = async (): Promise<User> => {
|
|
127
|
+
// create user / new tenant
|
|
128
|
+
const res = await this.api.users.createAdminUser()
|
|
129
|
+
|
|
130
|
+
// return the created user
|
|
131
|
+
const userRes = await this.api.users.getUser(res.userId, {
|
|
132
|
+
headers: {
|
|
133
|
+
...this.internalAPIHeaders(),
|
|
134
|
+
[constants.Headers.TENANT_ID]: res.tenantId,
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// create a session for the new user
|
|
139
|
+
const user = userRes.body
|
|
140
|
+
await this.createSession(user)
|
|
141
|
+
|
|
142
|
+
return user
|
|
143
|
+
}
|
|
144
|
+
|
|
117
145
|
getTenantId() {
|
|
118
146
|
try {
|
|
119
147
|
return tenancy.getTenantId()
|
|
@@ -122,35 +150,32 @@ class TestConfiguration {
|
|
|
122
150
|
}
|
|
123
151
|
}
|
|
124
152
|
|
|
125
|
-
//
|
|
153
|
+
// AUTH
|
|
126
154
|
|
|
127
|
-
async
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
password: "test",
|
|
155
|
+
async _createSession({
|
|
156
|
+
userId,
|
|
157
|
+
tenantId,
|
|
158
|
+
}: {
|
|
159
|
+
userId: string
|
|
160
|
+
tenantId: string
|
|
161
|
+
}) {
|
|
162
|
+
await sessions.createASession(userId!, {
|
|
163
|
+
sessionId: "sessionid",
|
|
164
|
+
tenantId: tenantId,
|
|
165
|
+
csrfToken: CSRF_TOKEN,
|
|
139
166
|
})
|
|
140
|
-
this.tenant1User = await this.createUser(user)
|
|
141
167
|
}
|
|
142
168
|
|
|
143
169
|
async createSession(user: User) {
|
|
144
|
-
|
|
145
|
-
sessionId: "sessionid",
|
|
146
|
-
tenantId: user.tenantId,
|
|
147
|
-
csrfToken: CSRF_TOKEN,
|
|
148
|
-
})
|
|
170
|
+
return this._createSession({ userId: user._id!, tenantId: user.tenantId })
|
|
149
171
|
}
|
|
150
172
|
|
|
151
173
|
cookieHeader(cookies: any) {
|
|
174
|
+
if (!Array.isArray(cookies)) {
|
|
175
|
+
cookies = [cookies]
|
|
176
|
+
}
|
|
152
177
|
return {
|
|
153
|
-
Cookie:
|
|
178
|
+
Cookie: cookies,
|
|
154
179
|
}
|
|
155
180
|
}
|
|
156
181
|
|
|
@@ -179,6 +204,32 @@ class TestConfiguration {
|
|
|
179
204
|
}
|
|
180
205
|
}
|
|
181
206
|
|
|
207
|
+
internalAPIHeaders() {
|
|
208
|
+
return { [constants.Headers.API_KEY]: env.INTERNAL_API_KEY }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
adminOnlyResponse = () => {
|
|
212
|
+
return { message: "Admin user only endpoint.", status: 403 }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// USERS
|
|
216
|
+
|
|
217
|
+
async createDefaultUser() {
|
|
218
|
+
const user = structures.users.adminUser({
|
|
219
|
+
email: "test@test.com",
|
|
220
|
+
password: "test",
|
|
221
|
+
})
|
|
222
|
+
this.defaultUser = await this.createUser(user)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async createTenant1User() {
|
|
226
|
+
const user = structures.users.adminUser({
|
|
227
|
+
email: "tenant1@test.com",
|
|
228
|
+
password: "test",
|
|
229
|
+
})
|
|
230
|
+
this.tenant1User = await this.createUser(user)
|
|
231
|
+
}
|
|
232
|
+
|
|
182
233
|
async getUser(email: string): Promise<User> {
|
|
183
234
|
return tenancy.doInTenant(this.getTenantId(), () => {
|
|
184
235
|
return users.getGlobalUserByEmail(email)
|
|
@@ -240,11 +291,6 @@ class TestConfiguration {
|
|
|
240
291
|
|
|
241
292
|
// CONFIGS - OIDC
|
|
242
293
|
|
|
243
|
-
getOIDConfigCookie(configId: string) {
|
|
244
|
-
const token = auth.jwt.sign(configId, env.JWT_SECRET)
|
|
245
|
-
return this.cookieHeader([[`${Cookies.OIDC_CONFIG}=${token}`]])
|
|
246
|
-
}
|
|
247
|
-
|
|
248
294
|
async saveOIDCConfig() {
|
|
249
295
|
await this.deleteConfig(Configs.OIDC)
|
|
250
296
|
const config = structures.configs.oidc()
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { Account, AccountMetadata } from "@budibase/types"
|
|
2
2
|
import TestConfiguration from "../TestConfiguration"
|
|
3
|
+
import { TestAPI } from "./base"
|
|
3
4
|
|
|
4
|
-
export class AccountAPI {
|
|
5
|
-
config: TestConfiguration
|
|
6
|
-
request: any
|
|
7
|
-
|
|
5
|
+
export class AccountAPI extends TestAPI {
|
|
8
6
|
constructor(config: TestConfiguration) {
|
|
9
|
-
|
|
10
|
-
this.request = config.request
|
|
7
|
+
super(config)
|
|
11
8
|
}
|
|
12
9
|
|
|
13
10
|
saveMetadata = async (account: Account) => {
|
|
14
11
|
const res = await this.request
|
|
15
12
|
.put(`/api/system/accounts/${account.accountId}/metadata`)
|
|
16
13
|
.send(account)
|
|
17
|
-
.set(this.config.
|
|
14
|
+
.set(this.config.internalAPIHeaders())
|
|
18
15
|
.expect("Content-Type", /json/)
|
|
19
16
|
.expect(200)
|
|
20
17
|
return res.body as AccountMetadata
|
|
@@ -23,6 +20,6 @@ export class AccountAPI {
|
|
|
23
20
|
destroyMetadata = (accountId: string) => {
|
|
24
21
|
return this.request
|
|
25
22
|
.del(`/api/system/accounts/${accountId}/metadata`)
|
|
26
|
-
.set(this.config.
|
|
23
|
+
.set(this.config.internalAPIHeaders())
|
|
27
24
|
}
|
|
28
25
|
}
|
package/src/tests/api/auth.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import TestConfiguration from "../TestConfiguration"
|
|
2
|
+
import { TestAPI } from "./base"
|
|
2
3
|
|
|
3
|
-
export class AuthAPI {
|
|
4
|
-
config: TestConfiguration
|
|
5
|
-
request: any
|
|
6
|
-
|
|
4
|
+
export class AuthAPI extends TestAPI {
|
|
7
5
|
constructor(config: TestConfiguration) {
|
|
8
|
-
|
|
9
|
-
this.request = config.request
|
|
6
|
+
super(config)
|
|
10
7
|
}
|
|
11
8
|
|
|
12
9
|
updatePassword = (code: string) => {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import TestConfiguration from "../TestConfiguration"
|
|
2
|
+
|
|
3
|
+
export interface TestAPIOpts {
|
|
4
|
+
headers?: any
|
|
5
|
+
status?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export abstract class TestAPI {
|
|
9
|
+
config: TestConfiguration
|
|
10
|
+
request: any
|
|
11
|
+
|
|
12
|
+
protected constructor(config: TestConfiguration) {
|
|
13
|
+
this.config = config
|
|
14
|
+
this.request = config.request
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/tests/api/configs.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import TestConfiguration from "../TestConfiguration"
|
|
2
|
+
import { TestAPI } from "./base"
|
|
2
3
|
|
|
3
|
-
export class ConfigAPI {
|
|
4
|
-
config: TestConfiguration
|
|
5
|
-
request: any
|
|
6
|
-
|
|
4
|
+
export class ConfigAPI extends TestAPI {
|
|
7
5
|
constructor(config: TestConfiguration) {
|
|
8
|
-
|
|
9
|
-
this.request = config.request
|
|
6
|
+
super(config)
|
|
10
7
|
}
|
|
11
8
|
|
|
12
9
|
getConfigChecklist = () => {
|
|
@@ -26,10 +23,20 @@ export class ConfigAPI {
|
|
|
26
23
|
.expect(200)
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
OIDCCallback = (configId: string) => {
|
|
26
|
+
OIDCCallback = (configId: string, preAuthRes: any) => {
|
|
27
|
+
const cookie = this.config.cookieHeader(preAuthRes.get("set-cookie"))
|
|
28
|
+
const setKoaSession = cookie.Cookie.find((c: string) =>
|
|
29
|
+
c.includes("koa:sess")
|
|
30
|
+
)
|
|
31
|
+
const koaSession = setKoaSession.split("=")[1] + "=="
|
|
32
|
+
const sessionContent = JSON.parse(
|
|
33
|
+
Buffer.from(koaSession, "base64").toString("utf-8")
|
|
34
|
+
)
|
|
35
|
+
const handle = sessionContent["openidconnect:localhost"].state.handle
|
|
30
36
|
return this.request
|
|
31
37
|
.get(`/api/global/auth/${this.config.getTenantId()}/oidc/callback`)
|
|
32
|
-
.
|
|
38
|
+
.query({ code: "test", state: handle })
|
|
39
|
+
.set(cookie)
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
getOIDCConfig = (configId: string) => {
|
package/src/tests/api/email.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import TestConfiguration from "../TestConfiguration"
|
|
2
|
+
import { TestAPI } from "./base"
|
|
2
3
|
|
|
3
|
-
export class EmailAPI {
|
|
4
|
-
config: TestConfiguration
|
|
5
|
-
request: any
|
|
6
|
-
|
|
4
|
+
export class EmailAPI extends TestAPI {
|
|
7
5
|
constructor(config: TestConfiguration) {
|
|
8
|
-
|
|
9
|
-
this.request = config.request
|
|
6
|
+
super(config)
|
|
10
7
|
}
|
|
11
8
|
|
|
12
9
|
sendEmail = (purpose: string) => {
|