Main body of work to handle the new approach of per app builders support.
This commit is contained in:
parent
af5d024d89
commit
39746e0bf0
|
@ -14,8 +14,6 @@ export enum ViewName {
|
|||
USER_BY_APP = "by_app",
|
||||
USER_BY_EMAIL = "by_email2",
|
||||
BY_API_KEY = "by_api_key",
|
||||
/** @deprecated - could be deleted */
|
||||
USER_BY_BUILDERS = "by_builders",
|
||||
LINK = "by_link",
|
||||
ROUTING = "screen_routes",
|
||||
AUTOMATION_LOGS = "automation_logs",
|
||||
|
|
|
@ -105,16 +105,6 @@ export const createApiKeyView = async () => {
|
|||
await createView(db, viewJs, ViewName.BY_API_KEY)
|
||||
}
|
||||
|
||||
export const createUserBuildersView = async () => {
|
||||
const db = getGlobalDB()
|
||||
const viewJs = `function(doc) {
|
||||
if (doc.builder && doc.builder.global === true) {
|
||||
emit(doc._id, doc._id)
|
||||
}
|
||||
}`
|
||||
await createView(db, viewJs, ViewName.USER_BY_BUILDERS)
|
||||
}
|
||||
|
||||
export interface QueryViewOptions {
|
||||
arrayResponse?: boolean
|
||||
}
|
||||
|
@ -223,7 +213,6 @@ export const queryPlatformView = async <T>(
|
|||
const CreateFuncByName: any = {
|
||||
[ViewName.USER_BY_EMAIL]: createNewUserEmailView,
|
||||
[ViewName.BY_API_KEY]: createApiKeyView,
|
||||
[ViewName.USER_BY_BUILDERS]: createUserBuildersView,
|
||||
[ViewName.USER_BY_APP]: createUserAppView,
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import { processors } from "./processors"
|
|||
import { newid } from "../utils"
|
||||
import * as installation from "../installation"
|
||||
import * as configs from "../configs"
|
||||
import * as users from "../users"
|
||||
import { withCache, TTL, CacheKey } from "../cache/generic"
|
||||
|
||||
/**
|
||||
|
@ -164,8 +165,8 @@ const identifyUser = async (
|
|||
const id = user._id as string
|
||||
const tenantId = await getEventTenantId(user.tenantId)
|
||||
const type = IdentityType.USER
|
||||
let builder = user.builder?.global || false
|
||||
let admin = user.admin?.global || false
|
||||
let builder = users.hasBuilderPermissions(user)
|
||||
let admin = users.hasAdminPermissions(user)
|
||||
let providerType
|
||||
if (isSSOUser(user)) {
|
||||
providerType = user.providerType
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { BBContext } from "@budibase/types"
|
||||
import { UserCtx } from "@budibase/types"
|
||||
import { isBuilder } from "../users"
|
||||
import { getAppId } from "../context"
|
||||
|
||||
export default async (ctx: BBContext, next: any) => {
|
||||
if (
|
||||
!ctx.internal &&
|
||||
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global)
|
||||
) {
|
||||
export default async (ctx: UserCtx, next: any) => {
|
||||
const appId = getAppId()
|
||||
if (!ctx.internal && !isBuilder(ctx.user, appId)) {
|
||||
ctx.throw(403, "Builder user only endpoint.")
|
||||
}
|
||||
return next()
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { BBContext } from "@budibase/types"
|
||||
import { UserCtx } from "@budibase/types"
|
||||
import { isBuilder, isAdmin } from "../users"
|
||||
import { getAppId } from "../context"
|
||||
|
||||
export default async (ctx: BBContext, next: any) => {
|
||||
if (
|
||||
!ctx.internal &&
|
||||
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global) &&
|
||||
(!ctx.user || !ctx.user.admin || !ctx.user.admin.global)
|
||||
) {
|
||||
ctx.throw(403, "Builder user only endpoint.")
|
||||
export default async (ctx: UserCtx, next: any) => {
|
||||
const appId = getAppId()
|
||||
if (!ctx.internal && !isBuilder(ctx.user, appId) && !isAdmin(ctx.user)) {
|
||||
ctx.throw(403, "Admin/Builder user only endpoint.")
|
||||
}
|
||||
return next()
|
||||
}
|
||||
|
|
|
@ -12,7 +12,12 @@ import {
|
|||
UNICODE_MAX,
|
||||
ViewName,
|
||||
} from "./db"
|
||||
import { BulkDocsResponse, SearchUsersRequest, User } from "@budibase/types"
|
||||
import {
|
||||
BulkDocsResponse,
|
||||
SearchUsersRequest,
|
||||
User,
|
||||
ContextUser,
|
||||
} from "@budibase/types"
|
||||
import { getGlobalDB } from "./context"
|
||||
import * as context from "./context"
|
||||
|
||||
|
@ -178,7 +183,7 @@ export const getGlobalUserByAppPage = (appId: string, user: User) => {
|
|||
* Performs a starts with search on the global email view.
|
||||
*/
|
||||
export const searchGlobalUsersByEmail = async (
|
||||
email: string,
|
||||
email: string | unknown,
|
||||
opts: any,
|
||||
getOpts?: GetOpts
|
||||
) => {
|
||||
|
@ -248,3 +253,56 @@ export async function getUserCount() {
|
|||
})
|
||||
return response.total_rows
|
||||
}
|
||||
|
||||
// checks if a user is specifically a builder, given an app ID
|
||||
export function isBuilder(user: User | ContextUser, appId?: string) {
|
||||
if (user.builder?.global) {
|
||||
return true
|
||||
} else if (appId && user.builder?.apps?.includes(appId)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// alias for hasAdminPermission, currently do the same thing
|
||||
// in future whether someone has admin permissions and whether they are
|
||||
// an admin for a specific resource could be separated
|
||||
export function isAdmin(user: User | ContextUser) {
|
||||
return hasAdminPermissions(user)
|
||||
}
|
||||
|
||||
// checks if a user is capable of building any app
|
||||
export function hasBuilderPermissions(user?: User | ContextUser) {
|
||||
if (!user) {
|
||||
return false
|
||||
}
|
||||
return user.builder?.global || user.builder?.apps?.length !== 0
|
||||
}
|
||||
|
||||
// checks if a user is capable of being an admin
|
||||
export function hasAdminPermissions(user?: User | ContextUser) {
|
||||
if (!user) {
|
||||
return false
|
||||
}
|
||||
return user.admin?.global
|
||||
}
|
||||
|
||||
// used to remove the builder/admin permissions, for processing the
|
||||
// user as an app user (they may have some specific role/group
|
||||
export function removePortalUserPermissions(user: User | ContextUser) {
|
||||
delete user.admin
|
||||
delete user.builder
|
||||
return user
|
||||
}
|
||||
|
||||
export function cleanseUserObject(user: User | ContextUser, base?: User) {
|
||||
delete user.admin
|
||||
delete user.builder
|
||||
delete user.roles
|
||||
if (base) {
|
||||
user.admin = base.admin
|
||||
user.builder = base.builder
|
||||
user.roles = base.roles
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import {
|
|||
objectStore,
|
||||
roles,
|
||||
tenancy,
|
||||
users,
|
||||
} from "@budibase/backend-core"
|
||||
import { USERS_TABLE_SCHEMA } from "../../constants"
|
||||
import {
|
||||
|
@ -222,6 +223,7 @@ export async function fetchAppDefinition(ctx: UserCtx) {
|
|||
|
||||
export async function fetchAppPackage(ctx: UserCtx) {
|
||||
const db = context.getAppDB()
|
||||
const appId = context.getAppId()
|
||||
let application = await db.get<any>(DocumentType.APP_METADATA)
|
||||
const layouts = await getLayouts()
|
||||
let screens = await getScreens()
|
||||
|
@ -233,7 +235,7 @@ export async function fetchAppPackage(ctx: UserCtx) {
|
|||
)
|
||||
|
||||
// Only filter screens if the user is not a builder
|
||||
if (!(ctx.user.builder && ctx.user.builder.global)) {
|
||||
if (!users.isBuilder(ctx.user, appId)) {
|
||||
const userRoleId = getUserRoleId(ctx)
|
||||
const accessController = new roles.AccessController()
|
||||
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { roles, permissions, auth, context } from "@budibase/backend-core"
|
||||
import {
|
||||
roles,
|
||||
permissions,
|
||||
auth,
|
||||
context,
|
||||
users,
|
||||
} from "@budibase/backend-core"
|
||||
import { Role } from "@budibase/types"
|
||||
import builderMiddleware from "./builder"
|
||||
import { isWebhookEndpoint } from "./utils"
|
||||
|
@ -21,8 +27,9 @@ const checkAuthorized = async (
|
|||
permType: any,
|
||||
permLevel: any
|
||||
) => {
|
||||
const appId = context.getAppId()
|
||||
// check if this is a builder api and the user is not a builder
|
||||
const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
|
||||
const isBuilder = users.isBuilder(ctx.user, appId)
|
||||
const isBuilderApi = permType === permissions.PermissionType.BUILDER
|
||||
if (isBuilderApi && !isBuilder) {
|
||||
return ctx.throw(403, "Not Authorized")
|
||||
|
|
|
@ -4,12 +4,14 @@ import {
|
|||
roles,
|
||||
tenancy,
|
||||
context,
|
||||
users,
|
||||
} from "@budibase/backend-core"
|
||||
import { generateUserMetadataID, isDevAppID } from "../db/utils"
|
||||
import { getCachedSelf } from "../utilities/global"
|
||||
import env from "../environment"
|
||||
import { isWebhookEndpoint } from "./utils"
|
||||
import { UserCtx } from "@budibase/types"
|
||||
import { UserCtx, ContextUser } from "@budibase/types"
|
||||
import { removePortalUserPermissions } from "@budibase/backend-core/src/users"
|
||||
|
||||
export default async (ctx: UserCtx, next: any) => {
|
||||
// try to get the appID from the request
|
||||
|
@ -42,8 +44,7 @@ export default async (ctx: UserCtx, next: any) => {
|
|||
roleId = globalUser.roleId || roleId
|
||||
|
||||
// Allow builders to specify their role via a header
|
||||
const isBuilder =
|
||||
globalUser && globalUser.builder && globalUser.builder.global
|
||||
const isBuilder = users.isBuilder(globalUser, appId)
|
||||
const isDevApp = appId && isDevAppID(appId)
|
||||
const roleHeader =
|
||||
ctx.request &&
|
||||
|
@ -56,8 +57,7 @@ export default async (ctx: UserCtx, next: any) => {
|
|||
roleId = roleHeader
|
||||
|
||||
// Delete admin and builder flags so that the specified role is honoured
|
||||
delete ctx.user.builder
|
||||
delete ctx.user.admin
|
||||
ctx.user = users.removePortalUserPermissions(ctx.user) as ContextUser
|
||||
}
|
||||
} catch (error) {
|
||||
// Swallow error and do nothing
|
||||
|
@ -81,9 +81,7 @@ export default async (ctx: UserCtx, next: any) => {
|
|||
!tenancy.isUserInAppTenant(requestAppId, ctx.user)
|
||||
) {
|
||||
// don't error, simply remove the users rights (they are a public user)
|
||||
delete ctx.user.builder
|
||||
delete ctx.user.admin
|
||||
delete ctx.user.roles
|
||||
ctx.user = users.cleanseUserObject(ctx.user) as ContextUser
|
||||
ctx.isAuthenticated = false
|
||||
roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||
skipCookie = true
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { events, db as dbUtils } from "@budibase/backend-core"
|
||||
import {
|
||||
events,
|
||||
db as dbUtils,
|
||||
users as usersCore,
|
||||
} from "@budibase/backend-core"
|
||||
import { User, CloudAccount } from "@budibase/types"
|
||||
import { DEFAULT_TIMESTAMP } from ".."
|
||||
|
||||
|
@ -30,11 +34,11 @@ export const backfill = async (
|
|||
await events.identification.identifyUser(user, account, timestamp)
|
||||
await events.user.created(user, timestamp)
|
||||
|
||||
if (user.admin?.global) {
|
||||
if (usersCore.hasAdminPermissions(user)) {
|
||||
await events.user.permissionAdminAssigned(user, timestamp)
|
||||
}
|
||||
|
||||
if (user.builder?.global) {
|
||||
if (usersCore.hasBuilderPermissions(user)) {
|
||||
await events.user.permissionBuilderAssigned(user, timestamp)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
cache,
|
||||
tenancy,
|
||||
context,
|
||||
users,
|
||||
} from "@budibase/backend-core"
|
||||
import env from "../environment"
|
||||
import { groups } from "@budibase/pro"
|
||||
|
@ -22,8 +23,7 @@ export function updateAppRole(
|
|||
}
|
||||
// if in an multi-tenancy environment make sure roles are never updated
|
||||
if (env.MULTI_TENANCY && appId && !tenancy.isUserInAppTenant(appId, user)) {
|
||||
delete user.builder
|
||||
delete user.admin
|
||||
user = users.removePortalUserPermissions(user)
|
||||
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||
return user
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ export function updateAppRole(
|
|||
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 && user.builder && user.builder.global) {
|
||||
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
|
||||
|
|
|
@ -47,7 +47,6 @@ export interface User extends Document {
|
|||
}
|
||||
admin?: {
|
||||
global: boolean
|
||||
apps?: string[]
|
||||
}
|
||||
password?: string
|
||||
status?: UserStatus
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
import env from "../../environment"
|
||||
import { events, accounts, tenancy } from "@budibase/backend-core"
|
||||
import { events, accounts, tenancy, users } from "@budibase/backend-core"
|
||||
import { User, UserRoles, CloudAccount } from "@budibase/types"
|
||||
|
||||
const hasBuilderPerm = users.hasBuilderPermissions
|
||||
const hasAdminPerm = users.hasAdminPermissions
|
||||
|
||||
export const handleDeleteEvents = async (user: any) => {
|
||||
await events.user.deleted(user)
|
||||
|
||||
if (isBuilder(user)) {
|
||||
if (hasBuilderPerm(user)) {
|
||||
await events.user.permissionBuilderRemoved(user)
|
||||
}
|
||||
|
||||
if (isAdmin(user)) {
|
||||
if (hasAdminPerm(user)) {
|
||||
await events.user.permissionAdminRemoved(user)
|
||||
}
|
||||
}
|
||||
|
@ -103,23 +106,20 @@ export const handleSaveEvents = async (
|
|||
await handleAppRoleEvents(user, existingUser)
|
||||
}
|
||||
|
||||
const isBuilder = (user: any) => user.builder && user.builder.global
|
||||
const isAdmin = (user: any) => user.admin && user.admin.global
|
||||
|
||||
export const isAddingBuilder = (user: any, existingUser: any) => {
|
||||
return isAddingPermission(user, existingUser, isBuilder)
|
||||
return isAddingPermission(user, existingUser, hasBuilderPerm)
|
||||
}
|
||||
|
||||
export const isRemovingBuilder = (user: any, existingUser: any) => {
|
||||
return isRemovingPermission(user, existingUser, isBuilder)
|
||||
return isRemovingPermission(user, existingUser, hasBuilderPerm)
|
||||
}
|
||||
|
||||
const isAddingAdmin = (user: any, existingUser: any) => {
|
||||
return isAddingPermission(user, existingUser, isAdmin)
|
||||
return isAddingPermission(user, existingUser, hasAdminPerm)
|
||||
}
|
||||
|
||||
const isRemovingAdmin = (user: any, existingUser: any) => {
|
||||
return isRemovingPermission(user, existingUser, isAdmin)
|
||||
return isRemovingPermission(user, existingUser, hasAdminPerm)
|
||||
}
|
||||
|
||||
const isOnboardingComplete = (user: any, existingUser: any) => {
|
||||
|
|
|
@ -252,9 +252,7 @@ export const save = async (
|
|||
let builtUser = await buildUser(user, opts, tenantId, dbUser)
|
||||
// don't allow a user to update its own roles/perms
|
||||
if (opts.currentUserId && opts.currentUserId === dbUser?._id) {
|
||||
builtUser.builder = dbUser.builder
|
||||
builtUser.admin = dbUser.admin
|
||||
builtUser.roles = dbUser.roles
|
||||
builtUser = usersCore.cleanseUserObject(builtUser, dbUser) as User
|
||||
}
|
||||
|
||||
if (!dbUser && roles?.length) {
|
||||
|
|
Loading…
Reference in New Issue