diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 7f3c064c92..93f2529987 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -22,6 +22,7 @@ "dependencies": { "@budibase/nano": "10.1.2", "@budibase/pouchdb-replication-stream": "1.2.10", + "@budibase/shared-core": "0.0.0", "@budibase/types": "0.0.0", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", diff --git a/packages/backend-core/src/constants/db.ts b/packages/backend-core/src/constants/db.ts index 34dab6c088..83f8298f54 100644 --- a/packages/backend-core/src/constants/db.ts +++ b/packages/backend-core/src/constants/db.ts @@ -1,5 +1,5 @@ -export const SEPARATOR = "_" -export const UNICODE_MAX = "\ufff0" +import { prefixed, DocumentType } from "@budibase/types" +export { SEPARATOR, UNICODE_MAX, DocumentType } from "@budibase/types" /** * Can be used to create a few different forms of querying a view. @@ -34,42 +34,6 @@ export enum InternalTable { 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 = { GLOBAL: { name: "global-db", @@ -93,7 +57,7 @@ export const StaticDatabases = { }, } -export const APP_PREFIX = DocumentType.APP + SEPARATOR -export const APP_DEV = DocumentType.APP_DEV + SEPARATOR +export const APP_PREFIX = prefixed(DocumentType.APP) +export const APP_DEV = prefixed(DocumentType.APP_DEV) export const APP_DEV_PREFIX = APP_DEV export const BUDIBASE_DATASOURCE_TYPE = "budibase" diff --git a/packages/backend-core/src/users.ts b/packages/backend-core/src/users.ts index 71cd599f87..7224d827e8 100644 --- a/packages/backend-core/src/users.ts +++ b/packages/backend-core/src/users.ts @@ -18,6 +18,7 @@ import { User, ContextUser, } from "@budibase/types" +import { sdk } from "@budibase/shared-core" import { getGlobalDB } from "./context" import * as context from "./context" @@ -38,6 +39,12 @@ function removeUserPassword(users: User | User[]) { 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 ( userIds: string[], opts?: GetOpts @@ -254,39 +261,6 @@ 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(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 // user as an app user (they may have some specific role/group export function removePortalUserPermissions(user: User | ContextUser) { diff --git a/packages/server/src/middleware/currentapp.ts b/packages/server/src/middleware/currentapp.ts index 1f580d80e4..800d43e69c 100644 --- a/packages/server/src/middleware/currentapp.ts +++ b/packages/server/src/middleware/currentapp.ts @@ -24,7 +24,7 @@ export default async (ctx: UserCtx, next: any) => { if ( isDevAppID(requestAppId) && !isWebhookEndpoint(ctx) && - (!ctx.user || !ctx.user.builder || !ctx.user.builder.global) + !users.isBuilder(ctx.user, requestAppId) ) { return ctx.redirect("/") } @@ -70,7 +70,6 @@ export default async (ctx: UserCtx, next: any) => { } return context.doInAppContext(appId, async () => { - let skipCookie = false // 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, if ( @@ -83,7 +82,6 @@ export default async (ctx: UserCtx, next: any) => { ctx.user = users.cleanseUserObject(ctx.user) as ContextUser ctx.isAuthenticated = false roleId = roles.BUILTIN_ROLE_IDS.PUBLIC - skipCookie = true } ctx.appId = appId diff --git a/packages/shared-core/src/index.ts b/packages/shared-core/src/index.ts index 21f2f2c639..4a47eda898 100644 --- a/packages/shared-core/src/index.ts +++ b/packages/shared-core/src/index.ts @@ -2,3 +2,4 @@ export * from "./constants" export * as dataFilters from "./filters" export * as helpers from "./helpers" export * as utils from "./utils" +export * as sdk from "./sdk" diff --git a/packages/shared-core/src/sdk/documents/applications.ts b/packages/shared-core/src/sdk/documents/applications.ts new file mode 100644 index 0000000000..05129f6e75 --- /dev/null +++ b/packages/shared-core/src/sdk/documents/applications.ts @@ -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}` +} diff --git a/packages/shared-core/src/sdk/documents/index.ts b/packages/shared-core/src/sdk/documents/index.ts new file mode 100644 index 0000000000..d20631eef4 --- /dev/null +++ b/packages/shared-core/src/sdk/documents/index.ts @@ -0,0 +1,2 @@ +export * as applications from "./applications" +export * as users from "./users" diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts new file mode 100644 index 0000000000..df3ef0025f --- /dev/null +++ b/packages/shared-core/src/sdk/documents/users.ts @@ -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 +} diff --git a/packages/shared-core/src/sdk/index.ts b/packages/shared-core/src/sdk/index.ts new file mode 100644 index 0000000000..87868e2723 --- /dev/null +++ b/packages/shared-core/src/sdk/index.ts @@ -0,0 +1 @@ +export * from "./documents" diff --git a/packages/types/src/documents/document.ts b/packages/types/src/documents/document.ts index ac05214b82..6ba318269b 100644 --- a/packages/types/src/documents/document.ts +++ b/packages/types/src/documents/document.ts @@ -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 { _id?: string _rev?: string diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index cfa68932d2..e0aa042995 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -176,7 +176,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => { export async function isPreventPasswordActions(user: User, account?: Account) { // when in maintenance mode we allow sso users with the admin role // 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 }