2023-02-23 23:38:03 +13:00
|
|
|
import {
|
2023-11-10 00:05:42 +13:00
|
|
|
getInviteCode,
|
|
|
|
deleteInviteCode,
|
2023-02-23 23:38:03 +13:00
|
|
|
getInviteCodes,
|
|
|
|
updateInviteCode,
|
2023-11-10 03:51:07 +13:00
|
|
|
} from "@budibase/backend-core/src/redis/invite"
|
2023-02-14 00:57:30 +13:00
|
|
|
import * as userSdk from "../../../sdk/users"
|
2022-05-26 21:13:26 +12:00
|
|
|
import env from "../../../environment"
|
2022-09-01 18:58:05 +12:00
|
|
|
import {
|
2023-02-21 21:23:53 +13:00
|
|
|
AcceptUserInviteRequest,
|
|
|
|
AcceptUserInviteResponse,
|
2022-09-22 05:05:45 +12:00
|
|
|
BulkUserRequest,
|
|
|
|
BulkUserResponse,
|
2022-09-01 18:58:05 +12:00
|
|
|
CloudAccount,
|
2022-11-12 04:43:41 +13:00
|
|
|
CreateAdminUserRequest,
|
2023-02-21 21:23:53 +13:00
|
|
|
CreateAdminUserResponse,
|
|
|
|
Ctx,
|
2022-09-01 18:58:05 +12:00
|
|
|
InviteUserRequest,
|
|
|
|
InviteUsersRequest,
|
2023-11-10 04:13:59 +13:00
|
|
|
InviteUsersResponse,
|
2023-02-14 00:57:30 +13:00
|
|
|
MigrationType,
|
2023-02-21 21:23:53 +13:00
|
|
|
SaveUserResponse,
|
2022-10-04 02:02:58 +13:00
|
|
|
SearchUsersRequest,
|
2022-09-01 18:58:05 +12:00
|
|
|
User,
|
2023-02-21 21:23:53 +13:00
|
|
|
UserCtx,
|
2022-09-01 18:58:05 +12:00
|
|
|
} from "@budibase/types"
|
2022-05-29 10:03:31 +12:00
|
|
|
import {
|
2022-05-29 11:25:40 +12:00
|
|
|
accounts,
|
2022-05-29 10:03:31 +12:00
|
|
|
cache,
|
2023-08-23 05:14:08 +12:00
|
|
|
ErrorCode,
|
2022-08-03 01:58:18 +12:00
|
|
|
events,
|
2023-02-14 00:57:30 +13:00
|
|
|
migrations,
|
|
|
|
platform,
|
2023-08-23 05:14:08 +12:00
|
|
|
tenancy,
|
2022-05-29 10:03:31 +12:00
|
|
|
} from "@budibase/backend-core"
|
2022-06-30 23:01:15 +12:00
|
|
|
import { checkAnyUserExists } from "../../../utilities/users"
|
2023-03-01 06:05:11 +13:00
|
|
|
import { isEmailConfigured } from "../../../utilities/email"
|
2022-08-03 01:58:18 +12:00
|
|
|
|
2022-07-26 23:17:01 +12:00
|
|
|
const MAX_USERS_UPLOAD_LIMIT = 1000
|
|
|
|
|
2023-02-21 21:23:53 +13:00
|
|
|
export const save = async (ctx: UserCtx<User, SaveUserResponse>) => {
|
2021-08-05 20:59:08 +12:00
|
|
|
try {
|
2023-06-20 05:28:38 +12:00
|
|
|
const currentUserId = ctx.user?._id
|
2023-02-21 21:23:53 +13:00
|
|
|
const requestUser = ctx.request.body
|
|
|
|
|
2023-07-26 04:48:57 +12:00
|
|
|
const user = await userSdk.db.save(requestUser, { currentUserId })
|
2023-02-21 21:23:53 +13:00
|
|
|
|
|
|
|
ctx.body = {
|
|
|
|
_id: user._id!,
|
|
|
|
_rev: user._rev!,
|
|
|
|
email: user.email,
|
|
|
|
}
|
2022-03-16 21:18:09 +13:00
|
|
|
} catch (err: any) {
|
2021-08-05 20:59:08 +12:00
|
|
|
ctx.throw(err.status || 400, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-22 05:05:45 +12:00
|
|
|
const bulkDelete = async (userIds: string[], currentUserId: string) => {
|
|
|
|
if (userIds?.indexOf(currentUserId) !== -1) {
|
|
|
|
throw new Error("Unable to delete self.")
|
|
|
|
}
|
2023-07-26 04:48:57 +12:00
|
|
|
return await userSdk.db.bulkDelete(userIds)
|
2022-09-22 05:05:45 +12:00
|
|
|
}
|
2022-07-26 23:17:01 +12:00
|
|
|
|
2022-09-22 05:05:45 +12:00
|
|
|
const bulkCreate = async (users: User[], groupIds: string[]) => {
|
|
|
|
if (!env.SELF_HOSTED && users.length > MAX_USERS_UPLOAD_LIMIT) {
|
|
|
|
throw new Error(
|
2022-07-26 23:17:01 +12:00
|
|
|
"Max limit for upload is 1000 users. Please reduce file size and try again."
|
|
|
|
)
|
|
|
|
}
|
2023-07-26 04:48:57 +12:00
|
|
|
return await userSdk.db.bulkCreate(users, groupIds)
|
2022-09-22 05:05:45 +12:00
|
|
|
}
|
2022-07-26 23:17:01 +12:00
|
|
|
|
2023-05-04 22:58:23 +12:00
|
|
|
export const bulkUpdate = async (
|
|
|
|
ctx: Ctx<BulkUserRequest, BulkUserResponse>
|
|
|
|
) => {
|
2022-09-22 05:05:45 +12:00
|
|
|
const currentUserId = ctx.user._id
|
2023-05-04 22:58:23 +12:00
|
|
|
const input = ctx.request.body
|
2022-09-22 05:05:45 +12:00
|
|
|
let created, deleted
|
2022-07-18 23:33:56 +12:00
|
|
|
try {
|
2022-09-22 05:05:45 +12:00
|
|
|
if (input.create) {
|
|
|
|
created = await bulkCreate(input.create.users, input.create.groups)
|
|
|
|
}
|
|
|
|
if (input.delete) {
|
|
|
|
deleted = await bulkDelete(input.delete.userIds, currentUserId)
|
|
|
|
}
|
2022-07-12 02:29:39 +12:00
|
|
|
} catch (err: any) {
|
2022-09-24 09:21:51 +12:00
|
|
|
ctx.throw(err.status || 400, err?.message || err)
|
2022-07-12 02:29:39 +12:00
|
|
|
}
|
2023-05-04 22:58:23 +12:00
|
|
|
ctx.body = { created, deleted }
|
2022-07-12 02:29:39 +12:00
|
|
|
}
|
|
|
|
|
2022-03-16 21:18:09 +13:00
|
|
|
const parseBooleanParam = (param: any) => {
|
2021-11-16 08:34:08 +13:00
|
|
|
return !(param && param === "false")
|
2021-09-22 05:20:26 +12:00
|
|
|
}
|
|
|
|
|
2023-02-21 21:23:53 +13:00
|
|
|
export const adminUser = async (
|
|
|
|
ctx: Ctx<CreateAdminUserRequest, CreateAdminUserResponse>
|
|
|
|
) => {
|
2023-09-23 11:10:12 +12:00
|
|
|
const { email, password, tenantId, ssoId } = ctx.request.body
|
2023-02-14 00:57:30 +13:00
|
|
|
|
|
|
|
if (await platform.tenants.exists(tenantId)) {
|
|
|
|
ctx.throw(403, "Organisation already exists.")
|
|
|
|
}
|
|
|
|
|
|
|
|
if (env.MULTI_TENANCY) {
|
|
|
|
// store the new tenant record in the platform db
|
|
|
|
await platform.tenants.addTenant(tenantId)
|
|
|
|
await migrations.backPopulateMigrations({
|
|
|
|
type: MigrationType.GLOBAL,
|
|
|
|
tenantId,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-05-29 10:03:31 +12:00
|
|
|
await tenancy.doInTenant(tenantId, async () => {
|
2022-05-27 03:48:26 +12:00
|
|
|
// account portal sends a pre-hashed password - honour param to prevent double hashing
|
|
|
|
const hashPassword = parseBooleanParam(ctx.request.query.hashPassword)
|
|
|
|
// account portal sends no password for SSO users
|
|
|
|
const requirePassword = parseBooleanParam(ctx.request.query.requirePassword)
|
2021-09-10 04:08:27 +12:00
|
|
|
|
2022-06-30 23:01:15 +12:00
|
|
|
const userExists = await checkAnyUserExists()
|
|
|
|
if (userExists) {
|
2022-05-27 03:48:26 +12:00
|
|
|
ctx.throw(
|
|
|
|
403,
|
|
|
|
"You cannot initialise once an global user has been created."
|
|
|
|
)
|
|
|
|
}
|
2021-05-06 09:06:31 +12:00
|
|
|
|
2022-05-29 11:25:40 +12:00
|
|
|
const user: User = {
|
2022-05-27 03:48:26 +12:00
|
|
|
email: email,
|
|
|
|
password: password,
|
|
|
|
createdAt: Date.now(),
|
|
|
|
roles: {},
|
|
|
|
builder: {
|
|
|
|
global: true,
|
|
|
|
},
|
|
|
|
admin: {
|
|
|
|
global: true,
|
|
|
|
},
|
2022-05-24 02:03:52 +12:00
|
|
|
tenantId,
|
2023-09-23 11:10:12 +12:00
|
|
|
ssoId,
|
2022-05-27 03:48:26 +12:00
|
|
|
}
|
|
|
|
try {
|
2022-07-07 03:42:38 +12:00
|
|
|
// always bust checklist beforehand, if an error occurs but can proceed, don't get
|
|
|
|
// stuck in a cycle
|
2022-11-25 07:48:51 +13:00
|
|
|
await cache.bustCache(cache.CacheKey.CHECKLIST)
|
2023-07-26 04:48:57 +12:00
|
|
|
const finalUser = await userSdk.db.save(user, {
|
2022-05-27 03:48:26 +12:00
|
|
|
hashPassword,
|
2022-05-29 11:25:40 +12:00
|
|
|
requirePassword,
|
2022-04-20 06:42:52 +12:00
|
|
|
})
|
2021-09-24 10:25:25 +12:00
|
|
|
|
2022-05-29 11:25:40 +12:00
|
|
|
// events
|
|
|
|
let account: CloudAccount | undefined
|
|
|
|
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
|
|
|
account = await accounts.getAccountByTenantId(tenantId)
|
|
|
|
}
|
|
|
|
await events.identification.identifyTenantGroup(tenantId, account)
|
2021-05-06 09:06:31 +12:00
|
|
|
|
2023-02-21 21:23:53 +13:00
|
|
|
ctx.body = {
|
|
|
|
_id: finalUser._id!,
|
|
|
|
_rev: finalUser._rev!,
|
|
|
|
email: finalUser.email,
|
|
|
|
}
|
2022-05-27 03:48:26 +12:00
|
|
|
} catch (err: any) {
|
|
|
|
ctx.throw(err.status || 400, err)
|
|
|
|
}
|
|
|
|
})
|
2021-04-21 04:17:44 +12:00
|
|
|
}
|
|
|
|
|
2022-07-30 00:10:00 +12:00
|
|
|
export const countByApp = async (ctx: any) => {
|
|
|
|
const appId = ctx.params.appId
|
|
|
|
try {
|
2023-07-26 04:48:57 +12:00
|
|
|
ctx.body = await userSdk.db.countUsersByApp(appId)
|
2022-07-30 00:10:00 +12:00
|
|
|
} catch (err: any) {
|
|
|
|
ctx.throw(err.status || 400, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 21:18:09 +13:00
|
|
|
export const destroy = async (ctx: any) => {
|
2022-04-08 12:28:22 +12:00
|
|
|
const id = ctx.params.id
|
2022-08-03 01:58:18 +12:00
|
|
|
if (id === ctx.user._id) {
|
|
|
|
ctx.throw(400, "Unable to delete self.")
|
|
|
|
}
|
2022-07-20 01:20:57 +12:00
|
|
|
|
2023-07-26 04:48:57 +12:00
|
|
|
await userSdk.db.destroy(id)
|
2022-07-20 01:20:57 +12:00
|
|
|
|
2021-04-19 22:34:07 +12:00
|
|
|
ctx.body = {
|
2022-04-08 12:28:22 +12:00
|
|
|
message: `User ${id} deleted.`,
|
2021-10-04 23:30:59 +13:00
|
|
|
}
|
2021-04-19 22:34:07 +12:00
|
|
|
}
|
2021-10-04 23:30:59 +13:00
|
|
|
|
2023-05-04 22:58:23 +12:00
|
|
|
export const getAppUsers = async (ctx: Ctx<SearchUsersRequest>) => {
|
|
|
|
const body = ctx.request.body
|
2023-10-21 03:45:35 +13:00
|
|
|
const users = await userSdk.db.getUsersByAppAccess({
|
|
|
|
appId: body.appId,
|
|
|
|
limit: body.limit,
|
|
|
|
})
|
2023-02-28 22:37:03 +13:00
|
|
|
|
|
|
|
ctx.body = { data: users }
|
|
|
|
}
|
|
|
|
|
2023-05-04 22:58:23 +12:00
|
|
|
export const search = async (ctx: Ctx<SearchUsersRequest>) => {
|
|
|
|
const body = ctx.request.body
|
2023-02-28 22:37:03 +13:00
|
|
|
|
2023-10-13 07:00:53 +13:00
|
|
|
// TODO: for now only one supported search key, string.email
|
|
|
|
if (body?.query && !userSdk.core.isSupportedUserSearch(body.query)) {
|
|
|
|
ctx.throw(501, "Can only search by string.email or equal._id")
|
|
|
|
}
|
|
|
|
|
|
|
|
if (body.paginate === false) {
|
2023-02-28 22:37:03 +13:00
|
|
|
await getAppUsers(ctx)
|
|
|
|
} else {
|
2023-03-11 05:06:53 +13:00
|
|
|
const paginated = await userSdk.core.paginatedUsers(body)
|
2023-02-28 22:37:03 +13:00
|
|
|
// user hashed password shouldn't ever be returned
|
|
|
|
for (let user of paginated.data) {
|
|
|
|
if (user) {
|
|
|
|
delete user.password
|
|
|
|
}
|
2021-04-19 22:34:07 +12:00
|
|
|
}
|
2023-02-28 22:37:03 +13:00
|
|
|
ctx.body = paginated
|
2021-04-19 22:34:07 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// called internally by app server user fetch
|
2022-03-16 21:18:09 +13:00
|
|
|
export const fetch = async (ctx: any) => {
|
2023-07-26 04:48:57 +12:00
|
|
|
const all = await userSdk.db.allUsers()
|
2021-04-19 22:34:07 +12:00
|
|
|
// user hashed password shouldn't ever be returned
|
2022-03-22 06:13:16 +13:00
|
|
|
for (let user of all) {
|
2021-04-19 22:34:07 +12:00
|
|
|
if (user) {
|
|
|
|
delete user.password
|
|
|
|
}
|
|
|
|
}
|
2022-03-26 05:08:12 +13:00
|
|
|
ctx.body = all
|
2021-04-19 22:34:07 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
// called internally by app server user find
|
2022-03-16 21:18:09 +13:00
|
|
|
export const find = async (ctx: any) => {
|
2023-07-26 04:48:57 +12:00
|
|
|
ctx.body = await userSdk.db.getUser(ctx.params.id)
|
2021-04-19 22:34:07 +12:00
|
|
|
}
|
2021-05-06 02:10:28 +12:00
|
|
|
|
2022-03-16 21:18:09 +13:00
|
|
|
export const tenantUserLookup = async (ctx: any) => {
|
2021-09-29 03:49:03 +13:00
|
|
|
const id = ctx.params.id
|
2023-07-26 04:48:57 +12:00
|
|
|
const user = await userSdk.core.getPlatformUser(id)
|
2021-09-29 03:49:03 +13:00
|
|
|
if (user) {
|
|
|
|
ctx.body = user
|
|
|
|
} else {
|
2021-09-18 00:41:22 +12:00
|
|
|
ctx.throw(400, "No tenant user found.")
|
2021-08-05 20:59:08 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-23 23:38:03 +13:00
|
|
|
/*
|
|
|
|
Encapsulate the app user onboarding flows here.
|
|
|
|
*/
|
2023-11-10 04:13:59 +13:00
|
|
|
export const onboardUsers = async (
|
|
|
|
ctx: Ctx<InviteUsersRequest, InviteUsersResponse>
|
|
|
|
) => {
|
2023-11-10 04:02:44 +13:00
|
|
|
if (await isEmailConfigured()) {
|
|
|
|
await inviteMultiple(ctx)
|
|
|
|
return
|
|
|
|
}
|
2023-02-28 22:37:03 +13:00
|
|
|
|
2023-11-10 04:02:44 +13:00
|
|
|
let createdPasswords: Record<string, string> = {}
|
|
|
|
const users: User[] = ctx.request.body.map(invite => {
|
|
|
|
let password = Math.random().toString(36).substring(2, 22)
|
|
|
|
createdPasswords[invite.email] = password
|
|
|
|
|
|
|
|
return {
|
|
|
|
email: invite.email,
|
|
|
|
password,
|
|
|
|
forceResetPassword: true,
|
|
|
|
roles: invite.userInfo.apps,
|
|
|
|
admin: invite.userInfo.admin,
|
|
|
|
builder: invite.userInfo.builder,
|
|
|
|
tenantId: tenancy.getTenantId(),
|
2023-02-23 23:38:03 +13:00
|
|
|
}
|
2023-11-10 04:02:44 +13:00
|
|
|
})
|
|
|
|
|
2023-11-10 04:13:59 +13:00
|
|
|
let resp = await userSdk.db.bulkCreate(users)
|
|
|
|
for (const user of resp.successful) {
|
2023-11-10 04:02:44 +13:00
|
|
|
user.password = createdPasswords[user.email]
|
2023-11-10 04:13:59 +13:00
|
|
|
}
|
2023-11-10 04:02:44 +13:00
|
|
|
ctx.body = { ...resp, created: true }
|
2023-02-23 23:38:03 +13:00
|
|
|
}
|
|
|
|
|
2023-05-04 22:58:23 +12:00
|
|
|
export const invite = async (ctx: Ctx<InviteUserRequest>) => {
|
|
|
|
const request = ctx.request.body
|
2023-03-01 11:27:02 +13:00
|
|
|
|
2023-05-04 22:58:23 +12:00
|
|
|
let multiRequest = [request]
|
2023-03-01 11:27:02 +13:00
|
|
|
const response = await userSdk.invite(multiRequest)
|
2022-09-01 18:58:05 +12:00
|
|
|
|
|
|
|
// explicitly throw for single user invite
|
|
|
|
if (response.unsuccessful.length) {
|
|
|
|
const reason = response.unsuccessful[0].reason
|
|
|
|
if (reason === "Unavailable") {
|
|
|
|
ctx.throw(400, reason)
|
|
|
|
} else {
|
|
|
|
ctx.throw(500, reason)
|
|
|
|
}
|
2022-03-16 21:18:09 +13:00
|
|
|
}
|
2022-09-01 18:58:05 +12:00
|
|
|
|
2021-05-06 02:17:15 +12:00
|
|
|
ctx.body = {
|
2021-05-06 02:19:44 +12:00
|
|
|
message: "Invitation has been sent.",
|
2023-02-23 23:38:03 +13:00
|
|
|
successful: response.successful,
|
|
|
|
unsuccessful: response.unsuccessful,
|
2021-05-06 02:17:15 +12:00
|
|
|
}
|
2021-05-06 02:10:28 +12:00
|
|
|
}
|
|
|
|
|
2023-05-04 22:58:23 +12:00
|
|
|
export const inviteMultiple = async (ctx: Ctx<InviteUsersRequest>) => {
|
2023-11-10 04:02:44 +13:00
|
|
|
ctx.body = await userSdk.invite(ctx.request.body)
|
2022-07-05 20:21:59 +12:00
|
|
|
}
|
|
|
|
|
2023-01-28 02:44:57 +13:00
|
|
|
export const checkInvite = async (ctx: any) => {
|
|
|
|
const { code } = ctx.params
|
|
|
|
let invite
|
|
|
|
try {
|
2023-11-10 00:05:42 +13:00
|
|
|
invite = await getInviteCode(code)
|
2023-01-28 02:44:57 +13:00
|
|
|
} catch (e) {
|
2023-04-24 20:31:48 +12:00
|
|
|
console.warn("Error getting invite from code", e)
|
2023-01-28 02:44:57 +13:00
|
|
|
ctx.throw(400, "There was a problem with the invite")
|
2023-11-10 00:05:42 +13:00
|
|
|
return
|
2023-01-28 02:44:57 +13:00
|
|
|
}
|
|
|
|
ctx.body = {
|
|
|
|
email: invite.email,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-23 23:38:03 +13:00
|
|
|
export const getUserInvites = async (ctx: any) => {
|
|
|
|
try {
|
|
|
|
// Restricted to the currently authenticated tenant
|
2023-11-10 00:15:44 +13:00
|
|
|
ctx.body = await getInviteCodes()
|
2023-02-23 23:38:03 +13:00
|
|
|
} catch (e) {
|
|
|
|
ctx.throw(400, "There was a problem fetching invites")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const updateInvite = async (ctx: any) => {
|
|
|
|
const { code } = ctx.params
|
|
|
|
let updateBody = { ...ctx.request.body }
|
|
|
|
|
|
|
|
delete updateBody.email
|
|
|
|
|
|
|
|
let invite
|
|
|
|
try {
|
2023-11-10 00:05:42 +13:00
|
|
|
invite = await getInviteCode(code)
|
2023-02-23 23:38:03 +13:00
|
|
|
} catch (e) {
|
|
|
|
ctx.throw(400, "There was a problem with the invite")
|
2023-11-10 00:05:42 +13:00
|
|
|
return
|
2023-02-23 23:38:03 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
let updated = {
|
|
|
|
...invite,
|
|
|
|
}
|
|
|
|
|
2023-09-05 20:25:06 +12:00
|
|
|
if (!updateBody?.builder?.apps && updated.info?.builder?.apps) {
|
|
|
|
updated.info.builder.apps = []
|
|
|
|
} else if (updateBody?.builder) {
|
|
|
|
updated.info.builder = updateBody.builder
|
2023-08-31 03:43:24 +12:00
|
|
|
}
|
|
|
|
|
2023-02-23 23:38:03 +13:00
|
|
|
if (!updateBody?.apps || !Object.keys(updateBody?.apps).length) {
|
|
|
|
updated.info.apps = []
|
|
|
|
} else {
|
|
|
|
updated.info = {
|
|
|
|
...invite.info,
|
|
|
|
apps: {
|
|
|
|
...invite.info.apps,
|
|
|
|
...updateBody.apps,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
await updateInviteCode(code, updated)
|
|
|
|
ctx.body = { ...invite }
|
|
|
|
}
|
|
|
|
|
2023-02-21 21:23:53 +13:00
|
|
|
export const inviteAccept = async (
|
|
|
|
ctx: Ctx<AcceptUserInviteRequest, AcceptUserInviteResponse>
|
|
|
|
) => {
|
2021-05-20 02:09:57 +12:00
|
|
|
const { inviteCode, password, firstName, lastName } = ctx.request.body
|
2021-05-06 02:17:15 +12:00
|
|
|
try {
|
2021-08-05 20:59:08 +12:00
|
|
|
// info is an extension of the user object that was stored by global
|
2023-11-10 00:05:42 +13:00
|
|
|
const { email, info }: any = await getInviteCode(inviteCode)
|
|
|
|
await deleteInviteCode(inviteCode)
|
2023-02-21 21:23:53 +13:00
|
|
|
const user = await tenancy.doInTenant(info.tenantId, async () => {
|
2023-08-30 01:41:56 +12:00
|
|
|
let request: any = {
|
2021-08-05 20:59:08 +12:00
|
|
|
firstName,
|
|
|
|
lastName,
|
|
|
|
password,
|
|
|
|
email,
|
2023-09-05 20:58:05 +12:00
|
|
|
admin: { global: info?.admin?.global || false },
|
2023-02-23 23:38:03 +13:00
|
|
|
roles: info.apps,
|
2023-02-24 00:19:29 +13:00
|
|
|
tenantId: info.tenantId,
|
2023-02-23 23:38:03 +13:00
|
|
|
}
|
2023-09-04 20:37:10 +12:00
|
|
|
let builder: { global: boolean; apps?: string[] } = {
|
2023-09-05 20:58:05 +12:00
|
|
|
global: info?.builder?.global || false,
|
2023-09-04 20:37:10 +12:00
|
|
|
}
|
2023-02-23 23:38:03 +13:00
|
|
|
|
2023-09-05 20:58:05 +12:00
|
|
|
if (info?.builder?.apps) {
|
2023-09-05 20:25:06 +12:00
|
|
|
builder.apps = info.builder.apps
|
2023-08-30 01:41:56 +12:00
|
|
|
request.builder = builder
|
|
|
|
}
|
2023-02-23 23:38:03 +13:00
|
|
|
delete info.apps
|
|
|
|
request = {
|
|
|
|
...request,
|
2021-08-05 20:59:08 +12:00
|
|
|
...info,
|
2023-02-23 23:38:03 +13:00
|
|
|
}
|
|
|
|
|
2023-07-26 04:48:57 +12:00
|
|
|
const saved = await userSdk.db.save(request)
|
2022-05-26 21:13:26 +12:00
|
|
|
const db = tenancy.getGlobalDB()
|
2023-07-18 21:41:51 +12:00
|
|
|
const user = await db.get<User>(saved._id)
|
2022-05-24 09:14:44 +12:00
|
|
|
await events.user.inviteAccepted(user)
|
2022-05-25 07:01:13 +12:00
|
|
|
return saved
|
2022-04-08 12:28:22 +12:00
|
|
|
})
|
2023-02-21 21:23:53 +13:00
|
|
|
|
|
|
|
ctx.body = {
|
2023-04-24 20:31:48 +12:00
|
|
|
_id: user._id!,
|
|
|
|
_rev: user._rev!,
|
2023-02-21 21:23:53 +13:00
|
|
|
email: user.email,
|
|
|
|
}
|
2022-03-16 21:18:09 +13:00
|
|
|
} catch (err: any) {
|
2023-03-16 21:42:02 +13:00
|
|
|
if (err.code === ErrorCode.USAGE_LIMIT_EXCEEDED) {
|
2022-03-05 02:42:50 +13:00
|
|
|
// explicitly re-throw limit exceeded errors
|
|
|
|
ctx.throw(400, err)
|
|
|
|
}
|
2023-04-27 19:25:53 +12:00
|
|
|
console.warn("Error inviting user", err)
|
2021-05-06 02:17:15 +12:00
|
|
|
ctx.throw(400, "Unable to create new user, invitation invalid.")
|
2021-05-06 02:10:28 +12:00
|
|
|
}
|
|
|
|
}
|