@budibase/backend-core 3.2.4 → 3.2.6
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/dist/index.js.map +1 -1
- package/dist/index.js.meta.json +1 -1
- package/dist/package.json +11 -4
- package/dist/plugins.js.meta.json +1 -1
- package/package.json +11 -4
- package/src/accounts/accounts.ts +0 -82
- package/src/accounts/api.ts +0 -59
- package/src/accounts/index.ts +0 -1
- package/src/auth/auth.ts +0 -210
- package/src/auth/index.ts +0 -1
- package/src/auth/tests/auth.spec.ts +0 -14
- package/src/blacklist/blacklist.ts +0 -54
- package/src/blacklist/index.ts +0 -1
- package/src/blacklist/tests/blacklist.spec.ts +0 -46
- package/src/cache/appMetadata.ts +0 -88
- package/src/cache/base/index.ts +0 -150
- package/src/cache/docWritethrough.ts +0 -105
- package/src/cache/generic.ts +0 -33
- package/src/cache/index.ts +0 -8
- package/src/cache/invite.ts +0 -86
- package/src/cache/passwordReset.ts +0 -49
- package/src/cache/tests/docWritethrough.spec.ts +0 -296
- package/src/cache/tests/user.spec.ts +0 -145
- package/src/cache/tests/writethrough.spec.ts +0 -139
- package/src/cache/user.ts +0 -154
- package/src/cache/writethrough.ts +0 -133
- package/src/configs/configs.ts +0 -263
- package/src/configs/index.ts +0 -1
- package/src/configs/tests/configs.spec.ts +0 -184
- package/src/constants/db.ts +0 -75
- package/src/constants/index.ts +0 -2
- package/src/constants/misc.ts +0 -36
- package/src/context/Context.ts +0 -14
- package/src/context/identity.ts +0 -58
- package/src/context/index.ts +0 -3
- package/src/context/mainContext.ts +0 -422
- package/src/context/tests/index.spec.ts +0 -255
- package/src/context/types.ts +0 -26
- package/src/db/Replication.ts +0 -94
- package/src/db/couch/DatabaseImpl.ts +0 -511
- package/src/db/couch/connections.ts +0 -89
- package/src/db/couch/index.ts +0 -4
- package/src/db/couch/pouchDB.ts +0 -97
- package/src/db/couch/pouchDump.ts +0 -0
- package/src/db/couch/tests/DatabaseImpl.spec.ts +0 -118
- package/src/db/couch/utils.ts +0 -55
- package/src/db/db.ts +0 -34
- package/src/db/errors.ts +0 -14
- package/src/db/index.ts +0 -12
- package/src/db/instrumentation.ts +0 -199
- package/src/db/lucene.ts +0 -721
- package/src/db/searchIndexes/index.ts +0 -1
- package/src/db/searchIndexes/searchIndexes.ts +0 -62
- package/src/db/tests/DatabaseImpl.spec.ts +0 -55
- package/src/db/tests/connections.spec.ts +0 -22
- package/src/db/tests/index.spec.ts +0 -32
- package/src/db/tests/lucene.spec.ts +0 -400
- package/src/db/tests/pouch.spec.js +0 -62
- package/src/db/tests/utils.spec.ts +0 -63
- package/src/db/utils.ts +0 -208
- package/src/db/views.ts +0 -245
- package/src/docIds/conversions.ts +0 -60
- package/src/docIds/ids.ts +0 -126
- package/src/docIds/index.ts +0 -2
- package/src/docIds/newid.ts +0 -5
- package/src/docIds/params.ts +0 -189
- package/src/docUpdates/index.ts +0 -24
- package/src/environment.ts +0 -293
- package/src/errors/errors.ts +0 -119
- package/src/errors/index.ts +0 -1
- package/src/events/analytics.ts +0 -6
- package/src/events/asyncEvents/index.ts +0 -2
- package/src/events/asyncEvents/publisher.ts +0 -12
- package/src/events/asyncEvents/queue.ts +0 -22
- package/src/events/backfill.ts +0 -183
- package/src/events/documentId.ts +0 -56
- package/src/events/events.ts +0 -47
- package/src/events/identification.ts +0 -311
- package/src/events/index.ts +0 -15
- package/src/events/processors/AnalyticsProcessor.ts +0 -64
- package/src/events/processors/AuditLogsProcessor.ts +0 -92
- package/src/events/processors/LoggingProcessor.ts +0 -36
- package/src/events/processors/Processors.ts +0 -52
- package/src/events/processors/async/DocumentUpdateProcessor.ts +0 -38
- package/src/events/processors/index.ts +0 -19
- package/src/events/processors/posthog/PosthogProcessor.ts +0 -118
- package/src/events/processors/posthog/index.ts +0 -3
- package/src/events/processors/posthog/rateLimiting.ts +0 -106
- package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +0 -164
- package/src/events/processors/types.ts +0 -1
- package/src/events/publishers/account.ts +0 -41
- package/src/events/publishers/ai.ts +0 -21
- package/src/events/publishers/app.ts +0 -168
- package/src/events/publishers/auditLog.ts +0 -26
- package/src/events/publishers/auth.ts +0 -73
- package/src/events/publishers/automation.ts +0 -110
- package/src/events/publishers/backfill.ts +0 -74
- package/src/events/publishers/backup.ts +0 -42
- package/src/events/publishers/datasource.ts +0 -48
- package/src/events/publishers/email.ts +0 -17
- package/src/events/publishers/environmentVariable.ts +0 -38
- package/src/events/publishers/group.ts +0 -99
- package/src/events/publishers/index.ts +0 -25
- package/src/events/publishers/installation.ts +0 -38
- package/src/events/publishers/layout.ts +0 -26
- package/src/events/publishers/license.ts +0 -84
- package/src/events/publishers/org.ts +0 -37
- package/src/events/publishers/plugin.ts +0 -47
- package/src/events/publishers/query.ts +0 -89
- package/src/events/publishers/role.ts +0 -62
- package/src/events/publishers/rows.ts +0 -29
- package/src/events/publishers/screen.ts +0 -36
- package/src/events/publishers/serve.ts +0 -43
- package/src/events/publishers/table.ts +0 -70
- package/src/events/publishers/user.ts +0 -202
- package/src/events/publishers/view.ts +0 -107
- package/src/features/features.ts +0 -277
- package/src/features/index.ts +0 -2
- package/src/features/tests/features.spec.ts +0 -267
- package/src/features/tests/utils.ts +0 -64
- package/src/helpers.ts +0 -9
- package/src/index.ts +0 -59
- package/src/installation.ts +0 -115
- package/src/logging/alerts.ts +0 -26
- package/src/logging/correlation/correlation.ts +0 -15
- package/src/logging/correlation/index.ts +0 -1
- package/src/logging/correlation/middleware.ts +0 -18
- package/src/logging/index.ts +0 -4
- package/src/logging/pino/logger.ts +0 -239
- package/src/logging/pino/middleware.ts +0 -48
- package/src/logging/system.ts +0 -81
- package/src/logging/tests/system.spec.ts +0 -61
- package/src/middleware/adminOnly.ts +0 -9
- package/src/middleware/auditLog.ts +0 -6
- package/src/middleware/authenticated.ts +0 -247
- package/src/middleware/builderOnly.ts +0 -21
- package/src/middleware/builderOrAdmin.ts +0 -21
- package/src/middleware/contentSecurityPolicy.ts +0 -113
- package/src/middleware/csrf.ts +0 -81
- package/src/middleware/errorHandling.ts +0 -43
- package/src/middleware/index.ts +0 -24
- package/src/middleware/internalApi.ts +0 -23
- package/src/middleware/ip.ts +0 -12
- package/src/middleware/joi-validator.ts +0 -58
- package/src/middleware/matchers.ts +0 -39
- package/src/middleware/passport/datasource/google.ts +0 -102
- package/src/middleware/passport/local.ts +0 -54
- package/src/middleware/passport/sso/google.ts +0 -77
- package/src/middleware/passport/sso/oidc.ts +0 -152
- package/src/middleware/passport/sso/sso.ts +0 -138
- package/src/middleware/passport/sso/tests/google.spec.ts +0 -68
- package/src/middleware/passport/sso/tests/oidc.spec.ts +0 -144
- package/src/middleware/passport/sso/tests/sso.spec.ts +0 -197
- package/src/middleware/passport/utils.ts +0 -38
- package/src/middleware/querystringToBody.ts +0 -28
- package/src/middleware/tenancy.ts +0 -36
- package/src/middleware/tests/builder.spec.ts +0 -181
- package/src/middleware/tests/contentSecurityPolicy.spec.ts +0 -75
- package/src/middleware/tests/matchers.spec.ts +0 -100
- package/src/migrations/definitions.ts +0 -40
- package/src/migrations/index.ts +0 -2
- package/src/migrations/migrations.ts +0 -186
- package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +0 -11
- package/src/migrations/tests/migrations.spec.ts +0 -64
- package/src/objectStore/buckets/app.ts +0 -53
- package/src/objectStore/buckets/global.ts +0 -29
- package/src/objectStore/buckets/index.ts +0 -3
- package/src/objectStore/buckets/plugins.ts +0 -71
- package/src/objectStore/buckets/tests/app.spec.ts +0 -161
- package/src/objectStore/buckets/tests/global.spec.ts +0 -74
- package/src/objectStore/buckets/tests/plugins.spec.ts +0 -111
- package/src/objectStore/cloudfront.ts +0 -41
- package/src/objectStore/index.ts +0 -3
- package/src/objectStore/objectStore.ts +0 -585
- package/src/objectStore/utils.ts +0 -113
- package/src/platform/index.ts +0 -3
- package/src/platform/platformDb.ts +0 -6
- package/src/platform/tenants.ts +0 -101
- package/src/platform/tests/tenants.spec.ts +0 -26
- package/src/platform/users.ts +0 -129
- package/src/plugin/index.ts +0 -1
- package/src/plugin/tests/validation.spec.ts +0 -209
- package/src/plugin/utils.ts +0 -175
- package/src/queue/constants.ts +0 -8
- package/src/queue/inMemoryQueue.ts +0 -189
- package/src/queue/index.ts +0 -2
- package/src/queue/listeners.ts +0 -199
- package/src/queue/queue.ts +0 -84
- package/src/redis/index.ts +0 -6
- package/src/redis/init.ts +0 -118
- package/src/redis/redis.ts +0 -358
- package/src/redis/redlockImpl.ts +0 -155
- package/src/redis/tests/redis.spec.ts +0 -207
- package/src/redis/tests/redlockImpl.spec.ts +0 -105
- package/src/redis/utils.ts +0 -128
- package/src/security/auth.ts +0 -24
- package/src/security/encryption.ts +0 -185
- package/src/security/index.ts +0 -1
- package/src/security/permissions.ts +0 -166
- package/src/security/roles.ts +0 -655
- package/src/security/secrets.ts +0 -20
- package/src/security/sessions.ts +0 -123
- package/src/security/tests/auth.spec.ts +0 -45
- package/src/security/tests/encryption.spec.ts +0 -31
- package/src/security/tests/permissions.spec.ts +0 -146
- package/src/security/tests/secrets.spec.ts +0 -35
- package/src/security/tests/sessions.spec.ts +0 -12
- package/src/sql/designDoc.ts +0 -17
- package/src/sql/index.ts +0 -5
- package/src/sql/sql.ts +0 -1854
- package/src/sql/sqlTable.ts +0 -319
- package/src/sql/utils.ts +0 -193
- package/src/tenancy/db.ts +0 -6
- package/src/tenancy/index.ts +0 -2
- package/src/tenancy/tenancy.ts +0 -148
- package/src/tenancy/tests/tenancy.spec.ts +0 -184
- package/src/timers/index.ts +0 -1
- package/src/timers/timers.ts +0 -22
- package/src/users/db.ts +0 -582
- package/src/users/events.ts +0 -176
- package/src/users/index.ts +0 -4
- package/src/users/lookup.ts +0 -99
- package/src/users/test/db.spec.ts +0 -188
- package/src/users/test/utils.spec.ts +0 -67
- package/src/users/users.ts +0 -353
- package/src/users/utils.ts +0 -81
- package/src/utils/Duration.ts +0 -56
- package/src/utils/hashing.ts +0 -15
- package/src/utils/index.ts +0 -4
- package/src/utils/stringUtils.ts +0 -8
- package/src/utils/tests/Duration.spec.ts +0 -19
- package/src/utils/tests/utils.spec.ts +0 -204
- package/src/utils/utils.ts +0 -249
- package/tests/core/logging.ts +0 -34
- package/tests/core/users/users.spec.js +0 -53
- package/tests/core/utilities/index.ts +0 -7
- package/tests/core/utilities/jestUtils.ts +0 -33
- package/tests/core/utilities/mocks/alerts.ts +0 -4
- package/tests/core/utilities/mocks/date.ts +0 -3
- package/tests/core/utilities/mocks/events.ts +0 -132
- package/tests/core/utilities/mocks/index.ts +0 -9
- package/tests/core/utilities/mocks/licenses.ts +0 -119
- package/tests/core/utilities/queue.ts +0 -9
- package/tests/core/utilities/structures/Chance.ts +0 -20
- package/tests/core/utilities/structures/accounts.ts +0 -80
- package/tests/core/utilities/structures/apps.ts +0 -21
- package/tests/core/utilities/structures/common.ts +0 -7
- package/tests/core/utilities/structures/db.ts +0 -12
- package/tests/core/utilities/structures/documents/index.ts +0 -1
- package/tests/core/utilities/structures/documents/platform/index.ts +0 -1
- package/tests/core/utilities/structures/documents/platform/installation.ts +0 -12
- package/tests/core/utilities/structures/generator.ts +0 -3
- package/tests/core/utilities/structures/index.ts +0 -15
- package/tests/core/utilities/structures/koa.ts +0 -16
- package/tests/core/utilities/structures/licenses.ts +0 -190
- package/tests/core/utilities/structures/plugins.ts +0 -19
- package/tests/core/utilities/structures/quotas.ts +0 -72
- package/tests/core/utilities/structures/scim.ts +0 -80
- package/tests/core/utilities/structures/sso.ts +0 -118
- package/tests/core/utilities/structures/tenants.ts +0 -5
- package/tests/core/utilities/structures/userGroups.ts +0 -10
- package/tests/core/utilities/structures/users.ts +0 -89
- package/tests/core/utilities/testContainerUtils.ts +0 -165
- package/tests/core/utilities/utils/index.ts +0 -2
- package/tests/core/utilities/utils/queue.ts +0 -27
- package/tests/core/utilities/utils/time.ts +0 -3
- package/tests/extra/DBTestConfiguration.ts +0 -36
- package/tests/extra/index.ts +0 -2
- package/tests/extra/testEnv.ts +0 -95
- package/tests/index.ts +0 -2
- package/tests/jestEnv.ts +0 -10
- package/tests/jestSetup.ts +0 -36
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import fetch from "node-fetch"
|
|
2
|
-
import * as sso from "./sso"
|
|
3
|
-
import { ssoCallbackUrl } from "../utils"
|
|
4
|
-
import { validEmail } from "../../../utils"
|
|
5
|
-
import {
|
|
6
|
-
ConfigType,
|
|
7
|
-
OIDCInnerConfig,
|
|
8
|
-
SSOProfile,
|
|
9
|
-
OIDCStrategyConfiguration,
|
|
10
|
-
SSOAuthDetails,
|
|
11
|
-
SSOProviderType,
|
|
12
|
-
JwtClaims,
|
|
13
|
-
SaveSSOUserFunction,
|
|
14
|
-
} from "@budibase/types"
|
|
15
|
-
|
|
16
|
-
const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy
|
|
17
|
-
|
|
18
|
-
export function buildVerifyFn(saveUserFn: SaveSSOUserFunction) {
|
|
19
|
-
/**
|
|
20
|
-
* @param issuer The identity provider base URL
|
|
21
|
-
* @param sub The user ID
|
|
22
|
-
* @param profile The user profile information. Created by passport from the /userinfo response
|
|
23
|
-
* @param jwtClaims The parsed id_token claims
|
|
24
|
-
* @param accessToken The access_token for contacting the identity provider - may or may not be a JWT
|
|
25
|
-
* @param refreshToken The refresh_token for obtaining a new access_token - usually not a JWT
|
|
26
|
-
* @param idToken The id_token - always a JWT
|
|
27
|
-
* @param params The response body from requesting an access_token
|
|
28
|
-
* @param done The passport callback: err, user, info
|
|
29
|
-
*/
|
|
30
|
-
return async (
|
|
31
|
-
issuer: string,
|
|
32
|
-
sub: string,
|
|
33
|
-
profile: SSOProfile,
|
|
34
|
-
jwtClaims: JwtClaims,
|
|
35
|
-
accessToken: string,
|
|
36
|
-
refreshToken: string,
|
|
37
|
-
idToken: string,
|
|
38
|
-
params: any,
|
|
39
|
-
done: Function
|
|
40
|
-
) => {
|
|
41
|
-
const details: SSOAuthDetails = {
|
|
42
|
-
// store the issuer info to enable sync in future
|
|
43
|
-
provider: issuer,
|
|
44
|
-
providerType: SSOProviderType.OIDC,
|
|
45
|
-
userId: profile.id,
|
|
46
|
-
profile: profile,
|
|
47
|
-
email: getEmail(profile, jwtClaims),
|
|
48
|
-
oauth2: {
|
|
49
|
-
accessToken: accessToken,
|
|
50
|
-
refreshToken: refreshToken,
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return sso.authenticate(
|
|
55
|
-
details,
|
|
56
|
-
false, // don't require local accounts to exist
|
|
57
|
-
done,
|
|
58
|
-
saveUserFn
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @param profile The structured profile created by passport using the user info endpoint
|
|
65
|
-
* @param jwtClaims The claims returned in the id token
|
|
66
|
-
*/
|
|
67
|
-
function getEmail(profile: SSOProfile, jwtClaims: JwtClaims) {
|
|
68
|
-
// profile not guaranteed to contain email e.g. github connected azure ad account
|
|
69
|
-
if (profile._json.email) {
|
|
70
|
-
return profile._json.email
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// fallback to id token email
|
|
74
|
-
if (jwtClaims.email) {
|
|
75
|
-
return jwtClaims.email
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// fallback to id token preferred username
|
|
79
|
-
const username = jwtClaims.preferred_username
|
|
80
|
-
if (username && validEmail(username)) {
|
|
81
|
-
return username
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
throw new Error(
|
|
85
|
-
`Could not determine user email from profile ${JSON.stringify(
|
|
86
|
-
profile
|
|
87
|
-
)} and claims ${JSON.stringify(jwtClaims)}`
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Create an instance of the oidc passport strategy. This wrapper fetches the configuration
|
|
93
|
-
* from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport.
|
|
94
|
-
* @returns Dynamically configured Passport OIDC Strategy
|
|
95
|
-
*/
|
|
96
|
-
export async function strategyFactory(
|
|
97
|
-
config: OIDCStrategyConfiguration,
|
|
98
|
-
saveUserFn: SaveSSOUserFunction
|
|
99
|
-
) {
|
|
100
|
-
try {
|
|
101
|
-
const verify = buildVerifyFn(saveUserFn)
|
|
102
|
-
const strategy = new OIDCStrategy(config, verify)
|
|
103
|
-
strategy.name = "oidc"
|
|
104
|
-
return strategy
|
|
105
|
-
} catch (err: any) {
|
|
106
|
-
throw new Error(`Error constructing OIDC authentication strategy - ${err}`)
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export async function fetchStrategyConfig(
|
|
111
|
-
oidcConfig: OIDCInnerConfig,
|
|
112
|
-
callbackUrl?: string
|
|
113
|
-
): Promise<OIDCStrategyConfiguration> {
|
|
114
|
-
try {
|
|
115
|
-
const { clientID, clientSecret, configUrl } = oidcConfig
|
|
116
|
-
|
|
117
|
-
if (!clientID || !clientSecret || !callbackUrl || !configUrl) {
|
|
118
|
-
// check for remote config and all required elements
|
|
119
|
-
throw new Error(
|
|
120
|
-
"Configuration invalid. Must contain clientID, clientSecret, callbackUrl and configUrl"
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const response = await fetch(configUrl)
|
|
125
|
-
|
|
126
|
-
if (!response.ok) {
|
|
127
|
-
throw new Error(
|
|
128
|
-
`Unexpected response when fetching openid-configuration: ${response.statusText}`
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const body = await response.json()
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
issuer: body.issuer,
|
|
136
|
-
authorizationURL: body.authorization_endpoint,
|
|
137
|
-
tokenURL: body.token_endpoint,
|
|
138
|
-
userInfoURL: body.userinfo_endpoint,
|
|
139
|
-
clientID: clientID,
|
|
140
|
-
clientSecret: clientSecret,
|
|
141
|
-
callbackURL: callbackUrl,
|
|
142
|
-
}
|
|
143
|
-
} catch (err) {
|
|
144
|
-
throw new Error(
|
|
145
|
-
`Error constructing OIDC authentication configuration - ${err}`
|
|
146
|
-
)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export async function getCallbackUrl() {
|
|
151
|
-
return ssoCallbackUrl(ConfigType.OIDC)
|
|
152
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { generateGlobalUserID } from "../../../db"
|
|
2
|
-
import { authError } from "../utils"
|
|
3
|
-
import * as users from "../../../users"
|
|
4
|
-
import * as context from "../../../context"
|
|
5
|
-
import {
|
|
6
|
-
SaveSSOUserFunction,
|
|
7
|
-
SSOAuthDetails,
|
|
8
|
-
SSOUser,
|
|
9
|
-
User,
|
|
10
|
-
} from "@budibase/types"
|
|
11
|
-
|
|
12
|
-
// no-op function for user save
|
|
13
|
-
// - this allows datasource auth and access token refresh to work correctly
|
|
14
|
-
// - prefer no-op over an optional argument to ensure function is provided to login flows
|
|
15
|
-
export const ssoSaveUserNoOp: SaveSSOUserFunction = (user: SSOUser) =>
|
|
16
|
-
Promise.resolve(user)
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Common authentication logic for third parties. e.g. OAuth, OIDC.
|
|
20
|
-
*/
|
|
21
|
-
export async function authenticate(
|
|
22
|
-
details: SSOAuthDetails,
|
|
23
|
-
requireLocalAccount: boolean = true,
|
|
24
|
-
done: any,
|
|
25
|
-
saveUserFn: SaveSSOUserFunction
|
|
26
|
-
) {
|
|
27
|
-
if (!saveUserFn) {
|
|
28
|
-
throw new Error("Save user function must be provided")
|
|
29
|
-
}
|
|
30
|
-
if (!details.userId) {
|
|
31
|
-
return authError(done, "sso user id required")
|
|
32
|
-
}
|
|
33
|
-
if (!details.email) {
|
|
34
|
-
return authError(done, "sso user email required")
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// use the third party id
|
|
38
|
-
const userId = generateGlobalUserID(details.userId)
|
|
39
|
-
|
|
40
|
-
let dbUser: User | undefined
|
|
41
|
-
|
|
42
|
-
// try to load by id
|
|
43
|
-
try {
|
|
44
|
-
dbUser = await users.getById(userId)
|
|
45
|
-
} catch (err: any) {
|
|
46
|
-
// abort when not 404 error
|
|
47
|
-
if (!err.status || err.status !== 404) {
|
|
48
|
-
return authError(
|
|
49
|
-
done,
|
|
50
|
-
"Unexpected error when retrieving existing user",
|
|
51
|
-
err
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// fallback to loading by email
|
|
57
|
-
if (!dbUser) {
|
|
58
|
-
dbUser = await users.getGlobalUserByEmail(details.email)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// exit early if there is still no user and auto creation is disabled
|
|
62
|
-
if (!dbUser && requireLocalAccount) {
|
|
63
|
-
return authError(
|
|
64
|
-
done,
|
|
65
|
-
"Email does not yet exist. You must set up your local budibase account first."
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// first time creation
|
|
70
|
-
if (!dbUser) {
|
|
71
|
-
// setup a blank user using the third party id
|
|
72
|
-
dbUser = {
|
|
73
|
-
_id: userId,
|
|
74
|
-
email: details.email,
|
|
75
|
-
roles: {},
|
|
76
|
-
tenantId: context.getTenantId(),
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let ssoUser = await syncUser(dbUser, details)
|
|
81
|
-
// never prompt for password reset
|
|
82
|
-
ssoUser.forceResetPassword = false
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
// don't try to re-save any existing password
|
|
86
|
-
delete ssoUser.password
|
|
87
|
-
// create or sync the user
|
|
88
|
-
ssoUser = (await saveUserFn(ssoUser, {
|
|
89
|
-
hashPassword: false,
|
|
90
|
-
requirePassword: false,
|
|
91
|
-
})) as SSOUser
|
|
92
|
-
} catch (err: any) {
|
|
93
|
-
return authError(done, "Error saving user", err)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return done(null, ssoUser)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* @returns a user that has been sync'd with third party information
|
|
101
|
-
*/
|
|
102
|
-
async function syncUser(user: User, details: SSOAuthDetails): Promise<SSOUser> {
|
|
103
|
-
let firstName
|
|
104
|
-
let lastName
|
|
105
|
-
let oauth2
|
|
106
|
-
|
|
107
|
-
if (details.profile) {
|
|
108
|
-
const profile = details.profile
|
|
109
|
-
|
|
110
|
-
if (profile.name) {
|
|
111
|
-
const name = profile.name
|
|
112
|
-
// first name
|
|
113
|
-
if (name.givenName) {
|
|
114
|
-
firstName = name.givenName
|
|
115
|
-
}
|
|
116
|
-
// last name
|
|
117
|
-
if (name.familyName) {
|
|
118
|
-
lastName = name.familyName
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// oauth tokens for future use
|
|
124
|
-
if (details.oauth2) {
|
|
125
|
-
oauth2 = {
|
|
126
|
-
...details.oauth2,
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
...user,
|
|
132
|
-
provider: details.provider,
|
|
133
|
-
providerType: details.providerType,
|
|
134
|
-
firstName,
|
|
135
|
-
lastName,
|
|
136
|
-
oauth2,
|
|
137
|
-
}
|
|
138
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { generator, structures } from "../../../../../tests"
|
|
2
|
-
import { SSOProviderType } from "@budibase/types"
|
|
3
|
-
|
|
4
|
-
jest.mock("passport-google-oauth")
|
|
5
|
-
const mockStrategy = require("passport-google-oauth").OAuth2Strategy
|
|
6
|
-
|
|
7
|
-
jest.mock("../sso")
|
|
8
|
-
import * as _sso from "../sso"
|
|
9
|
-
|
|
10
|
-
const sso = jest.mocked(_sso)
|
|
11
|
-
|
|
12
|
-
const mockSaveUserFn = jest.fn()
|
|
13
|
-
const mockDone = jest.fn()
|
|
14
|
-
|
|
15
|
-
import * as google from "../google"
|
|
16
|
-
|
|
17
|
-
describe("google", () => {
|
|
18
|
-
describe("strategyFactory", () => {
|
|
19
|
-
const googleConfig = structures.sso.googleConfig()
|
|
20
|
-
const callbackUrl = generator.url()
|
|
21
|
-
|
|
22
|
-
it("should create successfully create a google strategy", async () => {
|
|
23
|
-
await google.strategyFactory(googleConfig, callbackUrl, mockSaveUserFn)
|
|
24
|
-
|
|
25
|
-
const expectedOptions = {
|
|
26
|
-
clientID: googleConfig.clientID,
|
|
27
|
-
clientSecret: googleConfig.clientSecret,
|
|
28
|
-
callbackURL: callbackUrl,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
expect(mockStrategy).toHaveBeenCalledWith(
|
|
32
|
-
expectedOptions,
|
|
33
|
-
expect.anything()
|
|
34
|
-
)
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
describe("authenticate", () => {
|
|
39
|
-
const details = structures.sso.authDetails()
|
|
40
|
-
details.provider = "google"
|
|
41
|
-
details.providerType = SSOProviderType.GOOGLE
|
|
42
|
-
|
|
43
|
-
const profile = details.profile!
|
|
44
|
-
profile.provider = "google"
|
|
45
|
-
|
|
46
|
-
beforeEach(() => {
|
|
47
|
-
jest.clearAllMocks()
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it("delegates authentication to third party common", async () => {
|
|
51
|
-
const authenticate = await google.buildVerifyFn(mockSaveUserFn)
|
|
52
|
-
|
|
53
|
-
await authenticate(
|
|
54
|
-
details.oauth2.accessToken,
|
|
55
|
-
details.oauth2.refreshToken!,
|
|
56
|
-
profile,
|
|
57
|
-
mockDone
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
expect(sso.authenticate).toHaveBeenCalledWith(
|
|
61
|
-
details,
|
|
62
|
-
true,
|
|
63
|
-
mockDone,
|
|
64
|
-
mockSaveUserFn
|
|
65
|
-
)
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
})
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { generator, structures } from "../../../../../tests"
|
|
2
|
-
import {
|
|
3
|
-
JwtClaims,
|
|
4
|
-
OIDCInnerConfig,
|
|
5
|
-
SSOAuthDetails,
|
|
6
|
-
SSOProviderType,
|
|
7
|
-
} from "@budibase/types"
|
|
8
|
-
import * as _sso from "../sso"
|
|
9
|
-
import * as oidc from "../oidc"
|
|
10
|
-
import nock from "nock"
|
|
11
|
-
|
|
12
|
-
jest.mock("@techpass/passport-openidconnect")
|
|
13
|
-
const mockStrategy = require("@techpass/passport-openidconnect").Strategy
|
|
14
|
-
|
|
15
|
-
jest.mock("../sso")
|
|
16
|
-
const sso = jest.mocked(_sso)
|
|
17
|
-
|
|
18
|
-
const mockSaveUser = jest.fn()
|
|
19
|
-
const mockDone = jest.fn()
|
|
20
|
-
|
|
21
|
-
describe("oidc", () => {
|
|
22
|
-
const callbackUrl = generator.url()
|
|
23
|
-
const oidcConfig: OIDCInnerConfig = structures.sso.oidcConfig()
|
|
24
|
-
const wellKnownConfig = structures.sso.oidcWellKnownConfig()
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
nock.cleanAll()
|
|
28
|
-
nock(oidcConfig.configUrl).get("/").reply(200, wellKnownConfig)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
describe("strategyFactory", () => {
|
|
32
|
-
it("should create successfully create an oidc strategy", async () => {
|
|
33
|
-
const strategyConfiguration = await oidc.fetchStrategyConfig(
|
|
34
|
-
oidcConfig,
|
|
35
|
-
callbackUrl
|
|
36
|
-
)
|
|
37
|
-
await oidc.strategyFactory(strategyConfiguration, mockSaveUser)
|
|
38
|
-
|
|
39
|
-
const expectedOptions = {
|
|
40
|
-
issuer: wellKnownConfig.issuer,
|
|
41
|
-
authorizationURL: wellKnownConfig.authorization_endpoint,
|
|
42
|
-
tokenURL: wellKnownConfig.token_endpoint,
|
|
43
|
-
userInfoURL: wellKnownConfig.userinfo_endpoint,
|
|
44
|
-
clientID: oidcConfig.clientID,
|
|
45
|
-
clientSecret: oidcConfig.clientSecret,
|
|
46
|
-
callbackURL: callbackUrl,
|
|
47
|
-
}
|
|
48
|
-
expect(mockStrategy).toHaveBeenCalledWith(
|
|
49
|
-
expectedOptions,
|
|
50
|
-
expect.anything()
|
|
51
|
-
)
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
describe("authenticate", () => {
|
|
56
|
-
const details: SSOAuthDetails = structures.sso.authDetails()
|
|
57
|
-
details.providerType = SSOProviderType.OIDC
|
|
58
|
-
const profile = details.profile!
|
|
59
|
-
const issuer = profile.provider
|
|
60
|
-
|
|
61
|
-
const sub = generator.string()
|
|
62
|
-
const idToken = generator.string()
|
|
63
|
-
const params = {}
|
|
64
|
-
|
|
65
|
-
let authenticateFn: any
|
|
66
|
-
let jwtClaims: JwtClaims
|
|
67
|
-
|
|
68
|
-
beforeEach(async () => {
|
|
69
|
-
jest.clearAllMocks()
|
|
70
|
-
authenticateFn = await oidc.buildVerifyFn(mockSaveUser)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
async function authenticate() {
|
|
74
|
-
await authenticateFn(
|
|
75
|
-
issuer,
|
|
76
|
-
sub,
|
|
77
|
-
profile,
|
|
78
|
-
jwtClaims,
|
|
79
|
-
details.oauth2.accessToken,
|
|
80
|
-
details.oauth2.refreshToken,
|
|
81
|
-
idToken,
|
|
82
|
-
params,
|
|
83
|
-
mockDone
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
it("passes auth details to sso module", async () => {
|
|
88
|
-
await authenticate()
|
|
89
|
-
|
|
90
|
-
expect(sso.authenticate).toHaveBeenCalledWith(
|
|
91
|
-
details,
|
|
92
|
-
false,
|
|
93
|
-
mockDone,
|
|
94
|
-
mockSaveUser
|
|
95
|
-
)
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it("uses JWT email to get email", async () => {
|
|
99
|
-
delete profile._json.email
|
|
100
|
-
|
|
101
|
-
jwtClaims = {
|
|
102
|
-
email: details.email,
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
await authenticate()
|
|
106
|
-
|
|
107
|
-
expect(sso.authenticate).toHaveBeenCalledWith(
|
|
108
|
-
details,
|
|
109
|
-
false,
|
|
110
|
-
mockDone,
|
|
111
|
-
mockSaveUser
|
|
112
|
-
)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it("uses JWT username to get email", async () => {
|
|
116
|
-
delete profile._json.email
|
|
117
|
-
|
|
118
|
-
jwtClaims = {
|
|
119
|
-
email: details.email,
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
await authenticate()
|
|
123
|
-
|
|
124
|
-
expect(sso.authenticate).toHaveBeenCalledWith(
|
|
125
|
-
details,
|
|
126
|
-
false,
|
|
127
|
-
mockDone,
|
|
128
|
-
mockSaveUser
|
|
129
|
-
)
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
it("uses JWT invalid username to get email", async () => {
|
|
133
|
-
delete profile._json.email
|
|
134
|
-
|
|
135
|
-
jwtClaims = {
|
|
136
|
-
preferred_username: "invalidUsername",
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
await expect(authenticate()).rejects.toThrow(
|
|
140
|
-
"Could not determine user email from profile"
|
|
141
|
-
)
|
|
142
|
-
})
|
|
143
|
-
})
|
|
144
|
-
})
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { structures } from "../../../../../tests"
|
|
2
|
-
import { testEnv } from "../../../../../tests/extra"
|
|
3
|
-
import { SSOAuthDetails, User } from "@budibase/types"
|
|
4
|
-
|
|
5
|
-
import { HTTPError } from "../../../../errors"
|
|
6
|
-
import * as sso from "../sso"
|
|
7
|
-
import * as context from "../../../../context"
|
|
8
|
-
import nock from "nock"
|
|
9
|
-
|
|
10
|
-
const mockDone = jest.fn()
|
|
11
|
-
const mockSaveUser = jest.fn()
|
|
12
|
-
|
|
13
|
-
jest.mock("../../../../users")
|
|
14
|
-
import * as _users from "../../../../users"
|
|
15
|
-
|
|
16
|
-
const users = jest.mocked(_users)
|
|
17
|
-
|
|
18
|
-
const getErrorMessage = () => {
|
|
19
|
-
return mockDone.mock.calls[0][2].message
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe("sso", () => {
|
|
23
|
-
describe("authenticate", () => {
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
jest.clearAllMocks()
|
|
26
|
-
testEnv.singleTenant()
|
|
27
|
-
nock.cleanAll()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
describe("validation", () => {
|
|
31
|
-
const testValidation = async (
|
|
32
|
-
details: SSOAuthDetails,
|
|
33
|
-
message: string
|
|
34
|
-
) => {
|
|
35
|
-
await sso.authenticate(details, false, mockDone, mockSaveUser)
|
|
36
|
-
|
|
37
|
-
expect(mockDone.mock.calls.length).toBe(1)
|
|
38
|
-
expect(getErrorMessage()).toContain(message)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
it("user id fails", async () => {
|
|
42
|
-
const details = structures.sso.authDetails()
|
|
43
|
-
details.userId = undefined!
|
|
44
|
-
|
|
45
|
-
await testValidation(details, "sso user id required")
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it("email fails", async () => {
|
|
49
|
-
const details = structures.sso.authDetails()
|
|
50
|
-
details.email = undefined!
|
|
51
|
-
|
|
52
|
-
await testValidation(details, "sso user email required")
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
describe("when the user doesn't exist", () => {
|
|
57
|
-
let user: User
|
|
58
|
-
let details: SSOAuthDetails
|
|
59
|
-
|
|
60
|
-
beforeEach(() => {
|
|
61
|
-
users.getById.mockImplementationOnce(() => {
|
|
62
|
-
throw new HTTPError("", 404)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
nock("http://example.com").get("/").reply(200, undefined, {
|
|
66
|
-
"Content-Type": "image/png",
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
user = structures.users.user()
|
|
70
|
-
delete user._rev
|
|
71
|
-
delete user._id
|
|
72
|
-
|
|
73
|
-
details = structures.sso.authDetails(user)
|
|
74
|
-
details.userId = structures.uuid()
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
describe("when a local account is required", () => {
|
|
78
|
-
it("returns an error message", async () => {
|
|
79
|
-
const details = structures.sso.authDetails()
|
|
80
|
-
|
|
81
|
-
await sso.authenticate(details, true, mockDone, mockSaveUser)
|
|
82
|
-
|
|
83
|
-
expect(mockDone.mock.calls.length).toBe(1)
|
|
84
|
-
expect(getErrorMessage()).toContain(
|
|
85
|
-
"Email does not yet exist. You must set up your local budibase account first."
|
|
86
|
-
)
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
describe("when a local account isn't required", () => {
|
|
91
|
-
it("creates and authenticates the user", async () => {
|
|
92
|
-
const ssoUser = structures.users.ssoUser({ user, details })
|
|
93
|
-
mockSaveUser.mockReturnValueOnce(ssoUser)
|
|
94
|
-
|
|
95
|
-
await sso.authenticate(details, false, mockDone, mockSaveUser)
|
|
96
|
-
|
|
97
|
-
// default roles for new user
|
|
98
|
-
ssoUser.roles = {}
|
|
99
|
-
|
|
100
|
-
// modified external id to match user format
|
|
101
|
-
ssoUser._id = "us_" + details.userId
|
|
102
|
-
delete ssoUser.userId
|
|
103
|
-
|
|
104
|
-
// new sso user won't have a password
|
|
105
|
-
delete ssoUser.password
|
|
106
|
-
|
|
107
|
-
// new user isn't saved with rev
|
|
108
|
-
delete ssoUser._rev
|
|
109
|
-
|
|
110
|
-
// tenant id added
|
|
111
|
-
ssoUser.tenantId = context.getTenantId()
|
|
112
|
-
|
|
113
|
-
expect(mockSaveUser).toHaveBeenCalledWith(ssoUser, {
|
|
114
|
-
hashPassword: false,
|
|
115
|
-
requirePassword: false,
|
|
116
|
-
})
|
|
117
|
-
expect(mockDone).toHaveBeenCalledWith(null, ssoUser)
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
describe("when the user exists", () => {
|
|
123
|
-
let existingUser: User
|
|
124
|
-
let details: SSOAuthDetails
|
|
125
|
-
|
|
126
|
-
beforeEach(() => {
|
|
127
|
-
existingUser = structures.users.user()
|
|
128
|
-
existingUser._id = structures.uuid()
|
|
129
|
-
details = structures.sso.authDetails(existingUser)
|
|
130
|
-
nock("http://example.com").get("/").reply(200, undefined, {
|
|
131
|
-
"Content-Type": "image/png",
|
|
132
|
-
})
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
describe("exists by email", () => {
|
|
136
|
-
beforeEach(() => {
|
|
137
|
-
users.getById.mockImplementationOnce(() => {
|
|
138
|
-
throw new HTTPError("", 404)
|
|
139
|
-
})
|
|
140
|
-
users.getGlobalUserByEmail.mockReturnValueOnce(
|
|
141
|
-
Promise.resolve(existingUser)
|
|
142
|
-
)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it("syncs and authenticates the user", async () => {
|
|
146
|
-
const ssoUser = structures.users.ssoUser({
|
|
147
|
-
user: existingUser,
|
|
148
|
-
details,
|
|
149
|
-
})
|
|
150
|
-
mockSaveUser.mockReturnValueOnce(ssoUser)
|
|
151
|
-
|
|
152
|
-
await sso.authenticate(details, true, mockDone, mockSaveUser)
|
|
153
|
-
|
|
154
|
-
// roles preserved
|
|
155
|
-
ssoUser.roles = existingUser.roles
|
|
156
|
-
|
|
157
|
-
// existing id preserved
|
|
158
|
-
ssoUser._id = existingUser._id
|
|
159
|
-
|
|
160
|
-
expect(mockSaveUser).toHaveBeenCalledWith(ssoUser, {
|
|
161
|
-
hashPassword: false,
|
|
162
|
-
requirePassword: false,
|
|
163
|
-
})
|
|
164
|
-
expect(mockDone).toHaveBeenCalledWith(null, ssoUser)
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
describe("exists by id", () => {
|
|
169
|
-
beforeEach(() => {
|
|
170
|
-
users.getById.mockReturnValueOnce(Promise.resolve(existingUser))
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it("syncs and authenticates the user", async () => {
|
|
174
|
-
const ssoUser = structures.users.ssoUser({
|
|
175
|
-
user: existingUser,
|
|
176
|
-
details,
|
|
177
|
-
})
|
|
178
|
-
mockSaveUser.mockReturnValueOnce(ssoUser)
|
|
179
|
-
|
|
180
|
-
await sso.authenticate(details, true, mockDone, mockSaveUser)
|
|
181
|
-
|
|
182
|
-
// roles preserved
|
|
183
|
-
ssoUser.roles = existingUser.roles
|
|
184
|
-
|
|
185
|
-
// existing id preserved
|
|
186
|
-
ssoUser._id = existingUser._id
|
|
187
|
-
|
|
188
|
-
expect(mockSaveUser).toHaveBeenCalledWith(ssoUser, {
|
|
189
|
-
hashPassword: false,
|
|
190
|
-
requirePassword: false,
|
|
191
|
-
})
|
|
192
|
-
expect(mockDone).toHaveBeenCalledWith(null, ssoUser)
|
|
193
|
-
})
|
|
194
|
-
})
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
})
|