2022-11-15 18:35:17 +01:00
|
|
|
import { getMultiIDParams, getGlobalIDFromUserMetadataID } from "../db/utils"
|
|
|
|
import {
|
|
|
|
roles,
|
|
|
|
db as dbCore,
|
|
|
|
cache,
|
|
|
|
tenancy,
|
|
|
|
context,
|
2023-07-18 17:57:48 +02:00
|
|
|
users,
|
2022-11-15 18:35:17 +01:00
|
|
|
} from "@budibase/backend-core"
|
|
|
|
import env from "../environment"
|
|
|
|
import { groups } from "@budibase/pro"
|
2023-03-21 14:55:28 +01:00
|
|
|
import { UserCtx, ContextUser, User, UserGroup } from "@budibase/types"
|
2023-07-27 21:36:32 +02:00
|
|
|
import cloneDeep from "lodash/cloneDeep"
|
2022-11-15 18:35:17 +01:00
|
|
|
|
2023-08-22 19:14:08 +02:00
|
|
|
export async function processUser(
|
2022-11-15 18:35:17 +01:00
|
|
|
user: ContextUser,
|
2023-08-22 19:14:08 +02:00
|
|
|
opts: { appId?: string; groups?: UserGroup[] } = {}
|
2022-11-15 18:35:17 +01:00
|
|
|
) {
|
2023-04-12 20:59:05 +02:00
|
|
|
if (!user || (!user.roles && !user.userGroups)) {
|
2022-11-15 18:35:17 +01:00
|
|
|
return user
|
|
|
|
}
|
2023-08-22 19:14:08 +02:00
|
|
|
user = cloneDeep(user)
|
|
|
|
delete user.password
|
|
|
|
const appId = opts.appId || context.getAppId()
|
|
|
|
if (!appId) {
|
|
|
|
throw new Error("Unable to process user without app ID")
|
|
|
|
}
|
|
|
|
// if in a multi-tenancy environment and in wrong tenant make sure roles are never updated
|
2022-11-15 18:35:17 +01:00
|
|
|
if (env.MULTI_TENANCY && appId && !tenancy.isUserInAppTenant(appId, user)) {
|
2023-07-18 17:57:48 +02:00
|
|
|
user = users.removePortalUserPermissions(user)
|
2022-11-15 18:35:17 +01:00
|
|
|
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
|
|
|
return user
|
|
|
|
}
|
2023-08-22 19:14:08 +02:00
|
|
|
let groupList: UserGroup[] = []
|
|
|
|
if (appId && user?.userGroups?.length) {
|
|
|
|
groupList = opts.groups
|
|
|
|
? opts.groups
|
|
|
|
: await groups.getBulk(user.userGroups)
|
2022-11-15 18:35:17 +01:00
|
|
|
}
|
2023-08-22 19:14:08 +02:00
|
|
|
// check if a group provides builder access
|
2023-08-22 20:15:47 +02:00
|
|
|
const builderAppIds = await groups.getGroupBuilderAppIds(user, {
|
|
|
|
appId,
|
2023-08-22 19:14:08 +02:00
|
|
|
groups: groupList,
|
|
|
|
})
|
|
|
|
if (builderAppIds.length && !users.isBuilder(user, appId)) {
|
|
|
|
const existingApps = user.builder?.apps || []
|
|
|
|
user.builder = {
|
|
|
|
apps: [...new Set(existingApps.concat(builderAppIds))],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// builders are always admins within the app
|
|
|
|
if (users.isBuilder(user, appId)) {
|
2022-11-15 18:35:17 +01:00
|
|
|
user.roleId = roles.BUILTIN_ROLE_IDS.ADMIN
|
|
|
|
}
|
2023-08-22 19:14:08 +02:00
|
|
|
// try to get the role from the user list
|
|
|
|
if (!user.roleId && appId && user.roles) {
|
|
|
|
user.roleId = user.roles[dbCore.getProdAppID(appId)]
|
2022-11-15 18:35:17 +01:00
|
|
|
}
|
2023-08-22 19:14:08 +02:00
|
|
|
// try to get the role from the group list
|
|
|
|
if (!user.roleId && groupList) {
|
|
|
|
user.roleId = await groups.getGroupRoleId(user, appId, {
|
|
|
|
groups: groupList,
|
2023-03-21 14:55:28 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
// final fallback, simply couldn't find a role - user must be public
|
|
|
|
if (!user.roleId) {
|
|
|
|
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
2022-11-15 18:35:17 +01:00
|
|
|
}
|
2023-08-22 19:14:08 +02:00
|
|
|
// remove the roles as it is now set
|
|
|
|
delete user.roles
|
2022-11-15 18:35:17 +01:00
|
|
|
return user
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:14:52 +01:00
|
|
|
export async function getCachedSelf(
|
|
|
|
ctx: UserCtx,
|
|
|
|
appId: string
|
|
|
|
): Promise<ContextUser> {
|
2022-11-15 18:35:17 +01:00
|
|
|
// this has to be tenant aware, can't depend on the context to find it out
|
|
|
|
// running some middlewares before the tenancy causes context to break
|
|
|
|
const user = await cache.user.getUser(ctx.user?._id!)
|
|
|
|
return processUser(user, { appId })
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:14:52 +01:00
|
|
|
export async function getRawGlobalUser(userId: string): Promise<User> {
|
2022-11-15 18:35:17 +01:00
|
|
|
const db = tenancy.getGlobalDB()
|
2023-07-18 11:41:51 +02:00
|
|
|
return db.get<User>(getGlobalIDFromUserMetadataID(userId))
|
2022-11-15 18:35:17 +01:00
|
|
|
}
|
|
|
|
|
2023-11-07 19:14:52 +01:00
|
|
|
export async function getGlobalUser(userId: string): Promise<ContextUser> {
|
2022-11-15 18:35:17 +01:00
|
|
|
const appId = context.getAppId()
|
|
|
|
let user = await getRawGlobalUser(userId)
|
|
|
|
return processUser(user, { appId })
|
|
|
|
}
|
|
|
|
|
2023-11-07 19:14:52 +01:00
|
|
|
export async function getRawGlobalUsers(userIds?: string[]): Promise<User[]> {
|
2022-11-15 18:35:17 +01:00
|
|
|
const db = tenancy.getGlobalDB()
|
2023-11-07 19:14:52 +01:00
|
|
|
let globalUsers: User[]
|
2023-04-04 19:03:56 +02:00
|
|
|
if (userIds) {
|
2023-11-07 19:14:52 +01:00
|
|
|
globalUsers = (await db.allDocs<User>(getMultiIDParams(userIds))).rows.map(
|
|
|
|
row => row.doc!
|
2022-11-15 18:35:17 +01:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
globalUsers = (
|
2023-11-07 19:14:52 +01:00
|
|
|
await db.allDocs<User>(
|
2022-11-15 18:35:17 +01:00
|
|
|
dbCore.getGlobalUserParams(null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
2023-11-07 19:14:52 +01:00
|
|
|
).rows.map(row => row.doc!)
|
2022-11-15 18:35:17 +01:00
|
|
|
}
|
2023-11-07 19:14:52 +01:00
|
|
|
return globalUsers
|
2022-11-15 18:35:17 +01:00
|
|
|
.filter(user => user != null)
|
|
|
|
.map(user => {
|
|
|
|
delete user.password
|
|
|
|
delete user.forceResetPassword
|
|
|
|
return user
|
|
|
|
})
|
2023-11-07 19:14:52 +01:00
|
|
|
}
|
2022-11-15 18:35:17 +01:00
|
|
|
|
2023-11-07 19:14:52 +01:00
|
|
|
export async function getGlobalUsers(
|
|
|
|
userIds?: string[]
|
|
|
|
): Promise<ContextUser[]> {
|
|
|
|
const users = await getRawGlobalUsers(userIds)
|
|
|
|
const allGroups = await groups.fetch()
|
|
|
|
return Promise.all(
|
|
|
|
users.map(user => processUser(user, { groups: allGroups }))
|
|
|
|
)
|
2022-11-15 18:35:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function getGlobalUsersFromMetadata(users: ContextUser[]) {
|
2023-04-04 19:03:56 +02:00
|
|
|
const globalUsers = await getGlobalUsers(users.map(user => user._id!))
|
2022-11-15 18:35:17 +01:00
|
|
|
return users.map(user => {
|
|
|
|
const globalUser = globalUsers.find(
|
2023-11-07 19:14:52 +01:00
|
|
|
globalUser => globalUser && user._id?.includes(globalUser._id!)
|
2022-11-15 18:35:17 +01:00
|
|
|
)
|
|
|
|
return {
|
|
|
|
...globalUser,
|
|
|
|
// doing user second overwrites the id and rev (always metadata)
|
|
|
|
...user,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|