Adding last of support for per app group builder support, enriching the user on self return, as well as adding the functionality required to server middlewares.
This commit is contained in:
parent
466a2504e4
commit
656870db8b
|
@ -1 +1 @@
|
|||
Subproject commit a054a51726fa3f17879a469a04675778185b7ed7
|
||||
Subproject commit 39d76da2124d96b1bcec0f2352ca5ef9b0c84318
|
|
@ -2,9 +2,9 @@ import { outputProcessing } from "../../utilities/rowProcessor"
|
|||
import { InternalTables } from "../../db/utils"
|
||||
import { getFullUser } from "../../utilities/users"
|
||||
import { roles, context } from "@budibase/backend-core"
|
||||
import { groups } from "@budibase/pro"
|
||||
import { ContextUser, User, Row, UserCtx } from "@budibase/types"
|
||||
import { ContextUser, Row, UserCtx } from "@budibase/types"
|
||||
import sdk from "../../sdk"
|
||||
import { processUser } from "src/utilities/global"
|
||||
|
||||
const PUBLIC_ROLE = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||
|
||||
|
@ -26,7 +26,7 @@ export async function fetchSelf(ctx: UserCtx) {
|
|||
}
|
||||
|
||||
const appId = context.getAppId()
|
||||
const user: ContextUser = await getFullUser(ctx, userId)
|
||||
let user: ContextUser = await getFullUser(ctx, userId)
|
||||
// this shouldn't be returned by the app self
|
||||
delete user.roles
|
||||
// forward the csrf token from the session
|
||||
|
@ -36,8 +36,7 @@ export async function fetchSelf(ctx: UserCtx) {
|
|||
const db = context.getAppDB()
|
||||
// check for group permissions
|
||||
if (!user.roleId || user.roleId === PUBLIC_ROLE) {
|
||||
const groupRoleId = await groups.getGroupRoleId(user as User, appId)
|
||||
user.roleId = groupRoleId || user.roleId
|
||||
user = await processUser(user, { appId })
|
||||
}
|
||||
// remove the full roles structure
|
||||
delete user.roles
|
||||
|
|
|
@ -12,75 +12,64 @@ import { groups } from "@budibase/pro"
|
|||
import { UserCtx, ContextUser, User, UserGroup } from "@budibase/types"
|
||||
import cloneDeep from "lodash/cloneDeep"
|
||||
|
||||
export function updateAppRole(
|
||||
export async function processUser(
|
||||
user: ContextUser,
|
||||
{ appId }: { appId?: string } = {}
|
||||
opts: { appId?: string; groups?: UserGroup[] } = {}
|
||||
) {
|
||||
appId = appId || context.getAppId()
|
||||
|
||||
if (!user || (!user.roles && !user.userGroups)) {
|
||||
return user
|
||||
}
|
||||
// if in an multi-tenancy environment make sure roles are never updated
|
||||
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
|
||||
}
|
||||
// always use the deployed app
|
||||
if (appId && user.roles) {
|
||||
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)]
|
||||
}
|
||||
// if a role wasn't found then either set as admin (builder) or public (everyone else)
|
||||
if (!user.roleId && users.isBuilder(user, appId)) {
|
||||
user.roleId = roles.BUILTIN_ROLE_IDS.ADMIN
|
||||
} else if (!user.roleId && !user?.userGroups?.length) {
|
||||
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||
}
|
||||
|
||||
delete user.roles
|
||||
return user
|
||||
}
|
||||
|
||||
async function checkGroupRoles(
|
||||
user: ContextUser,
|
||||
opts: { appId?: string; groups?: UserGroup[] } = {}
|
||||
) {
|
||||
if (user.roleId && user.roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) {
|
||||
return user
|
||||
}
|
||||
if (opts.appId) {
|
||||
user.roleId = await groups.getGroupRoleId(user as User, opts.appId, {
|
||||
groups: opts.groups,
|
||||
// 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 processUser(
|
||||
user: ContextUser,
|
||||
opts: { appId?: string; groups?: UserGroup[] } = {}
|
||||
) {
|
||||
let clonedUser = cloneDeep(user)
|
||||
if (clonedUser) {
|
||||
delete clonedUser.password
|
||||
}
|
||||
const appId = opts.appId || context.getAppId()
|
||||
clonedUser = updateAppRole(clonedUser, { appId })
|
||||
if (!clonedUser.roleId && clonedUser?.userGroups?.length) {
|
||||
clonedUser = await checkGroupRoles(clonedUser, {
|
||||
appId,
|
||||
groups: opts?.groups,
|
||||
})
|
||||
}
|
||||
|
||||
return clonedUser
|
||||
}
|
||||
|
||||
export async function getCachedSelf(ctx: UserCtx, appId: string) {
|
||||
// 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
|
||||
|
|
|
@ -8,10 +8,9 @@ import {
|
|||
logging,
|
||||
env as coreEnv,
|
||||
} from "@budibase/backend-core"
|
||||
import { updateAppRole } from "./global"
|
||||
import { BBContext, User, EmailInvite } from "@budibase/types"
|
||||
import { Ctx, User, EmailInvite } from "@budibase/types"
|
||||
|
||||
export function request(ctx?: BBContext, request?: any) {
|
||||
export function request(ctx?: Ctx, request?: any) {
|
||||
if (!request.headers) {
|
||||
request.headers = {}
|
||||
}
|
||||
|
@ -43,7 +42,7 @@ export function request(ctx?: BBContext, request?: any) {
|
|||
async function checkResponse(
|
||||
response: any,
|
||||
errorMsg: string,
|
||||
{ ctx }: { ctx?: BBContext } = {}
|
||||
{ ctx }: { ctx?: Ctx } = {}
|
||||
) {
|
||||
if (response.status !== 200) {
|
||||
let error
|
||||
|
@ -105,21 +104,7 @@ export async function sendSmtpEmail({
|
|||
return checkResponse(response, "send email")
|
||||
}
|
||||
|
||||
export async function getGlobalSelf(ctx: BBContext, appId?: string) {
|
||||
const endpoint = `/api/global/self`
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
||||
// we don't want to use API key when getting self
|
||||
request(ctx, { method: "GET" })
|
||||
)
|
||||
let json = await checkResponse(response, "get self globally", { ctx })
|
||||
if (appId) {
|
||||
json = updateAppRole(json)
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
export async function removeAppFromUserRoles(ctx: BBContext, appId: string) {
|
||||
export async function removeAppFromUserRoles(ctx: Ctx, appId: string) {
|
||||
const prodAppId = dbCore.getProdAppID(appId)
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(env.WORKER_URL + `/api/global/roles/${prodAppId}`),
|
||||
|
@ -130,7 +115,7 @@ export async function removeAppFromUserRoles(ctx: BBContext, appId: string) {
|
|||
return checkResponse(response, "remove app role")
|
||||
}
|
||||
|
||||
export async function allGlobalUsers(ctx: BBContext) {
|
||||
export async function allGlobalUsers(ctx: Ctx) {
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(env.WORKER_URL + "/api/global/users"),
|
||||
// we don't want to use API key when getting self
|
||||
|
@ -139,7 +124,7 @@ export async function allGlobalUsers(ctx: BBContext) {
|
|||
return checkResponse(response, "get users", { ctx })
|
||||
}
|
||||
|
||||
export async function saveGlobalUser(ctx: BBContext) {
|
||||
export async function saveGlobalUser(ctx: Ctx) {
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(env.WORKER_URL + "/api/global/users"),
|
||||
// we don't want to use API key when getting self
|
||||
|
@ -148,7 +133,7 @@ export async function saveGlobalUser(ctx: BBContext) {
|
|||
return checkResponse(response, "save user", { ctx })
|
||||
}
|
||||
|
||||
export async function deleteGlobalUser(ctx: BBContext) {
|
||||
export async function deleteGlobalUser(ctx: Ctx) {
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(
|
||||
env.WORKER_URL + `/api/global/users/${ctx.params.userId}`
|
||||
|
@ -159,7 +144,7 @@ export async function deleteGlobalUser(ctx: BBContext) {
|
|||
return checkResponse(response, "delete user", { ctx })
|
||||
}
|
||||
|
||||
export async function readGlobalUser(ctx: BBContext): Promise<User> {
|
||||
export async function readGlobalUser(ctx: Ctx): Promise<User> {
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(
|
||||
env.WORKER_URL + `/api/global/users/${ctx.params.userId}`
|
||||
|
|
|
@ -48,7 +48,7 @@ export async function generateAPIKey(ctx: any) {
|
|||
} catch (err) {
|
||||
devInfo = { _id: id, userId }
|
||||
}
|
||||
devInfo.apiKey = await apiKey
|
||||
devInfo.apiKey = apiKey
|
||||
await db.put(devInfo)
|
||||
ctx.body = cleanupDevInfo(devInfo)
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export async function fetchAPIKey(ctx: any) {
|
|||
devInfo = {
|
||||
_id: id,
|
||||
userId: ctx.user._id,
|
||||
apiKey: await newApiKey(),
|
||||
apiKey: newApiKey(),
|
||||
}
|
||||
await db.put(devInfo)
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@ import {
|
|||
import {
|
||||
accounts,
|
||||
cache,
|
||||
ErrorCode,
|
||||
events,
|
||||
migrations,
|
||||
tenancy,
|
||||
platform,
|
||||
ErrorCode,
|
||||
tenancy,
|
||||
} from "@budibase/backend-core"
|
||||
import { checkAnyUserExists } from "../../../utilities/users"
|
||||
import { isEmailConfigured } from "../../../utilities/email"
|
||||
|
@ -280,7 +280,7 @@ export const onboardUsers = async (ctx: Ctx<InviteUsersRequest>) => {
|
|||
let bulkCreateReponse = await userSdk.db.bulkCreate(users, [])
|
||||
|
||||
// Apply temporary credentials
|
||||
let createWithCredentials = {
|
||||
ctx.body = {
|
||||
...bulkCreateReponse,
|
||||
successful: bulkCreateReponse?.successful.map(user => {
|
||||
return {
|
||||
|
@ -290,8 +290,6 @@ export const onboardUsers = async (ctx: Ctx<InviteUsersRequest>) => {
|
|||
}),
|
||||
created: true,
|
||||
}
|
||||
|
||||
ctx.body = createWithCredentials
|
||||
} else {
|
||||
ctx.throw(400, "User onboarding failed")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue