141 lines
4.1 KiB
TypeScript
141 lines
4.1 KiB
TypeScript
import { getGlobalIDFromUserMetadataID } from "../db/utils"
|
|
import {
|
|
roles,
|
|
db as dbCore,
|
|
cache,
|
|
tenancy,
|
|
context,
|
|
users,
|
|
} from "@budibase/backend-core"
|
|
import env from "../environment"
|
|
import { groups } from "@budibase/pro"
|
|
import { UserCtx, ContextUser, User, UserGroup } from "@budibase/types"
|
|
import cloneDeep from "lodash/cloneDeep"
|
|
|
|
export async function processUser(
|
|
user: ContextUser,
|
|
opts: { appId?: string; groups?: UserGroup[] } = {}
|
|
) {
|
|
if (!user || (!user.roles && !user.userGroups)) {
|
|
return user
|
|
}
|
|
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
|
|
if (env.MULTI_TENANCY && appId && !tenancy.isUserInAppTenant(appId, user)) {
|
|
user = users.removePortalUserPermissions(user)
|
|
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
|
return user
|
|
}
|
|
let groupList: UserGroup[] = []
|
|
if (appId && user?.userGroups?.length) {
|
|
groupList = opts.groups
|
|
? opts.groups
|
|
: await groups.getBulk(user.userGroups)
|
|
}
|
|
// check if a group provides builder access
|
|
const builderAppIds = await groups.getGroupBuilderAppIds(user, {
|
|
appId,
|
|
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)) {
|
|
user.roleId = roles.BUILTIN_ROLE_IDS.ADMIN
|
|
}
|
|
// try to get the role from the user list
|
|
if (!user.roleId && appId && user.roles) {
|
|
user.roleId = user.roles[dbCore.getProdAppID(appId)]
|
|
}
|
|
// try to get the role from the group list
|
|
if (!user.roleId && groupList) {
|
|
user.roleId = await groups.getGroupRoleId(user, appId, {
|
|
groups: groupList,
|
|
})
|
|
}
|
|
// final fallback, simply couldn't find a role - user must be public
|
|
if (!user.roleId) {
|
|
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
|
}
|
|
// remove the roles as it is now set
|
|
delete user.roles
|
|
return user
|
|
}
|
|
|
|
export async function getCachedSelf(
|
|
ctx: UserCtx,
|
|
appId: string
|
|
): Promise<ContextUser> {
|
|
// 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 })
|
|
}
|
|
|
|
export async function getRawGlobalUser(userId: string): Promise<User> {
|
|
const db = tenancy.getGlobalDB()
|
|
return db.get<User>(getGlobalIDFromUserMetadataID(userId))
|
|
}
|
|
|
|
export async function getGlobalUser(userId: string): Promise<ContextUser> {
|
|
const appId = context.getAppId()
|
|
let user = await getRawGlobalUser(userId)
|
|
return processUser(user, { appId })
|
|
}
|
|
|
|
export async function getRawGlobalUsers(userIds?: string[]): Promise<User[]> {
|
|
const db = tenancy.getGlobalDB()
|
|
let globalUsers: User[]
|
|
if (userIds) {
|
|
globalUsers = await db.getMultiple<User>(userIds, { allowMissing: true })
|
|
} else {
|
|
globalUsers = (
|
|
await db.allDocs<User>(
|
|
dbCore.getGlobalUserParams(null, {
|
|
include_docs: true,
|
|
})
|
|
)
|
|
).rows.map(row => row.doc!)
|
|
}
|
|
return globalUsers
|
|
.filter(user => user != null)
|
|
.map(user => {
|
|
delete user.password
|
|
delete user.forceResetPassword
|
|
return user
|
|
})
|
|
}
|
|
|
|
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 }))
|
|
)
|
|
}
|
|
|
|
export async function getGlobalUsersFromMetadata(users: ContextUser[]) {
|
|
const globalUsers = await getGlobalUsers(users.map(user => user._id!))
|
|
return users.map(user => {
|
|
const globalUser = globalUsers.find(
|
|
globalUser => globalUser && user._id?.includes(globalUser._id!)
|
|
)
|
|
return {
|
|
...globalUser,
|
|
// doing user second overwrites the id and rev (always metadata)
|
|
...user,
|
|
}
|
|
})
|
|
}
|