1
0
Fork 0
mirror of synced 2024-07-02 21:10:43 +12:00
budibase/packages/worker/src/sdk/users/users.ts

217 lines
5.5 KiB
TypeScript
Raw Normal View History

2022-04-08 12:28:22 +12:00
import env from "../../environment"
import { quotas } from "@budibase/pro"
import * as apps from "../../utilities/appService"
import * as eventHelpers from "./events"
import {
2022-04-08 12:28:22 +12:00
tenancy,
utils,
db as dbUtils,
2022-04-08 12:28:22 +12:00
constants,
cache,
users as usersCore,
2022-04-08 12:28:22 +12:00
deprovisioning,
sessions,
HTTPError,
accounts,
migrations,
} from "@budibase/backend-core"
import { MigrationType } from "@budibase/types"
2022-05-25 07:01:13 +12:00
const PAGE_LIMIT = 10
2022-04-08 12:28:22 +12:00
/**
* Retrieves all users from the current tenancy.
*/
export const allUsers = async () => {
const db = tenancy.getGlobalDB()
const response = await db.allDocs(
dbUtils.getGlobalUserParams(null, {
include_docs: true,
})
)
return response.rows.map((row: any) => row.doc)
}
export const paginatedUsers = async (page?: string) => {
const db = tenancy.getGlobalDB()
// get one extra document, to have the next page
const response = await db.allDocs(
dbUtils.getGlobalUserParams(null, {
include_docs: true,
limit: PAGE_LIMIT + 1,
startkey: page,
})
)
return dbUtils.pagination(response, PAGE_LIMIT)
}
2022-04-08 12:28:22 +12:00
/**
* Gets a user by ID from the global database, based on the current tenancy.
*/
export const getUser = async (userId: string) => {
const db = tenancy.getGlobalDB()
let user
try {
user = await db.get(userId)
} catch (err: any) {
// no user found, just return nothing
if (err.status === 404) {
return {}
}
throw err
}
if (user) {
delete user.password
}
return user
}
interface SaveUserOpts {
hashPassword?: boolean
requirePassword?: boolean
bulkCreate?: boolean
}
2022-04-08 12:28:22 +12:00
export const save = async (
user: any,
opts: SaveUserOpts = {
hashPassword: true,
requirePassword: true,
bulkCreate: false,
}
2022-04-08 12:28:22 +12:00
) => {
const tenantId = tenancy.getTenantId()
const db = tenancy.getGlobalDB()
2022-04-08 12:28:22 +12:00
let { email, password, _id } = user
// make sure another user isn't using the same email
let dbUser: any
if (opts.bulkCreate) {
dbUser = null
} else if (email) {
2022-04-08 12:28:22 +12:00
// check budibase users inside the tenant
dbUser = await usersCore.getGlobalUserByEmail(email)
if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) {
throw `Email address ${email} already in use.`
}
// check budibase users in other tenants
if (env.MULTI_TENANCY) {
const tenantUser = await tenancy.getTenantUser(email)
if (tenantUser != null && tenantUser.tenantId !== tenantId) {
throw `Email address ${email} already in use.`
}
}
// check root account users in account portal
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
const account = await accounts.getAccount(email)
if (account && account.verified && account.tenantId !== tenantId) {
throw `Email address ${email} already in use.`
}
}
} else if (_id) {
dbUser = await db.get(_id)
}
// get the password, make sure one is defined
let hashedPassword
if (password) {
hashedPassword = opts.hashPassword ? await utils.hash(password) : password
2022-04-08 12:28:22 +12:00
} else if (dbUser) {
hashedPassword = dbUser.password
} else if (opts.requirePassword) {
2022-04-08 12:28:22 +12:00
throw "Password must be specified."
}
2022-06-14 09:26:15 +12:00
_id = _id || dbUtils.generateGlobalUserID()
2022-04-08 12:28:22 +12:00
user = {
createdAt: Date.now(),
...dbUser,
...user,
_id,
password: hashedPassword,
tenantId,
}
// make sure the roles object is always present
if (!user.roles) {
user.roles = {}
}
// add the active status to a user if its not provided
if (user.status == null) {
user.status = constants.UserStatus.ACTIVE
}
try {
const putOpts = {
password: hashedPassword,
...user,
}
if (opts.bulkCreate) {
return putOpts
}
2022-04-08 12:28:22 +12:00
// save the user to db
let response
const putUserFn = () => {
return db.put(user)
}
2022-04-12 23:34:36 +12:00
if (eventHelpers.isAddingBuilder(user, dbUser)) {
2022-04-08 12:28:22 +12:00
response = await quotas.addDeveloper(putUserFn)
} else {
response = await putUserFn()
}
2022-05-20 23:29:31 +12:00
user._rev = response.rev
2022-04-08 12:28:22 +12:00
2022-05-24 09:14:44 +12:00
await eventHelpers.handleSaveEvents(user, dbUser)
if (env.MULTI_TENANCY) {
const afterCreateTenant = () =>
migrations.backPopulateMigrations({
type: MigrationType.GLOBAL,
tenantId,
})
await tenancy.tryAddTenant(tenantId, _id, email, afterCreateTenant)
}
2022-04-08 12:28:22 +12:00
await cache.user.invalidateUser(response.id)
// let server know to sync user
await apps.syncUserInApps(user._id)
return {
_id: response.id,
_rev: response.rev,
email,
}
} catch (err: any) {
if (err.status === 409) {
throw "User exists already"
} else {
throw err
}
}
}
export const destroy = async (id: string, currentUser: any) => {
const db = tenancy.getGlobalDB()
const dbUser = await db.get(id)
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
// root account holder can't be deleted from inside budibase
const email = dbUser.email
const account = await accounts.getAccount(email)
if (account) {
if (email === currentUser.email) {
throw new HTTPError('Please visit "Account" to delete this user', 400)
} else {
throw new HTTPError("Account holder cannot be deleted", 400)
}
}
}
await deprovisioning.removeUserFromInfoDB(dbUser)
await db.remove(dbUser._id, dbUser._rev)
2022-05-24 09:14:44 +12:00
await eventHelpers.handleDeleteEvents(dbUser)
2022-04-08 12:28:22 +12:00
await quotas.removeUser(dbUser)
await cache.user.invalidateUser(dbUser._id)
await sessions.invalidateSessions(dbUser._id)
// let server know to sync user
await apps.syncUserInApps(dbUser._id)
}