@budibase/worker 1.0.199 → 1.0.200-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/Dockerfile +3 -0
- package/nodemon.json +1 -1
- package/package.json +56 -46
- package/scripts/dev/manage.js +2 -0
- package/scripts/jestSetup.js +9 -0
- package/scripts/load/users.js +4 -4
- package/src/api/controllers/global/auth.ts +21 -14
- package/src/api/controllers/global/configs.js +126 -2
- package/src/api/controllers/global/roles.js +3 -3
- package/src/api/controllers/global/self.js +12 -3
- package/src/api/controllers/global/users.ts +48 -88
- package/src/api/routes/global/self.js +2 -2
- package/src/api/routes/global/users.js +3 -3
- package/src/api/routes/tests/auth.spec.js +38 -16
- package/src/api/routes/tests/configs.spec.js +223 -7
- package/src/api/routes/tests/email.spec.js +8 -7
- package/src/api/routes/tests/realEmail.spec.js +5 -5
- package/src/api/routes/tests/self.spec.js +58 -0
- package/src/api/routes/tests/users.spec.js +299 -13
- package/src/api/routes/validation/index.ts +1 -0
- package/src/api/{utilities/validation.js → routes/validation/users.ts} +4 -4
- package/src/index.ts +2 -0
- package/src/sdk/index.ts +1 -0
- package/src/sdk/users/events.ts +161 -0
- package/src/sdk/users/index.ts +1 -0
- package/src/sdk/users/users.ts +201 -0
- package/src/{api/routes/tests/utilities → tests}/TestConfiguration.js +68 -111
- package/src/tests/controllers.js +7 -0
- package/src/tests/index.js +12 -0
- package/src/tests/mocks/email.js +10 -0
- package/src/tests/mocks/index.js +5 -0
- package/src/tests/structures/configs.js +76 -0
- package/src/tests/structures/index.js +12 -0
- package/src/tests/structures/users.ts +28 -0
- package/tsconfig.build.json +25 -0
- package/tsconfig.json +21 -15
- package/src/api/routes/tests/utilities/controllers.js +0 -7
- package/src/api/routes/tests/utilities/index.js +0 -41
- package/src/api/routes/tests/utilities/structures.js +0 -2
- package/src/api/utilities/index.js +0 -33
|
@@ -1,35 +1,22 @@
|
|
|
1
|
-
const {
|
|
2
|
-
getGlobalUserParams,
|
|
3
|
-
StaticDatabases,
|
|
4
|
-
} = require("@budibase/backend-core/db")
|
|
5
|
-
const { getGlobalUserByEmail } = require("@budibase/backend-core/utils")
|
|
6
|
-
const { user: userCache } = require("@budibase/backend-core/cache")
|
|
7
|
-
const { invalidateSessions } = require("@budibase/backend-core/sessions")
|
|
8
|
-
const accounts = require("@budibase/backend-core/accounts")
|
|
9
|
-
const {
|
|
10
|
-
getGlobalDB,
|
|
11
|
-
doWithGlobalDB,
|
|
12
|
-
getTenantId,
|
|
13
|
-
getTenantUser,
|
|
14
|
-
doesTenantExist,
|
|
15
|
-
doInTenant,
|
|
16
|
-
} = require("@budibase/backend-core/tenancy")
|
|
17
|
-
const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision")
|
|
18
|
-
const { errors } = require("@budibase/backend-core")
|
|
19
|
-
const { CacheKeys, bustCache } = require("@budibase/backend-core/cache")
|
|
20
|
-
import env from "../../../environment"
|
|
21
|
-
import { syncUserInApps } from "../../../utilities/appService"
|
|
22
|
-
import { quotas, users } from "@budibase/pro"
|
|
23
|
-
import { allUsers, getUser } from "../../utilities"
|
|
24
1
|
import { EmailTemplatePurpose } from "../../../constants"
|
|
25
2
|
import { checkInviteCode } from "../../../utilities/redis"
|
|
26
3
|
import { sendEmail } from "../../../utilities/email"
|
|
4
|
+
import { users } from "../../../sdk"
|
|
5
|
+
import env from "../../../environment"
|
|
6
|
+
import { User, CloudAccount } from "@budibase/types"
|
|
7
|
+
import {
|
|
8
|
+
events,
|
|
9
|
+
errors,
|
|
10
|
+
accounts,
|
|
11
|
+
db as dbUtils,
|
|
12
|
+
users as usersCore,
|
|
13
|
+
tenancy,
|
|
14
|
+
cache,
|
|
15
|
+
} from "@budibase/backend-core"
|
|
27
16
|
|
|
28
17
|
export const save = async (ctx: any) => {
|
|
29
18
|
try {
|
|
30
|
-
const user
|
|
31
|
-
// let server know to sync user
|
|
32
|
-
await syncUserInApps(user._id)
|
|
19
|
+
const user = await users.save(ctx.request.body)
|
|
33
20
|
ctx.body = user
|
|
34
21
|
} catch (err: any) {
|
|
35
22
|
ctx.throw(err.status || 400, err)
|
|
@@ -42,38 +29,22 @@ const parseBooleanParam = (param: any) => {
|
|
|
42
29
|
|
|
43
30
|
export const adminUser = async (ctx: any) => {
|
|
44
31
|
const { email, password, tenantId } = ctx.request.body
|
|
45
|
-
await doInTenant(tenantId, async () => {
|
|
32
|
+
await tenancy.doInTenant(tenantId, async () => {
|
|
46
33
|
// account portal sends a pre-hashed password - honour param to prevent double hashing
|
|
47
34
|
const hashPassword = parseBooleanParam(ctx.request.query.hashPassword)
|
|
48
35
|
// account portal sends no password for SSO users
|
|
49
36
|
const requirePassword = parseBooleanParam(ctx.request.query.requirePassword)
|
|
50
37
|
|
|
51
|
-
if (await doesTenantExist(tenantId)) {
|
|
38
|
+
if (await tenancy.doesTenantExist(tenantId)) {
|
|
52
39
|
ctx.throw(403, "Organisation already exists.")
|
|
53
40
|
}
|
|
54
41
|
|
|
55
|
-
const response = await doWithGlobalDB(tenantId, async (db: any) => {
|
|
56
|
-
|
|
57
|
-
getGlobalUserParams(null, {
|
|
42
|
+
const response = await tenancy.doWithGlobalDB(tenantId, async (db: any) => {
|
|
43
|
+
return db.allDocs(
|
|
44
|
+
dbUtils.getGlobalUserParams(null, {
|
|
58
45
|
include_docs: true,
|
|
59
46
|
})
|
|
60
47
|
)
|
|
61
|
-
// write usage quotas for cloud
|
|
62
|
-
if (!env.SELF_HOSTED) {
|
|
63
|
-
// could be a scenario where it exists, make sure its clean
|
|
64
|
-
try {
|
|
65
|
-
const usageQuota = await db.get(
|
|
66
|
-
StaticDatabases.GLOBAL.docs.usageQuota
|
|
67
|
-
)
|
|
68
|
-
if (usageQuota) {
|
|
69
|
-
await db.remove(usageQuota._id, usageQuota._rev)
|
|
70
|
-
}
|
|
71
|
-
} catch (err) {
|
|
72
|
-
// don't worry about errors
|
|
73
|
-
}
|
|
74
|
-
await db.put(quotas.generateNewQuotaUsage())
|
|
75
|
-
}
|
|
76
|
-
return response
|
|
77
48
|
})
|
|
78
49
|
|
|
79
50
|
if (response.rows.some((row: any) => row.doc.admin)) {
|
|
@@ -83,7 +54,7 @@ export const adminUser = async (ctx: any) => {
|
|
|
83
54
|
)
|
|
84
55
|
}
|
|
85
56
|
|
|
86
|
-
const user = {
|
|
57
|
+
const user: User = {
|
|
87
58
|
email: email,
|
|
88
59
|
password: password,
|
|
89
60
|
createdAt: Date.now(),
|
|
@@ -97,13 +68,19 @@ export const adminUser = async (ctx: any) => {
|
|
|
97
68
|
tenantId,
|
|
98
69
|
}
|
|
99
70
|
try {
|
|
100
|
-
const finalUser = await users.save(
|
|
101
|
-
user,
|
|
102
|
-
tenantId,
|
|
71
|
+
const finalUser = await users.save(user, {
|
|
103
72
|
hashPassword,
|
|
104
|
-
requirePassword
|
|
105
|
-
)
|
|
106
|
-
await bustCache(CacheKeys.CHECKLIST)
|
|
73
|
+
requirePassword,
|
|
74
|
+
})
|
|
75
|
+
await cache.bustCache(cache.CacheKeys.CHECKLIST)
|
|
76
|
+
|
|
77
|
+
// events
|
|
78
|
+
let account: CloudAccount | undefined
|
|
79
|
+
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
80
|
+
account = await accounts.getAccountByTenantId(tenantId)
|
|
81
|
+
}
|
|
82
|
+
await events.identification.identifyTenantGroup(tenantId, account)
|
|
83
|
+
|
|
107
84
|
ctx.body = finalUser
|
|
108
85
|
} catch (err: any) {
|
|
109
86
|
ctx.throw(err.status || 400, err)
|
|
@@ -112,37 +89,16 @@ export const adminUser = async (ctx: any) => {
|
|
|
112
89
|
}
|
|
113
90
|
|
|
114
91
|
export const destroy = async (ctx: any) => {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
119
|
-
// root account holder can't be deleted from inside budibase
|
|
120
|
-
const email = dbUser.email
|
|
121
|
-
const account = await accounts.getAccount(email)
|
|
122
|
-
if (account) {
|
|
123
|
-
if (email === ctx.user.email) {
|
|
124
|
-
ctx.throw(400, 'Please visit "Account" to delete this user')
|
|
125
|
-
} else {
|
|
126
|
-
ctx.throw(400, "Account holder cannot be deleted")
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
await removeUserFromInfoDB(dbUser)
|
|
132
|
-
await db.remove(dbUser._id, dbUser._rev)
|
|
133
|
-
await quotas.removeUser(dbUser)
|
|
134
|
-
await userCache.invalidateUser(dbUser._id)
|
|
135
|
-
await invalidateSessions(dbUser._id)
|
|
136
|
-
// let server know to sync user
|
|
137
|
-
await syncUserInApps(dbUser._id)
|
|
92
|
+
const id = ctx.params.id
|
|
93
|
+
await users.destroy(id, ctx.user)
|
|
138
94
|
ctx.body = {
|
|
139
|
-
message: `User ${
|
|
95
|
+
message: `User ${id} deleted.`,
|
|
140
96
|
}
|
|
141
97
|
}
|
|
142
98
|
|
|
143
99
|
// called internally by app server user fetch
|
|
144
100
|
export const fetch = async (ctx: any) => {
|
|
145
|
-
const all = await allUsers()
|
|
101
|
+
const all = await users.allUsers()
|
|
146
102
|
// user hashed password shouldn't ever be returned
|
|
147
103
|
for (let user of all) {
|
|
148
104
|
if (user) {
|
|
@@ -154,12 +110,12 @@ export const fetch = async (ctx: any) => {
|
|
|
154
110
|
|
|
155
111
|
// called internally by app server user find
|
|
156
112
|
export const find = async (ctx: any) => {
|
|
157
|
-
ctx.body = await getUser(ctx.params.id)
|
|
113
|
+
ctx.body = await users.getUser(ctx.params.id)
|
|
158
114
|
}
|
|
159
115
|
|
|
160
116
|
export const tenantUserLookup = async (ctx: any) => {
|
|
161
117
|
const id = ctx.params.id
|
|
162
|
-
const user = await getTenantUser(id)
|
|
118
|
+
const user = await tenancy.getTenantUser(id)
|
|
163
119
|
if (user) {
|
|
164
120
|
ctx.body = user
|
|
165
121
|
} else {
|
|
@@ -169,14 +125,14 @@ export const tenantUserLookup = async (ctx: any) => {
|
|
|
169
125
|
|
|
170
126
|
export const invite = async (ctx: any) => {
|
|
171
127
|
let { email, userInfo } = ctx.request.body
|
|
172
|
-
const existing = await getGlobalUserByEmail(email)
|
|
128
|
+
const existing = await usersCore.getGlobalUserByEmail(email)
|
|
173
129
|
if (existing) {
|
|
174
130
|
ctx.throw(400, "Email address already in use.")
|
|
175
131
|
}
|
|
176
132
|
if (!userInfo) {
|
|
177
133
|
userInfo = {}
|
|
178
134
|
}
|
|
179
|
-
userInfo.tenantId = getTenantId()
|
|
135
|
+
userInfo.tenantId = tenancy.getTenantId()
|
|
180
136
|
const opts: any = {
|
|
181
137
|
subject: "{{ company }} platform invitation",
|
|
182
138
|
info: userInfo,
|
|
@@ -185,6 +141,7 @@ export const invite = async (ctx: any) => {
|
|
|
185
141
|
ctx.body = {
|
|
186
142
|
message: "Invitation has been sent.",
|
|
187
143
|
}
|
|
144
|
+
await events.user.invited()
|
|
188
145
|
}
|
|
189
146
|
|
|
190
147
|
export const inviteAccept = async (ctx: any) => {
|
|
@@ -192,16 +149,19 @@ export const inviteAccept = async (ctx: any) => {
|
|
|
192
149
|
try {
|
|
193
150
|
// info is an extension of the user object that was stored by global
|
|
194
151
|
const { email, info }: any = await checkInviteCode(inviteCode)
|
|
195
|
-
ctx.body = await
|
|
196
|
-
{
|
|
152
|
+
ctx.body = await tenancy.doInTenant(info.tenantId, async () => {
|
|
153
|
+
const saved = await users.save({
|
|
197
154
|
firstName,
|
|
198
155
|
lastName,
|
|
199
156
|
password,
|
|
200
157
|
email,
|
|
201
158
|
...info,
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
159
|
+
})
|
|
160
|
+
const db = tenancy.getGlobalDB()
|
|
161
|
+
const user = await db.get(saved._id)
|
|
162
|
+
await events.user.inviteAccepted(user)
|
|
163
|
+
return saved
|
|
164
|
+
})
|
|
205
165
|
} catch (err: any) {
|
|
206
166
|
if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
|
|
207
167
|
// explicitly re-throw limit exceeded errors
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const Router = require("@koa/router")
|
|
2
2
|
const controller = require("../../controllers/global/self")
|
|
3
3
|
const builderOnly = require("../../../middleware/builderOnly")
|
|
4
|
-
const {
|
|
4
|
+
const { users } = require("../validation")
|
|
5
5
|
|
|
6
6
|
const router = Router()
|
|
7
7
|
|
|
@@ -11,7 +11,7 @@ router
|
|
|
11
11
|
.get("/api/global/self", controller.getSelf)
|
|
12
12
|
.post(
|
|
13
13
|
"/api/global/self",
|
|
14
|
-
buildUserSaveValidation(true),
|
|
14
|
+
users.buildUserSaveValidation(true),
|
|
15
15
|
controller.updateSelf
|
|
16
16
|
)
|
|
17
17
|
|
|
@@ -4,7 +4,7 @@ const joiValidator = require("../../../middleware/joi-validator")
|
|
|
4
4
|
const adminOnly = require("../../../middleware/adminOnly")
|
|
5
5
|
const Joi = require("joi")
|
|
6
6
|
const cloudRestricted = require("../../../middleware/cloudRestricted")
|
|
7
|
-
const {
|
|
7
|
+
const { users } = require("../validation")
|
|
8
8
|
const selfController = require("../../controllers/global/self")
|
|
9
9
|
const builderOrAdmin = require("../../../middleware/builderOrAdmin")
|
|
10
10
|
|
|
@@ -42,7 +42,7 @@ router
|
|
|
42
42
|
.post(
|
|
43
43
|
"/api/global/users",
|
|
44
44
|
adminOnly,
|
|
45
|
-
buildUserSaveValidation(),
|
|
45
|
+
users.buildUserSaveValidation(),
|
|
46
46
|
controller.save
|
|
47
47
|
)
|
|
48
48
|
.get("/api/global/users", builderOrAdmin, controller.fetch)
|
|
@@ -73,7 +73,7 @@ router
|
|
|
73
73
|
.get("/api/global/users/self", selfController.getSelf)
|
|
74
74
|
.post(
|
|
75
75
|
"/api/global/users/self",
|
|
76
|
-
buildUserSaveValidation(true),
|
|
76
|
+
users.buildUserSaveValidation(true),
|
|
77
77
|
selfController.updateSelf
|
|
78
78
|
)
|
|
79
79
|
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
jest.mock("nodemailer")
|
|
2
|
-
const
|
|
3
|
-
const sendMailMock =
|
|
2
|
+
const { config, request, mocks, structures } = require("../../../tests")
|
|
3
|
+
const sendMailMock = mocks.email.mock()
|
|
4
|
+
const { events } = require("@budibase/backend-core")
|
|
4
5
|
|
|
5
|
-
const TENANT_ID =
|
|
6
|
+
const TENANT_ID = structures.TENANT_ID
|
|
6
7
|
|
|
7
8
|
describe("/api/global/auth", () => {
|
|
8
|
-
let request = setup.getRequest()
|
|
9
|
-
let config = setup.getConfig()
|
|
10
|
-
let code
|
|
11
9
|
|
|
12
10
|
beforeAll(async () => {
|
|
13
|
-
await config.
|
|
11
|
+
await config.beforeAll()
|
|
14
12
|
})
|
|
15
13
|
|
|
16
|
-
afterAll(
|
|
14
|
+
afterAll(async () => {
|
|
15
|
+
await config.afterAll()
|
|
16
|
+
})
|
|
17
17
|
|
|
18
18
|
afterEach(() => {
|
|
19
19
|
jest.clearAllMocks()
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
// initially configure settings
|
|
22
|
+
const requestPasswordReset = async () => {
|
|
24
23
|
await config.saveSmtpConfig()
|
|
25
24
|
await config.saveSettingsConfig()
|
|
26
25
|
await config.createUser()
|
|
@@ -31,16 +30,37 @@ describe("/api/global/auth", () => {
|
|
|
31
30
|
})
|
|
32
31
|
.expect("Content-Type", /json/)
|
|
33
32
|
.expect(200)
|
|
34
|
-
expect(res.body).toEqual({ message: "Please check your email for a reset link." })
|
|
35
|
-
expect(sendMailMock).toHaveBeenCalled()
|
|
36
33
|
const emailCall = sendMailMock.mock.calls[0][0]
|
|
37
|
-
// after this URL there should be a code
|
|
38
34
|
const parts = emailCall.html.split(`http://localhost:10000/builder/auth/reset?code=`)
|
|
39
|
-
code = parts[1].split("\"")[0].split("&")[0]
|
|
35
|
+
const code = parts[1].split("\"")[0].split("&")[0]
|
|
36
|
+
return { code, res }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
it("should logout", async () => {
|
|
40
|
+
await request
|
|
41
|
+
.post("/api/global/auth/logout")
|
|
42
|
+
.set(config.defaultHeaders())
|
|
43
|
+
.expect(200)
|
|
44
|
+
expect(events.auth.logout).toBeCalledTimes(1)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("should be able to generate password reset email", async () => {
|
|
48
|
+
const { res, code } = await requestPasswordReset()
|
|
49
|
+
const user = await config.getUser("test@test.com")
|
|
50
|
+
|
|
51
|
+
expect(res.body).toEqual({ message: "Please check your email for a reset link." })
|
|
52
|
+
expect(sendMailMock).toHaveBeenCalled()
|
|
53
|
+
|
|
40
54
|
expect(code).toBeDefined()
|
|
55
|
+
expect(events.user.passwordResetRequested).toBeCalledTimes(1)
|
|
56
|
+
expect(events.user.passwordResetRequested).toBeCalledWith(user)
|
|
41
57
|
})
|
|
42
58
|
|
|
43
59
|
it("should allow resetting user password with code", async () => {
|
|
60
|
+
const { code } = await requestPasswordReset()
|
|
61
|
+
const user = await config.getUser("test@test.com")
|
|
62
|
+
delete user.password
|
|
63
|
+
|
|
44
64
|
const res = await request
|
|
45
65
|
.post(`/api/global/auth/${TENANT_ID}/reset/update`)
|
|
46
66
|
.send({
|
|
@@ -50,14 +70,16 @@ describe("/api/global/auth", () => {
|
|
|
50
70
|
.expect("Content-Type", /json/)
|
|
51
71
|
.expect(200)
|
|
52
72
|
expect(res.body).toEqual({ message: "password reset successfully." })
|
|
73
|
+
expect(events.user.passwordReset).toBeCalledTimes(1)
|
|
74
|
+
expect(events.user.passwordReset).toBeCalledWith(user)
|
|
53
75
|
})
|
|
54
76
|
|
|
55
77
|
describe("oidc", () => {
|
|
56
78
|
const auth = require("@budibase/backend-core/auth")
|
|
57
79
|
|
|
58
80
|
// mock the oidc strategy implementation and return value
|
|
59
|
-
strategyFactory = jest.fn()
|
|
60
|
-
mockStrategyReturn = jest.fn()
|
|
81
|
+
let strategyFactory = jest.fn()
|
|
82
|
+
let mockStrategyReturn = jest.fn()
|
|
61
83
|
strategyFactory.mockReturnValue(mockStrategyReturn)
|
|
62
84
|
auth.oidc.strategyFactory = strategyFactory
|
|
63
85
|
|
|
@@ -1,17 +1,233 @@
|
|
|
1
1
|
// mock the email system
|
|
2
2
|
jest.mock("nodemailer")
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const { config, structures, mocks, request } = require("../../../tests")
|
|
4
|
+
mocks.email.mock()
|
|
5
|
+
const { Configs } = require("@budibase/backend-core/constants")
|
|
6
|
+
const { events } = require("@budibase/backend-core")
|
|
5
7
|
|
|
6
|
-
describe("
|
|
7
|
-
let request = setup.getRequest()
|
|
8
|
-
let config = setup.getConfig()
|
|
8
|
+
describe("configs", () => {
|
|
9
9
|
|
|
10
10
|
beforeAll(async () => {
|
|
11
|
-
await config.
|
|
11
|
+
await config.beforeAll()
|
|
12
12
|
})
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
await config.afterAll()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe("post /api/global/configs", () => {
|
|
23
|
+
|
|
24
|
+
const saveConfig = async (conf, _id, _rev) => {
|
|
25
|
+
const data = {
|
|
26
|
+
...conf,
|
|
27
|
+
_id,
|
|
28
|
+
_rev
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const res = await request
|
|
32
|
+
.post(`/api/global/configs`)
|
|
33
|
+
.send(data)
|
|
34
|
+
.set(config.defaultHeaders())
|
|
35
|
+
.expect("Content-Type", /json/)
|
|
36
|
+
.expect(200)
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
...data,
|
|
40
|
+
...res.body
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("google", () => {
|
|
45
|
+
const saveGoogleConfig = async (conf, _id, _rev) => {
|
|
46
|
+
const googleConfig = structures.configs.google(conf)
|
|
47
|
+
return saveConfig(googleConfig, _id, _rev)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("create", () => {
|
|
51
|
+
it ("should create activated google config", async () => {
|
|
52
|
+
await saveGoogleConfig()
|
|
53
|
+
expect(events.auth.SSOCreated).toBeCalledTimes(1)
|
|
54
|
+
expect(events.auth.SSOCreated).toBeCalledWith(Configs.GOOGLE)
|
|
55
|
+
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
56
|
+
expect(events.auth.SSOActivated).toBeCalledTimes(1)
|
|
57
|
+
expect(events.auth.SSOActivated).toBeCalledWith(Configs.GOOGLE)
|
|
58
|
+
await config.deleteConfig(Configs.GOOGLE)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it ("should create deactivated google config", async () => {
|
|
62
|
+
await saveGoogleConfig({ activated: false })
|
|
63
|
+
expect(events.auth.SSOCreated).toBeCalledTimes(1)
|
|
64
|
+
expect(events.auth.SSOCreated).toBeCalledWith(Configs.GOOGLE)
|
|
65
|
+
expect(events.auth.SSOActivated).not.toBeCalled()
|
|
66
|
+
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
67
|
+
await config.deleteConfig(Configs.GOOGLE)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe("update", () => {
|
|
72
|
+
it ("should update google config to deactivated", async () => {
|
|
73
|
+
const googleConf = await saveGoogleConfig()
|
|
74
|
+
jest.clearAllMocks()
|
|
75
|
+
await saveGoogleConfig({ ...googleConf.config, activated: false }, googleConf._id, googleConf._rev)
|
|
76
|
+
expect(events.auth.SSOUpdated).toBeCalledTimes(1)
|
|
77
|
+
expect(events.auth.SSOUpdated).toBeCalledWith(Configs.GOOGLE)
|
|
78
|
+
expect(events.auth.SSOActivated).not.toBeCalled()
|
|
79
|
+
expect(events.auth.SSODeactivated).toBeCalledTimes(1)
|
|
80
|
+
expect(events.auth.SSODeactivated).toBeCalledWith(Configs.GOOGLE)
|
|
81
|
+
await config.deleteConfig(Configs.GOOGLE)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it ("should update google config to activated", async () => {
|
|
85
|
+
const googleConf = await saveGoogleConfig({ activated: false })
|
|
86
|
+
jest.clearAllMocks()
|
|
87
|
+
await saveGoogleConfig({ ...googleConf.config, activated: true}, googleConf._id, googleConf._rev)
|
|
88
|
+
expect(events.auth.SSOUpdated).toBeCalledTimes(1)
|
|
89
|
+
expect(events.auth.SSOUpdated).toBeCalledWith(Configs.GOOGLE)
|
|
90
|
+
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
91
|
+
expect(events.auth.SSOActivated).toBeCalledTimes(1)
|
|
92
|
+
expect(events.auth.SSOActivated).toBeCalledWith(Configs.GOOGLE)
|
|
93
|
+
await config.deleteConfig(Configs.GOOGLE)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe("oidc", () => {
|
|
99
|
+
const saveOIDCConfig = async (conf, _id, _rev) => {
|
|
100
|
+
const oidcConfig = structures.configs.oidc(conf)
|
|
101
|
+
return saveConfig(oidcConfig, _id, _rev)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
describe("create", () => {
|
|
105
|
+
it ("should create activated OIDC config", async () => {
|
|
106
|
+
await saveOIDCConfig()
|
|
107
|
+
expect(events.auth.SSOCreated).toBeCalledTimes(1)
|
|
108
|
+
expect(events.auth.SSOCreated).toBeCalledWith(Configs.OIDC)
|
|
109
|
+
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
110
|
+
expect(events.auth.SSOActivated).toBeCalledTimes(1)
|
|
111
|
+
expect(events.auth.SSOActivated).toBeCalledWith(Configs.OIDC)
|
|
112
|
+
await config.deleteConfig(Configs.OIDC)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it ("should create deactivated OIDC config", async () => {
|
|
116
|
+
await saveOIDCConfig({ activated: false })
|
|
117
|
+
expect(events.auth.SSOCreated).toBeCalledTimes(1)
|
|
118
|
+
expect(events.auth.SSOCreated).toBeCalledWith(Configs.OIDC)
|
|
119
|
+
expect(events.auth.SSOActivated).not.toBeCalled()
|
|
120
|
+
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
121
|
+
await config.deleteConfig(Configs.OIDC)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe("update", () => {
|
|
126
|
+
it ("should update OIDC config to deactivated", async () => {
|
|
127
|
+
const oidcConf = await saveOIDCConfig()
|
|
128
|
+
jest.clearAllMocks()
|
|
129
|
+
await saveOIDCConfig({ ...oidcConf.config.configs[0], activated: false }, oidcConf._id, oidcConf._rev)
|
|
130
|
+
expect(events.auth.SSOUpdated).toBeCalledTimes(1)
|
|
131
|
+
expect(events.auth.SSOUpdated).toBeCalledWith(Configs.OIDC)
|
|
132
|
+
expect(events.auth.SSOActivated).not.toBeCalled()
|
|
133
|
+
expect(events.auth.SSODeactivated).toBeCalledTimes(1)
|
|
134
|
+
expect(events.auth.SSODeactivated).toBeCalledWith(Configs.OIDC)
|
|
135
|
+
await config.deleteConfig(Configs.OIDC)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it ("should update OIDC config to activated", async () => {
|
|
139
|
+
const oidcConf = await saveOIDCConfig({ activated: false })
|
|
140
|
+
jest.clearAllMocks()
|
|
141
|
+
await saveOIDCConfig({ ...oidcConf.config.configs[0], activated: true}, oidcConf._id, oidcConf._rev)
|
|
142
|
+
expect(events.auth.SSOUpdated).toBeCalledTimes(1)
|
|
143
|
+
expect(events.auth.SSOUpdated).toBeCalledWith(Configs.OIDC)
|
|
144
|
+
expect(events.auth.SSODeactivated).not.toBeCalled()
|
|
145
|
+
expect(events.auth.SSOActivated).toBeCalledTimes(1)
|
|
146
|
+
expect(events.auth.SSOActivated).toBeCalledWith(Configs.OIDC)
|
|
147
|
+
await config.deleteConfig(Configs.OIDC)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe("smtp", () => {
|
|
154
|
+
const saveSMTPConfig = async (conf, _id, _rev) => {
|
|
155
|
+
const smtpConfig = structures.configs.smtp(conf)
|
|
156
|
+
return saveConfig(smtpConfig, _id, _rev)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
describe("create", () => {
|
|
160
|
+
it ("should create SMTP config", async () => {
|
|
161
|
+
await config.deleteConfig(Configs.SMTP)
|
|
162
|
+
await saveSMTPConfig()
|
|
163
|
+
expect(events.email.SMTPUpdated).not.toBeCalled()
|
|
164
|
+
expect(events.email.SMTPCreated).toBeCalledTimes(1)
|
|
165
|
+
await config.deleteConfig(Configs.SMTP)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
describe("update", () => {
|
|
170
|
+
it ("should update SMTP config", async () => {
|
|
171
|
+
const smtpConf = await saveSMTPConfig()
|
|
172
|
+
jest.clearAllMocks()
|
|
173
|
+
await saveSMTPConfig(smtpConf.config, smtpConf._id, smtpConf._rev)
|
|
174
|
+
expect(events.email.SMTPCreated).not.toBeCalled()
|
|
175
|
+
expect(events.email.SMTPUpdated).toBeCalledTimes(1)
|
|
176
|
+
await config.deleteConfig(Configs.SMTP)
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
describe("settings", () => {
|
|
182
|
+
const saveSettingsConfig = async (conf, _id, _rev) => {
|
|
183
|
+
const settingsConfig = structures.configs.settings(conf)
|
|
184
|
+
return saveConfig(settingsConfig, _id, _rev)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
describe("create", () => {
|
|
188
|
+
it ("should create settings config with default settings", async () => {
|
|
189
|
+
await config.deleteConfig(Configs.SETTINGS)
|
|
190
|
+
|
|
191
|
+
await saveSettingsConfig()
|
|
192
|
+
|
|
193
|
+
expect(events.org.nameUpdated).not.toBeCalled()
|
|
194
|
+
expect(events.org.logoUpdated).not.toBeCalled()
|
|
195
|
+
expect(events.org.platformURLUpdated).not.toBeCalled()
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it ("should create settings config with non-default settings", async () => {
|
|
199
|
+
await config.deleteConfig(Configs.SETTINGS)
|
|
200
|
+
const conf = {
|
|
201
|
+
company: "acme",
|
|
202
|
+
logoUrl: "http://example.com",
|
|
203
|
+
platformUrl: "http://example.com"
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
await saveSettingsConfig(conf)
|
|
207
|
+
|
|
208
|
+
expect(events.org.nameUpdated).toBeCalledTimes(1)
|
|
209
|
+
expect(events.org.logoUpdated).toBeCalledTimes(1)
|
|
210
|
+
expect(events.org.platformURLUpdated).toBeCalledTimes(1)
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe("update", () => {
|
|
215
|
+
it ("should update settings config", async () => {
|
|
216
|
+
await config.deleteConfig(Configs.SETTINGS)
|
|
217
|
+
const settingsConfig = await saveSettingsConfig()
|
|
218
|
+
settingsConfig.config.company = "acme"
|
|
219
|
+
settingsConfig.config.logoUrl = "http://example.com"
|
|
220
|
+
settingsConfig.config.platformUrl = "http://example.com"
|
|
221
|
+
|
|
222
|
+
await saveSettingsConfig(settingsConfig.config, settingsConfig._id, settingsConfig._rev)
|
|
223
|
+
|
|
224
|
+
expect(events.org.nameUpdated).toBeCalledTimes(1)
|
|
225
|
+
expect(events.org.logoUpdated).toBeCalledTimes(1)
|
|
226
|
+
expect(events.org.platformURLUpdated).toBeCalledTimes(1)
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
})
|
|
15
231
|
|
|
16
232
|
it("should return the correct checklist status based on the state of the budibase installation", async () => {
|
|
17
233
|
await config.saveSmtpConfig()
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
jest.mock("nodemailer")
|
|
2
|
-
const
|
|
3
|
-
const sendMailMock =
|
|
2
|
+
const { config, mocks, structures, request } = require("../../../tests")
|
|
3
|
+
const sendMailMock = mocks.email.mock()
|
|
4
4
|
|
|
5
5
|
const { EmailTemplatePurpose } = require("../../../constants")
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
const TENANT_ID = structures.TENANT_ID
|
|
7
8
|
|
|
8
9
|
describe("/api/global/email", () => {
|
|
9
|
-
let request = setup.getRequest()
|
|
10
|
-
let config = setup.getConfig()
|
|
11
10
|
|
|
12
11
|
beforeAll(async () => {
|
|
13
|
-
await config.
|
|
12
|
+
await config.beforeAll()
|
|
14
13
|
})
|
|
15
14
|
|
|
16
|
-
afterAll(
|
|
15
|
+
afterAll(async () => {
|
|
16
|
+
await config.afterAll()
|
|
17
|
+
})
|
|
17
18
|
|
|
18
19
|
it("should be able to send an email (with mocking)", async () => {
|
|
19
20
|
// initially configure settings
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { config, request } = require("../../../tests")
|
|
2
2
|
const { EmailTemplatePurpose } = require("../../../constants")
|
|
3
3
|
const nodemailer = require("nodemailer")
|
|
4
4
|
const fetch = require("node-fetch")
|
|
@@ -7,14 +7,14 @@ const fetch = require("node-fetch")
|
|
|
7
7
|
jest.setTimeout(30000)
|
|
8
8
|
|
|
9
9
|
describe("/api/global/email", () => {
|
|
10
|
-
let request = setup.getRequest()
|
|
11
|
-
let config = setup.getConfig()
|
|
12
10
|
|
|
13
11
|
beforeAll(async () => {
|
|
14
|
-
await config.
|
|
12
|
+
await config.beforeAll()
|
|
15
13
|
})
|
|
16
14
|
|
|
17
|
-
afterAll(
|
|
15
|
+
afterAll(async () => {
|
|
16
|
+
await config.afterAll()
|
|
17
|
+
})
|
|
18
18
|
|
|
19
19
|
async function sendRealEmail(purpose) {
|
|
20
20
|
let response, text
|