Moving user admin/builder functions to shared-core for frontend to use.

This commit is contained in:
mike12345567 2023-07-19 16:19:34 +01:00
parent 91847504c8
commit 85dea47a31
11 changed files with 123 additions and 77 deletions

View File

@ -22,6 +22,7 @@
"dependencies": { "dependencies": {
"@budibase/nano": "10.1.2", "@budibase/nano": "10.1.2",
"@budibase/pouchdb-replication-stream": "1.2.10", "@budibase/pouchdb-replication-stream": "1.2.10",
"@budibase/shared-core": "0.0.0",
"@budibase/types": "0.0.0", "@budibase/types": "0.0.0",
"@shopify/jest-koa-mocks": "5.0.1", "@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",

View File

@ -1,5 +1,5 @@
export const SEPARATOR = "_" import { prefixed, DocumentType } from "@budibase/types"
export const UNICODE_MAX = "\ufff0" export { SEPARATOR, UNICODE_MAX, DocumentType } from "@budibase/types"
/** /**
* Can be used to create a few different forms of querying a view. * Can be used to create a few different forms of querying a view.
@ -34,42 +34,6 @@ export enum InternalTable {
USER_METADATA = "ta_users", USER_METADATA = "ta_users",
} }
export enum DocumentType {
USER = "us",
GROUP = "gr",
WORKSPACE = "workspace",
CONFIG = "config",
TEMPLATE = "template",
APP = "app",
DEV = "dev",
APP_DEV = "app_dev",
APP_METADATA = "app_metadata",
ROLE = "role",
MIGRATIONS = "migrations",
DEV_INFO = "devinfo",
AUTOMATION_LOG = "log_au",
ACCOUNT_METADATA = "acc_metadata",
PLUGIN = "plg",
DATASOURCE = "datasource",
DATASOURCE_PLUS = "datasource_plus",
APP_BACKUP = "backup",
TABLE = "ta",
ROW = "ro",
AUTOMATION = "au",
LINK = "li",
WEBHOOK = "wh",
INSTANCE = "inst",
LAYOUT = "layout",
SCREEN = "screen",
QUERY = "query",
DEPLOYMENTS = "deployments",
METADATA = "metadata",
MEM_VIEW = "view",
USER_FLAG = "flag",
AUTOMATION_METADATA = "meta_au",
AUDIT_LOG = "al",
}
export const StaticDatabases = { export const StaticDatabases = {
GLOBAL: { GLOBAL: {
name: "global-db", name: "global-db",
@ -93,7 +57,7 @@ export const StaticDatabases = {
}, },
} }
export const APP_PREFIX = DocumentType.APP + SEPARATOR export const APP_PREFIX = prefixed(DocumentType.APP)
export const APP_DEV = DocumentType.APP_DEV + SEPARATOR export const APP_DEV = prefixed(DocumentType.APP_DEV)
export const APP_DEV_PREFIX = APP_DEV export const APP_DEV_PREFIX = APP_DEV
export const BUDIBASE_DATASOURCE_TYPE = "budibase" export const BUDIBASE_DATASOURCE_TYPE = "budibase"

View File

@ -18,6 +18,7 @@ import {
User, User,
ContextUser, ContextUser,
} from "@budibase/types" } from "@budibase/types"
import { sdk } from "@budibase/shared-core"
import { getGlobalDB } from "./context" import { getGlobalDB } from "./context"
import * as context from "./context" import * as context from "./context"
@ -38,6 +39,12 @@ function removeUserPassword(users: User | User[]) {
return users return users
} }
// extract from shared-core to make easily accessible from backend-core
export const isBuilder = sdk.users.isBuilder
export const isAdmin = sdk.users.isAdmin
export const hasAdminPermissions = sdk.users.hasAdminPermissions
export const hasBuilderPermissions = sdk.users.hasBuilderPermissions
export const bulkGetGlobalUsersById = async ( export const bulkGetGlobalUsersById = async (
userIds: string[], userIds: string[],
opts?: GetOpts opts?: GetOpts
@ -254,39 +261,6 @@ export async function getUserCount() {
return response.total_rows 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(getProdAppID(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 // used to remove the builder/admin permissions, for processing the
// user as an app user (they may have some specific role/group // user as an app user (they may have some specific role/group
export function removePortalUserPermissions(user: User | ContextUser) { export function removePortalUserPermissions(user: User | ContextUser) {

View File

@ -24,7 +24,7 @@ export default async (ctx: UserCtx, next: any) => {
if ( if (
isDevAppID(requestAppId) && isDevAppID(requestAppId) &&
!isWebhookEndpoint(ctx) && !isWebhookEndpoint(ctx) &&
(!ctx.user || !ctx.user.builder || !ctx.user.builder.global) !users.isBuilder(ctx.user, requestAppId)
) { ) {
return ctx.redirect("/") return ctx.redirect("/")
} }
@ -70,7 +70,6 @@ export default async (ctx: UserCtx, next: any) => {
} }
return context.doInAppContext(appId, async () => { return context.doInAppContext(appId, async () => {
let skipCookie = false
// if the user not in the right tenant then make sure they have no permissions // if the user not in the right tenant then make sure they have no permissions
// need to judge this only based on the request app ID, // need to judge this only based on the request app ID,
if ( if (
@ -83,7 +82,6 @@ export default async (ctx: UserCtx, next: any) => {
ctx.user = users.cleanseUserObject(ctx.user) as ContextUser ctx.user = users.cleanseUserObject(ctx.user) as ContextUser
ctx.isAuthenticated = false ctx.isAuthenticated = false
roleId = roles.BUILTIN_ROLE_IDS.PUBLIC roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
skipCookie = true
} }
ctx.appId = appId ctx.appId = appId

View File

@ -2,3 +2,4 @@ export * from "./constants"
export * as dataFilters from "./filters" export * as dataFilters from "./filters"
export * as helpers from "./helpers" export * as helpers from "./helpers"
export * as utils from "./utils" export * as utils from "./utils"
export * as sdk from "./sdk"

View File

@ -0,0 +1,29 @@
import { DocumentType, prefixed } from "@budibase/types"
const APP_PREFIX = prefixed(DocumentType.APP)
const APP_DEV_PREFIX = prefixed(DocumentType.APP_DEV)
export function getDevAppID(appId: string) {
if (!appId || appId.startsWith(APP_DEV_PREFIX)) {
return appId
}
// split to take off the app_ element, then join it together incase any other app_ exist
const split = appId.split(APP_PREFIX)
split.shift()
const rest = split.join(APP_PREFIX)
return `${APP_DEV_PREFIX}${rest}`
}
/**
* Convert a development app ID to a deployed app ID.
*/
export function getProdAppID(appId: string) {
if (!appId || !appId.startsWith(APP_DEV_PREFIX)) {
return appId
}
// split to take off the app_dev element, then join it together incase any other app_ exist
const split = appId.split(APP_DEV_PREFIX)
split.shift()
const rest = split.join(APP_DEV_PREFIX)
return `${APP_PREFIX}${rest}`
}

View File

@ -0,0 +1,2 @@
export * as applications from "./applications"
export * as users from "./users"

View File

@ -0,0 +1,35 @@
import { ContextUser, User } from "@budibase/types"
import { getProdAppID } from "./applications"
// 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(getProdAppID(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
}

View File

@ -0,0 +1 @@
export * from "./documents"

View File

@ -1,3 +1,44 @@
export const SEPARATOR = "_"
export const UNICODE_MAX = "\ufff0"
export const prefixed = (type: DocumentType) => `${type}${SEPARATOR}`
export enum DocumentType {
USER = "us",
GROUP = "gr",
WORKSPACE = "workspace",
CONFIG = "config",
TEMPLATE = "template",
APP = "app",
DEV = "dev",
APP_DEV = "app_dev",
APP_METADATA = "app_metadata",
ROLE = "role",
MIGRATIONS = "migrations",
DEV_INFO = "devinfo",
AUTOMATION_LOG = "log_au",
ACCOUNT_METADATA = "acc_metadata",
PLUGIN = "plg",
DATASOURCE = "datasource",
DATASOURCE_PLUS = "datasource_plus",
APP_BACKUP = "backup",
TABLE = "ta",
ROW = "ro",
AUTOMATION = "au",
LINK = "li",
WEBHOOK = "wh",
INSTANCE = "inst",
LAYOUT = "layout",
SCREEN = "screen",
QUERY = "query",
DEPLOYMENTS = "deployments",
METADATA = "metadata",
MEM_VIEW = "view",
USER_FLAG = "flag",
AUTOMATION_METADATA = "meta_au",
AUDIT_LOG = "al",
}
export interface Document { export interface Document {
_id?: string _id?: string
_rev?: string _rev?: string

View File

@ -176,7 +176,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
export async function isPreventPasswordActions(user: User, account?: Account) { export async function isPreventPasswordActions(user: User, account?: Account) {
// when in maintenance mode we allow sso users with the admin role // when in maintenance mode we allow sso users with the admin role
// to perform any password action - this prevents lockout // to perform any password action - this prevents lockout
if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE && user.admin?.global) { if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE && usersCore.isAdmin(user)) {
return false return false
} }