@budibase/worker 1.2.57 → 1.2.58-alpha.2
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 +7 -6
- package/scripts/jestSetup.js +6 -0
- package/src/api/controllers/global/auth.ts +14 -1
- package/src/api/controllers/global/users.ts +6 -8
- package/src/api/controllers/system/accounts.ts +21 -0
- package/src/api/{index.js → index.ts} +12 -16
- package/src/api/routes/global/configs.js +1 -0
- package/src/api/routes/{tests/auth.spec.js → global/tests/auth.spec.ts} +32 -56
- package/src/api/routes/{tests/configs.spec.js → global/tests/configs.spec.ts} +76 -47
- package/src/api/routes/{tests/email.spec.js → global/tests/email.spec.ts} +7 -15
- package/src/api/routes/{tests/realEmail.spec.js → global/tests/realEmail.spec.ts} +20 -21
- package/src/api/routes/{tests/self.spec.js → global/tests/self.spec.ts} +10 -18
- package/src/api/routes/global/tests/users.spec.ts +464 -0
- package/src/api/routes/index.js +2 -0
- package/src/api/routes/system/accounts.ts +19 -0
- package/src/api/routes/system/tests/accounts.spec.ts +57 -0
- package/src/{environment.js → environment.ts} +10 -9
- package/src/index.ts +6 -4
- package/src/sdk/accounts/accounts.ts +53 -0
- package/src/sdk/accounts/index.ts +1 -0
- package/src/sdk/index.ts +1 -0
- package/src/sdk/users/users.ts +217 -77
- package/src/tests/TestConfiguration.ts +273 -0
- package/src/tests/api/accounts.ts +28 -0
- package/src/tests/api/auth.ts +48 -0
- package/src/tests/api/configs.ts +40 -0
- package/src/tests/api/email.ts +24 -0
- package/src/tests/api/index.ts +25 -0
- package/src/tests/api/self.ts +21 -0
- package/src/tests/api/users.ts +95 -0
- package/src/tests/index.ts +14 -0
- package/src/tests/mocks/index.ts +7 -0
- package/src/tests/structures/accounts.ts +24 -0
- package/src/tests/structures/index.ts +20 -0
- package/src/tests/structures/users.ts +12 -4
- package/src/utilities/redis.js +1 -0
- package/scripts/load/users.js +0 -97
- package/src/api/routes/tests/users.spec.js +0 -390
- package/src/tests/TestConfiguration.js +0 -231
- package/src/tests/index.js +0 -12
- package/src/tests/mocks/index.js +0 -5
- package/src/tests/structures/index.js +0 -14
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
jest.mock("nodemailer")
|
|
2
|
-
|
|
2
|
+
import { TestConfiguration, mocks, API } from "../../../../tests"
|
|
3
3
|
const sendMailMock = mocks.email.mock()
|
|
4
|
-
|
|
5
|
-
const { EmailTemplatePurpose } = require("../../../constants")
|
|
6
|
-
|
|
7
|
-
const TENANT_ID = structures.TENANT_ID
|
|
4
|
+
import { EmailTemplatePurpose } from "../../../../constants"
|
|
8
5
|
|
|
9
6
|
describe("/api/global/email", () => {
|
|
7
|
+
const config = new TestConfiguration()
|
|
8
|
+
const api = new API(config)
|
|
10
9
|
|
|
11
10
|
beforeAll(async () => {
|
|
12
11
|
await config.beforeAll()
|
|
@@ -20,16 +19,9 @@ describe("/api/global/email", () => {
|
|
|
20
19
|
// initially configure settings
|
|
21
20
|
await config.saveSmtpConfig()
|
|
22
21
|
await config.saveSettingsConfig()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
email: "test@test.com",
|
|
27
|
-
purpose: EmailTemplatePurpose.INVITATION,
|
|
28
|
-
tenantId: TENANT_ID,
|
|
29
|
-
})
|
|
30
|
-
.set(config.defaultHeaders())
|
|
31
|
-
.expect("Content-Type", /json/)
|
|
32
|
-
.expect(200)
|
|
22
|
+
|
|
23
|
+
const res = await api.emails.sendEmail(EmailTemplatePurpose.INVITATION)
|
|
24
|
+
|
|
33
25
|
expect(res.body.message).toBeDefined()
|
|
34
26
|
expect(sendMailMock).toHaveBeenCalled()
|
|
35
27
|
const emailCall = sendMailMock.mock.calls[0][0]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { TestConfiguration, API } from "../../../../tests"
|
|
2
|
+
import { EmailTemplatePurpose } from "../../../../constants"
|
|
3
3
|
const nodemailer = require("nodemailer")
|
|
4
4
|
const fetch = require("node-fetch")
|
|
5
5
|
|
|
@@ -7,6 +7,8 @@ const fetch = require("node-fetch")
|
|
|
7
7
|
jest.setTimeout(30000)
|
|
8
8
|
|
|
9
9
|
describe("/api/global/email", () => {
|
|
10
|
+
const config = new TestConfiguration()
|
|
11
|
+
const api = new API(config)
|
|
10
12
|
|
|
11
13
|
beforeAll(async () => {
|
|
12
14
|
await config.beforeAll()
|
|
@@ -16,27 +18,24 @@ describe("/api/global/email", () => {
|
|
|
16
18
|
await config.afterAll()
|
|
17
19
|
})
|
|
18
20
|
|
|
19
|
-
async function sendRealEmail(purpose) {
|
|
21
|
+
async function sendRealEmail(purpose: string) {
|
|
20
22
|
let response, text
|
|
21
23
|
try {
|
|
22
|
-
const timeout = () =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
const timeout = () =>
|
|
25
|
+
new Promise((resolve, reject) =>
|
|
26
|
+
setTimeout(
|
|
27
|
+
() =>
|
|
28
|
+
reject({
|
|
29
|
+
status: 301,
|
|
30
|
+
errno: "ETIME",
|
|
31
|
+
}),
|
|
32
|
+
20000
|
|
33
|
+
)
|
|
34
|
+
)
|
|
28
35
|
await Promise.race([config.saveEtherealSmtpConfig(), timeout()])
|
|
29
36
|
await Promise.race([config.saveSettingsConfig(), timeout()])
|
|
30
|
-
|
|
31
|
-
const res = await
|
|
32
|
-
.post(`/api/global/email/send`)
|
|
33
|
-
.send({
|
|
34
|
-
email: "test@test.com",
|
|
35
|
-
purpose,
|
|
36
|
-
userId: user._id,
|
|
37
|
-
})
|
|
38
|
-
.set(config.defaultHeaders())
|
|
39
|
-
.timeout(20000)
|
|
37
|
+
|
|
38
|
+
const res = await api.emails.sendEmail(purpose).timeout(20000)
|
|
40
39
|
// ethereal hiccup, can't test right now
|
|
41
40
|
if (res.status >= 300) {
|
|
42
41
|
return
|
|
@@ -47,7 +46,7 @@ describe("/api/global/email", () => {
|
|
|
47
46
|
expect(testUrl).toBeDefined()
|
|
48
47
|
response = await fetch(testUrl)
|
|
49
48
|
text = await response.text()
|
|
50
|
-
} catch (err) {
|
|
49
|
+
} catch (err: any) {
|
|
51
50
|
// ethereal hiccup, can't test right now
|
|
52
51
|
if (parseInt(err.status) >= 300 || (err && err.errno === "ETIME")) {
|
|
53
52
|
return
|
|
@@ -81,4 +80,4 @@ describe("/api/global/email", () => {
|
|
|
81
80
|
it("should be able to send a password recovery email", async () => {
|
|
82
81
|
await sendRealEmail(EmailTemplatePurpose.PASSWORD_RECOVERY)
|
|
83
82
|
})
|
|
84
|
-
})
|
|
83
|
+
})
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
jest.mock("nodemailer")
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { TestConfiguration, API } from "../../../../tests"
|
|
3
|
+
import { events } from "@budibase/backend-core"
|
|
4
4
|
|
|
5
5
|
describe("/api/global/self", () => {
|
|
6
|
+
const config = new TestConfiguration()
|
|
7
|
+
const api = new API(config)
|
|
6
8
|
|
|
7
9
|
beforeAll(async () => {
|
|
8
10
|
await config.beforeAll()
|
|
@@ -16,23 +18,13 @@ describe("/api/global/self", () => {
|
|
|
16
18
|
jest.clearAllMocks()
|
|
17
19
|
})
|
|
18
20
|
|
|
19
|
-
const updateSelf = async (user) => {
|
|
20
|
-
const res = await request
|
|
21
|
-
.post(`/api/global/self`)
|
|
22
|
-
.send(user)
|
|
23
|
-
.set(config.defaultHeaders())
|
|
24
|
-
.expect("Content-Type", /json/)
|
|
25
|
-
.expect(200)
|
|
26
|
-
return res
|
|
27
|
-
}
|
|
28
|
-
|
|
29
21
|
describe("update", () => {
|
|
30
|
-
|
|
31
22
|
it("should update self", async () => {
|
|
32
23
|
const user = await config.createUser()
|
|
24
|
+
await config.createSession(user)
|
|
33
25
|
|
|
34
26
|
delete user.password
|
|
35
|
-
const res = await updateSelf(user)
|
|
27
|
+
const res = await api.self.updateSelf(user)
|
|
36
28
|
|
|
37
29
|
expect(res.body._id).toBe(user._id)
|
|
38
30
|
expect(events.user.updated).toBeCalledTimes(1)
|
|
@@ -42,10 +34,10 @@ describe("/api/global/self", () => {
|
|
|
42
34
|
|
|
43
35
|
it("should update password", async () => {
|
|
44
36
|
const user = await config.createUser()
|
|
45
|
-
|
|
46
|
-
user.password = password
|
|
37
|
+
await config.createSession(user)
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
user.password = "newPassword"
|
|
40
|
+
const res = await api.self.updateSelf(user)
|
|
49
41
|
|
|
50
42
|
delete user.password
|
|
51
43
|
expect(res.body._id).toBe(user._id)
|
|
@@ -55,4 +47,4 @@ describe("/api/global/self", () => {
|
|
|
55
47
|
expect(events.user.passwordUpdated).toBeCalledWith(user)
|
|
56
48
|
})
|
|
57
49
|
})
|
|
58
|
-
})
|
|
50
|
+
})
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
jest.mock("nodemailer")
|
|
2
|
+
import {
|
|
3
|
+
TestConfiguration,
|
|
4
|
+
mocks,
|
|
5
|
+
structures,
|
|
6
|
+
TENANT_1,
|
|
7
|
+
API,
|
|
8
|
+
} from "../../../../tests"
|
|
9
|
+
const sendMailMock = mocks.email.mock()
|
|
10
|
+
import { events, tenancy } from "@budibase/backend-core"
|
|
11
|
+
|
|
12
|
+
describe("/api/global/users", () => {
|
|
13
|
+
const config = new TestConfiguration()
|
|
14
|
+
const api = new API(config)
|
|
15
|
+
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
await config.beforeAll()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
afterAll(async () => {
|
|
21
|
+
await config.afterAll()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
jest.clearAllMocks()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe("invite", () => {
|
|
29
|
+
it("should be able to generate an invitation", async () => {
|
|
30
|
+
const { code, res } = await api.users.sendUserInvite(sendMailMock)
|
|
31
|
+
|
|
32
|
+
expect(res.body).toEqual({ message: "Invitation has been sent." })
|
|
33
|
+
expect(sendMailMock).toHaveBeenCalled()
|
|
34
|
+
expect(code).toBeDefined()
|
|
35
|
+
expect(events.user.invited).toBeCalledTimes(1)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it("should be able to create new user from invite", async () => {
|
|
39
|
+
const { code } = await api.users.sendUserInvite(sendMailMock)
|
|
40
|
+
|
|
41
|
+
const res = await api.users.acceptInvite(code)
|
|
42
|
+
|
|
43
|
+
expect(res.body._id).toBeDefined()
|
|
44
|
+
const user = await config.getUser("invite@test.com")
|
|
45
|
+
expect(user).toBeDefined()
|
|
46
|
+
expect(user._id).toEqual(res.body._id)
|
|
47
|
+
expect(events.user.inviteAccepted).toBeCalledTimes(1)
|
|
48
|
+
expect(events.user.inviteAccepted).toBeCalledWith(user)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe("bulkCreate", () => {
|
|
53
|
+
it("should ignore users existing in the same tenant", async () => {
|
|
54
|
+
const user = await config.createUser()
|
|
55
|
+
jest.clearAllMocks()
|
|
56
|
+
|
|
57
|
+
const response = await api.users.bulkCreateUsers([user])
|
|
58
|
+
|
|
59
|
+
expect(response.successful.length).toBe(0)
|
|
60
|
+
expect(response.unsuccessful.length).toBe(1)
|
|
61
|
+
expect(response.unsuccessful[0].email).toBe(user.email)
|
|
62
|
+
expect(events.user.created).toBeCalledTimes(0)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it("should ignore users existing in other tenants", async () => {
|
|
66
|
+
const user = await config.createUser()
|
|
67
|
+
jest.resetAllMocks()
|
|
68
|
+
|
|
69
|
+
await tenancy.doInTenant(TENANT_1, async () => {
|
|
70
|
+
const response = await api.users.bulkCreateUsers([user])
|
|
71
|
+
|
|
72
|
+
expect(response.successful.length).toBe(0)
|
|
73
|
+
expect(response.unsuccessful.length).toBe(1)
|
|
74
|
+
expect(response.unsuccessful[0].email).toBe(user.email)
|
|
75
|
+
expect(events.user.created).toBeCalledTimes(0)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("should ignore accounts using the same email", async () => {
|
|
80
|
+
const account = structures.accounts.account()
|
|
81
|
+
const resp = await api.accounts.saveMetadata(account)
|
|
82
|
+
const user = structures.users.user({ email: resp.email })
|
|
83
|
+
jest.clearAllMocks()
|
|
84
|
+
|
|
85
|
+
const response = await api.users.bulkCreateUsers([user])
|
|
86
|
+
|
|
87
|
+
expect(response.successful.length).toBe(0)
|
|
88
|
+
expect(response.unsuccessful.length).toBe(1)
|
|
89
|
+
expect(response.unsuccessful[0].email).toBe(user.email)
|
|
90
|
+
expect(events.user.created).toBeCalledTimes(0)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it("should be able to bulkCreate users", async () => {
|
|
94
|
+
const builder = structures.users.builderUser()
|
|
95
|
+
const admin = structures.users.adminUser()
|
|
96
|
+
const user = structures.users.user()
|
|
97
|
+
|
|
98
|
+
const response = await api.users.bulkCreateUsers([builder, admin, user])
|
|
99
|
+
|
|
100
|
+
expect(response.successful.length).toBe(3)
|
|
101
|
+
expect(response.successful[0].email).toBe(builder.email)
|
|
102
|
+
expect(response.successful[1].email).toBe(admin.email)
|
|
103
|
+
expect(response.successful[2].email).toBe(user.email)
|
|
104
|
+
expect(response.unsuccessful.length).toBe(0)
|
|
105
|
+
expect(events.user.created).toBeCalledTimes(3)
|
|
106
|
+
expect(events.user.permissionAdminAssigned).toBeCalledTimes(1)
|
|
107
|
+
expect(events.user.permissionBuilderAssigned).toBeCalledTimes(2)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe("create", () => {
|
|
112
|
+
it("should be able to create a basic user", async () => {
|
|
113
|
+
const user = structures.users.user()
|
|
114
|
+
|
|
115
|
+
await api.users.saveUser(user)
|
|
116
|
+
|
|
117
|
+
expect(events.user.created).toBeCalledTimes(1)
|
|
118
|
+
expect(events.user.updated).not.toBeCalled()
|
|
119
|
+
expect(events.user.permissionBuilderAssigned).not.toBeCalled()
|
|
120
|
+
expect(events.user.permissionAdminAssigned).not.toBeCalled()
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it("should be able to create an admin user", async () => {
|
|
124
|
+
const user = structures.users.adminUser()
|
|
125
|
+
|
|
126
|
+
await api.users.saveUser(user)
|
|
127
|
+
|
|
128
|
+
expect(events.user.created).toBeCalledTimes(1)
|
|
129
|
+
expect(events.user.updated).not.toBeCalled()
|
|
130
|
+
expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1)
|
|
131
|
+
expect(events.user.permissionAdminAssigned).toBeCalledTimes(1)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it("should be able to create a builder user", async () => {
|
|
135
|
+
const user = structures.users.builderUser()
|
|
136
|
+
|
|
137
|
+
await api.users.saveUser(user)
|
|
138
|
+
|
|
139
|
+
expect(events.user.created).toBeCalledTimes(1)
|
|
140
|
+
expect(events.user.updated).not.toBeCalled()
|
|
141
|
+
expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1)
|
|
142
|
+
expect(events.user.permissionAdminAssigned).not.toBeCalled()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it("should be able to assign app roles", async () => {
|
|
146
|
+
const user = structures.users.user()
|
|
147
|
+
user.roles = {
|
|
148
|
+
app_123: "role1",
|
|
149
|
+
app_456: "role2",
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
await api.users.saveUser(user)
|
|
153
|
+
|
|
154
|
+
const savedUser = await config.getUser(user.email)
|
|
155
|
+
expect(events.user.created).toBeCalledTimes(1)
|
|
156
|
+
expect(events.user.updated).not.toBeCalled()
|
|
157
|
+
expect(events.role.assigned).toBeCalledTimes(2)
|
|
158
|
+
expect(events.role.assigned).toBeCalledWith(savedUser, "role1")
|
|
159
|
+
expect(events.role.assigned).toBeCalledWith(savedUser, "role2")
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("should not be able to create user that exists in same tenant", async () => {
|
|
163
|
+
const user = await config.createUser()
|
|
164
|
+
jest.clearAllMocks()
|
|
165
|
+
delete user._id
|
|
166
|
+
delete user._rev
|
|
167
|
+
|
|
168
|
+
const response = await api.users.saveUser(user, 400)
|
|
169
|
+
|
|
170
|
+
expect(response.body.message).toBe(`Unavailable`)
|
|
171
|
+
expect(events.user.created).toBeCalledTimes(0)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it("should not be able to create user that exists in other tenant", async () => {
|
|
175
|
+
const user = await config.createUser()
|
|
176
|
+
jest.resetAllMocks()
|
|
177
|
+
|
|
178
|
+
await tenancy.doInTenant(TENANT_1, async () => {
|
|
179
|
+
delete user._id
|
|
180
|
+
const response = await api.users.saveUser(user, 400)
|
|
181
|
+
|
|
182
|
+
expect(response.body.message).toBe(`Unavailable`)
|
|
183
|
+
expect(events.user.created).toBeCalledTimes(0)
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it("should not be able to create user with the same email as an account", async () => {
|
|
188
|
+
const user = structures.users.user()
|
|
189
|
+
const account = structures.accounts.cloudAccount()
|
|
190
|
+
mocks.accounts.getAccount.mockReturnValueOnce(account)
|
|
191
|
+
|
|
192
|
+
const response = await api.users.saveUser(user, 400)
|
|
193
|
+
|
|
194
|
+
expect(response.body.message).toBe(`Unavailable`)
|
|
195
|
+
expect(events.user.created).toBeCalledTimes(0)
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
describe("update", () => {
|
|
200
|
+
it("should be able to update a basic user", async () => {
|
|
201
|
+
const user = await config.createUser()
|
|
202
|
+
jest.clearAllMocks()
|
|
203
|
+
|
|
204
|
+
await api.users.saveUser(user)
|
|
205
|
+
|
|
206
|
+
expect(events.user.created).not.toBeCalled()
|
|
207
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
208
|
+
expect(events.user.permissionBuilderAssigned).not.toBeCalled()
|
|
209
|
+
expect(events.user.permissionAdminAssigned).not.toBeCalled()
|
|
210
|
+
expect(events.user.passwordForceReset).not.toBeCalled()
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it("should be able to force reset password", async () => {
|
|
214
|
+
const user = await config.createUser()
|
|
215
|
+
jest.clearAllMocks()
|
|
216
|
+
|
|
217
|
+
user.forceResetPassword = true
|
|
218
|
+
user.password = "tempPassword"
|
|
219
|
+
await api.users.saveUser(user)
|
|
220
|
+
|
|
221
|
+
expect(events.user.created).not.toBeCalled()
|
|
222
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
223
|
+
expect(events.user.permissionBuilderAssigned).not.toBeCalled()
|
|
224
|
+
expect(events.user.permissionAdminAssigned).not.toBeCalled()
|
|
225
|
+
expect(events.user.passwordForceReset).toBeCalledTimes(1)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it("should be able to update a basic user to an admin user", async () => {
|
|
229
|
+
const user = await config.createUser()
|
|
230
|
+
jest.clearAllMocks()
|
|
231
|
+
|
|
232
|
+
await api.users.saveUser(structures.users.adminUser(user))
|
|
233
|
+
|
|
234
|
+
expect(events.user.created).not.toBeCalled()
|
|
235
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
236
|
+
expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1)
|
|
237
|
+
expect(events.user.permissionAdminAssigned).toBeCalledTimes(1)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it("should be able to update a basic user to a builder user", async () => {
|
|
241
|
+
const user = await config.createUser()
|
|
242
|
+
jest.clearAllMocks()
|
|
243
|
+
|
|
244
|
+
await api.users.saveUser(structures.users.builderUser(user))
|
|
245
|
+
|
|
246
|
+
expect(events.user.created).not.toBeCalled()
|
|
247
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
248
|
+
expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1)
|
|
249
|
+
expect(events.user.permissionAdminAssigned).not.toBeCalled()
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it("should be able to update an admin user to a basic user", async () => {
|
|
253
|
+
const user = await config.createUser(structures.users.adminUser())
|
|
254
|
+
jest.clearAllMocks()
|
|
255
|
+
user.admin!.global = false
|
|
256
|
+
user.builder!.global = false
|
|
257
|
+
|
|
258
|
+
await api.users.saveUser(user)
|
|
259
|
+
|
|
260
|
+
expect(events.user.created).not.toBeCalled()
|
|
261
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
262
|
+
expect(events.user.permissionAdminRemoved).toBeCalledTimes(1)
|
|
263
|
+
expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it("should be able to update an builder user to a basic user", async () => {
|
|
267
|
+
const user = await config.createUser(structures.users.builderUser())
|
|
268
|
+
jest.clearAllMocks()
|
|
269
|
+
user.builder!.global = false
|
|
270
|
+
|
|
271
|
+
await api.users.saveUser(user)
|
|
272
|
+
|
|
273
|
+
expect(events.user.created).not.toBeCalled()
|
|
274
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
275
|
+
expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1)
|
|
276
|
+
expect(events.user.permissionAdminRemoved).not.toBeCalled()
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it("should be able to assign app roles", async () => {
|
|
280
|
+
const user = await config.createUser()
|
|
281
|
+
jest.clearAllMocks()
|
|
282
|
+
user.roles = {
|
|
283
|
+
app_123: "role1",
|
|
284
|
+
app_456: "role2",
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await api.users.saveUser(user)
|
|
288
|
+
|
|
289
|
+
const savedUser = await config.getUser(user.email)
|
|
290
|
+
expect(events.user.created).not.toBeCalled()
|
|
291
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
292
|
+
expect(events.role.assigned).toBeCalledTimes(2)
|
|
293
|
+
expect(events.role.assigned).toBeCalledWith(savedUser, "role1")
|
|
294
|
+
expect(events.role.assigned).toBeCalledWith(savedUser, "role2")
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
it("should be able to unassign app roles", async () => {
|
|
298
|
+
let user = structures.users.user()
|
|
299
|
+
user.roles = {
|
|
300
|
+
app_123: "role1",
|
|
301
|
+
app_456: "role2",
|
|
302
|
+
}
|
|
303
|
+
user = await config.createUser(user)
|
|
304
|
+
jest.clearAllMocks()
|
|
305
|
+
user.roles = {}
|
|
306
|
+
|
|
307
|
+
await api.users.saveUser(user)
|
|
308
|
+
|
|
309
|
+
const savedUser = await config.getUser(user.email)
|
|
310
|
+
expect(events.user.created).not.toBeCalled()
|
|
311
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
312
|
+
expect(events.role.unassigned).toBeCalledTimes(2)
|
|
313
|
+
expect(events.role.unassigned).toBeCalledWith(savedUser, "role1")
|
|
314
|
+
expect(events.role.unassigned).toBeCalledWith(savedUser, "role2")
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it("should be able to update existing app roles", async () => {
|
|
318
|
+
let user = structures.users.user()
|
|
319
|
+
user.roles = {
|
|
320
|
+
app_123: "role1",
|
|
321
|
+
app_456: "role2",
|
|
322
|
+
}
|
|
323
|
+
user = await config.createUser(user)
|
|
324
|
+
jest.clearAllMocks()
|
|
325
|
+
user.roles = {
|
|
326
|
+
app_123: "role1",
|
|
327
|
+
app_456: "role2-edit",
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
await api.users.saveUser(user)
|
|
331
|
+
|
|
332
|
+
const savedUser = await config.getUser(user.email)
|
|
333
|
+
expect(events.user.created).not.toBeCalled()
|
|
334
|
+
expect(events.user.updated).toBeCalledTimes(1)
|
|
335
|
+
expect(events.role.unassigned).toBeCalledTimes(1)
|
|
336
|
+
expect(events.role.unassigned).toBeCalledWith(savedUser, "role2")
|
|
337
|
+
expect(events.role.assigned).toBeCalledTimes(1)
|
|
338
|
+
expect(events.role.assigned).toBeCalledWith(savedUser, "role2-edit")
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it("should not be able to update email address", async () => {
|
|
342
|
+
const email = "email@test.com"
|
|
343
|
+
const user = await config.createUser(structures.users.user({ email }))
|
|
344
|
+
user.email = "new@test.com"
|
|
345
|
+
|
|
346
|
+
const response = await api.users.saveUser(user, 400)
|
|
347
|
+
|
|
348
|
+
const dbUser = await config.getUser(email)
|
|
349
|
+
user.email = email
|
|
350
|
+
expect(user).toStrictEqual(dbUser)
|
|
351
|
+
expect(response.body.message).toBe("Email address cannot be changed")
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
describe("bulkDelete", () => {
|
|
356
|
+
it("should not be able to bulkDelete current user", async () => {
|
|
357
|
+
const user = await config.defaultUser!
|
|
358
|
+
const request = { userIds: [user._id!] }
|
|
359
|
+
|
|
360
|
+
const response = await api.users.bulkDeleteUsers(request, 400)
|
|
361
|
+
|
|
362
|
+
expect(response.body.message).toBe("Unable to delete self.")
|
|
363
|
+
expect(events.user.deleted).not.toBeCalled()
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it("should not be able to bulkDelete account owner", async () => {
|
|
367
|
+
const user = await config.createUser()
|
|
368
|
+
const account = structures.accounts.cloudAccount()
|
|
369
|
+
account.budibaseUserId = user._id!
|
|
370
|
+
mocks.accounts.getAccountByTenantId.mockReturnValue(account)
|
|
371
|
+
|
|
372
|
+
const request = { userIds: [user._id!] }
|
|
373
|
+
|
|
374
|
+
const response = await api.users.bulkDeleteUsers(request)
|
|
375
|
+
|
|
376
|
+
expect(response.body.successful.length).toBe(0)
|
|
377
|
+
expect(response.body.unsuccessful.length).toBe(1)
|
|
378
|
+
expect(response.body.unsuccessful[0].reason).toBe(
|
|
379
|
+
"Account holder cannot be deleted"
|
|
380
|
+
)
|
|
381
|
+
expect(response.body.unsuccessful[0]._id).toBe(user._id)
|
|
382
|
+
expect(events.user.deleted).not.toBeCalled()
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
it("should be able to bulk delete users", async () => {
|
|
386
|
+
const account = structures.accounts.cloudAccount()
|
|
387
|
+
mocks.accounts.getAccountByTenantId.mockReturnValue(account)
|
|
388
|
+
|
|
389
|
+
const builder = structures.users.builderUser()
|
|
390
|
+
const admin = structures.users.adminUser()
|
|
391
|
+
const user = structures.users.user()
|
|
392
|
+
const createdUsers = await api.users.bulkCreateUsers([
|
|
393
|
+
builder,
|
|
394
|
+
admin,
|
|
395
|
+
user,
|
|
396
|
+
])
|
|
397
|
+
const request = { userIds: createdUsers.successful.map(u => u._id!) }
|
|
398
|
+
|
|
399
|
+
const response = await api.users.bulkDeleteUsers(request)
|
|
400
|
+
|
|
401
|
+
expect(response.body.successful.length).toBe(3)
|
|
402
|
+
expect(response.body.unsuccessful.length).toBe(0)
|
|
403
|
+
expect(events.user.deleted).toBeCalledTimes(3)
|
|
404
|
+
expect(events.user.permissionAdminRemoved).toBeCalledTimes(1)
|
|
405
|
+
expect(events.user.permissionBuilderRemoved).toBeCalledTimes(2)
|
|
406
|
+
})
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
describe("destroy", () => {
|
|
410
|
+
it("should be able to destroy a basic user", async () => {
|
|
411
|
+
const user = await config.createUser()
|
|
412
|
+
jest.clearAllMocks()
|
|
413
|
+
|
|
414
|
+
await api.users.deleteUser(user._id!)
|
|
415
|
+
|
|
416
|
+
expect(events.user.deleted).toBeCalledTimes(1)
|
|
417
|
+
expect(events.user.permissionBuilderRemoved).not.toBeCalled()
|
|
418
|
+
expect(events.user.permissionAdminRemoved).not.toBeCalled()
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
it("should be able to destroy an admin user", async () => {
|
|
422
|
+
const user = await config.createUser(structures.users.adminUser())
|
|
423
|
+
jest.clearAllMocks()
|
|
424
|
+
|
|
425
|
+
await api.users.deleteUser(user._id!)
|
|
426
|
+
|
|
427
|
+
expect(events.user.deleted).toBeCalledTimes(1)
|
|
428
|
+
expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1)
|
|
429
|
+
expect(events.user.permissionAdminRemoved).toBeCalledTimes(1)
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
it("should be able to destroy a builder user", async () => {
|
|
433
|
+
const user = await config.createUser(structures.users.builderUser())
|
|
434
|
+
jest.clearAllMocks()
|
|
435
|
+
|
|
436
|
+
await api.users.deleteUser(user._id!)
|
|
437
|
+
|
|
438
|
+
expect(events.user.deleted).toBeCalledTimes(1)
|
|
439
|
+
expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1)
|
|
440
|
+
expect(events.user.permissionAdminRemoved).not.toBeCalled()
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
it("should not be able to destroy account owner", async () => {
|
|
444
|
+
const user = await config.createUser()
|
|
445
|
+
const account = structures.accounts.cloudAccount()
|
|
446
|
+
mocks.accounts.getAccount.mockReturnValueOnce(account)
|
|
447
|
+
|
|
448
|
+
const response = await api.users.deleteUser(user._id!, 400)
|
|
449
|
+
|
|
450
|
+
expect(response.body.message).toBe("Account holder cannot be deleted")
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
it("should not be able to destroy account owner as account owner", async () => {
|
|
454
|
+
const user = await config.defaultUser!
|
|
455
|
+
const account = structures.accounts.cloudAccount()
|
|
456
|
+
account.email = user.email
|
|
457
|
+
mocks.accounts.getAccount.mockReturnValueOnce(account)
|
|
458
|
+
|
|
459
|
+
const response = await api.users.deleteUser(user._id!, 400)
|
|
460
|
+
|
|
461
|
+
expect(response.body.message).toBe("Unable to delete self.")
|
|
462
|
+
})
|
|
463
|
+
})
|
|
464
|
+
})
|
package/src/api/routes/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const statusRoutes = require("./system/status")
|
|
|
12
12
|
const selfRoutes = require("./global/self")
|
|
13
13
|
const licenseRoutes = require("./global/license")
|
|
14
14
|
const migrationRoutes = require("./system/migrations")
|
|
15
|
+
const accountRoutes = require("./system/accounts")
|
|
15
16
|
|
|
16
17
|
let userGroupRoutes = api.groups
|
|
17
18
|
exports.routes = [
|
|
@@ -29,4 +30,5 @@ exports.routes = [
|
|
|
29
30
|
licenseRoutes,
|
|
30
31
|
userGroupRoutes,
|
|
31
32
|
migrationRoutes,
|
|
33
|
+
accountRoutes,
|
|
32
34
|
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Router from "@koa/router"
|
|
2
|
+
import * as controller from "../../controllers/system/accounts"
|
|
3
|
+
import { middleware } from "@budibase/backend-core"
|
|
4
|
+
|
|
5
|
+
const router = new Router()
|
|
6
|
+
|
|
7
|
+
router
|
|
8
|
+
.put(
|
|
9
|
+
"/api/system/accounts/:accountId/metadata",
|
|
10
|
+
middleware.internalApi,
|
|
11
|
+
controller.save
|
|
12
|
+
)
|
|
13
|
+
.delete(
|
|
14
|
+
"/api/system/accounts/:accountId/metadata",
|
|
15
|
+
middleware.internalApi,
|
|
16
|
+
controller.destroy
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export = router
|