@budibase/worker 2.3.18-alpha.3 → 2.3.18-alpha.30
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/jest.config.ts +1 -1
- package/package.json +8 -7
- package/scripts/test.sh +12 -0
- package/src/api/controllers/global/auth.ts +38 -47
- package/src/api/controllers/global/configs.ts +150 -142
- package/src/api/controllers/global/users.ts +149 -10
- package/src/api/routes/global/configs.ts +6 -12
- package/src/api/routes/global/tests/auditLogs.spec.ts +111 -0
- package/src/api/routes/global/tests/auth.spec.ts +5 -15
- package/src/api/routes/global/tests/configs.spec.ts +88 -60
- package/src/api/routes/global/tests/realEmail.spec.ts +1 -0
- package/src/api/routes/global/tests/users.spec.ts +5 -1
- package/src/api/routes/global/users.ts +19 -14
- package/src/api/routes/index.ts +3 -0
- package/src/db/index.ts +8 -2
- package/src/environment.ts +1 -11
- package/src/index.ts +18 -2
- package/src/sdk/auth/auth.ts +2 -2
- package/src/sdk/users/tests/users.spec.ts +31 -7
- package/src/sdk/users/users.ts +37 -11
- package/src/tests/api/auditLogs.ts +26 -0
- package/src/tests/api/auth.ts +7 -5
- package/src/tests/api/configs.ts +8 -0
- package/src/tests/api/email.ts +1 -0
- package/src/tests/api/index.ts +3 -0
- package/src/tests/structures/configs.ts +20 -13
- package/src/utilities/email.ts +19 -65
- package/src/utilities/redis.ts +40 -2
- package/src/utilities/templates.ts +4 -9
|
@@ -30,7 +30,11 @@ describe("/api/global/users", () => {
|
|
|
30
30
|
email
|
|
31
31
|
)
|
|
32
32
|
|
|
33
|
-
expect(res.body).
|
|
33
|
+
expect(res.body?.message).toBe("Invitation has been sent.")
|
|
34
|
+
expect(res.body?.unsuccessful.length).toBe(0)
|
|
35
|
+
expect(res.body?.successful.length).toBe(1)
|
|
36
|
+
expect(res.body?.successful[0].email).toBe(email)
|
|
37
|
+
|
|
34
38
|
expect(sendMailMock).toHaveBeenCalled()
|
|
35
39
|
expect(code).toBeDefined()
|
|
36
40
|
expect(events.user.invited).toBeCalledTimes(1)
|
|
@@ -38,13 +38,6 @@ function buildInviteMultipleValidation() {
|
|
|
38
38
|
))
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function buildInviteLookupValidation() {
|
|
42
|
-
// prettier-ignore
|
|
43
|
-
return auth.joiValidator.params(Joi.object({
|
|
44
|
-
code: Joi.string().required()
|
|
45
|
-
}).unknown(true))
|
|
46
|
-
}
|
|
47
|
-
|
|
48
41
|
const createUserAdminOnly = (ctx: any, next: any) => {
|
|
49
42
|
if (!ctx.request.body._id) {
|
|
50
43
|
return auth.adminOnly(ctx, next)
|
|
@@ -57,8 +50,8 @@ function buildInviteAcceptValidation() {
|
|
|
57
50
|
// prettier-ignore
|
|
58
51
|
return auth.joiValidator.body(Joi.object({
|
|
59
52
|
inviteCode: Joi.string().required(),
|
|
60
|
-
password: Joi.string().
|
|
61
|
-
firstName: Joi.string().
|
|
53
|
+
password: Joi.string().optional(),
|
|
54
|
+
firstName: Joi.string().optional(),
|
|
62
55
|
lastName: Joi.string().optional(),
|
|
63
56
|
}).required().unknown(true))
|
|
64
57
|
}
|
|
@@ -88,22 +81,34 @@ router
|
|
|
88
81
|
.get("/api/global/roles/:appId")
|
|
89
82
|
.post(
|
|
90
83
|
"/api/global/users/invite",
|
|
91
|
-
auth.
|
|
84
|
+
auth.builderOrAdmin,
|
|
92
85
|
buildInviteValidation(),
|
|
93
86
|
controller.invite
|
|
94
87
|
)
|
|
88
|
+
.post(
|
|
89
|
+
"/api/global/users/onboard",
|
|
90
|
+
auth.builderOrAdmin,
|
|
91
|
+
buildInviteMultipleValidation(),
|
|
92
|
+
controller.onboardUsers
|
|
93
|
+
)
|
|
95
94
|
.post(
|
|
96
95
|
"/api/global/users/multi/invite",
|
|
97
|
-
auth.
|
|
96
|
+
auth.builderOrAdmin,
|
|
98
97
|
buildInviteMultipleValidation(),
|
|
99
98
|
controller.inviteMultiple
|
|
100
99
|
)
|
|
101
100
|
|
|
102
101
|
// non-global endpoints
|
|
102
|
+
.get("/api/global/users/invite/:code", controller.checkInvite)
|
|
103
|
+
.post(
|
|
104
|
+
"/api/global/users/invite/update/:code",
|
|
105
|
+
auth.builderOrAdmin,
|
|
106
|
+
controller.updateInvite
|
|
107
|
+
)
|
|
103
108
|
.get(
|
|
104
|
-
"/api/global/users/
|
|
105
|
-
|
|
106
|
-
controller.
|
|
109
|
+
"/api/global/users/invites",
|
|
110
|
+
auth.builderOrAdmin,
|
|
111
|
+
controller.getUserInvites
|
|
107
112
|
)
|
|
108
113
|
.post(
|
|
109
114
|
"/api/global/users/invite/accept",
|
package/src/api/routes/index.ts
CHANGED
|
@@ -18,6 +18,8 @@ import accountRoutes from "./system/accounts"
|
|
|
18
18
|
import restoreRoutes from "./system/restore"
|
|
19
19
|
|
|
20
20
|
let userGroupRoutes = api.groups
|
|
21
|
+
let auditLogRoutes = api.auditLogs
|
|
22
|
+
|
|
21
23
|
export const routes: Router[] = [
|
|
22
24
|
configRoutes,
|
|
23
25
|
userRoutes,
|
|
@@ -32,6 +34,7 @@ export const routes: Router[] = [
|
|
|
32
34
|
selfRoutes,
|
|
33
35
|
licenseRoutes,
|
|
34
36
|
userGroupRoutes,
|
|
37
|
+
auditLogRoutes,
|
|
35
38
|
migrationRoutes,
|
|
36
39
|
accountRoutes,
|
|
37
40
|
restoreRoutes,
|
package/src/db/index.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import * as core from "@budibase/backend-core"
|
|
2
2
|
import env from "../environment"
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
const dbConfig: any = {
|
|
4
|
+
export function init() {
|
|
5
|
+
const dbConfig: any = {
|
|
6
|
+
replication: true,
|
|
7
|
+
find: true,
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
if (env.isTest() && !env.COUCH_DB_URL) {
|
|
7
11
|
dbConfig.inMemory = true
|
|
12
|
+
dbConfig.allDbs = true
|
|
8
13
|
}
|
|
14
|
+
|
|
9
15
|
core.init({ db: dbConfig })
|
|
10
16
|
}
|
package/src/environment.ts
CHANGED
|
@@ -26,8 +26,6 @@ function parseIntSafe(number: any) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const selfHosted = !!parseInt(process.env.SELF_HOSTED || "")
|
|
30
|
-
|
|
31
29
|
const environment = {
|
|
32
30
|
// auth
|
|
33
31
|
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
|
|
@@ -51,7 +49,7 @@ const environment = {
|
|
|
51
49
|
CLUSTER_PORT: process.env.CLUSTER_PORT,
|
|
52
50
|
// flags
|
|
53
51
|
NODE_ENV: process.env.NODE_ENV,
|
|
54
|
-
SELF_HOSTED:
|
|
52
|
+
SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED || ""),
|
|
55
53
|
LOG_LEVEL: process.env.LOG_LEVEL,
|
|
56
54
|
MULTI_TENANCY: process.env.MULTI_TENANCY,
|
|
57
55
|
DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL,
|
|
@@ -71,14 +69,6 @@ const environment = {
|
|
|
71
69
|
* Mock the email service in use - links to ethereal hosted emails are logged instead.
|
|
72
70
|
*/
|
|
73
71
|
ENABLE_EMAIL_TEST_MODE: process.env.ENABLE_EMAIL_TEST_MODE,
|
|
74
|
-
/**
|
|
75
|
-
* Enable to allow an admin user to login using a password.
|
|
76
|
-
* This can be useful to prevent lockout when configuring SSO.
|
|
77
|
-
* However, this should be turned OFF by default for security purposes.
|
|
78
|
-
*/
|
|
79
|
-
ENABLE_SSO_MAINTENANCE_MODE: selfHosted
|
|
80
|
-
? process.env.ENABLE_SSO_MAINTENANCE_MODE
|
|
81
|
-
: false,
|
|
82
72
|
_set(key: any, value: any) {
|
|
83
73
|
process.env[key] = value
|
|
84
74
|
// @ts-ignore
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,15 @@ import { Event } from "@sentry/types/dist/event"
|
|
|
13
13
|
import Application from "koa"
|
|
14
14
|
import { bootstrap } from "global-agent"
|
|
15
15
|
import * as db from "./db"
|
|
16
|
-
import {
|
|
16
|
+
import { sdk as proSdk } from "@budibase/pro"
|
|
17
|
+
import {
|
|
18
|
+
auth,
|
|
19
|
+
logging,
|
|
20
|
+
events,
|
|
21
|
+
middleware,
|
|
22
|
+
queue,
|
|
23
|
+
env as coreEnv,
|
|
24
|
+
} from "@budibase/backend-core"
|
|
17
25
|
db.init()
|
|
18
26
|
import Koa from "koa"
|
|
19
27
|
import koaBody from "koa-body"
|
|
@@ -23,9 +31,15 @@ import * as redis from "./utilities/redis"
|
|
|
23
31
|
const Sentry = require("@sentry/node")
|
|
24
32
|
const koaSession = require("koa-session")
|
|
25
33
|
const logger = require("koa-pino-logger")
|
|
34
|
+
const { userAgent } = require("koa-useragent")
|
|
35
|
+
|
|
26
36
|
import destroyable from "server-destroy"
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
// configure events to use the pro audit log write
|
|
39
|
+
// can't integrate directly into backend-core due to cyclic issues
|
|
40
|
+
events.processors.init(proSdk.auditLogs.write)
|
|
41
|
+
|
|
42
|
+
if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE) {
|
|
29
43
|
console.warn(
|
|
30
44
|
"Warning: ENABLE_SSO_MAINTENANCE_MODE is set. It is recommended this flag is disabled if maintenance is not in progress"
|
|
31
45
|
)
|
|
@@ -43,6 +57,7 @@ app.use(koaBody({ multipart: true }))
|
|
|
43
57
|
app.use(koaSession(app))
|
|
44
58
|
app.use(middleware.logging)
|
|
45
59
|
app.use(logger(logging.pinoSettings()))
|
|
60
|
+
app.use(userAgent)
|
|
46
61
|
|
|
47
62
|
// authentication
|
|
48
63
|
app.use(auth.passport.initialize())
|
|
@@ -78,6 +93,7 @@ server.on("close", async () => {
|
|
|
78
93
|
console.log("Server Closed")
|
|
79
94
|
await redis.shutdown()
|
|
80
95
|
await events.shutdown()
|
|
96
|
+
await queue.shutdown()
|
|
81
97
|
if (!env.isTest()) {
|
|
82
98
|
process.exit(errCode)
|
|
83
99
|
}
|
package/src/sdk/auth/auth.ts
CHANGED
|
@@ -58,8 +58,8 @@ export const reset = async (email: string) => {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// exit if user has sso
|
|
61
|
-
if (await userSdk.
|
|
62
|
-
|
|
61
|
+
if (await userSdk.isPreventPasswordActions(user)) {
|
|
62
|
+
return
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// send password reset
|
|
@@ -1,26 +1,50 @@
|
|
|
1
1
|
import { structures } from "../../../tests"
|
|
2
|
-
import * as users from "../users"
|
|
3
|
-
import env from "../../../environment"
|
|
4
2
|
import { mocks } from "@budibase/backend-core/tests"
|
|
3
|
+
import { env } from "@budibase/backend-core"
|
|
4
|
+
import * as users from "../users"
|
|
5
5
|
import { CloudAccount } from "@budibase/types"
|
|
6
|
+
import { isPreventPasswordActions } from "../users"
|
|
7
|
+
|
|
8
|
+
jest.mock("@budibase/pro")
|
|
9
|
+
import * as _pro from "@budibase/pro"
|
|
10
|
+
const pro = jest.mocked(_pro, true)
|
|
6
11
|
|
|
7
12
|
describe("users", () => {
|
|
8
|
-
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe("isPreventPasswordActions", () => {
|
|
18
|
+
it("returns false for non sso user", async () => {
|
|
19
|
+
const user = structures.users.user()
|
|
20
|
+
const result = await users.isPreventPasswordActions(user)
|
|
21
|
+
expect(result).toBe(false)
|
|
22
|
+
})
|
|
23
|
+
|
|
9
24
|
it("returns true for sso account user", async () => {
|
|
10
25
|
const user = structures.users.user()
|
|
11
26
|
mocks.accounts.getAccount.mockReturnValue(
|
|
12
27
|
Promise.resolve(structures.accounts.ssoAccount() as CloudAccount)
|
|
13
28
|
)
|
|
14
|
-
const result = await users.
|
|
29
|
+
const result = await users.isPreventPasswordActions(user)
|
|
15
30
|
expect(result).toBe(true)
|
|
16
31
|
})
|
|
17
32
|
|
|
18
33
|
it("returns true for sso user", async () => {
|
|
19
34
|
const user = structures.users.ssoUser()
|
|
20
|
-
const result = await users.
|
|
35
|
+
const result = await users.isPreventPasswordActions(user)
|
|
21
36
|
expect(result).toBe(true)
|
|
22
37
|
})
|
|
23
38
|
|
|
39
|
+
describe("enforced sso", () => {
|
|
40
|
+
it("returns true for all users when sso is enforced", async () => {
|
|
41
|
+
const user = structures.users.user()
|
|
42
|
+
pro.features.isSSOEnforced.mockReturnValue(Promise.resolve(true))
|
|
43
|
+
const result = await users.isPreventPasswordActions(user)
|
|
44
|
+
expect(result).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
24
48
|
describe("sso maintenance mode", () => {
|
|
25
49
|
beforeEach(() => {
|
|
26
50
|
env._set("ENABLE_SSO_MAINTENANCE_MODE", true)
|
|
@@ -33,7 +57,7 @@ describe("users", () => {
|
|
|
33
57
|
describe("non-admin user", () => {
|
|
34
58
|
it("returns true", async () => {
|
|
35
59
|
const user = structures.users.ssoUser()
|
|
36
|
-
const result = await users.
|
|
60
|
+
const result = await users.isPreventPasswordActions(user)
|
|
37
61
|
expect(result).toBe(true)
|
|
38
62
|
})
|
|
39
63
|
})
|
|
@@ -43,7 +67,7 @@ describe("users", () => {
|
|
|
43
67
|
const user = structures.users.ssoUser({
|
|
44
68
|
user: structures.users.adminUser(),
|
|
45
69
|
})
|
|
46
|
-
const result = await users.
|
|
70
|
+
const result = await users.isPreventPasswordActions(user)
|
|
47
71
|
expect(result).toBe(false)
|
|
48
72
|
})
|
|
49
73
|
})
|
package/src/sdk/users/users.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
users as usersCore,
|
|
15
15
|
utils,
|
|
16
16
|
ViewName,
|
|
17
|
+
env as coreEnv,
|
|
17
18
|
} from "@budibase/backend-core"
|
|
18
19
|
import {
|
|
19
20
|
AccountMetadata,
|
|
@@ -34,7 +35,7 @@ import {
|
|
|
34
35
|
} from "@budibase/types"
|
|
35
36
|
import { sendEmail } from "../../utilities/email"
|
|
36
37
|
import { EmailTemplatePurpose } from "../../constants"
|
|
37
|
-
import
|
|
38
|
+
import * as pro from "@budibase/pro"
|
|
38
39
|
import * as accountSdk from "../accounts"
|
|
39
40
|
|
|
40
41
|
const PAGE_LIMIT = 8
|
|
@@ -56,11 +57,22 @@ export const countUsersByApp = async (appId: string) => {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
export const getUsersByAppAccess = async (appId?: string) => {
|
|
61
|
+
const opts: any = {
|
|
62
|
+
include_docs: true,
|
|
63
|
+
limit: 50,
|
|
64
|
+
}
|
|
65
|
+
let response: User[] = await usersCore.searchGlobalUsersByAppAccess(
|
|
66
|
+
appId,
|
|
67
|
+
opts
|
|
68
|
+
)
|
|
69
|
+
return response
|
|
70
|
+
}
|
|
71
|
+
|
|
59
72
|
export const paginatedUsers = async ({
|
|
60
73
|
page,
|
|
61
74
|
email,
|
|
62
75
|
appId,
|
|
63
|
-
userIds,
|
|
64
76
|
}: SearchUsersRequest = {}) => {
|
|
65
77
|
const db = tenancy.getGlobalDB()
|
|
66
78
|
// get one extra document, to have the next page
|
|
@@ -122,13 +134,18 @@ const buildUser = async (
|
|
|
122
134
|
|
|
123
135
|
let hashedPassword
|
|
124
136
|
if (password) {
|
|
125
|
-
if (await
|
|
126
|
-
throw new HTTPError("
|
|
137
|
+
if (await isPreventPasswordActions(user)) {
|
|
138
|
+
throw new HTTPError("Password change is disabled for this user", 400)
|
|
127
139
|
}
|
|
128
140
|
hashedPassword = opts.hashPassword ? await utils.hash(password) : password
|
|
129
141
|
} else if (dbUser) {
|
|
130
142
|
hashedPassword = dbUser.password
|
|
131
|
-
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// passwords are never required if sso is enforced
|
|
146
|
+
const requirePasswords =
|
|
147
|
+
opts.requirePassword && !(await pro.features.isSSOEnforced())
|
|
148
|
+
if (!hashedPassword && requirePasswords) {
|
|
132
149
|
throw "Password must be specified."
|
|
133
150
|
}
|
|
134
151
|
|
|
@@ -188,13 +205,18 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
|
|
|
188
205
|
}
|
|
189
206
|
}
|
|
190
207
|
|
|
191
|
-
export async function
|
|
208
|
+
export async function isPreventPasswordActions(user: User) {
|
|
192
209
|
// when in maintenance mode we allow sso users with the admin role
|
|
193
210
|
// to perform any password action - this prevents lockout
|
|
194
|
-
if (
|
|
211
|
+
if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE && user.admin?.global) {
|
|
195
212
|
return false
|
|
196
213
|
}
|
|
197
214
|
|
|
215
|
+
// SSO is enforced for all users
|
|
216
|
+
if (await pro.features.isSSOEnforced()) {
|
|
217
|
+
return true
|
|
218
|
+
}
|
|
219
|
+
|
|
198
220
|
// Check local sso
|
|
199
221
|
if (isSSOUser(user)) {
|
|
200
222
|
return true
|
|
@@ -228,7 +250,7 @@ export const save = async (
|
|
|
228
250
|
const tenantId = tenancy.getTenantId()
|
|
229
251
|
const db = tenancy.getGlobalDB()
|
|
230
252
|
|
|
231
|
-
let { email, _id, userGroups = [] } = user
|
|
253
|
+
let { email, _id, userGroups = [], roles } = user
|
|
232
254
|
|
|
233
255
|
if (!email && !_id) {
|
|
234
256
|
throw new Error("_id or email is required")
|
|
@@ -270,6 +292,10 @@ export const save = async (
|
|
|
270
292
|
builtUser.roles = dbUser.roles
|
|
271
293
|
}
|
|
272
294
|
|
|
295
|
+
if (!dbUser && roles?.length) {
|
|
296
|
+
builtUser.roles = { ...roles }
|
|
297
|
+
}
|
|
298
|
+
|
|
273
299
|
// make sure we set the _id field for a new user
|
|
274
300
|
// Also if this is a new user, associate groups with them
|
|
275
301
|
let groupPromises = []
|
|
@@ -278,7 +304,7 @@ export const save = async (
|
|
|
278
304
|
|
|
279
305
|
if (userGroups.length > 0) {
|
|
280
306
|
for (let groupId of userGroups) {
|
|
281
|
-
groupPromises.push(
|
|
307
|
+
groupPromises.push(pro.groups.addUsers(groupId, [_id]))
|
|
282
308
|
}
|
|
283
309
|
}
|
|
284
310
|
}
|
|
@@ -456,7 +482,7 @@ export const bulkCreate = async (
|
|
|
456
482
|
const groupPromises = []
|
|
457
483
|
const createdUserIds = saved.map(user => user._id)
|
|
458
484
|
for (let groupId of groups) {
|
|
459
|
-
groupPromises.push(
|
|
485
|
+
groupPromises.push(pro.groups.addUsers(groupId, createdUserIds))
|
|
460
486
|
}
|
|
461
487
|
await Promise.all(groupPromises)
|
|
462
488
|
}
|
|
@@ -631,7 +657,7 @@ export const invite = async (
|
|
|
631
657
|
}
|
|
632
658
|
await sendEmail(user.email, EmailTemplatePurpose.INVITATION, opts)
|
|
633
659
|
response.successful.push({ email: user.email })
|
|
634
|
-
await events.user.invited()
|
|
660
|
+
await events.user.invited(user.email)
|
|
635
661
|
} catch (e) {
|
|
636
662
|
console.error(`Failed to send email invitation email=${user.email}`, e)
|
|
637
663
|
response.unsuccessful.push({
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AuditLogSearchParams, SearchAuditLogsResponse } from "@budibase/types"
|
|
2
|
+
import TestConfiguration from "../TestConfiguration"
|
|
3
|
+
import { TestAPI } from "./base"
|
|
4
|
+
|
|
5
|
+
export class AuditLogAPI extends TestAPI {
|
|
6
|
+
constructor(config: TestConfiguration) {
|
|
7
|
+
super(config)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
search = async (search: AuditLogSearchParams) => {
|
|
11
|
+
const res = await this.request
|
|
12
|
+
.post("/api/global/auditlogs/search")
|
|
13
|
+
.send(search)
|
|
14
|
+
.set(this.config.defaultHeaders())
|
|
15
|
+
.expect("Content-Type", /json/)
|
|
16
|
+
.expect(200)
|
|
17
|
+
return res.body as SearchAuditLogsResponse
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
download = (search: AuditLogSearchParams) => {
|
|
21
|
+
const query = encodeURIComponent(JSON.stringify(search))
|
|
22
|
+
return this.request
|
|
23
|
+
.get(`/api/global/auditlogs/download?query=${query}`)
|
|
24
|
+
.set(this.config.defaultHeaders())
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/tests/api/auth.ts
CHANGED
|
@@ -61,11 +61,13 @@ export class AuthAPI extends TestAPI {
|
|
|
61
61
|
|
|
62
62
|
let code: string | undefined
|
|
63
63
|
if (res.status === 200) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
if (sendMailMock.mock.calls.length) {
|
|
65
|
+
const emailCall = sendMailMock.mock.calls[0][0]
|
|
66
|
+
const parts = emailCall.html.split(
|
|
67
|
+
`http://localhost:10000/builder/auth/reset?code=`
|
|
68
|
+
)
|
|
69
|
+
code = parts[1].split('"')[0].split("&")[0]
|
|
70
|
+
}
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
return { code, res }
|
package/src/tests/api/configs.ts
CHANGED
|
@@ -14,6 +14,14 @@ export class ConfigAPI extends TestAPI {
|
|
|
14
14
|
.expect("Content-Type", /json/)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
getPublicSettings = () => {
|
|
18
|
+
return this.request
|
|
19
|
+
.get(`/api/global/configs/public`)
|
|
20
|
+
.set(this.config.defaultHeaders())
|
|
21
|
+
.expect(200)
|
|
22
|
+
.expect("Content-Type", /json/)
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
saveConfig = (data: any) => {
|
|
18
26
|
return this.request
|
|
19
27
|
.post(`/api/global/configs`)
|
package/src/tests/api/email.ts
CHANGED
package/src/tests/api/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { GroupsAPI } from "./groups"
|
|
|
14
14
|
import { RolesAPI } from "./roles"
|
|
15
15
|
import { TemplatesAPI } from "./templates"
|
|
16
16
|
import { LicenseAPI } from "./license"
|
|
17
|
+
import { AuditLogAPI } from "./auditLogs"
|
|
17
18
|
export default class API {
|
|
18
19
|
accounts: AccountAPI
|
|
19
20
|
auth: AuthAPI
|
|
@@ -30,6 +31,7 @@ export default class API {
|
|
|
30
31
|
roles: RolesAPI
|
|
31
32
|
templates: TemplatesAPI
|
|
32
33
|
license: LicenseAPI
|
|
34
|
+
auditLogs: AuditLogAPI
|
|
33
35
|
|
|
34
36
|
constructor(config: TestConfiguration) {
|
|
35
37
|
this.accounts = new AccountAPI(config)
|
|
@@ -47,5 +49,6 @@ export default class API {
|
|
|
47
49
|
this.roles = new RolesAPI(config)
|
|
48
50
|
this.templates = new TemplatesAPI(config)
|
|
49
51
|
this.license = new LicenseAPI(config)
|
|
52
|
+
this.auditLogs = new AuditLogAPI(config)
|
|
50
53
|
}
|
|
51
54
|
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { Config } from "../../constants"
|
|
2
1
|
import { utils } from "@budibase/backend-core"
|
|
2
|
+
import {
|
|
3
|
+
SettingsConfig,
|
|
4
|
+
ConfigType,
|
|
5
|
+
SMTPConfig,
|
|
6
|
+
GoogleConfig,
|
|
7
|
+
OIDCConfig,
|
|
8
|
+
} from "@budibase/types"
|
|
3
9
|
|
|
4
|
-
export function oidc(conf?: any) {
|
|
10
|
+
export function oidc(conf?: any): OIDCConfig {
|
|
5
11
|
return {
|
|
6
|
-
type:
|
|
12
|
+
type: ConfigType.OIDC,
|
|
7
13
|
config: {
|
|
8
14
|
configs: [
|
|
9
15
|
{
|
|
@@ -21,9 +27,9 @@ export function oidc(conf?: any) {
|
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
export function google(conf?: any) {
|
|
30
|
+
export function google(conf?: any): GoogleConfig {
|
|
25
31
|
return {
|
|
26
|
-
type:
|
|
32
|
+
type: ConfigType.GOOGLE,
|
|
27
33
|
config: {
|
|
28
34
|
clientID: "clientId",
|
|
29
35
|
clientSecret: "clientSecret",
|
|
@@ -33,9 +39,9 @@ export function google(conf?: any) {
|
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
export function smtp(conf?: any) {
|
|
42
|
+
export function smtp(conf?: any): SMTPConfig {
|
|
37
43
|
return {
|
|
38
|
-
type:
|
|
44
|
+
type: ConfigType.SMTP,
|
|
39
45
|
config: {
|
|
40
46
|
port: 12345,
|
|
41
47
|
host: "smtptesthost.com",
|
|
@@ -47,25 +53,26 @@ export function smtp(conf?: any) {
|
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
export function smtpEthereal() {
|
|
56
|
+
export function smtpEthereal(): SMTPConfig {
|
|
51
57
|
return {
|
|
52
|
-
type:
|
|
58
|
+
type: ConfigType.SMTP,
|
|
53
59
|
config: {
|
|
54
60
|
port: 587,
|
|
55
61
|
host: "smtp.ethereal.email",
|
|
62
|
+
from: "testfrom@test.com",
|
|
56
63
|
secure: false,
|
|
57
64
|
auth: {
|
|
58
|
-
user: "
|
|
59
|
-
pass: "
|
|
65
|
+
user: "wyatt.zulauf29@ethereal.email",
|
|
66
|
+
pass: "tEwDtHBWWxusVWAPfa",
|
|
60
67
|
},
|
|
61
68
|
connectionTimeout: 1000, // must be less than the jest default of 5000
|
|
62
69
|
},
|
|
63
70
|
}
|
|
64
71
|
}
|
|
65
72
|
|
|
66
|
-
export function settings(conf?: any) {
|
|
73
|
+
export function settings(conf?: any): SettingsConfig {
|
|
67
74
|
return {
|
|
68
|
-
type:
|
|
75
|
+
type: ConfigType.SETTINGS,
|
|
69
76
|
config: {
|
|
70
77
|
platformUrl: "http://localhost:10000",
|
|
71
78
|
logoUrl: "",
|