@budibase/worker 2.5.6-alpha.9 → 2.5.7
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 +5 -5
- package/nodemon.json +2 -6
- package/package.json +8 -9
- package/scripts/dev/manage.js +0 -1
- package/src/api/controllers/global/users.ts +2 -2
- package/src/api/index.ts +1 -1
- package/src/api/routes/global/configs.ts +1 -9
- package/src/api/routes/global/tests/users.spec.ts +4 -10
- package/src/api/routes/index.ts +6 -4
- package/src/elasticApm.ts +10 -0
- package/src/environment.ts +1 -0
- package/src/index.ts +7 -7
- package/src/sdk/users/users.ts +54 -7
- package/src/tests/TestConfiguration.ts +1 -50
- package/src/tests/api/index.ts +0 -7
- package/src/tests/jestEnv.ts +1 -0
- package/src/tests/jestSetup.ts +2 -0
- package/src/tests/logging.ts +34 -0
- package/src/utilities/appService.ts +46 -0
- package/tsconfig.json +14 -4
- package/src/api/routes/global/tests/scim.spec.ts +0 -1029
- package/src/initPro.ts +0 -13
- package/src/middleware/handleScimBody.ts +0 -12
- package/src/tests/api/scim/groups.ts +0 -101
- package/src/tests/api/scim/shared.ts +0 -44
- package/src/tests/api/scim/users.ts +0 -94
package/jest.config.ts
CHANGED
|
@@ -20,11 +20,11 @@ const config: Config.InitialOptions = {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// add pro sources if they exist
|
|
23
|
-
if (fs.existsSync("
|
|
24
|
-
config.moduleNameMapper
|
|
25
|
-
"<rootDir
|
|
26
|
-
config.moduleNameMapper
|
|
27
|
-
"<rootDir
|
|
23
|
+
if (fs.existsSync("../../../budibase-pro")) {
|
|
24
|
+
config.moduleNameMapper["@budibase/pro/(.*)"] =
|
|
25
|
+
"<rootDir>/../../../budibase-pro/packages/pro/$1"
|
|
26
|
+
config.moduleNameMapper["@budibase/pro"] =
|
|
27
|
+
"<rootDir>/../../../budibase-pro/packages/pro/src"
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export default config
|
package/nodemon.json
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"watch": ["src", "../backend-core", "
|
|
2
|
+
"watch": ["src", "../backend-core", "../../../budibase-pro/packages/pro"],
|
|
3
3
|
"ext": "js,ts,json",
|
|
4
|
-
"ignore": [
|
|
5
|
-
"src/**/*.spec.ts",
|
|
6
|
-
"src/**/*.spec.js",
|
|
7
|
-
"../backend-core/dist/**/*"
|
|
8
|
-
],
|
|
4
|
+
"ignore": ["src/**/*.spec.ts", "src/**/*.spec.js", "../backend-core/dist/**/*"],
|
|
9
5
|
"exec": "ts-node src/index.ts"
|
|
10
6
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.5.
|
|
4
|
+
"version": "2.5.7",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION",
|
|
23
23
|
"dev:stack:init": "node ./scripts/dev/manage.js init",
|
|
24
24
|
"dev:builder": "npm run dev:stack:init && nodemon",
|
|
25
|
-
"dev:built": "yarn run dev:stack:init && yarn run run:docker",
|
|
26
25
|
"test": "bash scripts/test.sh",
|
|
27
26
|
"test:watch": "jest --watch",
|
|
28
27
|
"env:multi:enable": "node scripts/multiTenancy.js enable",
|
|
@@ -37,10 +36,10 @@
|
|
|
37
36
|
"author": "Budibase",
|
|
38
37
|
"license": "GPL-3.0",
|
|
39
38
|
"dependencies": {
|
|
40
|
-
"@budibase/backend-core": "2.5.
|
|
41
|
-
"@budibase/pro": "2.5.6
|
|
42
|
-
"@budibase/string-templates": "2.5.
|
|
43
|
-
"@budibase/types": "2.5.
|
|
39
|
+
"@budibase/backend-core": "^2.5.7",
|
|
40
|
+
"@budibase/pro": "2.5.6",
|
|
41
|
+
"@budibase/string-templates": "^2.5.7",
|
|
42
|
+
"@budibase/types": "^2.5.7",
|
|
44
43
|
"@koa/router": "8.0.8",
|
|
45
44
|
"@sentry/node": "6.17.7",
|
|
46
45
|
"@techpass/passport-openidconnect": "0.3.2",
|
|
@@ -57,6 +56,7 @@
|
|
|
57
56
|
"koa-body": "4.2.0",
|
|
58
57
|
"koa-compress": "4.0.1",
|
|
59
58
|
"koa-passport": "4.1.4",
|
|
59
|
+
"koa-pino-logger": "3.0.0",
|
|
60
60
|
"koa-send": "5.0.1",
|
|
61
61
|
"koa-session": "5.13.1",
|
|
62
62
|
"koa-static": "5.0.0",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"passport-google-oauth": "2.0.0",
|
|
67
67
|
"passport-jwt": "4.0.0",
|
|
68
68
|
"passport-local": "1.0.0",
|
|
69
|
+
"pino-pretty": "4.8.0",
|
|
69
70
|
"pouchdb": "7.3.0",
|
|
70
71
|
"pouchdb-all-dbs": "1.1.1",
|
|
71
72
|
"server-destroy": "1.0.1"
|
|
@@ -78,7 +79,6 @@
|
|
|
78
79
|
"@types/jsonwebtoken": "8.5.1",
|
|
79
80
|
"@types/koa": "2.13.4",
|
|
80
81
|
"@types/koa__router": "8.0.8",
|
|
81
|
-
"@types/lodash": "^4.14.191",
|
|
82
82
|
"@types/node": "14.18.20",
|
|
83
83
|
"@types/node-fetch": "2.6.1",
|
|
84
84
|
"@types/pouchdb": "6.4.0",
|
|
@@ -89,7 +89,6 @@
|
|
|
89
89
|
"copyfiles": "2.4.1",
|
|
90
90
|
"eslint": "6.8.0",
|
|
91
91
|
"jest": "28.1.1",
|
|
92
|
-
"lodash": "4.17.21",
|
|
93
92
|
"nodemon": "2.0.15",
|
|
94
93
|
"pouchdb-adapter-memory": "7.2.2",
|
|
95
94
|
"prettier": "2.3.1",
|
|
@@ -102,5 +101,5 @@
|
|
|
102
101
|
"typescript": "4.7.3",
|
|
103
102
|
"update-dotenv": "1.1.1"
|
|
104
103
|
},
|
|
105
|
-
"gitHead": "
|
|
104
|
+
"gitHead": "6c37b6a6c69a1da43a5c0930419e743cc18b5b42"
|
|
106
105
|
}
|
package/scripts/dev/manage.js
CHANGED
|
@@ -177,7 +177,7 @@ export const destroy = async (ctx: any) => {
|
|
|
177
177
|
ctx.throw(400, "Unable to delete self.")
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
await userSdk.destroy(id)
|
|
180
|
+
await userSdk.destroy(id, ctx.user)
|
|
181
181
|
|
|
182
182
|
ctx.body = {
|
|
183
183
|
message: `User ${id} deleted.`,
|
|
@@ -197,7 +197,7 @@ export const search = async (ctx: any) => {
|
|
|
197
197
|
if (body.paginated === false) {
|
|
198
198
|
await getAppUsers(ctx)
|
|
199
199
|
} else {
|
|
200
|
-
const paginated = await userSdk.
|
|
200
|
+
const paginated = await userSdk.paginatedUsers(body)
|
|
201
201
|
// user hashed password shouldn't ever be returned
|
|
202
202
|
for (let user of paginated.data) {
|
|
203
203
|
if (user) {
|
package/src/api/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Router from "@koa/router"
|
|
2
2
|
const compress = require("koa-compress")
|
|
3
|
-
|
|
3
|
+
const zlib = require("zlib")
|
|
4
4
|
import { routes } from "./routes"
|
|
5
5
|
import { middleware as pro } from "@budibase/pro"
|
|
6
6
|
import { auth, middleware } from "@budibase/backend-core"
|
|
@@ -58,13 +58,6 @@ function oidcValidation() {
|
|
|
58
58
|
}).unknown(true)
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
function scimValidation() {
|
|
62
|
-
// prettier-ignore
|
|
63
|
-
return Joi.object({
|
|
64
|
-
enabled: Joi.boolean().required(),
|
|
65
|
-
}).unknown(true)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
61
|
function buildConfigSaveValidation() {
|
|
69
62
|
// prettier-ignore
|
|
70
63
|
return auth.joiValidator.body(Joi.object({
|
|
@@ -81,8 +74,7 @@ function buildConfigSaveValidation() {
|
|
|
81
74
|
{ is: ConfigType.SETTINGS, then: settingValidation() },
|
|
82
75
|
{ is: ConfigType.ACCOUNT, then: Joi.object().unknown(true) },
|
|
83
76
|
{ is: ConfigType.GOOGLE, then: googleValidation() },
|
|
84
|
-
{ is: ConfigType.OIDC, then: oidcValidation() }
|
|
85
|
-
{ is: ConfigType.SCIM, then: scimValidation() }
|
|
77
|
+
{ is: ConfigType.OIDC, then: oidcValidation() }
|
|
86
78
|
],
|
|
87
79
|
}),
|
|
88
80
|
}).required().unknown(true),
|
|
@@ -48,7 +48,7 @@ describe("/api/global/users", () => {
|
|
|
48
48
|
400
|
|
49
49
|
)
|
|
50
50
|
|
|
51
|
-
expect(res.body.message).toBe(
|
|
51
|
+
expect(res.body.message).toBe("Unavailable")
|
|
52
52
|
expect(sendMailMock).toHaveBeenCalledTimes(0)
|
|
53
53
|
expect(code).toBeUndefined()
|
|
54
54
|
expect(events.user.invited).toBeCalledTimes(0)
|
|
@@ -225,9 +225,7 @@ describe("/api/global/users", () => {
|
|
|
225
225
|
|
|
226
226
|
const response = await config.api.users.saveUser(user, 400)
|
|
227
227
|
|
|
228
|
-
expect(response.body.message).toBe(
|
|
229
|
-
`Email already in use: '${user.email}'`
|
|
230
|
-
)
|
|
228
|
+
expect(response.body.message).toBe(`Unavailable`)
|
|
231
229
|
expect(events.user.created).toBeCalledTimes(0)
|
|
232
230
|
})
|
|
233
231
|
|
|
@@ -239,9 +237,7 @@ describe("/api/global/users", () => {
|
|
|
239
237
|
delete user._id
|
|
240
238
|
const response = await config.api.users.saveUser(user, 400)
|
|
241
239
|
|
|
242
|
-
expect(response.body.message).toBe(
|
|
243
|
-
`Email already in use: '${user.email}'`
|
|
244
|
-
)
|
|
240
|
+
expect(response.body.message).toBe(`Unavailable`)
|
|
245
241
|
expect(events.user.created).toBeCalledTimes(0)
|
|
246
242
|
})
|
|
247
243
|
})
|
|
@@ -253,9 +249,7 @@ describe("/api/global/users", () => {
|
|
|
253
249
|
|
|
254
250
|
const response = await config.api.users.saveUser(user, 400)
|
|
255
251
|
|
|
256
|
-
expect(response.body.message).toBe(
|
|
257
|
-
`Email already in use: '${user.email}'`
|
|
258
|
-
)
|
|
252
|
+
expect(response.body.message).toBe(`Unavailable`)
|
|
259
253
|
expect(events.user.created).toBeCalledTimes(0)
|
|
260
254
|
})
|
|
261
255
|
|
package/src/api/routes/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Router from "@koa/router"
|
|
2
|
-
import { api
|
|
2
|
+
import { api } from "@budibase/pro"
|
|
3
3
|
import userRoutes from "./global/users"
|
|
4
4
|
import configRoutes from "./global/configs"
|
|
5
5
|
import workspaceRoutes from "./global/workspaces"
|
|
@@ -17,6 +17,9 @@ import migrationRoutes from "./system/migrations"
|
|
|
17
17
|
import accountRoutes from "./system/accounts"
|
|
18
18
|
import restoreRoutes from "./system/restore"
|
|
19
19
|
|
|
20
|
+
let userGroupRoutes = api.groups
|
|
21
|
+
let auditLogRoutes = api.auditLogs
|
|
22
|
+
|
|
20
23
|
export const routes: Router[] = [
|
|
21
24
|
configRoutes,
|
|
22
25
|
userRoutes,
|
|
@@ -30,11 +33,10 @@ export const routes: Router[] = [
|
|
|
30
33
|
statusRoutes,
|
|
31
34
|
selfRoutes,
|
|
32
35
|
licenseRoutes,
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
userGroupRoutes,
|
|
37
|
+
auditLogRoutes,
|
|
35
38
|
migrationRoutes,
|
|
36
39
|
accountRoutes,
|
|
37
40
|
restoreRoutes,
|
|
38
41
|
eventRoutes,
|
|
39
|
-
pro.scim,
|
|
40
42
|
]
|
package/src/environment.ts
CHANGED
|
@@ -47,6 +47,7 @@ const environment = {
|
|
|
47
47
|
// flags
|
|
48
48
|
NODE_ENV: process.env.NODE_ENV,
|
|
49
49
|
SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED || ""),
|
|
50
|
+
LOG_LEVEL: process.env.LOG_LEVEL,
|
|
50
51
|
MULTI_TENANCY: process.env.MULTI_TENANCY,
|
|
51
52
|
DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL,
|
|
52
53
|
SMTP_FALLBACK_ENABLED: process.env.SMTP_FALLBACK_ENABLED,
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,10 @@ if (process.env.DD_APM_ENABLED) {
|
|
|
2
2
|
require("./ddApm")
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
if (process.env.ELASTIC_APM_ENABLED) {
|
|
6
|
+
require("./elasticApm")
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
// need to load environment first
|
|
6
10
|
import env from "./environment"
|
|
7
11
|
import { Scope } from "@sentry/node"
|
|
@@ -27,11 +31,10 @@ import api from "./api"
|
|
|
27
31
|
import * as redis from "./utilities/redis"
|
|
28
32
|
const Sentry = require("@sentry/node")
|
|
29
33
|
const koaSession = require("koa-session")
|
|
34
|
+
const logger = require("koa-pino-logger")
|
|
30
35
|
const { userAgent } = require("koa-useragent")
|
|
31
36
|
|
|
32
37
|
import destroyable from "server-destroy"
|
|
33
|
-
import { initPro } from "./initPro"
|
|
34
|
-
import { handleScimBody } from "./middleware/handleScimBody"
|
|
35
38
|
|
|
36
39
|
// configure events to use the pro audit log write
|
|
37
40
|
// can't integrate directly into backend-core due to cyclic issues
|
|
@@ -51,12 +54,10 @@ const app: Application = new Koa()
|
|
|
51
54
|
app.keys = ["secret", "key"]
|
|
52
55
|
|
|
53
56
|
// set up top level koa middleware
|
|
54
|
-
app.use(handleScimBody)
|
|
55
57
|
app.use(koaBody({ multipart: true }))
|
|
56
|
-
|
|
57
58
|
app.use(koaSession(app))
|
|
58
|
-
app.use(middleware.
|
|
59
|
-
app.use(
|
|
59
|
+
app.use(middleware.logging)
|
|
60
|
+
app.use(logger(logging.pinoSettings()))
|
|
60
61
|
app.use(userAgent)
|
|
61
62
|
|
|
62
63
|
// authentication
|
|
@@ -107,7 +108,6 @@ const shutdown = () => {
|
|
|
107
108
|
|
|
108
109
|
export default server.listen(parseInt(env.PORT || "4002"), async () => {
|
|
109
110
|
console.log(`Worker running on ${JSON.stringify(server.address())}`)
|
|
110
|
-
await initPro()
|
|
111
111
|
await redis.init()
|
|
112
112
|
})
|
|
113
113
|
|
package/src/sdk/users/users.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import env from "../../environment"
|
|
2
|
+
import * as apps from "../../utilities/appService"
|
|
2
3
|
import * as eventHelpers from "./events"
|
|
3
4
|
import {
|
|
4
5
|
accounts,
|
|
@@ -14,8 +15,6 @@ import {
|
|
|
14
15
|
utils,
|
|
15
16
|
ViewName,
|
|
16
17
|
env as coreEnv,
|
|
17
|
-
context,
|
|
18
|
-
EmailUnavailableError,
|
|
19
18
|
} from "@budibase/backend-core"
|
|
20
19
|
import {
|
|
21
20
|
AccountMetadata,
|
|
@@ -29,6 +28,7 @@ import {
|
|
|
29
28
|
PlatformUser,
|
|
30
29
|
PlatformUserByEmail,
|
|
31
30
|
RowResponse,
|
|
31
|
+
SearchUsersRequest,
|
|
32
32
|
User,
|
|
33
33
|
SaveUserOpts,
|
|
34
34
|
Account,
|
|
@@ -38,6 +38,8 @@ import { EmailTemplatePurpose } from "../../constants"
|
|
|
38
38
|
import * as pro from "@budibase/pro"
|
|
39
39
|
import * as accountSdk from "../accounts"
|
|
40
40
|
|
|
41
|
+
const PAGE_LIMIT = 8
|
|
42
|
+
|
|
41
43
|
export const allUsers = async () => {
|
|
42
44
|
const db = tenancy.getGlobalDB()
|
|
43
45
|
const response = await db.allDocs(
|
|
@@ -67,6 +69,43 @@ export const getUsersByAppAccess = async (appId?: string) => {
|
|
|
67
69
|
return response
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
export const paginatedUsers = async ({
|
|
73
|
+
page,
|
|
74
|
+
email,
|
|
75
|
+
appId,
|
|
76
|
+
}: SearchUsersRequest = {}) => {
|
|
77
|
+
const db = tenancy.getGlobalDB()
|
|
78
|
+
// get one extra document, to have the next page
|
|
79
|
+
const opts: any = {
|
|
80
|
+
include_docs: true,
|
|
81
|
+
limit: PAGE_LIMIT + 1,
|
|
82
|
+
}
|
|
83
|
+
// add a startkey if the page was specified (anchor)
|
|
84
|
+
if (page) {
|
|
85
|
+
opts.startkey = page
|
|
86
|
+
}
|
|
87
|
+
// property specifies what to use for the page/anchor
|
|
88
|
+
let userList,
|
|
89
|
+
property = "_id",
|
|
90
|
+
getKey
|
|
91
|
+
if (appId) {
|
|
92
|
+
userList = await usersCore.searchGlobalUsersByApp(appId, opts)
|
|
93
|
+
getKey = (doc: any) => usersCore.getGlobalUserByAppPage(appId, doc)
|
|
94
|
+
} else if (email) {
|
|
95
|
+
userList = await usersCore.searchGlobalUsersByEmail(email, opts)
|
|
96
|
+
property = "email"
|
|
97
|
+
} else {
|
|
98
|
+
// no search, query allDocs
|
|
99
|
+
const response = await db.allDocs(dbUtils.getGlobalUserParams(null, opts))
|
|
100
|
+
userList = response.rows.map((row: any) => row.doc)
|
|
101
|
+
}
|
|
102
|
+
return dbUtils.pagination(userList, PAGE_LIMIT, {
|
|
103
|
+
paginate: true,
|
|
104
|
+
property,
|
|
105
|
+
getKey,
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
70
109
|
export async function getUserByEmail(email: string) {
|
|
71
110
|
return usersCore.getGlobalUserByEmail(email)
|
|
72
111
|
}
|
|
@@ -159,7 +198,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
|
|
|
159
198
|
if (env.MULTI_TENANCY) {
|
|
160
199
|
const tenantUser = await getPlatformUser(email)
|
|
161
200
|
if (tenantUser != null && tenantUser.tenantId !== tenantId) {
|
|
162
|
-
throw
|
|
201
|
+
throw `Unavailable`
|
|
163
202
|
}
|
|
164
203
|
}
|
|
165
204
|
|
|
@@ -167,7 +206,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
|
|
|
167
206
|
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
168
207
|
const account = await accounts.getAccount(email)
|
|
169
208
|
if (account && account.verified && account.tenantId !== tenantId) {
|
|
170
|
-
throw
|
|
209
|
+
throw `Unavailable`
|
|
171
210
|
}
|
|
172
211
|
}
|
|
173
212
|
}
|
|
@@ -238,7 +277,7 @@ export const save = async (
|
|
|
238
277
|
// no id was specified - load from email instead
|
|
239
278
|
dbUser = await usersCore.getGlobalUserByEmail(email)
|
|
240
279
|
if (dbUser && dbUser._id !== _id) {
|
|
241
|
-
throw
|
|
280
|
+
throw `Unavailable`
|
|
242
281
|
}
|
|
243
282
|
}
|
|
244
283
|
|
|
@@ -278,6 +317,9 @@ export const save = async (
|
|
|
278
317
|
await platform.users.addUser(tenantId, builtUser._id!, builtUser.email)
|
|
279
318
|
await cache.user.invalidateUser(response.id)
|
|
280
319
|
|
|
320
|
+
// let server know to sync user
|
|
321
|
+
await apps.syncUserInApps(_id, dbUser)
|
|
322
|
+
|
|
281
323
|
await Promise.all(groupPromises)
|
|
282
324
|
|
|
283
325
|
// finally returned the saved user from the db
|
|
@@ -427,6 +469,7 @@ export const bulkCreate = async (
|
|
|
427
469
|
// instead of relying on looping tenant creation
|
|
428
470
|
await platform.users.addUser(tenantId, user._id, user.email)
|
|
429
471
|
await eventHelpers.handleSaveEvents(user, undefined)
|
|
472
|
+
await apps.syncUserInApps(user._id)
|
|
430
473
|
}
|
|
431
474
|
|
|
432
475
|
const saved = usersToBulkSave.map(user => {
|
|
@@ -540,7 +583,7 @@ export const bulkDelete = async (
|
|
|
540
583
|
return response
|
|
541
584
|
}
|
|
542
585
|
|
|
543
|
-
export const destroy = async (id: string) => {
|
|
586
|
+
export const destroy = async (id: string, currentUser: any) => {
|
|
544
587
|
const db = tenancy.getGlobalDB()
|
|
545
588
|
const dbUser = (await db.get(id)) as User
|
|
546
589
|
const userId = dbUser._id as string
|
|
@@ -550,7 +593,7 @@ export const destroy = async (id: string) => {
|
|
|
550
593
|
const email = dbUser.email
|
|
551
594
|
const account = await accounts.getAccount(email)
|
|
552
595
|
if (account) {
|
|
553
|
-
if (
|
|
596
|
+
if (email === currentUser.email) {
|
|
554
597
|
throw new HTTPError('Please visit "Account" to delete this user', 400)
|
|
555
598
|
} else {
|
|
556
599
|
throw new HTTPError("Account holder cannot be deleted", 400)
|
|
@@ -565,6 +608,8 @@ export const destroy = async (id: string) => {
|
|
|
565
608
|
await eventHelpers.handleDeleteEvents(dbUser)
|
|
566
609
|
await cache.user.invalidateUser(userId)
|
|
567
610
|
await sessions.invalidateSessions(userId, { reason: "deletion" })
|
|
611
|
+
// let server know to sync user
|
|
612
|
+
await apps.syncUserInApps(userId, dbUser)
|
|
568
613
|
}
|
|
569
614
|
|
|
570
615
|
const bulkDeleteProcessing = async (dbUser: User) => {
|
|
@@ -573,6 +618,8 @@ const bulkDeleteProcessing = async (dbUser: User) => {
|
|
|
573
618
|
await eventHelpers.handleDeleteEvents(dbUser)
|
|
574
619
|
await cache.user.invalidateUser(userId)
|
|
575
620
|
await sessions.invalidateSessions(userId, { reason: "bulk-deletion" })
|
|
621
|
+
// let server know to sync user
|
|
622
|
+
await apps.syncUserInApps(userId, dbUser)
|
|
576
623
|
}
|
|
577
624
|
|
|
578
625
|
export const invite = async (
|
|
@@ -20,18 +20,9 @@ import {
|
|
|
20
20
|
auth,
|
|
21
21
|
constants,
|
|
22
22
|
env as coreEnv,
|
|
23
|
-
db as dbCore,
|
|
24
|
-
encryption,
|
|
25
|
-
utils,
|
|
26
23
|
} from "@budibase/backend-core"
|
|
27
24
|
import structures, { CSRF_TOKEN } from "./structures"
|
|
28
|
-
import {
|
|
29
|
-
SaveUserResponse,
|
|
30
|
-
User,
|
|
31
|
-
AuthToken,
|
|
32
|
-
SCIMConfig,
|
|
33
|
-
ConfigType,
|
|
34
|
-
} from "@budibase/types"
|
|
25
|
+
import { SaveUserResponse, User, AuthToken } from "@budibase/types"
|
|
35
26
|
import API from "./api"
|
|
36
27
|
|
|
37
28
|
class TestConfiguration {
|
|
@@ -40,7 +31,6 @@ class TestConfiguration {
|
|
|
40
31
|
api: API
|
|
41
32
|
tenantId: string
|
|
42
33
|
user?: User
|
|
43
|
-
apiKey?: string
|
|
44
34
|
userPassword = "test"
|
|
45
35
|
|
|
46
36
|
constructor(opts: { openServer: boolean } = { openServer: true }) {
|
|
@@ -59,12 +49,6 @@ class TestConfiguration {
|
|
|
59
49
|
this.api = new API(this)
|
|
60
50
|
}
|
|
61
51
|
|
|
62
|
-
async useNewTenant() {
|
|
63
|
-
this.tenantId = structures.tenant.id()
|
|
64
|
-
|
|
65
|
-
await this.beforeAll()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
52
|
getRequest() {
|
|
69
53
|
return this.request
|
|
70
54
|
}
|
|
@@ -217,12 +201,6 @@ class TestConfiguration {
|
|
|
217
201
|
return { [constants.Header.API_KEY]: coreEnv.INTERNAL_API_KEY }
|
|
218
202
|
}
|
|
219
203
|
|
|
220
|
-
bearerAPIHeaders() {
|
|
221
|
-
return {
|
|
222
|
-
[constants.Header.AUTHORIZATION]: `Bearer ${this.apiKey}`,
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
204
|
adminOnlyResponse = () => {
|
|
227
205
|
return { message: "Admin user only endpoint.", status: 403 }
|
|
228
206
|
}
|
|
@@ -235,20 +213,6 @@ class TestConfiguration {
|
|
|
235
213
|
})
|
|
236
214
|
await context.doInTenant(this.tenantId!, async () => {
|
|
237
215
|
this.user = await this.createUser(user)
|
|
238
|
-
|
|
239
|
-
const db = context.getGlobalDB()
|
|
240
|
-
|
|
241
|
-
const id = dbCore.generateDevInfoID(this.user._id)
|
|
242
|
-
// TODO: dry
|
|
243
|
-
this.apiKey = encryption.encrypt(
|
|
244
|
-
`${this.tenantId}${dbCore.SEPARATOR}${utils.newid()}`
|
|
245
|
-
)
|
|
246
|
-
const devInfo = {
|
|
247
|
-
_id: id,
|
|
248
|
-
userId: this.user._id,
|
|
249
|
-
apiKey: this.apiKey,
|
|
250
|
-
}
|
|
251
|
-
await db.put(devInfo)
|
|
252
216
|
})
|
|
253
217
|
}
|
|
254
218
|
|
|
@@ -341,19 +305,6 @@ class TestConfiguration {
|
|
|
341
305
|
controllers.config.save
|
|
342
306
|
)
|
|
343
307
|
}
|
|
344
|
-
|
|
345
|
-
// CONFIGS - SCIM
|
|
346
|
-
|
|
347
|
-
async setSCIMConfig(enabled: boolean) {
|
|
348
|
-
await this.deleteConfig(Config.SCIM)
|
|
349
|
-
const config: SCIMConfig = {
|
|
350
|
-
type: ConfigType.SCIM,
|
|
351
|
-
config: { enabled },
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
await this._req(config, null, controllers.config.save)
|
|
355
|
-
return config
|
|
356
|
-
}
|
|
357
308
|
}
|
|
358
309
|
|
|
359
310
|
export default TestConfiguration
|
package/src/tests/api/index.ts
CHANGED
|
@@ -15,9 +15,6 @@ import { RolesAPI } from "./roles"
|
|
|
15
15
|
import { TemplatesAPI } from "./templates"
|
|
16
16
|
import { LicenseAPI } from "./license"
|
|
17
17
|
import { AuditLogAPI } from "./auditLogs"
|
|
18
|
-
import { ScimUsersAPI } from "./scim/users"
|
|
19
|
-
import { ScimGroupsAPI } from "./scim/groups"
|
|
20
|
-
|
|
21
18
|
export default class API {
|
|
22
19
|
accounts: AccountAPI
|
|
23
20
|
auth: AuthAPI
|
|
@@ -35,8 +32,6 @@ export default class API {
|
|
|
35
32
|
templates: TemplatesAPI
|
|
36
33
|
license: LicenseAPI
|
|
37
34
|
auditLogs: AuditLogAPI
|
|
38
|
-
scimUsersAPI: ScimUsersAPI
|
|
39
|
-
scimGroupsAPI: ScimGroupsAPI
|
|
40
35
|
|
|
41
36
|
constructor(config: TestConfiguration) {
|
|
42
37
|
this.accounts = new AccountAPI(config)
|
|
@@ -55,7 +50,5 @@ export default class API {
|
|
|
55
50
|
this.templates = new TemplatesAPI(config)
|
|
56
51
|
this.license = new LicenseAPI(config)
|
|
57
52
|
this.auditLogs = new AuditLogAPI(config)
|
|
58
|
-
this.scimUsersAPI = new ScimUsersAPI(config)
|
|
59
|
-
this.scimGroupsAPI = new ScimGroupsAPI(config)
|
|
60
53
|
}
|
|
61
54
|
}
|
package/src/tests/jestEnv.ts
CHANGED
|
@@ -2,6 +2,7 @@ process.env.SELF_HOSTED = "0"
|
|
|
2
2
|
process.env.NODE_ENV = "jest"
|
|
3
3
|
process.env.JWT_SECRET = "test-jwtsecret"
|
|
4
4
|
process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
|
|
5
|
+
process.env.ENABLE_4XX_HTTP_LOGGING = "0"
|
|
5
6
|
process.env.MULTI_TENANCY = "1"
|
|
6
7
|
process.env.MINIO_URL = "http://localhost"
|
|
7
8
|
process.env.MINIO_ACCESS_KEY = "test"
|
package/src/tests/jestSetup.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export enum LogLevel {
|
|
2
|
+
TRACE = "trace",
|
|
3
|
+
DEBUG = "debug",
|
|
4
|
+
INFO = "info",
|
|
5
|
+
WARN = "warn",
|
|
6
|
+
ERROR = "error",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const LOG_INDEX: { [key in LogLevel]: number } = {
|
|
10
|
+
[LogLevel.TRACE]: 1,
|
|
11
|
+
[LogLevel.DEBUG]: 2,
|
|
12
|
+
[LogLevel.INFO]: 3,
|
|
13
|
+
[LogLevel.WARN]: 4,
|
|
14
|
+
[LogLevel.ERROR]: 5,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const setIndex = LOG_INDEX[process.env.LOG_LEVEL as LogLevel]
|
|
18
|
+
|
|
19
|
+
if (setIndex > LOG_INDEX.trace) {
|
|
20
|
+
global.console.trace = jest.fn()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (setIndex > LOG_INDEX.debug) {
|
|
24
|
+
global.console.debug = jest.fn()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (setIndex > LOG_INDEX.info) {
|
|
28
|
+
global.console.info = jest.fn()
|
|
29
|
+
global.console.log = jest.fn()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (setIndex > LOG_INDEX.warn) {
|
|
33
|
+
global.console.warn = jest.fn()
|
|
34
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fetch from "node-fetch"
|
|
2
|
+
import {
|
|
3
|
+
constants,
|
|
4
|
+
tenancy,
|
|
5
|
+
logging,
|
|
6
|
+
env as coreEnv,
|
|
7
|
+
} from "@budibase/backend-core"
|
|
8
|
+
import { checkSlashesInUrl } from "../utilities"
|
|
9
|
+
import env from "../environment"
|
|
10
|
+
import { SyncUserRequest, User } from "@budibase/types"
|
|
11
|
+
|
|
12
|
+
async function makeAppRequest(url: string, method: string, body: any) {
|
|
13
|
+
if (env.isTest()) {
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
const request: any = { headers: {} }
|
|
17
|
+
request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY
|
|
18
|
+
if (tenancy.isTenantIdSet()) {
|
|
19
|
+
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
|
|
20
|
+
}
|
|
21
|
+
if (body) {
|
|
22
|
+
request.headers["Content-Type"] = "application/json"
|
|
23
|
+
request.body = JSON.stringify(body)
|
|
24
|
+
}
|
|
25
|
+
request.method = method
|
|
26
|
+
|
|
27
|
+
// add x-budibase-correlation-id header
|
|
28
|
+
logging.correlation.setHeader(request.headers)
|
|
29
|
+
|
|
30
|
+
return fetch(checkSlashesInUrl(env.APPS_URL + url), request)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function syncUserInApps(userId: string, previousUser?: User) {
|
|
34
|
+
const body: SyncUserRequest = {
|
|
35
|
+
previousUser,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const response = await makeAppRequest(
|
|
39
|
+
`/api/users/metadata/sync/${userId}`,
|
|
40
|
+
"POST",
|
|
41
|
+
body
|
|
42
|
+
)
|
|
43
|
+
if (response && response.status !== 200) {
|
|
44
|
+
throw "Unable to sync user."
|
|
45
|
+
}
|
|
46
|
+
}
|