@budibase/worker 1.0.105-alpha.4 → 1.0.105-alpha.40
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/nodemon.json +1 -1
- package/package.json +11 -6
- package/src/api/controllers/global/{auth.js → auth.ts} +31 -27
- package/src/api/controllers/global/license.ts +30 -0
- package/src/api/controllers/global/self.js +32 -7
- package/src/api/controllers/global/{users.js → users.ts} +38 -34
- package/src/api/index.js +6 -1
- package/src/api/routes/global/license.ts +12 -0
- package/src/api/routes/index.js +2 -0
- package/src/api/routes/tests/auth.spec.js +2 -1
- package/src/index.ts +4 -0
package/nodemon.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/worker",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "1.0.105-alpha.
|
|
4
|
+
"version": "1.0.105-alpha.40",
|
|
5
5
|
"description": "Budibase background service",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -31,14 +31,17 @@
|
|
|
31
31
|
"author": "Budibase",
|
|
32
32
|
"license": "GPL-3.0",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@budibase/backend-core": "^1.0.105-alpha.
|
|
35
|
-
"@budibase/
|
|
34
|
+
"@budibase/backend-core": "^1.0.105-alpha.40",
|
|
35
|
+
"@budibase/pro": "1.0.105-alpha.39",
|
|
36
|
+
"@budibase/string-templates": "^1.0.105-alpha.40",
|
|
36
37
|
"@koa/router": "^8.0.0",
|
|
37
|
-
"@sentry/node": "
|
|
38
|
+
"@sentry/node": "6.17.7",
|
|
38
39
|
"@techpass/passport-openidconnect": "^0.3.0",
|
|
40
|
+
"@types/global-agent": "^2.1.1",
|
|
39
41
|
"aws-sdk": "^2.811.0",
|
|
40
42
|
"bcryptjs": "^2.4.3",
|
|
41
43
|
"dotenv": "^8.2.0",
|
|
44
|
+
"global-agent": "^3.0.0",
|
|
42
45
|
"got": "^11.8.1",
|
|
43
46
|
"joi": "^17.4.0",
|
|
44
47
|
"koa": "^2.7.0",
|
|
@@ -63,7 +66,9 @@
|
|
|
63
66
|
"@types/jest": "^26.0.23",
|
|
64
67
|
"@types/koa": "^2.13.3",
|
|
65
68
|
"@types/koa-router": "^7.4.2",
|
|
69
|
+
"@types/koa__router": "^8.0.11",
|
|
66
70
|
"@types/node": "^15.12.4",
|
|
71
|
+
"@typescript-eslint/parser": "5.12.0",
|
|
67
72
|
"copyfiles": "^2.4.1",
|
|
68
73
|
"eslint": "^6.8.0",
|
|
69
74
|
"jest": "^27.0.5",
|
|
@@ -74,7 +79,7 @@
|
|
|
74
79
|
"supertest": "^6.1.3",
|
|
75
80
|
"ts-jest": "^27.0.3",
|
|
76
81
|
"ts-node": "^10.0.0",
|
|
77
|
-
"typescript": "4.
|
|
82
|
+
"typescript": "4.5.5",
|
|
78
83
|
"update-dotenv": "^1.1.1"
|
|
79
84
|
},
|
|
80
85
|
"jest": {
|
|
@@ -84,5 +89,5 @@
|
|
|
84
89
|
"./scripts/jestSetup.js"
|
|
85
90
|
]
|
|
86
91
|
},
|
|
87
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "879b8d8026297042e1c73e0fa7a1d48b9b4c4a1e"
|
|
88
93
|
}
|
|
@@ -21,8 +21,9 @@ const {
|
|
|
21
21
|
isMultiTenant,
|
|
22
22
|
} = require("@budibase/backend-core/tenancy")
|
|
23
23
|
const env = require("../../../environment")
|
|
24
|
+
import { users } from "@budibase/pro"
|
|
24
25
|
|
|
25
|
-
const ssoCallbackUrl = async (config, type) => {
|
|
26
|
+
const ssoCallbackUrl = async (config: any, type: any) => {
|
|
26
27
|
// incase there is a callback URL from before
|
|
27
28
|
if (config && config.callbackURL) {
|
|
28
29
|
return config.callbackURL
|
|
@@ -42,15 +43,15 @@ const ssoCallbackUrl = async (config, type) => {
|
|
|
42
43
|
return `${publicConfig.platformUrl}${callbackUrl}`
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
export const googleCallbackUrl = async (config: any) => {
|
|
46
47
|
return ssoCallbackUrl(config, "google")
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
export const oidcCallbackUrl = async (config: any) => {
|
|
50
51
|
return ssoCallbackUrl(config, "oidc")
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
async function authInternal(ctx, user, err = null, info = null) {
|
|
54
|
+
async function authInternal(ctx: any, user: any, err = null, info = null) {
|
|
54
55
|
if (err) {
|
|
55
56
|
console.error("Authentication error", err)
|
|
56
57
|
return ctx.throw(403, info ? info : "Unauthorized")
|
|
@@ -71,20 +72,23 @@ async function authInternal(ctx, user, err = null, info = null) {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
return passport.authenticate(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
export const authenticate = async (ctx: any, next: any) => {
|
|
76
|
+
return passport.authenticate(
|
|
77
|
+
"local",
|
|
78
|
+
async (err: any, user: any, info: any) => {
|
|
79
|
+
await authInternal(ctx, user, err, info)
|
|
80
|
+
ctx.status = 200
|
|
81
|
+
}
|
|
82
|
+
)(ctx, next)
|
|
79
83
|
}
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
export const setInitInfo = (ctx: any) => {
|
|
82
86
|
const initInfo = ctx.request.body
|
|
83
87
|
setCookie(ctx, initInfo, Cookies.Init)
|
|
84
88
|
ctx.status = 200
|
|
85
89
|
}
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
export const getInitInfo = (ctx: any) => {
|
|
88
92
|
try {
|
|
89
93
|
ctx.body = getCookie(ctx, Cookies.Init) || {}
|
|
90
94
|
} catch (err) {
|
|
@@ -96,7 +100,7 @@ exports.getInitInfo = ctx => {
|
|
|
96
100
|
/**
|
|
97
101
|
* Reset the user password, used as part of a forgotten password flow.
|
|
98
102
|
*/
|
|
99
|
-
|
|
103
|
+
export const reset = async (ctx: any) => {
|
|
100
104
|
const { email } = ctx.request.body
|
|
101
105
|
const configured = await isEmailConfigured()
|
|
102
106
|
if (!configured) {
|
|
@@ -126,7 +130,7 @@ exports.reset = async ctx => {
|
|
|
126
130
|
/**
|
|
127
131
|
* Perform the user password update if the provided reset code is valid.
|
|
128
132
|
*/
|
|
129
|
-
|
|
133
|
+
export const resetUpdate = async (ctx: any) => {
|
|
130
134
|
const { resetCode, password } = ctx.request.body
|
|
131
135
|
try {
|
|
132
136
|
const { userId } = await checkResetPasswordCode(resetCode)
|
|
@@ -142,14 +146,14 @@ exports.resetUpdate = async ctx => {
|
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
|
|
149
|
+
export const logout = async (ctx: any) => {
|
|
146
150
|
if (ctx.user && ctx.user._id) {
|
|
147
151
|
await platformLogout({ ctx, userId: ctx.user._id })
|
|
148
152
|
}
|
|
149
153
|
ctx.body = { message: "User logged out." }
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
|
|
156
|
+
export const datasourcePreAuth = async (ctx: any, next: any) => {
|
|
153
157
|
const provider = ctx.params.provider
|
|
154
158
|
const middleware = require(`@budibase/backend-core/middleware`)
|
|
155
159
|
const handler = middleware.datasource[provider]
|
|
@@ -167,7 +171,7 @@ exports.datasourcePreAuth = async (ctx, next) => {
|
|
|
167
171
|
return handler.preAuth(passport, ctx, next)
|
|
168
172
|
}
|
|
169
173
|
|
|
170
|
-
|
|
174
|
+
export const datasourceAuth = async (ctx: any, next: any) => {
|
|
171
175
|
const authStateCookie = getCookie(ctx, Cookies.DatasourceAuth)
|
|
172
176
|
const provider = authStateCookie.provider
|
|
173
177
|
const middleware = require(`@budibase/backend-core/middleware`)
|
|
@@ -179,7 +183,7 @@ exports.datasourceAuth = async (ctx, next) => {
|
|
|
179
183
|
* The initial call that google authentication makes to take you to the google login screen.
|
|
180
184
|
* On a successful login, you will be redirected to the googleAuth callback route.
|
|
181
185
|
*/
|
|
182
|
-
|
|
186
|
+
export const googlePreAuth = async (ctx: any, next: any) => {
|
|
183
187
|
const db = getGlobalDB()
|
|
184
188
|
|
|
185
189
|
const config = await core.db.getScopedConfig(db, {
|
|
@@ -187,14 +191,14 @@ exports.googlePreAuth = async (ctx, next) => {
|
|
|
187
191
|
workspace: ctx.query.workspace,
|
|
188
192
|
})
|
|
189
193
|
let callbackUrl = await exports.googleCallbackUrl(config)
|
|
190
|
-
const strategy = await google.strategyFactory(config, callbackUrl)
|
|
194
|
+
const strategy = await google.strategyFactory(config, callbackUrl, users.save)
|
|
191
195
|
|
|
192
196
|
return passport.authenticate(strategy, {
|
|
193
197
|
scope: ["profile", "email"],
|
|
194
198
|
})(ctx, next)
|
|
195
199
|
}
|
|
196
200
|
|
|
197
|
-
|
|
201
|
+
export const googleAuth = async (ctx: any, next: any) => {
|
|
198
202
|
const db = getGlobalDB()
|
|
199
203
|
|
|
200
204
|
const config = await core.db.getScopedConfig(db, {
|
|
@@ -202,12 +206,12 @@ exports.googleAuth = async (ctx, next) => {
|
|
|
202
206
|
workspace: ctx.query.workspace,
|
|
203
207
|
})
|
|
204
208
|
const callbackUrl = await exports.googleCallbackUrl(config)
|
|
205
|
-
const strategy = await google.strategyFactory(config, callbackUrl)
|
|
209
|
+
const strategy = await google.strategyFactory(config, callbackUrl, users.save)
|
|
206
210
|
|
|
207
211
|
return passport.authenticate(
|
|
208
212
|
strategy,
|
|
209
213
|
{ successRedirect: "/", failureRedirect: "/error" },
|
|
210
|
-
async (err, user, info) => {
|
|
214
|
+
async (err: any, user: any, info: any) => {
|
|
211
215
|
await authInternal(ctx, user, err, info)
|
|
212
216
|
|
|
213
217
|
ctx.redirect("/")
|
|
@@ -215,24 +219,24 @@ exports.googleAuth = async (ctx, next) => {
|
|
|
215
219
|
)(ctx, next)
|
|
216
220
|
}
|
|
217
221
|
|
|
218
|
-
async function oidcStrategyFactory(ctx, configId) {
|
|
222
|
+
async function oidcStrategyFactory(ctx: any, configId: any) {
|
|
219
223
|
const db = getGlobalDB()
|
|
220
224
|
const config = await core.db.getScopedConfig(db, {
|
|
221
225
|
type: Configs.OIDC,
|
|
222
226
|
group: ctx.query.group,
|
|
223
227
|
})
|
|
224
228
|
|
|
225
|
-
const chosenConfig = config.configs.filter(c => c.uuid === configId)[0]
|
|
229
|
+
const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0]
|
|
226
230
|
let callbackUrl = await exports.oidcCallbackUrl(chosenConfig)
|
|
227
231
|
|
|
228
|
-
return oidc.strategyFactory(chosenConfig, callbackUrl)
|
|
232
|
+
return oidc.strategyFactory(chosenConfig, callbackUrl, users.save)
|
|
229
233
|
}
|
|
230
234
|
|
|
231
235
|
/**
|
|
232
236
|
* The initial call that OIDC authentication makes to take you to the configured OIDC login screen.
|
|
233
237
|
* On a successful login, you will be redirected to the oidcAuth callback route.
|
|
234
238
|
*/
|
|
235
|
-
|
|
239
|
+
export const oidcPreAuth = async (ctx: any, next: any) => {
|
|
236
240
|
const { configId } = ctx.params
|
|
237
241
|
const strategy = await oidcStrategyFactory(ctx, configId)
|
|
238
242
|
|
|
@@ -244,14 +248,14 @@ exports.oidcPreAuth = async (ctx, next) => {
|
|
|
244
248
|
})(ctx, next)
|
|
245
249
|
}
|
|
246
250
|
|
|
247
|
-
|
|
251
|
+
export const oidcAuth = async (ctx: any, next: any) => {
|
|
248
252
|
const configId = getCookie(ctx, Cookies.OIDC_CONFIG)
|
|
249
253
|
const strategy = await oidcStrategyFactory(ctx, configId)
|
|
250
254
|
|
|
251
255
|
return passport.authenticate(
|
|
252
256
|
strategy,
|
|
253
257
|
{ successRedirect: "/", failureRedirect: "/error" },
|
|
254
|
-
async (err, user, info) => {
|
|
258
|
+
async (err: any, user: any, info: any) => {
|
|
255
259
|
await authInternal(ctx, user, err, info)
|
|
256
260
|
|
|
257
261
|
ctx.redirect("/")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { licensing, quotas } from "@budibase/pro"
|
|
2
|
+
|
|
3
|
+
export const activate = async (ctx: any) => {
|
|
4
|
+
const { licenseKey } = ctx.request.body
|
|
5
|
+
if (!licenseKey) {
|
|
6
|
+
ctx.throw(400, "licenseKey is required")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
await licensing.activateLicenseKey(licenseKey)
|
|
10
|
+
ctx.status = 200
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const refresh = async (ctx: any) => {
|
|
14
|
+
await licensing.cache.refresh()
|
|
15
|
+
ctx.status = 200
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const getInfo = async (ctx: any) => {
|
|
19
|
+
const licenseInfo = await licensing.getLicenseInfo()
|
|
20
|
+
if (licenseInfo) {
|
|
21
|
+
licenseInfo.licenseKey = "*"
|
|
22
|
+
ctx.body = licenseInfo
|
|
23
|
+
}
|
|
24
|
+
ctx.status = 200
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const getQuotaUsage = async (ctx: any) => {
|
|
28
|
+
const usage = await quotas.getQuotaUsage()
|
|
29
|
+
ctx.body = usage
|
|
30
|
+
}
|
|
@@ -15,6 +15,7 @@ const { encrypt } = require("@budibase/backend-core/encryption")
|
|
|
15
15
|
const { newid } = require("@budibase/backend-core/utils")
|
|
16
16
|
const { getUser } = require("../../utilities")
|
|
17
17
|
const { Cookies } = require("@budibase/backend-core/constants")
|
|
18
|
+
const { featureFlags } = require("@budibase/backend-core")
|
|
18
19
|
|
|
19
20
|
function newApiKey() {
|
|
20
21
|
return encrypt(`${getTenantId()}${SEPARATOR}${newid()}`)
|
|
@@ -68,6 +69,29 @@ const checkCurrentApp = ctx => {
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Add the attributes that are session based to the current user.
|
|
74
|
+
*/
|
|
75
|
+
const addSessionAttributesToUser = ctx => {
|
|
76
|
+
ctx.body.account = ctx.user.account
|
|
77
|
+
ctx.body.license = ctx.user.license
|
|
78
|
+
ctx.body.budibaseAccess = ctx.user.budibaseAccess
|
|
79
|
+
ctx.body.accountPortalAccess = ctx.user.accountPortalAccess
|
|
80
|
+
ctx.body.csrfToken = ctx.user.csrfToken
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Remove the attributes that are session based from the current user,
|
|
85
|
+
* so that stale values are not written to the db
|
|
86
|
+
*/
|
|
87
|
+
const removeSessionAttributesFromUser = ctx => {
|
|
88
|
+
delete ctx.request.body.csrfToken
|
|
89
|
+
delete ctx.request.body.account
|
|
90
|
+
delete ctx.request.body.accountPortalAccess
|
|
91
|
+
delete ctx.request.body.budibaseAccess
|
|
92
|
+
delete ctx.request.body.license
|
|
93
|
+
}
|
|
94
|
+
|
|
71
95
|
exports.getSelf = async ctx => {
|
|
72
96
|
if (!ctx.user) {
|
|
73
97
|
ctx.throw(403, "User not logged in")
|
|
@@ -81,11 +105,12 @@ exports.getSelf = async ctx => {
|
|
|
81
105
|
|
|
82
106
|
// get the main body of the user
|
|
83
107
|
ctx.body = await getUser(userId)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
ctx.body.
|
|
88
|
-
|
|
108
|
+
|
|
109
|
+
// add the feature flags for this tenant
|
|
110
|
+
const tenantId = getTenantId()
|
|
111
|
+
ctx.body.featureFlags = featureFlags.getTenantFeatureFlags(tenantId)
|
|
112
|
+
|
|
113
|
+
addSessionAttributesToUser(ctx)
|
|
89
114
|
}
|
|
90
115
|
|
|
91
116
|
exports.updateSelf = async ctx => {
|
|
@@ -104,8 +129,8 @@ exports.updateSelf = async ctx => {
|
|
|
104
129
|
// don't allow sending up an ID/Rev, always use the existing one
|
|
105
130
|
delete ctx.request.body._id
|
|
106
131
|
delete ctx.request.body._rev
|
|
107
|
-
|
|
108
|
-
|
|
132
|
+
removeSessionAttributesFromUser(ctx)
|
|
133
|
+
|
|
109
134
|
const response = await db.put({
|
|
110
135
|
...user,
|
|
111
136
|
...ctx.request.body,
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
const {
|
|
2
2
|
getGlobalUserParams,
|
|
3
3
|
StaticDatabases,
|
|
4
|
-
generateNewUsageQuotaDoc,
|
|
5
4
|
} = require("@budibase/backend-core/db")
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
const { EmailTemplatePurpose } = require("../../../constants")
|
|
11
|
-
const { checkInviteCode } = require("../../../utilities/redis")
|
|
12
|
-
const { sendEmail } = require("../../../utilities/email")
|
|
5
|
+
const { getGlobalUserByEmail } = require("@budibase/backend-core/utils")
|
|
6
|
+
import { EmailTemplatePurpose } from "../../../constants"
|
|
7
|
+
import { checkInviteCode } from "../../../utilities/redis"
|
|
8
|
+
import { sendEmail } from "../../../utilities/email"
|
|
13
9
|
const { user: userCache } = require("@budibase/backend-core/cache")
|
|
14
10
|
const { invalidateSessions } = require("@budibase/backend-core/sessions")
|
|
15
11
|
const accounts = require("@budibase/backend-core/accounts")
|
|
@@ -20,26 +16,28 @@ const {
|
|
|
20
16
|
doesTenantExist,
|
|
21
17
|
} = require("@budibase/backend-core/tenancy")
|
|
22
18
|
const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
import env from "../../../environment"
|
|
20
|
+
import { syncUserInApps } from "../../../utilities/appService"
|
|
21
|
+
import { quotas, users } from "@budibase/pro"
|
|
22
|
+
const { errors } = require("@budibase/backend-core")
|
|
23
|
+
import { allUsers, getUser } from "../../utilities"
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
export const save = async (ctx: any) => {
|
|
28
26
|
try {
|
|
29
|
-
const user = await
|
|
27
|
+
const user: any = await users.save(ctx.request.body, getTenantId())
|
|
30
28
|
// let server know to sync user
|
|
31
29
|
await syncUserInApps(user._id)
|
|
32
30
|
ctx.body = user
|
|
33
|
-
} catch (err) {
|
|
31
|
+
} catch (err: any) {
|
|
34
32
|
ctx.throw(err.status || 400, err)
|
|
35
33
|
}
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
const parseBooleanParam = param => {
|
|
36
|
+
const parseBooleanParam = (param: any) => {
|
|
39
37
|
return !(param && param === "false")
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
|
|
40
|
+
export const adminUser = async (ctx: any) => {
|
|
43
41
|
const { email, password, tenantId } = ctx.request.body
|
|
44
42
|
|
|
45
43
|
// account portal sends a pre-hashed password - honour param to prevent double hashing
|
|
@@ -69,10 +67,10 @@ exports.adminUser = async ctx => {
|
|
|
69
67
|
} catch (err) {
|
|
70
68
|
// don't worry about errors
|
|
71
69
|
}
|
|
72
|
-
await db.put(
|
|
70
|
+
await db.put(quotas.generateNewQuotaUsage())
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
if (response.rows.some(row => row.doc.admin)) {
|
|
73
|
+
if (response.rows.some((row: any) => row.doc.admin)) {
|
|
76
74
|
ctx.throw(
|
|
77
75
|
403,
|
|
78
76
|
"You cannot initialise once an global user has been created."
|
|
@@ -93,13 +91,13 @@ exports.adminUser = async ctx => {
|
|
|
93
91
|
tenantId,
|
|
94
92
|
}
|
|
95
93
|
try {
|
|
96
|
-
ctx.body = await
|
|
97
|
-
} catch (err) {
|
|
94
|
+
ctx.body = await users.save(user, tenantId, hashPassword, requirePassword)
|
|
95
|
+
} catch (err: any) {
|
|
98
96
|
ctx.throw(err.status || 400, err)
|
|
99
97
|
}
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
|
|
100
|
+
export const destroy = async (ctx: any) => {
|
|
103
101
|
const db = getGlobalDB()
|
|
104
102
|
const dbUser = await db.get(ctx.params.id)
|
|
105
103
|
|
|
@@ -118,6 +116,7 @@ exports.destroy = async ctx => {
|
|
|
118
116
|
|
|
119
117
|
await removeUserFromInfoDB(dbUser)
|
|
120
118
|
await db.remove(dbUser._id, dbUser._rev)
|
|
119
|
+
await quotas.removeUser(dbUser)
|
|
121
120
|
await userCache.invalidateUser(dbUser._id)
|
|
122
121
|
await invalidateSessions(dbUser._id)
|
|
123
122
|
// let server know to sync user
|
|
@@ -128,23 +127,23 @@ exports.destroy = async ctx => {
|
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
// called internally by app server user fetch
|
|
131
|
-
|
|
132
|
-
const
|
|
130
|
+
export const fetch = async (ctx: any) => {
|
|
131
|
+
const all = await allUsers()
|
|
133
132
|
// user hashed password shouldn't ever be returned
|
|
134
|
-
for (let user of
|
|
133
|
+
for (let user of all) {
|
|
135
134
|
if (user) {
|
|
136
135
|
delete user.password
|
|
137
136
|
}
|
|
138
137
|
}
|
|
139
|
-
ctx.body =
|
|
138
|
+
ctx.body = all
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
// called internally by app server user find
|
|
143
|
-
|
|
142
|
+
export const find = async (ctx: any) => {
|
|
144
143
|
ctx.body = await getUser(ctx.params.id)
|
|
145
144
|
}
|
|
146
145
|
|
|
147
|
-
|
|
146
|
+
export const tenantUserLookup = async (ctx: any) => {
|
|
148
147
|
const id = ctx.params.id
|
|
149
148
|
const user = await getTenantUser(id)
|
|
150
149
|
if (user) {
|
|
@@ -154,7 +153,7 @@ exports.tenantUserLookup = async ctx => {
|
|
|
154
153
|
}
|
|
155
154
|
}
|
|
156
155
|
|
|
157
|
-
|
|
156
|
+
export const invite = async (ctx: any) => {
|
|
158
157
|
let { email, userInfo } = ctx.request.body
|
|
159
158
|
const existing = await getGlobalUserByEmail(email)
|
|
160
159
|
if (existing) {
|
|
@@ -164,21 +163,22 @@ exports.invite = async ctx => {
|
|
|
164
163
|
userInfo = {}
|
|
165
164
|
}
|
|
166
165
|
userInfo.tenantId = getTenantId()
|
|
167
|
-
|
|
166
|
+
const opts: any = {
|
|
168
167
|
subject: "{{ company }} platform invitation",
|
|
169
168
|
info: userInfo,
|
|
170
|
-
}
|
|
169
|
+
}
|
|
170
|
+
await sendEmail(email, EmailTemplatePurpose.INVITATION, opts)
|
|
171
171
|
ctx.body = {
|
|
172
172
|
message: "Invitation has been sent.",
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
export const inviteAccept = async (ctx: any) => {
|
|
177
177
|
const { inviteCode, password, firstName, lastName } = ctx.request.body
|
|
178
178
|
try {
|
|
179
179
|
// info is an extension of the user object that was stored by global
|
|
180
|
-
const { email, info } = await checkInviteCode(inviteCode)
|
|
181
|
-
ctx.body = await
|
|
180
|
+
const { email, info }: any = await checkInviteCode(inviteCode)
|
|
181
|
+
ctx.body = await users.save(
|
|
182
182
|
{
|
|
183
183
|
firstName,
|
|
184
184
|
lastName,
|
|
@@ -188,7 +188,11 @@ exports.inviteAccept = async ctx => {
|
|
|
188
188
|
},
|
|
189
189
|
info.tenantId
|
|
190
190
|
)
|
|
191
|
-
} catch (err) {
|
|
191
|
+
} catch (err: any) {
|
|
192
|
+
if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
|
|
193
|
+
// explicitly re-throw limit exceeded errors
|
|
194
|
+
ctx.throw(400, err)
|
|
195
|
+
}
|
|
192
196
|
ctx.throw(400, "Unable to create new user, invitation invalid.")
|
|
193
197
|
}
|
|
194
198
|
}
|
package/src/api/index.js
CHANGED
|
@@ -8,6 +8,8 @@ const {
|
|
|
8
8
|
buildTenancyMiddleware,
|
|
9
9
|
buildCsrfMiddleware,
|
|
10
10
|
} = require("@budibase/backend-core/auth")
|
|
11
|
+
const { middleware: pro } = require("@budibase/pro")
|
|
12
|
+
const { errors } = require("@budibase/backend-core")
|
|
11
13
|
|
|
12
14
|
const PUBLIC_ENDPOINTS = [
|
|
13
15
|
// old deprecated endpoints kept for backwards compat
|
|
@@ -98,6 +100,7 @@ router
|
|
|
98
100
|
.use(buildAuthMiddleware(PUBLIC_ENDPOINTS))
|
|
99
101
|
.use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS))
|
|
100
102
|
.use(buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_ENDPOINTS }))
|
|
103
|
+
.use(pro.licensing())
|
|
101
104
|
// for now no public access is allowed to worker (bar health check)
|
|
102
105
|
.use((ctx, next) => {
|
|
103
106
|
if (ctx.publicEndpoint) {
|
|
@@ -110,16 +113,18 @@ router
|
|
|
110
113
|
})
|
|
111
114
|
.use(auditLog)
|
|
112
115
|
|
|
113
|
-
// error handling middleware
|
|
116
|
+
// error handling middleware - TODO: This could be moved to backend-core
|
|
114
117
|
router.use(async (ctx, next) => {
|
|
115
118
|
try {
|
|
116
119
|
await next()
|
|
117
120
|
} catch (err) {
|
|
118
121
|
ctx.log.error(err)
|
|
119
122
|
ctx.status = err.status || err.statusCode || 500
|
|
123
|
+
const error = errors.getPublicError(err)
|
|
120
124
|
ctx.body = {
|
|
121
125
|
message: err.message,
|
|
122
126
|
status: ctx.status,
|
|
127
|
+
error,
|
|
123
128
|
}
|
|
124
129
|
}
|
|
125
130
|
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Router from "@koa/router"
|
|
2
|
+
import * as controller from "../../controllers/global/license"
|
|
3
|
+
|
|
4
|
+
const router = new Router()
|
|
5
|
+
|
|
6
|
+
router
|
|
7
|
+
.post("/api/global/license/activate", controller.activate)
|
|
8
|
+
.post("/api/global/license/refresh", controller.refresh)
|
|
9
|
+
.get("/api/global/license/info", controller.getInfo)
|
|
10
|
+
.get("/api/global/license/usage", controller.getQuotaUsage)
|
|
11
|
+
|
|
12
|
+
export = router
|
package/src/api/routes/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const environmentRoutes = require("./system/environment")
|
|
|
10
10
|
const tenantsRoutes = require("./system/tenants")
|
|
11
11
|
const statusRoutes = require("./system/status")
|
|
12
12
|
const selfRoutes = require("./global/self")
|
|
13
|
+
const licenseRoutes = require("./global/license")
|
|
13
14
|
|
|
14
15
|
exports.routes = [
|
|
15
16
|
configRoutes,
|
|
@@ -24,4 +25,5 @@ exports.routes = [
|
|
|
24
25
|
environmentRoutes,
|
|
25
26
|
statusRoutes,
|
|
26
27
|
selfRoutes,
|
|
28
|
+
licenseRoutes,
|
|
27
29
|
]
|
|
@@ -75,7 +75,8 @@ describe("/api/global/auth", () => {
|
|
|
75
75
|
afterEach(() => {
|
|
76
76
|
expect(strategyFactory).toBeCalledWith(
|
|
77
77
|
chosenConfig,
|
|
78
|
-
`http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback
|
|
78
|
+
`http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback`,
|
|
79
|
+
expect.any(Function)
|
|
79
80
|
)
|
|
80
81
|
})
|
|
81
82
|
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Scope } from "@sentry/node"
|
|
3
3
|
import { Event } from "@sentry/types/dist/event"
|
|
4
4
|
import Application from "koa"
|
|
5
|
+
import { bootstrap } from "global-agent"
|
|
5
6
|
|
|
6
7
|
const env = require("./environment")
|
|
7
8
|
const CouchDB = require("./db")
|
|
@@ -17,6 +18,9 @@ const api = require("./api")
|
|
|
17
18
|
const redis = require("./utilities/redis")
|
|
18
19
|
const Sentry = require("@sentry/node")
|
|
19
20
|
|
|
21
|
+
// this will setup http and https proxies form env variables
|
|
22
|
+
bootstrap()
|
|
23
|
+
|
|
20
24
|
const app: Application = new Koa()
|
|
21
25
|
|
|
22
26
|
app.keys = ["secret", "key"]
|