@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
package/src/users/db.ts
DELETED
|
@@ -1,582 +0,0 @@
|
|
|
1
|
-
import env from "../environment"
|
|
2
|
-
import * as eventHelpers from "./events"
|
|
3
|
-
import * as accountSdk from "../accounts"
|
|
4
|
-
import * as cache from "../cache"
|
|
5
|
-
import { getGlobalDB, getIdentity, getTenantId } from "../context"
|
|
6
|
-
import * as dbUtils from "../db"
|
|
7
|
-
import { EmailUnavailableError, HTTPError } from "../errors"
|
|
8
|
-
import * as platform from "../platform"
|
|
9
|
-
import * as sessions from "../security/sessions"
|
|
10
|
-
import * as usersCore from "./users"
|
|
11
|
-
import {
|
|
12
|
-
Account,
|
|
13
|
-
BulkUserCreated,
|
|
14
|
-
BulkUserDeleted,
|
|
15
|
-
isSSOAccount,
|
|
16
|
-
isSSOUser,
|
|
17
|
-
SaveUserOpts,
|
|
18
|
-
User,
|
|
19
|
-
UserGroup,
|
|
20
|
-
UserIdentifier,
|
|
21
|
-
UserStatus,
|
|
22
|
-
PlatformUserBySsoId,
|
|
23
|
-
PlatformUserById,
|
|
24
|
-
AnyDocument,
|
|
25
|
-
} from "@budibase/types"
|
|
26
|
-
import {
|
|
27
|
-
getAccountHolderFromUsers,
|
|
28
|
-
isAdmin,
|
|
29
|
-
isCreator,
|
|
30
|
-
validateUniqueUser,
|
|
31
|
-
} from "./utils"
|
|
32
|
-
import {
|
|
33
|
-
getFirstPlatformUser,
|
|
34
|
-
getPlatformUsers,
|
|
35
|
-
searchExistingEmails,
|
|
36
|
-
} from "./lookup"
|
|
37
|
-
import { hash } from "../utils"
|
|
38
|
-
import { validatePassword } from "../security"
|
|
39
|
-
|
|
40
|
-
type QuotaUpdateFn = (
|
|
41
|
-
change: number,
|
|
42
|
-
creatorsChange: number,
|
|
43
|
-
cb?: () => Promise<any>
|
|
44
|
-
) => Promise<any>
|
|
45
|
-
type GroupUpdateFn = (groupId: string, userIds: string[]) => Promise<any>
|
|
46
|
-
type FeatureFn = () => Promise<Boolean>
|
|
47
|
-
type GroupGetFn = (ids: string[]) => Promise<UserGroup[]>
|
|
48
|
-
type GroupBuildersFn = (user: User) => Promise<string[]>
|
|
49
|
-
type QuotaFns = { addUsers: QuotaUpdateFn; removeUsers: QuotaUpdateFn }
|
|
50
|
-
type GroupFns = {
|
|
51
|
-
addUsers: GroupUpdateFn
|
|
52
|
-
getBulk: GroupGetFn
|
|
53
|
-
getGroupBuilderAppIds: GroupBuildersFn
|
|
54
|
-
}
|
|
55
|
-
type CreateAdminUserOpts = {
|
|
56
|
-
password?: string
|
|
57
|
-
ssoId?: string
|
|
58
|
-
hashPassword?: boolean
|
|
59
|
-
requirePassword?: boolean
|
|
60
|
-
skipPasswordValidation?: boolean
|
|
61
|
-
firstName?: string
|
|
62
|
-
lastName?: string
|
|
63
|
-
}
|
|
64
|
-
type FeatureFns = { isSSOEnforced: FeatureFn; isAppBuildersEnabled: FeatureFn }
|
|
65
|
-
|
|
66
|
-
const bulkDeleteProcessing = async (dbUser: User) => {
|
|
67
|
-
const userId = dbUser._id as string
|
|
68
|
-
await platform.users.removeUser(dbUser)
|
|
69
|
-
await eventHelpers.handleDeleteEvents(dbUser)
|
|
70
|
-
await cache.user.invalidateUser(userId)
|
|
71
|
-
await sessions.invalidateSessions(userId, { reason: "bulk-deletion" })
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export class UserDB {
|
|
75
|
-
static quotas: QuotaFns
|
|
76
|
-
static groups: GroupFns
|
|
77
|
-
static features: FeatureFns
|
|
78
|
-
|
|
79
|
-
static init(quotaFns: QuotaFns, groupFns: GroupFns, featureFns: FeatureFns) {
|
|
80
|
-
UserDB.quotas = quotaFns
|
|
81
|
-
UserDB.groups = groupFns
|
|
82
|
-
UserDB.features = featureFns
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
static async isPreventPasswordActions(user: User, account?: Account) {
|
|
86
|
-
// when in maintenance mode we allow sso users with the admin role
|
|
87
|
-
// to perform any password action - this prevents lockout
|
|
88
|
-
if (env.ENABLE_SSO_MAINTENANCE_MODE && isAdmin(user)) {
|
|
89
|
-
return false
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// SSO is enforced for all users
|
|
93
|
-
if (await UserDB.features.isSSOEnforced()) {
|
|
94
|
-
return true
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check local sso
|
|
98
|
-
if (isSSOUser(user)) {
|
|
99
|
-
return true
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Check account sso
|
|
103
|
-
if (!account) {
|
|
104
|
-
account = await accountSdk.getAccountByTenantId(getTenantId())
|
|
105
|
-
}
|
|
106
|
-
return !!(account && account.email === user.email && isSSOAccount(account))
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
static async buildUser(
|
|
110
|
-
user: User,
|
|
111
|
-
opts: SaveUserOpts = {
|
|
112
|
-
hashPassword: true,
|
|
113
|
-
requirePassword: true,
|
|
114
|
-
},
|
|
115
|
-
tenantId: string,
|
|
116
|
-
dbUser?: any,
|
|
117
|
-
account?: Account
|
|
118
|
-
): Promise<User> {
|
|
119
|
-
let { password, _id } = user
|
|
120
|
-
|
|
121
|
-
// don't require a password if the db user doesn't already have one
|
|
122
|
-
if (dbUser && !dbUser.password) {
|
|
123
|
-
opts.requirePassword = false
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
let hashedPassword
|
|
127
|
-
if (password) {
|
|
128
|
-
if (await UserDB.isPreventPasswordActions(user, account)) {
|
|
129
|
-
throw new HTTPError("Password change is disabled for this user", 400)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (!opts.skipPasswordValidation) {
|
|
133
|
-
const passwordValidation = validatePassword(password)
|
|
134
|
-
if (!passwordValidation.valid) {
|
|
135
|
-
throw new HTTPError(passwordValidation.error, 400)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
hashedPassword = opts.hashPassword ? await hash(password) : password
|
|
140
|
-
} else if (dbUser) {
|
|
141
|
-
hashedPassword = dbUser.password
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// passwords are never required if sso is enforced
|
|
145
|
-
const requirePasswords =
|
|
146
|
-
opts.requirePassword && !(await UserDB.features.isSSOEnforced())
|
|
147
|
-
if (!hashedPassword && requirePasswords) {
|
|
148
|
-
throw "Password must be specified."
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
_id = _id || dbUtils.generateGlobalUserID()
|
|
152
|
-
|
|
153
|
-
const fullUser = {
|
|
154
|
-
createdAt: Date.now(),
|
|
155
|
-
...dbUser,
|
|
156
|
-
...user,
|
|
157
|
-
_id,
|
|
158
|
-
password: hashedPassword,
|
|
159
|
-
tenantId,
|
|
160
|
-
}
|
|
161
|
-
// make sure the roles object is always present
|
|
162
|
-
if (!fullUser.roles) {
|
|
163
|
-
fullUser.roles = {}
|
|
164
|
-
}
|
|
165
|
-
// add the active status to a user if it's not provided
|
|
166
|
-
if (fullUser.status == null) {
|
|
167
|
-
fullUser.status = UserStatus.ACTIVE
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return fullUser
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
static async allUsers() {
|
|
174
|
-
const db = getGlobalDB()
|
|
175
|
-
const response = await db.allDocs<User>(
|
|
176
|
-
dbUtils.getGlobalUserParams(null, {
|
|
177
|
-
include_docs: true,
|
|
178
|
-
})
|
|
179
|
-
)
|
|
180
|
-
return response.rows.map(row => row.doc!)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
static async countUsersByApp(appId: string) {
|
|
184
|
-
let response: any = await usersCore.searchGlobalUsersByApp(appId, {})
|
|
185
|
-
return {
|
|
186
|
-
userCount: response.length,
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
static async getUsersByAppAccess(opts: { appId?: string; limit?: number }) {
|
|
191
|
-
let response: User[] = await usersCore.searchGlobalUsersByAppAccess(
|
|
192
|
-
opts.appId,
|
|
193
|
-
{ limit: opts.limit || 50 }
|
|
194
|
-
)
|
|
195
|
-
return response
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
static async getUserByEmail(email: string) {
|
|
199
|
-
return usersCore.getGlobalUserByEmail(email)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Gets a user by ID from the global database, based on the current tenancy.
|
|
204
|
-
*/
|
|
205
|
-
static async getUser(userId: string) {
|
|
206
|
-
const user = await usersCore.getById(userId)
|
|
207
|
-
if (user) {
|
|
208
|
-
delete user.password
|
|
209
|
-
}
|
|
210
|
-
return user
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
static async bulkGet(userIds: string[]) {
|
|
214
|
-
return await usersCore.bulkGetGlobalUsersById(userIds)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
static async bulkUpdate(users: User[]) {
|
|
218
|
-
return await usersCore.bulkUpdateGlobalUsers(users)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
static async save(user: User, opts: SaveUserOpts = {}): Promise<User> {
|
|
222
|
-
// default booleans to true
|
|
223
|
-
if (opts.hashPassword == null) {
|
|
224
|
-
opts.hashPassword = true
|
|
225
|
-
}
|
|
226
|
-
if (opts.requirePassword == null) {
|
|
227
|
-
opts.requirePassword = true
|
|
228
|
-
}
|
|
229
|
-
const tenantId = getTenantId()
|
|
230
|
-
const db = getGlobalDB()
|
|
231
|
-
|
|
232
|
-
const { email, _id, userGroups = [], roles } = user
|
|
233
|
-
|
|
234
|
-
if (!email && !_id) {
|
|
235
|
-
throw new Error("_id or email is required")
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
let dbUser: User | undefined
|
|
239
|
-
if (_id) {
|
|
240
|
-
// try to get existing user from db
|
|
241
|
-
try {
|
|
242
|
-
dbUser = await usersCore.getById(_id)
|
|
243
|
-
if (email && dbUser.email !== email && !opts.allowChangingEmail) {
|
|
244
|
-
throw new Error("Email address cannot be changed")
|
|
245
|
-
}
|
|
246
|
-
} catch (e: any) {
|
|
247
|
-
if (e.status === 404) {
|
|
248
|
-
// do nothing, save this new user with the id specified - required for SSO auth
|
|
249
|
-
} else {
|
|
250
|
-
throw e
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (!dbUser && email) {
|
|
256
|
-
// no id was specified - load from email instead
|
|
257
|
-
dbUser = await usersCore.getGlobalUserByEmail(email)
|
|
258
|
-
if (dbUser && dbUser._id !== _id) {
|
|
259
|
-
throw new EmailUnavailableError(email)
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const change = dbUser ? 0 : 1 // no change if there is existing user
|
|
264
|
-
const creatorsChange =
|
|
265
|
-
(await isCreator(dbUser)) !== (await isCreator(user)) ? 1 : 0
|
|
266
|
-
return UserDB.quotas.addUsers(change, creatorsChange, async () => {
|
|
267
|
-
await validateUniqueUser(email, tenantId)
|
|
268
|
-
|
|
269
|
-
let builtUser = await UserDB.buildUser(user, opts, tenantId, dbUser)
|
|
270
|
-
// don't allow a user to update its own roles/perms
|
|
271
|
-
if (opts.currentUserId && opts.currentUserId === dbUser?._id) {
|
|
272
|
-
builtUser = usersCore.cleanseUserObject(builtUser, dbUser) as User
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (!dbUser && roles?.length) {
|
|
276
|
-
builtUser.roles = { ...roles }
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// make sure we set the _id field for a new user
|
|
280
|
-
// Also if this is a new user, associate groups with them
|
|
281
|
-
const groupPromises = []
|
|
282
|
-
if (!_id) {
|
|
283
|
-
if (userGroups.length > 0) {
|
|
284
|
-
for (let groupId of userGroups) {
|
|
285
|
-
groupPromises.push(
|
|
286
|
-
UserDB.groups.addUsers(groupId, [builtUser._id!])
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
try {
|
|
293
|
-
// save the user to db
|
|
294
|
-
let response = await db.put(builtUser)
|
|
295
|
-
builtUser._rev = response.rev
|
|
296
|
-
|
|
297
|
-
await eventHelpers.handleSaveEvents(builtUser, dbUser)
|
|
298
|
-
if (dbUser && builtUser.email !== dbUser.email) {
|
|
299
|
-
// Remove the plaform email reference if the email changed
|
|
300
|
-
await platform.users.removeUser({ email: dbUser.email } as User)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
await platform.users.addUser(
|
|
304
|
-
tenantId,
|
|
305
|
-
builtUser._id!,
|
|
306
|
-
builtUser.email,
|
|
307
|
-
builtUser.ssoId
|
|
308
|
-
)
|
|
309
|
-
await cache.user.invalidateUser(response.id)
|
|
310
|
-
|
|
311
|
-
await Promise.all(groupPromises)
|
|
312
|
-
|
|
313
|
-
// finally returned the saved user from the db
|
|
314
|
-
return db.get(builtUser._id!)
|
|
315
|
-
} catch (err: any) {
|
|
316
|
-
if (err.status === 409) {
|
|
317
|
-
throw "User exists already"
|
|
318
|
-
} else {
|
|
319
|
-
throw err
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
})
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
static async bulkCreate(
|
|
326
|
-
newUsersRequested: User[],
|
|
327
|
-
groups?: string[]
|
|
328
|
-
): Promise<BulkUserCreated> {
|
|
329
|
-
const tenantId = getTenantId()
|
|
330
|
-
|
|
331
|
-
let usersToSave: any[] = []
|
|
332
|
-
let newUsers: any[] = []
|
|
333
|
-
let newCreators: any[] = []
|
|
334
|
-
|
|
335
|
-
const emails = newUsersRequested.map((user: User) => user.email)
|
|
336
|
-
const existingEmails = await searchExistingEmails(emails)
|
|
337
|
-
const unsuccessful: { email: string; reason: string }[] = []
|
|
338
|
-
|
|
339
|
-
for (const newUser of newUsersRequested) {
|
|
340
|
-
if (
|
|
341
|
-
newUsers.find(
|
|
342
|
-
(x: User) => x.email.toLowerCase() === newUser.email.toLowerCase()
|
|
343
|
-
) ||
|
|
344
|
-
existingEmails.includes(newUser.email.toLowerCase())
|
|
345
|
-
) {
|
|
346
|
-
unsuccessful.push({
|
|
347
|
-
email: newUser.email,
|
|
348
|
-
reason: `Unavailable`,
|
|
349
|
-
})
|
|
350
|
-
continue
|
|
351
|
-
}
|
|
352
|
-
newUser.userGroups = groups || []
|
|
353
|
-
newUsers.push(newUser)
|
|
354
|
-
if (await isCreator(newUser)) {
|
|
355
|
-
newCreators.push(newUser)
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const account = await accountSdk.getAccountByTenantId(tenantId)
|
|
360
|
-
return UserDB.quotas.addUsers(
|
|
361
|
-
newUsers.length,
|
|
362
|
-
newCreators.length,
|
|
363
|
-
async () => {
|
|
364
|
-
// create the promises array that will be called by bulkDocs
|
|
365
|
-
newUsers.forEach((user: any) => {
|
|
366
|
-
usersToSave.push(
|
|
367
|
-
UserDB.buildUser(
|
|
368
|
-
user,
|
|
369
|
-
{
|
|
370
|
-
hashPassword: true,
|
|
371
|
-
requirePassword: user.requirePassword,
|
|
372
|
-
},
|
|
373
|
-
tenantId,
|
|
374
|
-
undefined, // no dbUser
|
|
375
|
-
account
|
|
376
|
-
)
|
|
377
|
-
)
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
const usersToBulkSave = await Promise.all(usersToSave)
|
|
381
|
-
await usersCore.bulkUpdateGlobalUsers(usersToBulkSave)
|
|
382
|
-
|
|
383
|
-
// Post-processing of bulk added users, e.g. events and cache operations
|
|
384
|
-
for (const user of usersToBulkSave) {
|
|
385
|
-
// TODO: Refactor to bulk insert users into the info db
|
|
386
|
-
// instead of relying on looping tenant creation
|
|
387
|
-
await platform.users.addUser(tenantId, user._id, user.email)
|
|
388
|
-
await eventHelpers.handleSaveEvents(user, undefined)
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const saved = usersToBulkSave.map(user => {
|
|
392
|
-
return {
|
|
393
|
-
_id: user._id,
|
|
394
|
-
email: user.email,
|
|
395
|
-
}
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
// now update the groups
|
|
399
|
-
if (Array.isArray(saved) && groups) {
|
|
400
|
-
const groupPromises = []
|
|
401
|
-
const createdUserIds = saved.map(user => user._id)
|
|
402
|
-
for (let groupId of groups) {
|
|
403
|
-
groupPromises.push(UserDB.groups.addUsers(groupId, createdUserIds))
|
|
404
|
-
}
|
|
405
|
-
await Promise.all(groupPromises)
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
return {
|
|
409
|
-
successful: saved,
|
|
410
|
-
unsuccessful,
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
)
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
static async bulkDelete(
|
|
417
|
-
users: Array<UserIdentifier>
|
|
418
|
-
): Promise<BulkUserDeleted> {
|
|
419
|
-
const db = getGlobalDB()
|
|
420
|
-
|
|
421
|
-
const response: BulkUserDeleted = {
|
|
422
|
-
successful: [],
|
|
423
|
-
unsuccessful: [],
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// remove the account holder from the delete request if present
|
|
427
|
-
const accountHolder = await getAccountHolderFromUsers(users)
|
|
428
|
-
if (accountHolder) {
|
|
429
|
-
users = users.filter(u => u.userId !== accountHolder.userId)
|
|
430
|
-
// mark user as unsuccessful
|
|
431
|
-
response.unsuccessful.push({
|
|
432
|
-
_id: accountHolder.userId,
|
|
433
|
-
email: accountHolder.email,
|
|
434
|
-
reason: "Account holder cannot be deleted",
|
|
435
|
-
})
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Get users and delete
|
|
439
|
-
const allDocsResponse = await db.allDocs<User>({
|
|
440
|
-
include_docs: true,
|
|
441
|
-
keys: users.map(u => u.userId),
|
|
442
|
-
})
|
|
443
|
-
const usersToDelete = allDocsResponse.rows.map(user => {
|
|
444
|
-
return user.doc!
|
|
445
|
-
})
|
|
446
|
-
|
|
447
|
-
// Delete from DB
|
|
448
|
-
const toDelete = usersToDelete.map(user => ({
|
|
449
|
-
...user,
|
|
450
|
-
_deleted: true,
|
|
451
|
-
}))
|
|
452
|
-
const dbResponse = await usersCore.bulkUpdateGlobalUsers(toDelete)
|
|
453
|
-
|
|
454
|
-
const creatorsEval = await Promise.all(usersToDelete.map(isCreator))
|
|
455
|
-
const creatorsToDeleteCount = creatorsEval.filter(
|
|
456
|
-
creator => !!creator
|
|
457
|
-
).length
|
|
458
|
-
|
|
459
|
-
const ssoUsersToDelete: AnyDocument[] = []
|
|
460
|
-
for (let user of usersToDelete) {
|
|
461
|
-
const platformUser = (await getFirstPlatformUser(
|
|
462
|
-
user._id!
|
|
463
|
-
)) as PlatformUserById
|
|
464
|
-
const ssoId = platformUser.ssoId
|
|
465
|
-
if (ssoId) {
|
|
466
|
-
// Need to get the _rev of the SSO user doc to delete it. The view also returns docs that have the ssoId property, so we need to ignore those.
|
|
467
|
-
const ssoUsers = (await getPlatformUsers(
|
|
468
|
-
ssoId
|
|
469
|
-
)) as PlatformUserBySsoId[]
|
|
470
|
-
ssoUsers
|
|
471
|
-
.filter(user => user.ssoId == null)
|
|
472
|
-
.forEach(user => {
|
|
473
|
-
ssoUsersToDelete.push({
|
|
474
|
-
...user,
|
|
475
|
-
_deleted: true,
|
|
476
|
-
})
|
|
477
|
-
})
|
|
478
|
-
}
|
|
479
|
-
await bulkDeleteProcessing(user)
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Delete any associated SSO user docs
|
|
483
|
-
await platform.getPlatformDB().bulkDocs(ssoUsersToDelete)
|
|
484
|
-
|
|
485
|
-
await UserDB.quotas.removeUsers(toDelete.length, creatorsToDeleteCount)
|
|
486
|
-
|
|
487
|
-
// Build Response
|
|
488
|
-
// index users by id
|
|
489
|
-
const userIndex: { [key: string]: User } = {}
|
|
490
|
-
usersToDelete.reduce((prev, current) => {
|
|
491
|
-
prev[current._id!] = current
|
|
492
|
-
return prev
|
|
493
|
-
}, userIndex)
|
|
494
|
-
|
|
495
|
-
// add the successful and unsuccessful users to response
|
|
496
|
-
dbResponse.forEach(item => {
|
|
497
|
-
const email = userIndex[item.id].email
|
|
498
|
-
if (item.ok) {
|
|
499
|
-
response.successful.push({ _id: item.id, email })
|
|
500
|
-
} else {
|
|
501
|
-
response.unsuccessful.push({
|
|
502
|
-
_id: item.id,
|
|
503
|
-
email,
|
|
504
|
-
reason: "Database error",
|
|
505
|
-
})
|
|
506
|
-
}
|
|
507
|
-
})
|
|
508
|
-
|
|
509
|
-
return response
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
static async destroy(id: string) {
|
|
513
|
-
const db = getGlobalDB()
|
|
514
|
-
const dbUser = (await db.get(id)) as User
|
|
515
|
-
const userId = dbUser._id as string
|
|
516
|
-
|
|
517
|
-
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
518
|
-
// root account holder can't be deleted from inside budibase
|
|
519
|
-
const email = dbUser.email
|
|
520
|
-
const account = await accountSdk.getAccount(email)
|
|
521
|
-
if (account) {
|
|
522
|
-
if (dbUser.userId === getIdentity()!._id) {
|
|
523
|
-
throw new HTTPError('Please visit "Account" to delete this user', 400)
|
|
524
|
-
} else {
|
|
525
|
-
throw new HTTPError("Account holder cannot be deleted", 400)
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
await platform.users.removeUser(dbUser)
|
|
531
|
-
|
|
532
|
-
await db.remove(userId, dbUser._rev!)
|
|
533
|
-
|
|
534
|
-
const creatorsToDelete = (await isCreator(dbUser)) ? 1 : 0
|
|
535
|
-
await UserDB.quotas.removeUsers(1, creatorsToDelete)
|
|
536
|
-
await eventHelpers.handleDeleteEvents(dbUser)
|
|
537
|
-
await cache.user.invalidateUser(userId)
|
|
538
|
-
await sessions.invalidateSessions(userId, { reason: "deletion" })
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
static async createAdminUser(
|
|
542
|
-
email: string,
|
|
543
|
-
tenantId: string,
|
|
544
|
-
opts?: CreateAdminUserOpts
|
|
545
|
-
) {
|
|
546
|
-
const password = opts?.password
|
|
547
|
-
const user: User = {
|
|
548
|
-
email: email,
|
|
549
|
-
password,
|
|
550
|
-
createdAt: Date.now(),
|
|
551
|
-
roles: {},
|
|
552
|
-
builder: {
|
|
553
|
-
global: true,
|
|
554
|
-
},
|
|
555
|
-
admin: {
|
|
556
|
-
global: true,
|
|
557
|
-
},
|
|
558
|
-
tenantId,
|
|
559
|
-
firstName: opts?.firstName,
|
|
560
|
-
lastName: opts?.lastName,
|
|
561
|
-
}
|
|
562
|
-
if (opts?.ssoId) {
|
|
563
|
-
user.ssoId = opts.ssoId
|
|
564
|
-
}
|
|
565
|
-
// always bust checklist beforehand, if an error occurs but can proceed, don't get
|
|
566
|
-
// stuck in a cycle
|
|
567
|
-
await cache.bustCache(cache.CacheKey.CHECKLIST)
|
|
568
|
-
return await UserDB.save(user, {
|
|
569
|
-
hashPassword: opts?.hashPassword,
|
|
570
|
-
requirePassword: opts?.requirePassword,
|
|
571
|
-
skipPasswordValidation: opts?.skipPasswordValidation,
|
|
572
|
-
})
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
static async getGroups(groupIds: string[]) {
|
|
576
|
-
return await this.groups.getBulk(groupIds)
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
static async getGroupBuilderAppIds(user: User) {
|
|
580
|
-
return await this.groups.getGroupBuilderAppIds(user)
|
|
581
|
-
}
|
|
582
|
-
}
|