@budibase/worker 2.3.18-alpha.14 → 2.3.18-alpha.16
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 +7 -6
- package/src/api/controllers/global/auth.ts +6 -6
- package/src/api/controllers/global/configs.ts +70 -1
- package/src/api/routes/global/configs.ts +6 -6
- package/src/api/routes/global/tests/auditLogs.spec.ts +111 -0
- package/src/api/routes/global/tests/auth.spec.ts +1 -1
- package/src/api/routes/index.ts +3 -0
- package/src/db/index.ts +8 -2
- package/src/index.ts +10 -0
- package/src/sdk/users/users.ts +1 -1
- package/src/tests/api/auditLogs.ts +26 -0
- package/src/tests/api/index.ts +3 -0
- package/src/utilities/email.ts +1 -1
package/jest.config.ts
CHANGED
|
@@ -7,7 +7,7 @@ const config: Config.InitialOptions = {
|
|
|
7
7
|
preset: "@trendyol/jest-testcontainers",
|
|
8
8
|
setupFiles: ["./src/tests/jestEnv.ts"],
|
|
9
9
|
setupFilesAfterEnv: ["./src/tests/jestSetup.ts"],
|
|
10
|
-
collectCoverageFrom: ["src/**/*.{js,ts}"],
|
|
10
|
+
collectCoverageFrom: ["src/**/*.{js,ts}", "../backend-core/src/**/*.{js,ts}"],
|
|
11
11
|
coverageReporters: ["lcov", "json", "clover"],
|
|
12
12
|
transform: {
|
|
13
13
|
"^.+\\.ts?$": "@swc/jest",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.3.18-alpha.
|
|
4
|
+
"version": "2.3.18-alpha.16",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"author": "Budibase",
|
|
37
37
|
"license": "GPL-3.0",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@budibase/backend-core": "2.3.18-alpha.
|
|
40
|
-
"@budibase/pro": "2.3.18-alpha.
|
|
41
|
-
"@budibase/string-templates": "2.3.18-alpha.
|
|
42
|
-
"@budibase/types": "2.3.18-alpha.
|
|
39
|
+
"@budibase/backend-core": "2.3.18-alpha.16",
|
|
40
|
+
"@budibase/pro": "2.3.18-alpha.15",
|
|
41
|
+
"@budibase/string-templates": "2.3.18-alpha.16",
|
|
42
|
+
"@budibase/types": "2.3.18-alpha.16",
|
|
43
43
|
"@koa/router": "8.0.8",
|
|
44
44
|
"@sentry/node": "6.17.7",
|
|
45
45
|
"@techpass/passport-openidconnect": "0.3.2",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"koa-send": "5.0.1",
|
|
61
61
|
"koa-session": "5.13.1",
|
|
62
62
|
"koa-static": "5.0.0",
|
|
63
|
+
"koa-useragent": "^4.1.0",
|
|
63
64
|
"node-fetch": "2.6.7",
|
|
64
65
|
"nodemailer": "6.7.2",
|
|
65
66
|
"passport-google-oauth": "2.0.0",
|
|
@@ -100,5 +101,5 @@
|
|
|
100
101
|
"typescript": "4.7.3",
|
|
101
102
|
"update-dotenv": "1.1.1"
|
|
102
103
|
},
|
|
103
|
-
"gitHead": "
|
|
104
|
+
"gitHead": "c350c15ae0869875936c804b89fcd46e4da4d8bb"
|
|
104
105
|
}
|
|
@@ -69,8 +69,8 @@ export const login = async (ctx: Ctx<LoginRequest>, next: any) => {
|
|
|
69
69
|
"local",
|
|
70
70
|
async (err: any, user: User, info: any) => {
|
|
71
71
|
await passportCallback(ctx, user, err, info)
|
|
72
|
-
await context.identity.doInUserContext(user, async () => {
|
|
73
|
-
await events.auth.login("local")
|
|
72
|
+
await context.identity.doInUserContext(user, ctx, async () => {
|
|
73
|
+
await events.auth.login("local", user.email)
|
|
74
74
|
})
|
|
75
75
|
ctx.status = 200
|
|
76
76
|
}
|
|
@@ -207,8 +207,8 @@ export const googleCallback = async (ctx: any, next: any) => {
|
|
|
207
207
|
{ successRedirect: "/", failureRedirect: "/error" },
|
|
208
208
|
async (err: any, user: SSOUser, info: any) => {
|
|
209
209
|
await passportCallback(ctx, user, err, info)
|
|
210
|
-
await context.identity.doInUserContext(user, async () => {
|
|
211
|
-
await events.auth.login("google-internal")
|
|
210
|
+
await context.identity.doInUserContext(user, ctx, async () => {
|
|
211
|
+
await events.auth.login("google-internal", user.email)
|
|
212
212
|
})
|
|
213
213
|
ctx.redirect("/")
|
|
214
214
|
}
|
|
@@ -272,8 +272,8 @@ export const oidcCallback = async (ctx: any, next: any) => {
|
|
|
272
272
|
{ successRedirect: "/", failureRedirect: "/error" },
|
|
273
273
|
async (err: any, user: SSOUser, info: any) => {
|
|
274
274
|
await passportCallback(ctx, user, err, info)
|
|
275
|
-
await context.identity.doInUserContext(user, async () => {
|
|
276
|
-
await events.auth.login("oidc")
|
|
275
|
+
await context.identity.doInUserContext(user, ctx, async () => {
|
|
276
|
+
await events.auth.login("oidc", user.email)
|
|
277
277
|
})
|
|
278
278
|
ctx.redirect("/")
|
|
279
279
|
}
|
|
@@ -17,10 +17,15 @@ import {
|
|
|
17
17
|
Ctx,
|
|
18
18
|
GetPublicOIDCConfigResponse,
|
|
19
19
|
GetPublicSettingsResponse,
|
|
20
|
+
GoogleInnerConfig,
|
|
20
21
|
isGoogleConfig,
|
|
21
22
|
isOIDCConfig,
|
|
22
23
|
isSettingsConfig,
|
|
23
24
|
isSMTPConfig,
|
|
25
|
+
OIDCConfigs,
|
|
26
|
+
SettingsInnerConfig,
|
|
27
|
+
SSOConfig,
|
|
28
|
+
SSOConfigType,
|
|
24
29
|
UserCtx,
|
|
25
30
|
} from "@budibase/types"
|
|
26
31
|
import * as pro from "@budibase/pro"
|
|
@@ -119,6 +124,61 @@ const getEventFns = async (config: Config, existing?: Config) => {
|
|
|
119
124
|
return fns
|
|
120
125
|
}
|
|
121
126
|
|
|
127
|
+
type SSOConfigs = { [key in SSOConfigType]: SSOConfig | undefined }
|
|
128
|
+
|
|
129
|
+
async function getSSOConfigs(): Promise<SSOConfigs> {
|
|
130
|
+
const google = await configs.getGoogleConfig()
|
|
131
|
+
const oidc = await configs.getOIDCConfig()
|
|
132
|
+
return {
|
|
133
|
+
[ConfigType.GOOGLE]: google,
|
|
134
|
+
[ConfigType.OIDC]: oidc,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function hasActivatedConfig(ssoConfigs?: SSOConfigs) {
|
|
139
|
+
if (!ssoConfigs) {
|
|
140
|
+
ssoConfigs = await getSSOConfigs()
|
|
141
|
+
}
|
|
142
|
+
return !!Object.values(ssoConfigs).find(c => c?.activated)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function verifySettingsConfig(config: SettingsInnerConfig) {
|
|
146
|
+
if (config.isSSOEnforced) {
|
|
147
|
+
const valid = await hasActivatedConfig()
|
|
148
|
+
if (!valid) {
|
|
149
|
+
throw new Error("Cannot enforce SSO without an activated configuration")
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function verifySSOConfig(type: SSOConfigType, config: SSOConfig) {
|
|
155
|
+
const settings = await configs.getSettingsConfig()
|
|
156
|
+
if (settings.isSSOEnforced && !config.activated) {
|
|
157
|
+
// config is being saved as deactivated
|
|
158
|
+
// ensure there is at least one other activated sso config
|
|
159
|
+
const ssoConfigs = await getSSOConfigs()
|
|
160
|
+
|
|
161
|
+
// overwrite the config being updated
|
|
162
|
+
// to reflect the desired state
|
|
163
|
+
ssoConfigs[type] = config
|
|
164
|
+
|
|
165
|
+
const activated = await hasActivatedConfig(ssoConfigs)
|
|
166
|
+
if (!activated) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
"Configuration cannot be deactivated while SSO is enforced"
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function verifyGoogleConfig(config: GoogleInnerConfig) {
|
|
175
|
+
await verifySSOConfig(ConfigType.GOOGLE, config)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function verifyOIDCConfig(config: OIDCConfigs) {
|
|
179
|
+
await verifySSOConfig(ConfigType.OIDC, config.configs[0])
|
|
180
|
+
}
|
|
181
|
+
|
|
122
182
|
export async function save(ctx: UserCtx<Config>) {
|
|
123
183
|
const body = ctx.request.body
|
|
124
184
|
const type = body.type
|
|
@@ -133,10 +193,19 @@ export async function save(ctx: UserCtx<Config>) {
|
|
|
133
193
|
|
|
134
194
|
try {
|
|
135
195
|
// verify the configuration
|
|
136
|
-
switch (
|
|
196
|
+
switch (type) {
|
|
137
197
|
case ConfigType.SMTP:
|
|
138
198
|
await email.verifyConfig(config)
|
|
139
199
|
break
|
|
200
|
+
case ConfigType.SETTINGS:
|
|
201
|
+
await verifySettingsConfig(config)
|
|
202
|
+
break
|
|
203
|
+
case ConfigType.GOOGLE:
|
|
204
|
+
await verifyGoogleConfig(config)
|
|
205
|
+
break
|
|
206
|
+
case ConfigType.OIDC:
|
|
207
|
+
await verifyOIDCConfig(config)
|
|
208
|
+
break
|
|
140
209
|
}
|
|
141
210
|
} catch (err: any) {
|
|
142
211
|
ctx.throw(400, err)
|
|
@@ -34,8 +34,8 @@ function settingValidation() {
|
|
|
34
34
|
function googleValidation() {
|
|
35
35
|
// prettier-ignore
|
|
36
36
|
return Joi.object({
|
|
37
|
-
clientID: Joi.
|
|
38
|
-
clientSecret: Joi.
|
|
37
|
+
clientID: Joi.string().required(),
|
|
38
|
+
clientSecret: Joi.string().required(),
|
|
39
39
|
activated: Joi.boolean().required(),
|
|
40
40
|
}).unknown(true)
|
|
41
41
|
}
|
|
@@ -45,12 +45,12 @@ function oidcValidation() {
|
|
|
45
45
|
return Joi.object({
|
|
46
46
|
configs: Joi.array().items(
|
|
47
47
|
Joi.object({
|
|
48
|
-
clientID: Joi.
|
|
49
|
-
clientSecret: Joi.
|
|
50
|
-
configUrl: Joi.
|
|
48
|
+
clientID: Joi.string().required(),
|
|
49
|
+
clientSecret: Joi.string().required(),
|
|
50
|
+
configUrl: Joi.string().required(),
|
|
51
51
|
logo: Joi.string().allow("", null),
|
|
52
52
|
name: Joi.string().allow("", null),
|
|
53
|
-
uuid: Joi.
|
|
53
|
+
uuid: Joi.string().required(),
|
|
54
54
|
activated: Joi.boolean().required(),
|
|
55
55
|
scopes: Joi.array().optional()
|
|
56
56
|
})
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { mocks, structures } from "@budibase/backend-core/tests"
|
|
2
|
+
import { context, events } from "@budibase/backend-core"
|
|
3
|
+
import { Event, IdentityType } from "@budibase/types"
|
|
4
|
+
import { TestConfiguration } from "../../../../tests"
|
|
5
|
+
|
|
6
|
+
mocks.licenses.useAuditLogs()
|
|
7
|
+
|
|
8
|
+
const BASE_IDENTITY = {
|
|
9
|
+
account: undefined,
|
|
10
|
+
type: IdentityType.USER,
|
|
11
|
+
}
|
|
12
|
+
const USER_AUDIT_LOG_COUNT = 3
|
|
13
|
+
const APP_ID = "app_1"
|
|
14
|
+
|
|
15
|
+
describe("/api/global/auditlogs", () => {
|
|
16
|
+
const config = new TestConfiguration()
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
await config.beforeAll()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
afterAll(async () => {
|
|
23
|
+
await config.afterAll()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe("POST /api/global/auditlogs/search", () => {
|
|
27
|
+
it("should be able to fire some events (create audit logs)", async () => {
|
|
28
|
+
await context.doInTenant(config.tenantId, async () => {
|
|
29
|
+
const userId = config.user!._id!
|
|
30
|
+
const identity = {
|
|
31
|
+
...BASE_IDENTITY,
|
|
32
|
+
_id: userId,
|
|
33
|
+
tenantId: config.tenantId,
|
|
34
|
+
}
|
|
35
|
+
await context.doInIdentityContext(identity, async () => {
|
|
36
|
+
for (let i = 0; i < USER_AUDIT_LOG_COUNT; i++) {
|
|
37
|
+
await events.user.created(structures.users.user())
|
|
38
|
+
}
|
|
39
|
+
await context.doInAppContext(APP_ID, async () => {
|
|
40
|
+
await events.app.created(structures.apps.app(APP_ID))
|
|
41
|
+
})
|
|
42
|
+
// fetch the user created events
|
|
43
|
+
const response = await config.api.auditLogs.search({
|
|
44
|
+
events: [Event.USER_CREATED],
|
|
45
|
+
})
|
|
46
|
+
expect(response.data).toBeDefined()
|
|
47
|
+
// there will be an initial event which comes from the default user creation
|
|
48
|
+
expect(response.data.length).toBe(USER_AUDIT_LOG_COUNT + 1)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it("should be able to search by event", async () => {
|
|
54
|
+
const response = await config.api.auditLogs.search({
|
|
55
|
+
events: [Event.USER_CREATED],
|
|
56
|
+
})
|
|
57
|
+
expect(response.data.length).toBeGreaterThan(0)
|
|
58
|
+
for (let log of response.data) {
|
|
59
|
+
expect(log.event).toBe(Event.USER_CREATED)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it("should be able to search by time range (frozen)", async () => {
|
|
64
|
+
// this is frozen, only need to add 1 and minus 1
|
|
65
|
+
const now = new Date()
|
|
66
|
+
const start = new Date()
|
|
67
|
+
start.setSeconds(now.getSeconds() - 1)
|
|
68
|
+
const end = new Date()
|
|
69
|
+
end.setSeconds(now.getSeconds() + 1)
|
|
70
|
+
const response = await config.api.auditLogs.search({
|
|
71
|
+
startDate: start.toISOString(),
|
|
72
|
+
endDate: end.toISOString(),
|
|
73
|
+
})
|
|
74
|
+
expect(response.data.length).toBeGreaterThan(0)
|
|
75
|
+
for (let log of response.data) {
|
|
76
|
+
expect(log.timestamp).toBe(now.toISOString())
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it("should be able to search by user ID", async () => {
|
|
81
|
+
const userId = config.user!._id!
|
|
82
|
+
const response = await config.api.auditLogs.search({
|
|
83
|
+
userIds: [userId],
|
|
84
|
+
})
|
|
85
|
+
expect(response.data.length).toBeGreaterThan(0)
|
|
86
|
+
for (let log of response.data) {
|
|
87
|
+
expect(log.user._id).toBe(userId)
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it("should be able to search by app ID", async () => {
|
|
92
|
+
const response = await config.api.auditLogs.search({
|
|
93
|
+
appIds: [APP_ID],
|
|
94
|
+
})
|
|
95
|
+
expect(response.data.length).toBeGreaterThan(0)
|
|
96
|
+
for (let log of response.data) {
|
|
97
|
+
expect(log.app?._id).toBe(APP_ID)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("should be able to search by full string", async () => {
|
|
102
|
+
const response = await config.api.auditLogs.search({
|
|
103
|
+
fullSearch: "User",
|
|
104
|
+
})
|
|
105
|
+
expect(response.data.length).toBeGreaterThan(0)
|
|
106
|
+
for (let log of response.data) {
|
|
107
|
+
expect(log.name.includes("User")).toBe(true)
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
})
|
|
@@ -367,7 +367,7 @@ describe("/api/global/auth", () => {
|
|
|
367
367
|
|
|
368
368
|
const res = await config.api.configs.OIDCCallback(configId, preAuthRes)
|
|
369
369
|
|
|
370
|
-
expect(events.auth.login).toBeCalledWith("oidc")
|
|
370
|
+
expect(events.auth.login).toBeCalledWith("oidc", "oauth@example.com")
|
|
371
371
|
expect(events.auth.login).toBeCalledTimes(1)
|
|
372
372
|
expect(res.status).toBe(302)
|
|
373
373
|
const location: string = res.get("location")
|
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/index.ts
CHANGED
|
@@ -13,11 +13,13 @@ 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 { sdk as proSdk } from "@budibase/pro"
|
|
16
17
|
import {
|
|
17
18
|
auth,
|
|
18
19
|
logging,
|
|
19
20
|
events,
|
|
20
21
|
middleware,
|
|
22
|
+
queue,
|
|
21
23
|
env as coreEnv,
|
|
22
24
|
} from "@budibase/backend-core"
|
|
23
25
|
db.init()
|
|
@@ -29,8 +31,14 @@ import * as redis from "./utilities/redis"
|
|
|
29
31
|
const Sentry = require("@sentry/node")
|
|
30
32
|
const koaSession = require("koa-session")
|
|
31
33
|
const logger = require("koa-pino-logger")
|
|
34
|
+
const { userAgent } = require("koa-useragent")
|
|
35
|
+
|
|
32
36
|
import destroyable from "server-destroy"
|
|
33
37
|
|
|
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
|
+
|
|
34
42
|
if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE) {
|
|
35
43
|
console.warn(
|
|
36
44
|
"Warning: ENABLE_SSO_MAINTENANCE_MODE is set. It is recommended this flag is disabled if maintenance is not in progress"
|
|
@@ -49,6 +57,7 @@ app.use(koaBody({ multipart: true }))
|
|
|
49
57
|
app.use(koaSession(app))
|
|
50
58
|
app.use(middleware.logging)
|
|
51
59
|
app.use(logger(logging.pinoSettings()))
|
|
60
|
+
app.use(userAgent)
|
|
52
61
|
|
|
53
62
|
// authentication
|
|
54
63
|
app.use(auth.passport.initialize())
|
|
@@ -84,6 +93,7 @@ server.on("close", async () => {
|
|
|
84
93
|
console.log("Server Closed")
|
|
85
94
|
await redis.shutdown()
|
|
86
95
|
await events.shutdown()
|
|
96
|
+
await queue.shutdown()
|
|
87
97
|
if (!env.isTest()) {
|
|
88
98
|
process.exit(errCode)
|
|
89
99
|
}
|
package/src/sdk/users/users.ts
CHANGED
|
@@ -637,7 +637,7 @@ export const invite = async (
|
|
|
637
637
|
}
|
|
638
638
|
await sendEmail(user.email, EmailTemplatePurpose.INVITATION, opts)
|
|
639
639
|
response.successful.push({ email: user.email })
|
|
640
|
-
await events.user.invited()
|
|
640
|
+
await events.user.invited(user.email)
|
|
641
641
|
} catch (e) {
|
|
642
642
|
console.error(`Failed to send email invitation email=${user.email}`, e)
|
|
643
643
|
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/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
|
}
|
package/src/utilities/email.ts
CHANGED
|
@@ -203,7 +203,7 @@ export async function sendEmail(
|
|
|
203
203
|
* @param {object} config an SMTP configuration - this is based on the nodemailer API.
|
|
204
204
|
* @return {Promise<boolean>} returns true if the configuration is valid.
|
|
205
205
|
*/
|
|
206
|
-
export async function verifyConfig(config:
|
|
206
|
+
export async function verifyConfig(config: SMTPInnerConfig) {
|
|
207
207
|
const transport = createSMTPTransport(config)
|
|
208
208
|
await transport.verify()
|
|
209
209
|
}
|