@budibase/worker 2.3.18-alpha.2 → 2.3.18-alpha.21
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 +3 -3
- 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 +17 -12
- 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 +31 -10
- package/src/tests/api/auditLogs.ts +26 -0
- 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 +39 -1
- package/src/utilities/templates.ts +4 -9
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.21",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -22,7 +22,7 @@
|
|
|
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
|
-
"test": "
|
|
25
|
+
"test": "bash scripts/test.sh",
|
|
26
26
|
"test:watch": "jest --watch",
|
|
27
27
|
"env:multi:enable": "node scripts/multiTenancy.js enable",
|
|
28
28
|
"env:multi:disable": "node scripts/multiTenancy.js disable",
|
|
@@ -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.21",
|
|
40
|
+
"@budibase/pro": "2.3.18-alpha.20",
|
|
41
|
+
"@budibase/string-templates": "2.3.18-alpha.21",
|
|
42
|
+
"@budibase/types": "2.3.18-alpha.21",
|
|
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": "2dfb0df2890303846ae7e6835b7d570aad57f775"
|
|
104
105
|
}
|
package/scripts/test.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
if [[ -n $CI ]]
|
|
4
|
+
then
|
|
5
|
+
# --runInBand performs better in ci where resources are limited
|
|
6
|
+
echo "jest --coverage --runInBand"
|
|
7
|
+
jest --coverage --runInBand
|
|
8
|
+
else
|
|
9
|
+
# --maxWorkers performs better in development
|
|
10
|
+
echo "jest --coverage --maxWorkers=2"
|
|
11
|
+
jest --coverage --maxWorkers=2
|
|
12
|
+
fi
|
|
@@ -2,10 +2,9 @@ import {
|
|
|
2
2
|
auth as authCore,
|
|
3
3
|
constants,
|
|
4
4
|
context,
|
|
5
|
-
db as dbCore,
|
|
6
5
|
events,
|
|
7
|
-
tenancy,
|
|
8
6
|
utils as utilsCore,
|
|
7
|
+
configs,
|
|
9
8
|
} from "@budibase/backend-core"
|
|
10
9
|
import {
|
|
11
10
|
ConfigType,
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
SSOUser,
|
|
16
15
|
PasswordResetRequest,
|
|
17
16
|
PasswordResetUpdateRequest,
|
|
17
|
+
GoogleInnerConfig,
|
|
18
18
|
} from "@budibase/types"
|
|
19
19
|
import env from "../../../environment"
|
|
20
20
|
|
|
@@ -61,16 +61,16 @@ export const login = async (ctx: Ctx<LoginRequest>, next: any) => {
|
|
|
61
61
|
const email = ctx.request.body.username
|
|
62
62
|
|
|
63
63
|
const user = await userSdk.getUserByEmail(email)
|
|
64
|
-
if (user && (await userSdk.
|
|
65
|
-
ctx.throw(400, "
|
|
64
|
+
if (user && (await userSdk.isPreventPasswordActions(user))) {
|
|
65
|
+
ctx.throw(400, "Password login is disabled for this user")
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
return passport.authenticate(
|
|
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
|
}
|
|
@@ -163,8 +163,8 @@ export const datasourceAuth = async (ctx: any, next: any) => {
|
|
|
163
163
|
|
|
164
164
|
// GOOGLE SSO
|
|
165
165
|
|
|
166
|
-
export async function googleCallbackUrl(config?:
|
|
167
|
-
return ssoCallbackUrl(
|
|
166
|
+
export async function googleCallbackUrl(config?: GoogleInnerConfig) {
|
|
167
|
+
return ssoCallbackUrl(ConfigType.GOOGLE, config)
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/**
|
|
@@ -172,12 +172,10 @@ export async function googleCallbackUrl(config?: { callbackURL?: string }) {
|
|
|
172
172
|
* On a successful login, you will be redirected to the googleAuth callback route.
|
|
173
173
|
*/
|
|
174
174
|
export const googlePreAuth = async (ctx: any, next: any) => {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
workspace: ctx.query.workspace,
|
|
180
|
-
})
|
|
175
|
+
const config = await configs.getGoogleConfig()
|
|
176
|
+
if (!config) {
|
|
177
|
+
return ctx.throw(400, "Google config not found")
|
|
178
|
+
}
|
|
181
179
|
let callbackUrl = await googleCallbackUrl(config)
|
|
182
180
|
const strategy = await google.strategyFactory(
|
|
183
181
|
config,
|
|
@@ -193,12 +191,10 @@ export const googlePreAuth = async (ctx: any, next: any) => {
|
|
|
193
191
|
}
|
|
194
192
|
|
|
195
193
|
export const googleCallback = async (ctx: any, next: any) => {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
workspace: ctx.query.workspace,
|
|
201
|
-
})
|
|
194
|
+
const config = await configs.getGoogleConfig()
|
|
195
|
+
if (!config) {
|
|
196
|
+
return ctx.throw(400, "Google config not found")
|
|
197
|
+
}
|
|
202
198
|
const callbackUrl = await googleCallbackUrl(config)
|
|
203
199
|
const strategy = await google.strategyFactory(
|
|
204
200
|
config,
|
|
@@ -211,8 +207,8 @@ export const googleCallback = async (ctx: any, next: any) => {
|
|
|
211
207
|
{ successRedirect: "/", failureRedirect: "/error" },
|
|
212
208
|
async (err: any, user: SSOUser, info: any) => {
|
|
213
209
|
await passportCallback(ctx, user, err, info)
|
|
214
|
-
await context.identity.doInUserContext(user, async () => {
|
|
215
|
-
await events.auth.login("google-internal")
|
|
210
|
+
await context.identity.doInUserContext(user, ctx, async () => {
|
|
211
|
+
await events.auth.login("google-internal", user.email)
|
|
216
212
|
})
|
|
217
213
|
ctx.redirect("/")
|
|
218
214
|
}
|
|
@@ -221,25 +217,20 @@ export const googleCallback = async (ctx: any, next: any) => {
|
|
|
221
217
|
|
|
222
218
|
// OIDC SSO
|
|
223
219
|
|
|
224
|
-
export async function oidcCallbackUrl(
|
|
225
|
-
return ssoCallbackUrl(
|
|
220
|
+
export async function oidcCallbackUrl() {
|
|
221
|
+
return ssoCallbackUrl(ConfigType.OIDC)
|
|
226
222
|
}
|
|
227
223
|
|
|
228
224
|
export const oidcStrategyFactory = async (ctx: any, configId: any) => {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
})
|
|
225
|
+
const config = await configs.getOIDCConfig()
|
|
226
|
+
if (!config) {
|
|
227
|
+
return ctx.throw(400, "OIDC config not found")
|
|
228
|
+
}
|
|
234
229
|
|
|
235
|
-
|
|
236
|
-
let callbackUrl = await oidcCallbackUrl(chosenConfig)
|
|
230
|
+
let callbackUrl = await oidcCallbackUrl()
|
|
237
231
|
|
|
238
232
|
//Remote Config
|
|
239
|
-
const enrichedConfig = await oidc.fetchStrategyConfig(
|
|
240
|
-
chosenConfig,
|
|
241
|
-
callbackUrl
|
|
242
|
-
)
|
|
233
|
+
const enrichedConfig = await oidc.fetchStrategyConfig(config, callbackUrl)
|
|
243
234
|
return oidc.strategyFactory(enrichedConfig, userSdk.save)
|
|
244
235
|
}
|
|
245
236
|
|
|
@@ -247,23 +238,23 @@ export const oidcStrategyFactory = async (ctx: any, configId: any) => {
|
|
|
247
238
|
* The initial call that OIDC authentication makes to take you to the configured OIDC login screen.
|
|
248
239
|
* On a successful login, you will be redirected to the oidcAuth callback route.
|
|
249
240
|
*/
|
|
250
|
-
export const oidcPreAuth = async (ctx:
|
|
241
|
+
export const oidcPreAuth = async (ctx: Ctx, next: any) => {
|
|
251
242
|
const { configId } = ctx.params
|
|
243
|
+
if (!configId) {
|
|
244
|
+
ctx.throw(400, "OIDC config id is required")
|
|
245
|
+
}
|
|
252
246
|
const strategy = await oidcStrategyFactory(ctx, configId)
|
|
253
247
|
|
|
254
248
|
setCookie(ctx, configId, Cookie.OIDC_CONFIG)
|
|
255
249
|
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
|
|
250
|
+
const config = await configs.getOIDCConfigById(configId)
|
|
251
|
+
if (!config) {
|
|
252
|
+
return ctx.throw(400, "OIDC config not found")
|
|
253
|
+
}
|
|
263
254
|
|
|
264
255
|
let authScopes =
|
|
265
|
-
|
|
266
|
-
?
|
|
256
|
+
config.scopes?.length > 0
|
|
257
|
+
? config.scopes
|
|
267
258
|
: ["profile", "email", "offline_access"]
|
|
268
259
|
|
|
269
260
|
return passport.authenticate(strategy, {
|
|
@@ -281,8 +272,8 @@ export const oidcCallback = async (ctx: any, next: any) => {
|
|
|
281
272
|
{ successRedirect: "/", failureRedirect: "/error" },
|
|
282
273
|
async (err: any, user: SSOUser, info: any) => {
|
|
283
274
|
await passportCallback(ctx, user, err, info)
|
|
284
|
-
await context.identity.doInUserContext(user, async () => {
|
|
285
|
-
await events.auth.login("oidc")
|
|
275
|
+
await context.identity.doInUserContext(user, ctx, async () => {
|
|
276
|
+
await events.auth.login("oidc", user.email)
|
|
286
277
|
})
|
|
287
278
|
ctx.redirect("/")
|
|
288
279
|
}
|
|
@@ -2,38 +2,37 @@ import * as email from "../../../utilities/email"
|
|
|
2
2
|
import env from "../../../environment"
|
|
3
3
|
import { googleCallbackUrl, oidcCallbackUrl } from "./auth"
|
|
4
4
|
import {
|
|
5
|
-
events,
|
|
6
5
|
cache,
|
|
7
|
-
|
|
8
|
-
tenancy,
|
|
6
|
+
configs,
|
|
9
7
|
db as dbCore,
|
|
10
8
|
env as coreEnv,
|
|
9
|
+
events,
|
|
10
|
+
objectStore,
|
|
11
|
+
tenancy,
|
|
11
12
|
} from "@budibase/backend-core"
|
|
12
13
|
import { checkAnyUserExists } from "../../../utilities/users"
|
|
13
14
|
import {
|
|
14
|
-
|
|
15
|
-
Config as ConfigDoc,
|
|
15
|
+
Config,
|
|
16
16
|
ConfigType,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
Ctx,
|
|
18
|
+
GetPublicOIDCConfigResponse,
|
|
19
|
+
GetPublicSettingsResponse,
|
|
20
|
+
GoogleInnerConfig,
|
|
21
21
|
isGoogleConfig,
|
|
22
22
|
isOIDCConfig,
|
|
23
23
|
isSettingsConfig,
|
|
24
24
|
isSMTPConfig,
|
|
25
|
-
|
|
25
|
+
OIDCConfigs,
|
|
26
|
+
SettingsInnerConfig,
|
|
27
|
+
SSOConfig,
|
|
28
|
+
SSOConfigType,
|
|
26
29
|
UserCtx,
|
|
27
30
|
} from "@budibase/types"
|
|
31
|
+
import * as pro from "@budibase/pro"
|
|
28
32
|
|
|
29
|
-
const getEventFns = async (
|
|
33
|
+
const getEventFns = async (config: Config, existing?: Config) => {
|
|
30
34
|
const fns = []
|
|
31
35
|
|
|
32
|
-
let existing
|
|
33
|
-
if (config._id) {
|
|
34
|
-
existing = await db.get(config._id)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
36
|
if (!existing) {
|
|
38
37
|
if (isSMTPConfig(config)) {
|
|
39
38
|
fns.push(events.email.SMTPCreated)
|
|
@@ -125,31 +124,96 @@ const getEventFns = async (db: Database, config: ConfigDoc) => {
|
|
|
125
124
|
return fns
|
|
126
125
|
}
|
|
127
126
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
+
|
|
182
|
+
export async function save(ctx: UserCtx<Config>) {
|
|
183
|
+
const body = ctx.request.body
|
|
184
|
+
const type = body.type
|
|
185
|
+
const config = body.config
|
|
186
|
+
|
|
187
|
+
const existingConfig = await configs.getConfig(type)
|
|
188
|
+
let eventFns = await getEventFns(ctx.request.body, existingConfig)
|
|
189
|
+
|
|
190
|
+
if (existingConfig) {
|
|
191
|
+
body._rev = existingConfig._rev
|
|
139
192
|
}
|
|
193
|
+
|
|
140
194
|
try {
|
|
141
195
|
// verify the configuration
|
|
142
196
|
switch (type) {
|
|
143
197
|
case ConfigType.SMTP:
|
|
144
198
|
await email.verifyConfig(config)
|
|
145
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
|
|
146
209
|
}
|
|
147
210
|
} catch (err: any) {
|
|
148
211
|
ctx.throw(400, err)
|
|
149
212
|
}
|
|
150
213
|
|
|
151
214
|
try {
|
|
152
|
-
|
|
215
|
+
body._id = configs.generateConfigID(type)
|
|
216
|
+
const response = await configs.save(body)
|
|
153
217
|
await cache.bustCache(cache.CacheKey.CHECKLIST)
|
|
154
218
|
await cache.bustCache(cache.CacheKey.ANALYTICS_ENABLED)
|
|
155
219
|
|
|
@@ -167,44 +231,11 @@ export async function save(ctx: UserCtx) {
|
|
|
167
231
|
}
|
|
168
232
|
}
|
|
169
233
|
|
|
170
|
-
export async function fetch(ctx: UserCtx) {
|
|
171
|
-
const db = tenancy.getGlobalDB()
|
|
172
|
-
const response = await db.allDocs(
|
|
173
|
-
dbCore.getConfigParams(
|
|
174
|
-
{ type: ctx.params.type },
|
|
175
|
-
{
|
|
176
|
-
include_docs: true,
|
|
177
|
-
}
|
|
178
|
-
)
|
|
179
|
-
)
|
|
180
|
-
ctx.body = response.rows.map(row => row.doc)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Gets the most granular config for a particular configuration type.
|
|
185
|
-
* The hierarchy is type -> workspace -> user.
|
|
186
|
-
*/
|
|
187
234
|
export async function find(ctx: UserCtx) {
|
|
188
|
-
const db = tenancy.getGlobalDB()
|
|
189
|
-
|
|
190
|
-
const { userId, workspaceId } = ctx.query
|
|
191
|
-
if (workspaceId && userId) {
|
|
192
|
-
const workspace = await db.get(workspaceId as string)
|
|
193
|
-
const userInWorkspace = workspace.users.some(
|
|
194
|
-
(workspaceUser: any) => workspaceUser === userId
|
|
195
|
-
)
|
|
196
|
-
if (!ctx.user!.admin && !userInWorkspace) {
|
|
197
|
-
ctx.throw(400, `User is not in specified workspace: ${workspace}.`)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
235
|
try {
|
|
202
236
|
// Find the config with the most granular scope based on context
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
user: userId,
|
|
206
|
-
workspace: workspaceId,
|
|
207
|
-
})
|
|
237
|
+
const type = ctx.params.type
|
|
238
|
+
const scopedConfig = await configs.getConfig(type)
|
|
208
239
|
|
|
209
240
|
if (scopedConfig) {
|
|
210
241
|
ctx.body = scopedConfig
|
|
@@ -217,85 +248,70 @@ export async function find(ctx: UserCtx) {
|
|
|
217
248
|
}
|
|
218
249
|
}
|
|
219
250
|
|
|
220
|
-
export async function publicOidc(ctx: Ctx) {
|
|
221
|
-
const db = tenancy.getGlobalDB()
|
|
251
|
+
export async function publicOidc(ctx: Ctx<void, GetPublicOIDCConfigResponse>) {
|
|
222
252
|
try {
|
|
223
253
|
// Find the config with the most granular scope based on context
|
|
224
|
-
const
|
|
225
|
-
type: ConfigType.OIDC,
|
|
226
|
-
})
|
|
254
|
+
const config = await configs.getOIDCConfig()
|
|
227
255
|
|
|
228
|
-
if (!
|
|
229
|
-
ctx.body =
|
|
256
|
+
if (!config) {
|
|
257
|
+
ctx.body = []
|
|
230
258
|
} else {
|
|
231
|
-
ctx.body =
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
259
|
+
ctx.body = [
|
|
260
|
+
{
|
|
261
|
+
logo: config.logo,
|
|
262
|
+
name: config.name,
|
|
263
|
+
uuid: config.uuid,
|
|
264
|
+
},
|
|
265
|
+
]
|
|
236
266
|
}
|
|
237
267
|
} catch (err: any) {
|
|
238
268
|
ctx.throw(err.status, err)
|
|
239
269
|
}
|
|
240
270
|
}
|
|
241
271
|
|
|
242
|
-
export async function publicSettings(
|
|
243
|
-
|
|
244
|
-
|
|
272
|
+
export async function publicSettings(
|
|
273
|
+
ctx: Ctx<void, GetPublicSettingsResponse>
|
|
274
|
+
) {
|
|
245
275
|
try {
|
|
246
|
-
//
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
type: ConfigType.GOOGLE,
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
const oidcConfig = await dbCore.getScopedFullConfig(db, {
|
|
256
|
-
type: ConfigType.OIDC,
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
let config
|
|
260
|
-
if (!publicConfig) {
|
|
261
|
-
config = {
|
|
262
|
-
config: {},
|
|
263
|
-
}
|
|
264
|
-
} else {
|
|
265
|
-
config = publicConfig
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// enrich the logo url
|
|
269
|
-
// empty url means deleted
|
|
270
|
-
if (config.config.logoUrl && config.config.logoUrl !== "") {
|
|
271
|
-
config.config.logoUrl = objectStore.getGlobalFileUrl(
|
|
276
|
+
// settings
|
|
277
|
+
const configDoc = await configs.getSettingsConfigDoc()
|
|
278
|
+
const config = configDoc.config
|
|
279
|
+
// enrich the logo url - empty url means deleted
|
|
280
|
+
if (config.logoUrl && config.logoUrl !== "") {
|
|
281
|
+
config.logoUrl = objectStore.getGlobalFileUrl(
|
|
272
282
|
"settings",
|
|
273
283
|
"logoUrl",
|
|
274
|
-
config.
|
|
284
|
+
config.logoUrlEtag
|
|
275
285
|
)
|
|
276
286
|
}
|
|
277
287
|
|
|
278
|
-
// google
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
} else {
|
|
284
|
-
config.config.google = false
|
|
285
|
-
}
|
|
288
|
+
// google
|
|
289
|
+
const googleConfig = await configs.getGoogleConfig()
|
|
290
|
+
const preActivated = googleConfig?.activated == null
|
|
291
|
+
const google = preActivated || !!googleConfig?.activated
|
|
292
|
+
const _googleCallbackUrl = await googleCallbackUrl(googleConfig)
|
|
286
293
|
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
294
|
+
// oidc
|
|
295
|
+
const oidcConfig = await configs.getOIDCConfig()
|
|
296
|
+
const oidc = oidcConfig?.activated || false
|
|
297
|
+
const _oidcCallbackUrl = await oidcCallbackUrl()
|
|
290
298
|
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
config.config.oidc = oidcConfig.config.configs[0].activated
|
|
294
|
-
} else {
|
|
295
|
-
config.config.oidc = false
|
|
296
|
-
}
|
|
299
|
+
// sso enforced
|
|
300
|
+
const isSSOEnforced = await pro.features.isSSOEnforced({ config })
|
|
297
301
|
|
|
298
|
-
ctx.body =
|
|
302
|
+
ctx.body = {
|
|
303
|
+
type: ConfigType.SETTINGS,
|
|
304
|
+
_id: configDoc._id,
|
|
305
|
+
_rev: configDoc._rev,
|
|
306
|
+
config: {
|
|
307
|
+
...config,
|
|
308
|
+
google,
|
|
309
|
+
oidc,
|
|
310
|
+
isSSOEnforced,
|
|
311
|
+
oidcCallbackUrl: _oidcCallbackUrl,
|
|
312
|
+
googleCallbackUrl: _googleCallbackUrl,
|
|
313
|
+
},
|
|
314
|
+
}
|
|
299
315
|
} catch (err: any) {
|
|
300
316
|
ctx.throw(err.status, err)
|
|
301
317
|
}
|
|
@@ -319,12 +335,11 @@ export async function upload(ctx: UserCtx) {
|
|
|
319
335
|
})
|
|
320
336
|
|
|
321
337
|
// add to configuration structure
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
_id: dbCore.generateConfigID({ type }),
|
|
338
|
+
let config = await configs.getConfig(type)
|
|
339
|
+
if (!config) {
|
|
340
|
+
config = {
|
|
341
|
+
_id: configs.generateConfigID(type),
|
|
342
|
+
type,
|
|
328
343
|
config: {},
|
|
329
344
|
}
|
|
330
345
|
}
|
|
@@ -332,14 +347,14 @@ export async function upload(ctx: UserCtx) {
|
|
|
332
347
|
// save the Etag for cache bursting
|
|
333
348
|
const etag = result.ETag
|
|
334
349
|
if (etag) {
|
|
335
|
-
|
|
350
|
+
config.config[`${name}Etag`] = etag.replace(/"/g, "")
|
|
336
351
|
}
|
|
337
352
|
|
|
338
353
|
// save the file key
|
|
339
|
-
|
|
354
|
+
config.config[`${name}`] = key
|
|
340
355
|
|
|
341
356
|
// write back to db
|
|
342
|
-
await
|
|
357
|
+
await configs.save(config)
|
|
343
358
|
|
|
344
359
|
ctx.body = {
|
|
345
360
|
message: "File has been uploaded and url stored to config.",
|
|
@@ -360,7 +375,6 @@ export async function destroy(ctx: UserCtx) {
|
|
|
360
375
|
}
|
|
361
376
|
|
|
362
377
|
export async function configChecklist(ctx: Ctx) {
|
|
363
|
-
const db = tenancy.getGlobalDB()
|
|
364
378
|
const tenantId = tenancy.getTenantId()
|
|
365
379
|
|
|
366
380
|
try {
|
|
@@ -375,19 +389,13 @@ export async function configChecklist(ctx: Ctx) {
|
|
|
375
389
|
}
|
|
376
390
|
|
|
377
391
|
// They have set up SMTP
|
|
378
|
-
const smtpConfig = await
|
|
379
|
-
type: ConfigType.SMTP,
|
|
380
|
-
})
|
|
392
|
+
const smtpConfig = await configs.getSMTPConfig()
|
|
381
393
|
|
|
382
394
|
// They have set up Google Auth
|
|
383
|
-
const googleConfig = await
|
|
384
|
-
type: ConfigType.GOOGLE,
|
|
385
|
-
})
|
|
395
|
+
const googleConfig = await configs.getGoogleConfig()
|
|
386
396
|
|
|
387
397
|
// They have set up OIDC
|
|
388
|
-
const oidcConfig = await
|
|
389
|
-
type: ConfigType.OIDC,
|
|
390
|
-
})
|
|
398
|
+
const oidcConfig = await configs.getOIDCConfig()
|
|
391
399
|
|
|
392
400
|
// They have set up a global user
|
|
393
401
|
const userExists = await checkAnyUserExists()
|