From ada0eb79bcd7807d0122e026fc59b8e394347f32 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 9 Nov 2022 16:35:16 +0000 Subject: [PATCH 01/15] Update tenancy detection to honour any subdomain pattern according to platform url --- hosting/nginx.dev.conf.hbs | 39 ++++++-- hosting/nginx.prod.conf.hbs | 9 ++ package.json | 4 +- packages/backend-core/src/db/utils.ts | 18 ++-- .../backend-core/src/middleware/tenancy.js | 52 ----------- .../backend-core/src/middleware/tenancy.ts | 35 ++++++++ .../backend-core/src/migrations/migrations.ts | 8 +- packages/backend-core/src/tenancy/tenancy.ts | 87 +++++++++++++++++- .../backend-core/src/{utils.js => utils.ts} | 88 +++++++++++-------- packages/server/scripts/localdomain.js | 40 +++++++-- .../migrations/functions/backfill/global.ts | 2 +- .../functions/usageQuotas/syncRows.ts | 4 +- packages/types/src/sdk/index.ts | 1 + packages/types/src/sdk/middleware/index.ts | 2 + packages/types/src/sdk/middleware/matchers.ts | 4 + packages/types/src/sdk/middleware/tenancy.ts | 12 +++ packages/worker/scripts/localdomain.js | 40 +++++++-- scripts/localdomain.sh | 16 ++++ 18 files changed, 337 insertions(+), 124 deletions(-) delete mode 100644 packages/backend-core/src/middleware/tenancy.js create mode 100644 packages/backend-core/src/middleware/tenancy.ts rename packages/backend-core/src/{utils.js => utils.ts} (66%) create mode 100644 packages/types/src/sdk/middleware/index.ts create mode 100644 packages/types/src/sdk/middleware/matchers.ts create mode 100644 packages/types/src/sdk/middleware/tenancy.ts create mode 100755 scripts/localdomain.sh diff --git a/hosting/nginx.dev.conf.hbs b/hosting/nginx.dev.conf.hbs index 7bc8100e45..93a07435e5 100644 --- a/hosting/nginx.dev.conf.hbs +++ b/hosting/nginx.dev.conf.hbs @@ -58,12 +58,15 @@ http { } location ~ ^/api/(system|admin|global)/ { - proxy_pass http://worker-service; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://worker-service; } location /api/backups/ { @@ -78,60 +81,78 @@ http { location /api/ { proxy_read_timeout 120s; proxy_connect_timeout 120s; - proxy_send_timeout 120s; - proxy_pass http://app-service; + proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://app-service; } location = / { - proxy_pass http://app-service; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://app-service; } location /app_ { - proxy_pass http://app-service; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://app-service; } location /app { - proxy_pass http://app-service; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://app-service; } location /builder { - proxy_pass http://builder; proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection ""; + + proxy_pass http://builder; rewrite ^/builder(.*)$ /builder/$1 break; } location /builder/ { - proxy_pass http://builder; - proxy_http_version 1.1; + + proxy_set_header Host $host; proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 120s; proxy_connect_timeout 120s; proxy_send_timeout 120s; + + proxy_pass http://builder; } location /vite/ { diff --git a/hosting/nginx.prod.conf.hbs b/hosting/nginx.prod.conf.hbs index aa79fd92ad..15d7c6823e 100644 --- a/hosting/nginx.prod.conf.hbs +++ b/hosting/nginx.prod.conf.hbs @@ -100,18 +100,25 @@ http { location ~ ^/(builder|app_) { proxy_http_version 1.1; + proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_pass http://$apps:4002; } location ~ ^/api/(system|admin|global)/ { + proxy_set_header Host $host; + proxy_pass http://$worker:4003; } location /worker/ { + proxy_set_header Host $host; + proxy_pass http://$worker:4003; rewrite ^/worker/(.*)$ /$1 break; } @@ -139,6 +146,7 @@ http { proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; proxy_pass http://$apps:4002; } @@ -158,6 +166,7 @@ http { proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; proxy_pass http://$apps:4002; } diff --git a/package.json b/package.json index 7733a6df95..592e72dde7 100644 --- a/package.json +++ b/package.json @@ -75,8 +75,8 @@ "env:multi:disable": "lerna run env:multi:disable", "env:selfhost:enable": "lerna run env:selfhost:enable", "env:selfhost:disable": "lerna run env:selfhost:disable", - "env:localdomain:enable": "lerna run env:localdomain:enable", - "env:localdomain:disable": "lerna run env:localdomain:disable", + "env:localdomain:enable": "./scripts/localdomain.sh enable", + "env:localdomain:disable": "./scripts/localdomain.sh disable", "env:account:enable": "lerna run env:account:enable", "env:account:disable": "lerna run env:account:disable", "mode:self": "yarn env:selfhost:enable && yarn env:multi:disable && yarn env:account:disable", diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index c04da5da4f..ee100cfa7b 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -15,6 +15,7 @@ import { getAppMetadata } from "../cache/appMetadata" import { isDevApp, isDevAppID, getProdAppID } from "./conversions" import { APP_PREFIX } from "./constants" import * as events from "../events" +import { App } from "@budibase/types" export * from "./constants" export * from "./conversions" @@ -301,7 +302,12 @@ export async function getAllDbs(opts = { efficient: false }) { * * @return {Promise} returns the app information document stored in each app database. */ -export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) { +export async function getAllApps({ + dev, + all, + idsOnly, + efficient, +}: any = {}): Promise { let tenantId = getTenantId() if (!env.MULTI_TENANCY && !tenantId) { tenantId = DEFAULT_TENANT_ID @@ -373,18 +379,16 @@ export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) { * Utility function for getAllApps but filters to production apps only. */ export async function getProdAppIDs() { - return (await getAllApps({ idsOnly: true })).filter( - (id: any) => !isDevAppID(id) - ) + const apps = (await getAllApps({ idsOnly: true })) as string[] + return apps.filter((id: any) => !isDevAppID(id)) } /** * Utility function for the inverse of above. */ export async function getDevAppIDs() { - return (await getAllApps({ idsOnly: true })).filter((id: any) => - isDevAppID(id) - ) + const apps = (await getAllApps({ idsOnly: true })) as string[] + return apps.filter((id: any) => isDevAppID(id)) } export async function dbExists(dbName: any) { diff --git a/packages/backend-core/src/middleware/tenancy.js b/packages/backend-core/src/middleware/tenancy.js deleted file mode 100644 index 8083322b29..0000000000 --- a/packages/backend-core/src/middleware/tenancy.js +++ /dev/null @@ -1,52 +0,0 @@ -const { doInTenant, isMultiTenant, DEFAULT_TENANT_ID } = require("../tenancy") -const { buildMatcherRegex, matches } = require("./matchers") -const { Headers } = require("../constants") - -const getTenantID = (ctx, opts = { allowQs: false, allowNoTenant: false }) => { - // exit early if not multi-tenant - if (!isMultiTenant()) { - return DEFAULT_TENANT_ID - } - - let tenantId - const allowQs = opts && opts.allowQs - const allowNoTenant = opts && opts.allowNoTenant - const header = ctx.request.headers[Headers.TENANT_ID] - const user = ctx.user || {} - if (allowQs) { - const query = ctx.request.query || {} - tenantId = query.tenantId - } - // override query string (if allowed) by user, or header - // URL params cannot be used in a middleware, as they are - // processed later in the chain - tenantId = user.tenantId || header || tenantId - - // Set the tenantId from the subdomain - if (!tenantId) { - tenantId = ctx.subdomains && ctx.subdomains[0] - } - - if (!tenantId && !allowNoTenant) { - ctx.throw(403, "Tenant id not set") - } - - return tenantId -} - -module.exports = ( - allowQueryStringPatterns, - noTenancyPatterns, - opts = { noTenancyRequired: false } -) => { - const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns) - const noTenancyOptions = buildMatcherRegex(noTenancyPatterns) - - return async function (ctx, next) { - const allowNoTenant = - opts.noTenancyRequired || !!matches(ctx, noTenancyOptions) - const allowQs = !!matches(ctx, allowQsOptions) - const tenantId = getTenantID(ctx, { allowQs, allowNoTenant }) - return doInTenant(tenantId, next) - } -} diff --git a/packages/backend-core/src/middleware/tenancy.ts b/packages/backend-core/src/middleware/tenancy.ts new file mode 100644 index 0000000000..880daa9b5b --- /dev/null +++ b/packages/backend-core/src/middleware/tenancy.ts @@ -0,0 +1,35 @@ +import { doInTenant, getTenantIDFromCtx } from "../tenancy" +import { buildMatcherRegex, matches } from "./matchers" +import { + BBContext, + EndpointMatcher, + GetTenantIdOptions, + TenantResolutionStrategy, +} from "@budibase/types" + +const tenancy = ( + allowQueryStringPatterns: EndpointMatcher[], + noTenancyPatterns: EndpointMatcher, + opts = { noTenancyRequired: false } +) => { + const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns) + const noTenancyOptions = buildMatcherRegex(noTenancyPatterns) + + return async function (ctx: BBContext, next: any) { + const allowNoTenant = + opts.noTenancyRequired || !!matches(ctx, noTenancyOptions) + const tenantOpts: GetTenantIdOptions = { + allowNoTenant, + } + + const allowQs = !!matches(ctx, allowQsOptions) + if (!allowQs) { + tenantOpts.excludeStrategies = [TenantResolutionStrategy.QUERY] + } + + const tenantId = getTenantIDFromCtx(ctx, tenantOpts) + return doInTenant(tenantId, next) + } +} + +export = tenancy diff --git a/packages/backend-core/src/migrations/migrations.ts b/packages/backend-core/src/migrations/migrations.ts index 90a12acec2..bd50fecc87 100644 --- a/packages/backend-core/src/migrations/migrations.ts +++ b/packages/backend-core/src/migrations/migrations.ts @@ -12,6 +12,7 @@ import { MigrationOptions, MigrationType, MigrationNoOpOptions, + App, } from "@budibase/types" export const getMigrationsDoc = async (db: any) => { @@ -55,14 +56,17 @@ export const runMigration = async ( } // get the db to store the migration in - let dbNames + let dbNames: string[] if (migrationType === MigrationType.GLOBAL) { dbNames = [getGlobalDBName()] } else if (migrationType === MigrationType.APP) { if (options.noOp) { + if (!options.noOp.appId) { + throw new Error("appId is required for noOp app migration") + } dbNames = [options.noOp.appId] } else { - const apps = await getAllApps(migration.appOpts) + const apps = (await getAllApps(migration.appOpts)) as App[] dbNames = apps.map(app => app.appId) } } else if (migrationType === MigrationType.INSTALLATION) { diff --git a/packages/backend-core/src/tenancy/tenancy.ts b/packages/backend-core/src/tenancy/tenancy.ts index ad5c6b5287..0d3bf54d07 100644 --- a/packages/backend-core/src/tenancy/tenancy.ts +++ b/packages/backend-core/src/tenancy/tenancy.ts @@ -9,7 +9,13 @@ import { getTenantIDFromAppID, } from "../context" import env from "../environment" -import { PlatformUser } from "@budibase/types" +import { + BBContext, + PlatformUser, + TenantResolutionStrategy, + GetTenantIdOptions, +} from "@budibase/types" +import { Headers } from "../constants" const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name @@ -144,3 +150,82 @@ export const getTenantIds = async () => { return (tenants && tenants.tenantIds) || [] }) } + +const ALL_STRATEGIES = Object.values(TenantResolutionStrategy) + +export const getTenantIDFromCtx = ( + ctx: BBContext, + opts: GetTenantIdOptions +): string | null => { + // exit early if not multi-tenant + if (!isMultiTenant()) { + return DEFAULT_TENANT_ID + } + + // opt defaults + if (opts.allowNoTenant === undefined) { + opts.allowNoTenant = false + } + if (!opts.includeStrategies) { + opts.includeStrategies = ALL_STRATEGIES + } + if (!opts.excludeStrategies) { + opts.excludeStrategies = [] + } + + const isAllowed = (strategy: TenantResolutionStrategy) => { + // excluded takes precedence + if (opts.excludeStrategies?.includes(strategy)) { + return false + } + if (opts.includeStrategies?.includes(strategy)) { + return true + } + } + + // always use user first + if (isAllowed(TenantResolutionStrategy.USER)) { + const userTenantId = ctx.user?.tenantId + if (userTenantId) { + return userTenantId + } + } + + // header + if (isAllowed(TenantResolutionStrategy.HEADER)) { + const headerTenantId = ctx.request.headers[Headers.TENANT_ID] + if (headerTenantId) { + return headerTenantId as string + } + } + + // query param + if (isAllowed(TenantResolutionStrategy.QUERY)) { + const queryTenantId = ctx.request.query.tenantId + if (queryTenantId) { + return queryTenantId as string + } + } + + // subdomain + if (isAllowed(TenantResolutionStrategy.SUBDOMAIN)) { + // e.g. budibase.app or local.com:10000 + const platformHost = new URL(env.PLATFORM_URL).host.split(":")[0] + // e.g. tenant.budibase.app or tenant.local.com + const requestHost = ctx.host + // parse the tenant id from the difference + const tenantId = requestHost.substring( + 0, + requestHost.indexOf(`.${platformHost}`) + ) + if (tenantId) { + return tenantId + } + } + + if (!opts.allowNoTenant) { + ctx.throw(403, "Tenant id not set") + } + + return null +} diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.ts similarity index 66% rename from packages/backend-core/src/utils.js rename to packages/backend-core/src/utils.ts index 6b59c7cb72..03b4747ee3 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.ts @@ -1,38 +1,41 @@ -const { DocumentType, SEPARATOR, ViewName, getAllApps } = require("./db/utils") +import { DocumentType, SEPARATOR, ViewName, getAllApps } from "./db/utils" const jwt = require("jsonwebtoken") -const { options } = require("./middleware/passport/jwt") -const { queryGlobalView } = require("./db/views") -const { Headers, Cookies, MAX_VALID_DATE } = require("./constants") -const env = require("./environment") -const userCache = require("./cache/user") -const { - getSessionsForUser, - invalidateSessions, -} = require("./security/sessions") -const events = require("./events") -const tenancy = require("./tenancy") +import { options } from "./middleware/passport/jwt" +import { queryGlobalView } from "./db/views" +import { Headers, Cookies, MAX_VALID_DATE } from "./constants" +import env from "./environment" +import userCache from "./cache/user" +import { getSessionsForUser, invalidateSessions } from "./security/sessions" +import * as events from "./events" +import tenancy from "./tenancy" +import { App, BBContext, TenantResolutionStrategy } from "@budibase/types" +import { SetOption } from "cookies" const APP_PREFIX = DocumentType.APP + SEPARATOR const PROD_APP_PREFIX = "/app/" -function confirmAppId(possibleAppId) { +function confirmAppId(possibleAppId: string | undefined) { return possibleAppId && possibleAppId.startsWith(APP_PREFIX) ? possibleAppId : undefined } -async function resolveAppUrl(ctx) { +async function resolveAppUrl(ctx: BBContext) { const appUrl = ctx.path.split("/")[2] let possibleAppUrl = `/${appUrl.toLowerCase()}` let tenantId = tenancy.getTenantId() - if (!env.SELF_HOSTED && ctx.subdomains.length) { - // always use the tenant id from the url in cloud - tenantId = ctx.subdomains[0] + if (!env.SELF_HOSTED) { + // always use the tenant id from the subdomain in cloud + // this ensures the logged-in user tenant id doesn't overwrite + // e.g. in the case of viewing a public app while already logged-in to another tenant + tenantId = tenancy.getTenantIDFromCtx(ctx, { + includeStrategies: [TenantResolutionStrategy.SUBDOMAIN], + }) } // search prod apps for a url that matches - const apps = await tenancy.doInTenant(tenantId, () => + const apps: App[] = await tenancy.doInTenant(tenantId, () => getAllApps({ dev: false }) ) const app = apps.filter( @@ -42,7 +45,7 @@ async function resolveAppUrl(ctx) { return app && app.appId ? app.appId : undefined } -exports.isServingApp = ctx => { +export const isServingApp = (ctx: BBContext) => { // dev app if (ctx.path.startsWith(`/${APP_PREFIX}`)) { return true @@ -59,12 +62,12 @@ exports.isServingApp = ctx => { * @param {object} ctx The main request body to look through. * @returns {string|undefined} If an appId was found it will be returned. */ -exports.getAppIdFromCtx = async ctx => { +export const getAppIdFromCtx = async (ctx: BBContext) => { // look in headers const options = [ctx.headers[Headers.APP_ID]] let appId for (let option of options) { - appId = confirmAppId(option) + appId = confirmAppId(option as string) if (appId) { break } @@ -95,7 +98,7 @@ exports.getAppIdFromCtx = async ctx => { * opens the contents of the specified encrypted JWT. * @return {object} the contents of the token. */ -exports.openJwt = token => { +export const openJwt = (token: string) => { if (!token) { return token } @@ -107,14 +110,14 @@ exports.openJwt = token => { * @param {object} ctx The request which is to be manipulated. * @param {string} name The name of the cookie to get. */ -exports.getCookie = (ctx, name) => { +export const getCookie = (ctx: BBContext, name: string) => { const cookie = ctx.cookies.get(name) if (!cookie) { return cookie } - return exports.openJwt(cookie) + return openJwt(cookie) } /** @@ -124,12 +127,17 @@ exports.getCookie = (ctx, name) => { * @param {string|object} value The value of cookie which will be set. * @param {object} opts options like whether to sign. */ -exports.setCookie = (ctx, value, name = "builder", opts = { sign: true }) => { +export const setCookie = ( + ctx: BBContext, + value: any, + name = "builder", + opts = { sign: true } +) => { if (value && opts && opts.sign) { value = jwt.sign(value, options.secretOrKey) } - const config = { + const config: SetOption = { expires: MAX_VALID_DATE, path: "/", httpOnly: false, @@ -146,8 +154,8 @@ exports.setCookie = (ctx, value, name = "builder", opts = { sign: true }) => { /** * Utility function, simply calls setCookie with an empty string for value */ -exports.clearCookie = (ctx, name) => { - exports.setCookie(ctx, null, name) +export const clearCookie = (ctx: BBContext, name: string) => { + setCookie(ctx, null, name) } /** @@ -156,7 +164,7 @@ exports.clearCookie = (ctx, name) => { * @param {object} ctx The koa context object to be tested. * @return {boolean} returns true if the call is from the client lib (a built app rather than the builder). */ -exports.isClient = ctx => { +export const isClient = (ctx: BBContext) => { return ctx.headers[Headers.TYPE] === "client" } @@ -176,18 +184,28 @@ const getBuilders = async () => { } } -exports.getBuildersCount = async () => { +export const getBuildersCount = async () => { const builders = await getBuilders() return builders.length } +interface PlatformLogoutOpts { + ctx: BBContext + userId: string + keepActiveSession: boolean +} + /** * Logs a user out from budibase. Re-used across account portal and builder. */ -exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => { +export const platformLogout = async (opts: PlatformLogoutOpts) => { + const ctx = opts.ctx + const userId = opts.userId + const keepActiveSession = opts.keepActiveSession + if (!ctx) throw new Error("Koa context must be supplied to logout.") - const currentSession = exports.getCookie(ctx, Cookies.Auth) + const currentSession = getCookie(ctx, Cookies.Auth) let sessions = await getSessionsForUser(userId) if (keepActiveSession) { @@ -196,8 +214,8 @@ exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => { ) } else { // clear cookies - exports.clearCookie(ctx, Cookies.Auth) - exports.clearCookie(ctx, Cookies.CurrentApp) + clearCookie(ctx, Cookies.Auth) + clearCookie(ctx, Cookies.CurrentApp) } const sessionIds = sessions.map(({ sessionId }) => sessionId) @@ -206,6 +224,6 @@ exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => { await userCache.invalidateUser(userId) } -exports.timeout = timeMs => { +export const timeout = (timeMs: number) => { return new Promise(resolve => setTimeout(resolve, timeMs)) } diff --git a/packages/server/scripts/localdomain.js b/packages/server/scripts/localdomain.js index 7dd1c083b4..9317538e9f 100644 --- a/packages/server/scripts/localdomain.js +++ b/packages/server/scripts/localdomain.js @@ -2,6 +2,36 @@ const updateDotEnv = require("update-dotenv") const arg = process.argv.slice(2)[0] +const isEnable = arg === "enable" + +let domain = process.argv.slice(2)[1] +if (!domain) { + domain = "local.com" +} + +const getAccountPortalUrl = () => { + if (isEnable) { + return `http://account.${domain}:10001` + } else { + return `http://localhost:10001` + } +} + +const getBudibaseUrl = () => { + if (isEnable) { + return `http://${domain}:10000` + } else { + return `http://localhost:10000` + } +} + +const getCookieDomain = () => { + if (isEnable) { + return `.${domain}` + } else { + return "" + } +} /** * For testing multi tenancy sub domains locally. @@ -16,9 +46,7 @@ const arg = process.argv.slice(2)[0] * 127.0.0.1 t2.local.com */ updateDotEnv({ - ACCOUNT_PORTAL_URL: - arg === "enable" - ? "http://account.local.com:10001" - : "http://localhost:10001", - COOKIE_DOMAIN: arg === "enable" ? ".local.com" : "", -}).then(() => console.log("Updated worker!")) + ACCOUNT_PORTAL_URL: getAccountPortalUrl(), + COOKIE_DOMAIN: getCookieDomain(), + PLATFORM_URL: getBudibaseUrl(), +}).then(() => console.log("Updated server!")) diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index d7be61c130..e3a96b77dc 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -149,7 +149,7 @@ export const run = async (db: any) => { } try { - const allApps: App[] = await dbUtils.getAllApps({ dev: true }) + const allApps = (await dbUtils.getAllApps({ dev: true })) as App[] totals.apps = allApps.length totals.usage = await quotas.backfill(allApps) diff --git a/packages/server/src/migrations/functions/usageQuotas/syncRows.ts b/packages/server/src/migrations/functions/usageQuotas/syncRows.ts index 0b123d2357..e5c8a1743c 100644 --- a/packages/server/src/migrations/functions/usageQuotas/syncRows.ts +++ b/packages/server/src/migrations/functions/usageQuotas/syncRows.ts @@ -2,11 +2,11 @@ import { getTenantId } from "@budibase/backend-core/tenancy" import { getAllApps } from "@budibase/backend-core/db" import { getUniqueRows } from "../../../utilities/usageQuota/rows" import { quotas } from "@budibase/pro" -import { StaticQuotaName, QuotaUsageType } from "@budibase/types" +import { StaticQuotaName, QuotaUsageType, App } from "@budibase/types" export const run = async () => { // get all rows in all apps - const allApps = await getAllApps({ all: true }) + const allApps = (await getAllApps({ all: true })) as App[] const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : [] const { appRows } = await getUniqueRows(appIds) diff --git a/packages/types/src/sdk/index.ts b/packages/types/src/sdk/index.ts index 724b152303..a32c8e2077 100644 --- a/packages/types/src/sdk/index.ts +++ b/packages/types/src/sdk/index.ts @@ -9,3 +9,4 @@ export * from "./koa" export * from "./auth" export * from "./locks" export * from "./db" +export * from "./middleware" diff --git a/packages/types/src/sdk/middleware/index.ts b/packages/types/src/sdk/middleware/index.ts new file mode 100644 index 0000000000..bc4220e329 --- /dev/null +++ b/packages/types/src/sdk/middleware/index.ts @@ -0,0 +1,2 @@ +export * from "./matchers" +export * from "./tenancy" diff --git a/packages/types/src/sdk/middleware/matchers.ts b/packages/types/src/sdk/middleware/matchers.ts new file mode 100644 index 0000000000..026c3924b0 --- /dev/null +++ b/packages/types/src/sdk/middleware/matchers.ts @@ -0,0 +1,4 @@ +export interface EndpointMatcher { + route: string + method: string +} diff --git a/packages/types/src/sdk/middleware/tenancy.ts b/packages/types/src/sdk/middleware/tenancy.ts new file mode 100644 index 0000000000..dd94b680c3 --- /dev/null +++ b/packages/types/src/sdk/middleware/tenancy.ts @@ -0,0 +1,12 @@ +export interface GetTenantIdOptions { + allowNoTenant?: boolean + excludeStrategies?: TenantResolutionStrategy[] + includeStrategies?: TenantResolutionStrategy[] +} + +export enum TenantResolutionStrategy { + USER = "user", + HEADER = "header", + QUERY = "query", + SUBDOMAIN = "subdomain", +} diff --git a/packages/worker/scripts/localdomain.js b/packages/worker/scripts/localdomain.js index 4e181628b2..985840b401 100644 --- a/packages/worker/scripts/localdomain.js +++ b/packages/worker/scripts/localdomain.js @@ -2,6 +2,36 @@ const updateDotEnv = require("update-dotenv") const arg = process.argv.slice(2)[0] +const isEnable = arg === "enable" + +let domain = process.argv.slice(2)[1] +if (!domain) { + domain = "local.com" +} + +const getAccountPortalUrl = () => { + if (isEnable) { + return `http://account.${domain}:10001` + } else { + return `http://localhost:10001` + } +} + +const getBudibaseUrl = () => { + if (isEnable) { + return `http://${domain}:10000` + } else { + return `http://localhost:10000` + } +} + +const getCookieDomain = () => { + if (isEnable) { + return `.${domain}` + } else { + return "" + } +} /** * For testing multi tenancy sub domains locally. @@ -16,11 +46,7 @@ const arg = process.argv.slice(2)[0] * 127.0.0.1 t2.local.com */ updateDotEnv({ - ACCOUNT_PORTAL_URL: - arg === "enable" - ? "http://account.local.com:10001" - : "http://localhost:10001", - COOKIE_DOMAIN: arg === "enable" ? ".local.com" : "", - PLATFORM_URL: - arg === "enable" ? "http://local.com:10000" : "http://localhost:10000", + ACCOUNT_PORTAL_URL: getAccountPortalUrl(), + COOKIE_DOMAIN: getCookieDomain(), + PLATFORM_URL: getBudibaseUrl(), }).then(() => console.log("Updated worker!")) diff --git a/scripts/localdomain.sh b/scripts/localdomain.sh new file mode 100755 index 0000000000..d32dbcc116 --- /dev/null +++ b/scripts/localdomain.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +enable=$1 +domain=$2 + +if [ "$enable" = "enable" ]; then + lerna run env:localdomain:enable -- "$domain" + cd ../account-portal + yarn env:localdomain:enable "$domain" + cd - +else + lerna run env:localdomain:disable + cd ../account-portal + yarn env:localdomain:disable + cd - +fi \ No newline at end of file From 0bad2dd9ae9cbfa7d87798075b54af9bffb7edc2 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 11 Nov 2022 11:10:07 +0000 Subject: [PATCH 02/15] Support path variable tenancy detection, add /api/system/* tests, update no tenancy matchers to be more accurate --- .../{deprovision.js => deprovision.ts} | 45 +++++------ packages/backend-core/src/index.ts | 2 +- .../middleware/{matchers.js => matchers.ts} | 20 +++-- .../backend-core/src/middleware/tenancy.ts | 4 +- packages/backend-core/src/tenancy/tenancy.ts | 29 ++++++-- packages/backend-core/src/utils.ts | 13 ++-- .../backend-core/tests/utilities/index.ts | 1 + .../tests/utilities/mocks/accounts.ts | 2 + .../tests/utilities/structures/accounts.ts | 22 ++++-- .../types/src/documents/platform/index.ts | 1 + .../types/src/documents/platform/tenants.ts | 5 ++ packages/types/src/sdk/auth.ts | 8 ++ packages/types/src/sdk/middleware/matchers.ts | 18 +++++ packages/types/src/sdk/middleware/tenancy.ts | 1 + packages/worker/scripts/jestSetup.js | 5 +- .../system/{environment.js => environment.ts} | 5 +- .../system/{status.js => status.ts} | 7 +- .../src/api/controllers/system/tenants.ts | 59 ++------------- packages/worker/src/api/index.ts | 31 +++++++- packages/worker/src/api/routes/global/auth.js | 32 +------- .../api/routes/global/tests/configs.spec.ts | 4 +- .../api/routes/global/tests/license.spec.ts | 28 +++++++ .../src/api/routes/global/tests/roles.spec.ts | 24 ++++++ .../api/routes/global/tests/templates.spec.ts | 32 ++++++++ .../routes/global/tests/workspaces.spec.ts | 26 +++++++ .../src/api/routes/system/environment.js | 8 -- .../src/api/routes/system/environment.ts | 8 ++ .../worker/src/api/routes/system/status.js | 8 -- .../worker/src/api/routes/system/status.ts | 8 ++ .../worker/src/api/routes/system/tenants.js | 12 --- .../worker/src/api/routes/system/tenants.ts | 13 ++++ .../routes/system/tests/environment.spec.ts | 30 ++++++++ .../routes/system/tests/migrations.spec.ts | 64 ++++++++++++++++ .../api/routes/system/tests/restore.spec.ts | 37 ++++++++++ .../api/routes/system/tests/status.spec.ts | 49 ++++++++++++ .../api/routes/system/tests/tenants.spec.ts | 32 ++++++++ .../src/middleware/tests/tenancy.spec.ts | 74 +++++++++++++++++++ .../worker/src/tests/TestConfiguration.ts | 53 ++++++++----- packages/worker/src/tests/api/base.ts | 16 ++++ packages/worker/src/tests/api/environment.ts | 18 +++++ packages/worker/src/tests/api/index.ts | 15 ++++ packages/worker/src/tests/api/migrations.ts | 22 ++++++ packages/worker/src/tests/api/restore.ts | 14 ++++ packages/worker/src/tests/api/self.ts | 8 ++ packages/worker/src/tests/api/status.ts | 15 ++++ packages/worker/src/tests/api/tenants.ts | 15 ++++ packages/worker/src/tests/index.ts | 4 + .../worker/src/tests/structures/accounts.ts | 24 ------ packages/worker/src/tests/structures/index.ts | 6 +- packages/worker/src/tests/structures/users.ts | 1 + 50 files changed, 765 insertions(+), 213 deletions(-) rename packages/backend-core/src/context/{deprovision.js => deprovision.ts} (58%) rename packages/backend-core/src/middleware/{matchers.js => matchers.ts} (61%) create mode 100644 packages/types/src/documents/platform/tenants.ts rename packages/worker/src/api/controllers/system/{environment.js => environment.ts} (69%) rename packages/worker/src/api/controllers/system/{status.js => status.ts} (54%) create mode 100644 packages/worker/src/api/routes/global/tests/license.spec.ts create mode 100644 packages/worker/src/api/routes/global/tests/roles.spec.ts create mode 100644 packages/worker/src/api/routes/global/tests/templates.spec.ts create mode 100644 packages/worker/src/api/routes/global/tests/workspaces.spec.ts delete mode 100644 packages/worker/src/api/routes/system/environment.js create mode 100644 packages/worker/src/api/routes/system/environment.ts delete mode 100644 packages/worker/src/api/routes/system/status.js create mode 100644 packages/worker/src/api/routes/system/status.ts delete mode 100644 packages/worker/src/api/routes/system/tenants.js create mode 100644 packages/worker/src/api/routes/system/tenants.ts create mode 100644 packages/worker/src/api/routes/system/tests/environment.spec.ts create mode 100644 packages/worker/src/api/routes/system/tests/migrations.spec.ts create mode 100644 packages/worker/src/api/routes/system/tests/restore.spec.ts create mode 100644 packages/worker/src/api/routes/system/tests/status.spec.ts create mode 100644 packages/worker/src/api/routes/system/tests/tenants.spec.ts create mode 100644 packages/worker/src/middleware/tests/tenancy.spec.ts create mode 100644 packages/worker/src/tests/api/base.ts create mode 100644 packages/worker/src/tests/api/environment.ts create mode 100644 packages/worker/src/tests/api/migrations.ts create mode 100644 packages/worker/src/tests/api/restore.ts create mode 100644 packages/worker/src/tests/api/status.ts create mode 100644 packages/worker/src/tests/api/tenants.ts delete mode 100644 packages/worker/src/tests/structures/accounts.ts diff --git a/packages/backend-core/src/context/deprovision.js b/packages/backend-core/src/context/deprovision.ts similarity index 58% rename from packages/backend-core/src/context/deprovision.js rename to packages/backend-core/src/context/deprovision.ts index ba3c2d8449..2723b1841e 100644 --- a/packages/backend-core/src/context/deprovision.js +++ b/packages/backend-core/src/context/deprovision.ts @@ -1,15 +1,16 @@ -const { getGlobalUserParams, getAllApps } = require("../db/utils") -const { doWithDB } = require("../db") -const { doWithGlobalDB } = require("../tenancy") -const { StaticDatabases } = require("../db/constants") +import { getGlobalUserParams, getAllApps } from "../db/utils" +import { doWithDB } from "../db" +import { doWithGlobalDB } from "../tenancy" +import { StaticDatabases } from "../db/constants" +import { App, Tenants, User } from "@budibase/types" const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name -const removeTenantFromInfoDB = async tenantId => { +const removeTenantFromInfoDB = async (tenantId: string) => { try { - await doWithDB(PLATFORM_INFO_DB, async infoDb => { - let tenants = await infoDb.get(TENANT_DOC) + await doWithDB(PLATFORM_INFO_DB, async (infoDb: any) => { + const tenants = (await infoDb.get(TENANT_DOC)) as Tenants tenants.tenantIds = tenants.tenantIds.filter(id => id !== tenantId) await infoDb.put(tenants) @@ -20,14 +21,14 @@ const removeTenantFromInfoDB = async tenantId => { } } -exports.removeUserFromInfoDB = async dbUser => { - await doWithDB(PLATFORM_INFO_DB, async infoDb => { +export const removeUserFromInfoDB = async (dbUser: User) => { + await doWithDB(PLATFORM_INFO_DB, async (infoDb: any) => { const keys = [dbUser._id, dbUser.email] const userDocs = await infoDb.allDocs({ keys, include_docs: true, }) - const toDelete = userDocs.rows.map(row => { + const toDelete = userDocs.rows.map((row: any) => { return { ...row.doc, _deleted: true, @@ -37,18 +38,18 @@ exports.removeUserFromInfoDB = async dbUser => { }) } -const removeUsersFromInfoDB = async tenantId => { - return doWithGlobalDB(tenantId, async db => { +const removeUsersFromInfoDB = async (tenantId: string) => { + return doWithGlobalDB(tenantId, async (db: any) => { try { const allUsers = await db.allDocs( getGlobalUserParams(null, { include_docs: true, }) ) - await doWithDB(PLATFORM_INFO_DB, async infoDb => { - const allEmails = allUsers.rows.map(row => row.doc.email) + await doWithDB(PLATFORM_INFO_DB, async (infoDb: any) => { + const allEmails = allUsers.rows.map((row: any) => row.doc.email) // get the id docs - let keys = allUsers.rows.map(row => row.id) + let keys = allUsers.rows.map((row: any) => row.id) // and the email docs keys = keys.concat(allEmails) // retrieve the docs and delete them @@ -56,7 +57,7 @@ const removeUsersFromInfoDB = async tenantId => { keys, include_docs: true, }) - const toDelete = userDocs.rows.map(row => { + const toDelete = userDocs.rows.map((row: any) => { return { ...row.doc, _deleted: true, @@ -71,8 +72,8 @@ const removeUsersFromInfoDB = async tenantId => { }) } -const removeGlobalDB = async tenantId => { - return doWithGlobalDB(tenantId, async db => { +const removeGlobalDB = async (tenantId: string) => { + return doWithGlobalDB(tenantId, async (db: any) => { try { await db.destroy() } catch (err) { @@ -82,11 +83,11 @@ const removeGlobalDB = async tenantId => { }) } -const removeTenantApps = async tenantId => { +const removeTenantApps = async (tenantId: string) => { try { - const apps = await getAllApps({ all: true }) + const apps = (await getAllApps({ all: true })) as App[] const destroyPromises = apps.map(app => - doWithDB(app.appId, db => db.destroy()) + doWithDB(app.appId, (db: any) => db.destroy()) ) await Promise.allSettled(destroyPromises) } catch (err) { @@ -96,7 +97,7 @@ const removeTenantApps = async tenantId => { } // can't live in tenancy package due to circular dependency on db/utils -exports.deleteTenant = async tenantId => { +export const deleteTenant = async (tenantId: string) => { await removeTenantFromInfoDB(tenantId) await removeUsersFromInfoDB(tenantId) await removeGlobalDB(tenantId) diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index 17393b8ac3..f356a56cbf 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -11,7 +11,7 @@ import env from "./environment" import tenancy from "./tenancy" import featureFlags from "./featureFlags" import * as sessions from "./security/sessions" -import deprovisioning from "./context/deprovision" +import * as deprovisioning from "./context/deprovision" import auth from "./auth" import constants from "./constants" import * as dbConstants from "./db/constants" diff --git a/packages/backend-core/src/middleware/matchers.js b/packages/backend-core/src/middleware/matchers.ts similarity index 61% rename from packages/backend-core/src/middleware/matchers.js rename to packages/backend-core/src/middleware/matchers.ts index 3d5065c069..e40a9a61b3 100644 --- a/packages/backend-core/src/middleware/matchers.js +++ b/packages/backend-core/src/middleware/matchers.ts @@ -1,27 +1,35 @@ +import { BBContext, EndpointMatcher, RegexMatcher } from "@budibase/types" + const PARAM_REGEX = /\/:(.*?)(\/.*)?$/g -exports.buildMatcherRegex = patterns => { +export const buildMatcherRegex = ( + patterns: EndpointMatcher[] +): RegexMatcher[] => { if (!patterns) { return [] } return patterns.map(pattern => { - const isObj = typeof pattern === "object" && pattern.route - const method = isObj ? pattern.method : "GET" + let route = pattern.route + const method = pattern.method const strict = pattern.strict ? pattern.strict : false - let route = isObj ? pattern.route : pattern + // if there is a param in the route + // use a wildcard pattern const matches = route.match(PARAM_REGEX) if (matches) { for (let match of matches) { - const pattern = "/.*" + (match.endsWith("/") ? "/" : "") + const suffix = match.endsWith("/") ? "/" : "" + const pattern = "/.*" + suffix route = route.replace(match, pattern) + console.log(route) } } + return { regex: new RegExp(route), method, strict, route } }) } -exports.matches = (ctx, options) => { +export const matches = (ctx: BBContext, options: RegexMatcher[]) => { return options.find(({ regex, method, strict, route }) => { let urlMatch if (strict) { diff --git a/packages/backend-core/src/middleware/tenancy.ts b/packages/backend-core/src/middleware/tenancy.ts index 880daa9b5b..03dd9d11e6 100644 --- a/packages/backend-core/src/middleware/tenancy.ts +++ b/packages/backend-core/src/middleware/tenancy.ts @@ -1,5 +1,6 @@ import { doInTenant, getTenantIDFromCtx } from "../tenancy" import { buildMatcherRegex, matches } from "./matchers" +import { Headers } from "../constants" import { BBContext, EndpointMatcher, @@ -9,7 +10,7 @@ import { const tenancy = ( allowQueryStringPatterns: EndpointMatcher[], - noTenancyPatterns: EndpointMatcher, + noTenancyPatterns: EndpointMatcher[], opts = { noTenancyRequired: false } ) => { const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns) @@ -28,6 +29,7 @@ const tenancy = ( } const tenantId = getTenantIDFromCtx(ctx, tenantOpts) + ctx.set(Headers.TENANT_ID, tenantId as string) return doInTenant(tenantId, next) } } diff --git a/packages/backend-core/src/tenancy/tenancy.ts b/packages/backend-core/src/tenancy/tenancy.ts index 0d3bf54d07..66d170d3f8 100644 --- a/packages/backend-core/src/tenancy/tenancy.ts +++ b/packages/backend-core/src/tenancy/tenancy.ts @@ -214,12 +214,31 @@ export const getTenantIDFromCtx = ( // e.g. tenant.budibase.app or tenant.local.com const requestHost = ctx.host // parse the tenant id from the difference - const tenantId = requestHost.substring( - 0, - requestHost.indexOf(`.${platformHost}`) + if (requestHost.includes(platformHost)) { + const tenantId = requestHost.substring( + 0, + requestHost.indexOf(`.${platformHost}`) + ) + if (tenantId) { + return tenantId + } + } + } + + if (isAllowed(TenantResolutionStrategy.PATH)) { + // params - have to parse manually due to koa-router not run yet + const match = ctx.matched.find( + (m: any) => !!m.paramNames.find((p: any) => p.name === "tenantId") ) - if (tenantId) { - return tenantId + if (match) { + const params = match.params( + ctx.originalUrl, + match.captures(ctx.originalUrl), + {} + ) + if (params.tenantId) { + return params.tenantId + } } } diff --git a/packages/backend-core/src/utils.ts b/packages/backend-core/src/utils.ts index 03b4747ee3..215288b93c 100644 --- a/packages/backend-core/src/utils.ts +++ b/packages/backend-core/src/utils.ts @@ -8,7 +8,12 @@ import userCache from "./cache/user" import { getSessionsForUser, invalidateSessions } from "./security/sessions" import * as events from "./events" import tenancy from "./tenancy" -import { App, BBContext, TenantResolutionStrategy } from "@budibase/types" +import { + App, + BBContext, + PlatformLogoutOpts, + TenantResolutionStrategy, +} from "@budibase/types" import { SetOption } from "cookies" const APP_PREFIX = DocumentType.APP + SEPARATOR @@ -189,12 +194,6 @@ export const getBuildersCount = async () => { return builders.length } -interface PlatformLogoutOpts { - ctx: BBContext - userId: string - keepActiveSession: boolean -} - /** * Logs a user out from budibase. Re-used across account portal and builder. */ diff --git a/packages/backend-core/tests/utilities/index.ts b/packages/backend-core/tests/utilities/index.ts index 1e73be4c17..fb12717c89 100644 --- a/packages/backend-core/tests/utilities/index.ts +++ b/packages/backend-core/tests/utilities/index.ts @@ -1,2 +1,3 @@ export * as mocks from "./mocks" export * as structures from "./structures" +export { generator } from "./structures" diff --git a/packages/backend-core/tests/utilities/mocks/accounts.ts b/packages/backend-core/tests/utilities/mocks/accounts.ts index 79436443db..cb4c68b65e 100644 --- a/packages/backend-core/tests/utilities/mocks/accounts.ts +++ b/packages/backend-core/tests/utilities/mocks/accounts.ts @@ -1,7 +1,9 @@ export const getAccount = jest.fn() export const getAccountByTenantId = jest.fn() +export const getStatus = jest.fn() jest.mock("../../../src/cloud/accounts", () => ({ getAccount, getAccountByTenantId, + getStatus, })) diff --git a/packages/backend-core/tests/utilities/structures/accounts.ts b/packages/backend-core/tests/utilities/structures/accounts.ts index 5d23962575..f1718aecc0 100644 --- a/packages/backend-core/tests/utilities/structures/accounts.ts +++ b/packages/backend-core/tests/utilities/structures/accounts.ts @@ -1,23 +1,29 @@ import { generator, uuid } from "." -import { AuthType, CloudAccount, Hosting } from "@budibase/types" import * as db from "../../../src/db/utils" +import { Account, AuthType, CloudAccount, Hosting } from "@budibase/types" -export const cloudAccount = (): CloudAccount => { +export const account = (): Account => { return { accountId: uuid(), + tenantId: generator.word(), + email: generator.email(), + tenantName: generator.word(), + hosting: Hosting.SELF, createdAt: Date.now(), verified: true, verificationSent: true, - tier: "", - email: generator.email(), - tenantId: generator.word(), - hosting: Hosting.CLOUD, + tier: "FREE", // DEPRECATED authType: AuthType.PASSWORD, - password: generator.word(), - tenantName: generator.word(), name: generator.name(), size: "10+", profession: "Software Engineer", + } +} + +export const cloudAccount = (): CloudAccount => { + return { + ...account(), + hosting: Hosting.CLOUD, budibaseUserId: db.generateGlobalUserID(), } } diff --git a/packages/types/src/documents/platform/index.ts b/packages/types/src/documents/platform/index.ts index 1a7cef91cf..8f57ce85fa 100644 --- a/packages/types/src/documents/platform/index.ts +++ b/packages/types/src/documents/platform/index.ts @@ -1,3 +1,4 @@ export * from "./info" export * from "./users" export * from "./accounts" +export * from "./tenants" diff --git a/packages/types/src/documents/platform/tenants.ts b/packages/types/src/documents/platform/tenants.ts new file mode 100644 index 0000000000..dd994df1d2 --- /dev/null +++ b/packages/types/src/documents/platform/tenants.ts @@ -0,0 +1,5 @@ +import { Document } from "../document" + +export interface Tenants extends Document { + tenantIds: string[] +} diff --git a/packages/types/src/sdk/auth.ts b/packages/types/src/sdk/auth.ts index 6a040abf77..d61b679c62 100644 --- a/packages/types/src/sdk/auth.ts +++ b/packages/types/src/sdk/auth.ts @@ -1,3 +1,5 @@ +import { BBContext } from "./koa" + export interface AuthToken { userId: string tenantId: string @@ -25,3 +27,9 @@ export interface SessionKey { export interface ScannedSession { value: Session } + +export interface PlatformLogoutOpts { + ctx: BBContext + userId: string + keepActiveSession: boolean +} diff --git a/packages/types/src/sdk/middleware/matchers.ts b/packages/types/src/sdk/middleware/matchers.ts index 026c3924b0..fc4ceb323e 100644 --- a/packages/types/src/sdk/middleware/matchers.ts +++ b/packages/types/src/sdk/middleware/matchers.ts @@ -1,4 +1,22 @@ export interface EndpointMatcher { + /** + * The HTTP Path. e.g. /api/things/:thingId + */ route: string + /** + * The HTTP Verb. e.g. GET, POST, etc. + * ALL is also accepted to cover all verbs. + */ method: string + /** + * The route must match exactly - not just begins with + */ + strict?: boolean +} + +export interface RegexMatcher { + regex: RegExp + method: string + strict: boolean + route: string } diff --git a/packages/types/src/sdk/middleware/tenancy.ts b/packages/types/src/sdk/middleware/tenancy.ts index dd94b680c3..8bb362d049 100644 --- a/packages/types/src/sdk/middleware/tenancy.ts +++ b/packages/types/src/sdk/middleware/tenancy.ts @@ -9,4 +9,5 @@ export enum TenantResolutionStrategy { HEADER = "header", QUERY = "query", SUBDOMAIN = "subdomain", + PATH = "path", } diff --git a/packages/worker/scripts/jestSetup.js b/packages/worker/scripts/jestSetup.js index 8ee2d33d70..7539a65980 100644 --- a/packages/worker/scripts/jestSetup.js +++ b/packages/worker/scripts/jestSetup.js @@ -1,10 +1,13 @@ const env = require("../src/environment") -env._set("SELF_HOSTED", "1") +env._set("SELF_HOSTED", "0") env._set("NODE_ENV", "jest") env._set("JWT_SECRET", "test-jwtsecret") env._set("LOG_LEVEL", "silent") env._set("MULTI_TENANCY", true) +env._set("PLATFORM_URL", "http://localhost:10000") +env._set("INTERNAL_API_KEY", "test") +env._set("DISABLE_ACCOUNT_PORTAL", false) const { mocks } = require("@budibase/backend-core/tests") diff --git a/packages/worker/src/api/controllers/system/environment.js b/packages/worker/src/api/controllers/system/environment.ts similarity index 69% rename from packages/worker/src/api/controllers/system/environment.js rename to packages/worker/src/api/controllers/system/environment.ts index 4edf1ff8d3..8ae0fcda3f 100644 --- a/packages/worker/src/api/controllers/system/environment.js +++ b/packages/worker/src/api/controllers/system/environment.ts @@ -1,6 +1,7 @@ -const env = require("../../../environment") +import { BBContext } from "@budibase/types" +import env from "../../../environment" -exports.fetch = async ctx => { +export const fetch = async (ctx: BBContext) => { ctx.body = { multiTenancy: !!env.MULTI_TENANCY, cloud: !env.SELF_HOSTED, diff --git a/packages/worker/src/api/controllers/system/status.js b/packages/worker/src/api/controllers/system/status.ts similarity index 54% rename from packages/worker/src/api/controllers/system/status.js rename to packages/worker/src/api/controllers/system/status.ts index 9d2bd6ecda..b763a67d4f 100644 --- a/packages/worker/src/api/controllers/system/status.js +++ b/packages/worker/src/api/controllers/system/status.ts @@ -1,7 +1,8 @@ -const accounts = require("@budibase/backend-core/accounts") -const env = require("../../../environment") +import { accounts } from "@budibase/backend-core" +import env from "../../../environment" +import { BBContext } from "@budibase/types" -exports.fetch = async ctx => { +export const fetch = async (ctx: BBContext) => { if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { const status = await accounts.getStatus() ctx.body = status diff --git a/packages/worker/src/api/controllers/system/tenants.ts b/packages/worker/src/api/controllers/system/tenants.ts index d6e6261c22..8fd1446bcb 100644 --- a/packages/worker/src/api/controllers/system/tenants.ts +++ b/packages/worker/src/api/controllers/system/tenants.ts @@ -1,60 +1,17 @@ -const { StaticDatabases, doWithDB } = require("@budibase/backend-core/db") -const { getTenantId } = require("@budibase/backend-core/tenancy") -const { deleteTenant } = require("@budibase/backend-core/deprovision") +import { BBContext } from "@budibase/types" +import { deprovisioning } from "@budibase/backend-core" import { quotas } from "@budibase/pro" -export const exists = async (ctx: any) => { - const tenantId = ctx.request.params - ctx.body = { - exists: await doWithDB( - StaticDatabases.PLATFORM_INFO.name, - async (db: any) => { - let exists = false - try { - const tenantsDoc = await db.get( - StaticDatabases.PLATFORM_INFO.docs.tenants - ) - if (tenantsDoc) { - exists = tenantsDoc.tenantIds.indexOf(tenantId) !== -1 - } - } catch (err) { - // if error it doesn't exist - } - return exists - } - ), - } -} +const _delete = async (ctx: BBContext) => { + const user = ctx.user! + const tenantId = ctx.params.tenantId -export const fetch = async (ctx: any) => { - ctx.body = await doWithDB( - StaticDatabases.PLATFORM_INFO.name, - async (db: any) => { - let tenants = [] - try { - const tenantsDoc = await db.get( - StaticDatabases.PLATFORM_INFO.docs.tenants - ) - if (tenantsDoc) { - tenants = tenantsDoc.tenantIds - } - } catch (err) { - // if error it doesn't exist - } - return tenants - } - ) -} - -const _delete = async (ctx: any) => { - const tenantId = getTenantId() - - if (ctx.params.tenantId !== tenantId) { - ctx.throw(403, "Unauthorized") + if (tenantId !== user.tenantId) { + ctx.throw(403, "Tenant ID does not match current user") } try { - await deleteTenant(tenantId) + await deprovisioning.deleteTenant(tenantId) await quotas.bustCache() ctx.status = 204 } catch (err) { diff --git a/packages/worker/src/api/index.ts b/packages/worker/src/api/index.ts index 22ff159dff..28fbbdb4b5 100644 --- a/packages/worker/src/api/index.ts +++ b/packages/worker/src/api/index.ts @@ -7,11 +7,12 @@ import { errors, auth, middleware } from "@budibase/backend-core" import { APIError } from "@budibase/types" const PUBLIC_ENDPOINTS = [ - // old deprecated endpoints kept for backwards compat + // deprecated single tenant sso callback { route: "/api/admin/auth/google/callback", method: "GET", }, + // deprecated single tenant sso callback { route: "/api/admin/auth/oidc/callback", method: "GET", @@ -51,10 +52,12 @@ const PUBLIC_ENDPOINTS = [ route: "api/system/status", method: "GET", }, + // TODO: This should be an internal api { route: "/api/global/users/tenant/:id", method: "GET", }, + // TODO: This should be an internal api { route: "/api/system/restored", method: "POST", @@ -62,17 +65,37 @@ const PUBLIC_ENDPOINTS = [ ] const NO_TENANCY_ENDPOINTS = [ - ...PUBLIC_ENDPOINTS, + // system endpoints are not specific to any tenant { route: "/api/system", method: "ALL", }, + // tenant is determined in request body + // used for creating the tenant { - route: "/api/global/users/self", + route: "/api/global/users/init", + method: "POST", + }, + // deprecated single tenant sso callback + { + route: "/api/admin/auth/google/callback", method: "GET", }, + // deprecated single tenant sso callback { - route: "/api/global/self", + route: "/api/admin/auth/oidc/callback", + method: "GET", + }, + // tenant is determined from code in redis + { + route: "/api/global/users/invite/accept", + method: "POST", + }, + // global user search - no tenancy + // :id is user id + // TODO: this should really be `/api/system/users/:id` + { + route: "/api/global/users/tenant/:id", method: "GET", }, ] diff --git a/packages/worker/src/api/routes/global/auth.js b/packages/worker/src/api/routes/global/auth.js index 1c292cdc7f..674279a6f4 100644 --- a/packages/worker/src/api/routes/global/auth.js +++ b/packages/worker/src/api/routes/global/auth.js @@ -2,7 +2,6 @@ const Router = require("@koa/router") const authController = require("../../controllers/global/auth") const { joiValidator } = require("@budibase/backend-core/auth") const Joi = require("joi") -const { updateTenantId } = require("@budibase/backend-core/tenancy") const router = new Router() @@ -29,43 +28,28 @@ function buildResetUpdateValidation() { }).required().unknown(false)) } -function updateTenant(ctx, next) { - if (ctx.params) { - updateTenantId(ctx.params.tenantId) - } - return next() -} - router .post( "/api/global/auth/:tenantId/login", buildAuthValidation(), - updateTenant, authController.authenticate ) .post( "/api/global/auth/:tenantId/reset", buildResetValidation(), - updateTenant, authController.reset ) .post( "/api/global/auth/:tenantId/reset/update", buildResetUpdateValidation(), - updateTenant, authController.resetUpdate ) .post("/api/global/auth/logout", authController.logout) .post("/api/global/auth/init", authController.setInitInfo) .get("/api/global/auth/init", authController.getInitInfo) - .get( - "/api/global/auth/:tenantId/google", - updateTenant, - authController.googlePreAuth - ) + .get("/api/global/auth/:tenantId/google", authController.googlePreAuth) .get( "/api/global/auth/:tenantId/datasource/:provider", - updateTenant, authController.datasourcePreAuth ) // single tenancy endpoint @@ -75,29 +59,19 @@ router authController.datasourceAuth ) // multi-tenancy endpoint - .get( - "/api/global/auth/:tenantId/google/callback", - updateTenant, - authController.googleAuth - ) + .get("/api/global/auth/:tenantId/google/callback", authController.googleAuth) .get( "/api/global/auth/:tenantId/datasource/:provider/callback", - updateTenant, authController.datasourceAuth ) .get( "/api/global/auth/:tenantId/oidc/configs/:configId", - updateTenant, authController.oidcPreAuth ) // single tenancy endpoint .get("/api/global/auth/oidc/callback", authController.oidcAuth) // multi-tenancy endpoint - .get( - "/api/global/auth/:tenantId/oidc/callback", - updateTenant, - authController.oidcAuth - ) + .get("/api/global/auth/:tenantId/oidc/callback", authController.oidcAuth) // deprecated - used by the default system before tenancy .get("/api/admin/auth/google/callback", authController.googleAuth) .get("/api/admin/auth/oidc/callback", authController.oidcAuth) diff --git a/packages/worker/src/api/routes/global/tests/configs.spec.ts b/packages/worker/src/api/routes/global/tests/configs.spec.ts index 31510c03dd..99376988da 100644 --- a/packages/worker/src/api/routes/global/tests/configs.spec.ts +++ b/packages/worker/src/api/routes/global/tests/configs.spec.ts @@ -235,7 +235,7 @@ describe("configs", () => { expect(events.org.nameUpdated).toBeCalledTimes(1) expect(events.org.logoUpdated).toBeCalledTimes(1) expect(events.org.platformURLUpdated).toBeCalledTimes(1) - config.modeAccount() + config.modeCloud() }) }) @@ -257,7 +257,7 @@ describe("configs", () => { expect(events.org.nameUpdated).toBeCalledTimes(1) expect(events.org.logoUpdated).toBeCalledTimes(1) expect(events.org.platformURLUpdated).toBeCalledTimes(1) - config.modeAccount() + config.modeCloud() }) }) }) diff --git a/packages/worker/src/api/routes/global/tests/license.spec.ts b/packages/worker/src/api/routes/global/tests/license.spec.ts new file mode 100644 index 0000000000..a1566730ea --- /dev/null +++ b/packages/worker/src/api/routes/global/tests/license.spec.ts @@ -0,0 +1,28 @@ +import { TestConfiguration, API } from "../../../../tests" + +describe("/api/global/license", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("POST /api/global/license/activate", () => {}) + + describe("POST /api/global/license/refresh", () => {}) + + describe("GET /api/global/license/info", () => {}) + + describe("DELETE /api/global/license/info", () => {}) + + describe("GET /api/global/license/usage", () => {}) +}) diff --git a/packages/worker/src/api/routes/global/tests/roles.spec.ts b/packages/worker/src/api/routes/global/tests/roles.spec.ts new file mode 100644 index 0000000000..c9d6d085a5 --- /dev/null +++ b/packages/worker/src/api/routes/global/tests/roles.spec.ts @@ -0,0 +1,24 @@ +import { TestConfiguration, API } from "../../../../tests" + +describe("/api/global/roles", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("GET /api/global/roles", () => {}) + + describe("GET /api/global/roles/:appId", () => {}) + + describe("DELETE /api/global/roles/:appId", () => {}) +}) diff --git a/packages/worker/src/api/routes/global/tests/templates.spec.ts b/packages/worker/src/api/routes/global/tests/templates.spec.ts new file mode 100644 index 0000000000..8986639637 --- /dev/null +++ b/packages/worker/src/api/routes/global/tests/templates.spec.ts @@ -0,0 +1,32 @@ +import { TestConfiguration, API } from "../../../../tests" + +describe("/api/global/template", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("GET /api/global/template/definitions", () => {}) + + describe("POST /api/global/template", () => {}) + + describe("GET /api/global/template", () => {}) + + describe("GET /api/global/template/:type", () => {}) + + describe("GET /api/global/template/:ownerId", () => {}) + + describe("GET /api/global/template/:id", () => {}) + + describe("DELETE /api/global/template/:id/:rev", () => {}) +}) diff --git a/packages/worker/src/api/routes/global/tests/workspaces.spec.ts b/packages/worker/src/api/routes/global/tests/workspaces.spec.ts new file mode 100644 index 0000000000..f7ab1d9a7a --- /dev/null +++ b/packages/worker/src/api/routes/global/tests/workspaces.spec.ts @@ -0,0 +1,26 @@ +import { TestConfiguration, API } from "../../../../tests" + +describe("/api/global/workspaces", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("GET /api/global/workspaces", () => {}) + + describe("DELETE /api/global/workspaces/:id", () => {}) + + describe("GET /api/global/workspaces", () => {}) + + describe("GET /api/global/workspaces/:id", () => {}) +}) diff --git a/packages/worker/src/api/routes/system/environment.js b/packages/worker/src/api/routes/system/environment.js deleted file mode 100644 index 3d34046317..0000000000 --- a/packages/worker/src/api/routes/system/environment.js +++ /dev/null @@ -1,8 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../../controllers/system/environment") - -const router = new Router() - -router.get("/api/system/environment", controller.fetch) - -module.exports = router diff --git a/packages/worker/src/api/routes/system/environment.ts b/packages/worker/src/api/routes/system/environment.ts new file mode 100644 index 0000000000..360ec7ed84 --- /dev/null +++ b/packages/worker/src/api/routes/system/environment.ts @@ -0,0 +1,8 @@ +import Router from "@koa/router" +import * as controller from "../../controllers/system/environment" + +const router = new Router() + +router.get("/api/system/environment", controller.fetch) + +export default router diff --git a/packages/worker/src/api/routes/system/status.js b/packages/worker/src/api/routes/system/status.js deleted file mode 100644 index 17d2f8a5a6..0000000000 --- a/packages/worker/src/api/routes/system/status.js +++ /dev/null @@ -1,8 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../../controllers/system/status") - -const router = new Router() - -router.get("/api/system/status", controller.fetch) - -module.exports = router diff --git a/packages/worker/src/api/routes/system/status.ts b/packages/worker/src/api/routes/system/status.ts new file mode 100644 index 0000000000..a5b393b421 --- /dev/null +++ b/packages/worker/src/api/routes/system/status.ts @@ -0,0 +1,8 @@ +import Router from "@koa/router" +import * as controller from "../../controllers/system/status" + +const router = new Router() + +router.get("/api/system/status", controller.fetch) + +export default router diff --git a/packages/worker/src/api/routes/system/tenants.js b/packages/worker/src/api/routes/system/tenants.js deleted file mode 100644 index 6247e76058..0000000000 --- a/packages/worker/src/api/routes/system/tenants.js +++ /dev/null @@ -1,12 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../../controllers/system/tenants") -const { adminOnly } = require("@budibase/backend-core/auth") - -const router = new Router() - -router - .get("/api/system/tenants/:tenantId/exists", controller.exists) - .get("/api/system/tenants", adminOnly, controller.fetch) - .delete("/api/system/tenants/:tenantId", adminOnly, controller.delete) - -module.exports = router diff --git a/packages/worker/src/api/routes/system/tenants.ts b/packages/worker/src/api/routes/system/tenants.ts new file mode 100644 index 0000000000..7feb73a234 --- /dev/null +++ b/packages/worker/src/api/routes/system/tenants.ts @@ -0,0 +1,13 @@ +import Router from "@koa/router" +import * as controller from "../../controllers/system/tenants" +import { middleware } from "@budibase/backend-core" + +const router = new Router() + +router.delete( + "/api/system/tenants/:tenantId", + middleware.adminOnly, + controller.delete +) + +export default router diff --git a/packages/worker/src/api/routes/system/tests/environment.spec.ts b/packages/worker/src/api/routes/system/tests/environment.spec.ts new file mode 100644 index 0000000000..5cc3b0c6f1 --- /dev/null +++ b/packages/worker/src/api/routes/system/tests/environment.spec.ts @@ -0,0 +1,30 @@ +import { TestConfiguration, API } from "../../../../tests" + +describe("/api/system/environment", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("GET /api/system/environment", () => { + it("returns the expected environment", async () => { + const env = await api.environment.getEnvironment() + expect(env.body).toEqual({ + cloud: true, + disableAccountPortal: false, + isDev: false, + multiTenancy: true, + }) + }) + }) +}) diff --git a/packages/worker/src/api/routes/system/tests/migrations.spec.ts b/packages/worker/src/api/routes/system/tests/migrations.spec.ts new file mode 100644 index 0000000000..0e3883f9ef --- /dev/null +++ b/packages/worker/src/api/routes/system/tests/migrations.spec.ts @@ -0,0 +1,64 @@ +const migrateFn = jest.fn() + +import { TestConfiguration, API } from "../../../../tests" + +jest.mock("../../../../migrations", () => { + return { + ...jest.requireActual("../../../../migrations"), + migrate: migrateFn, + } +}) + +describe("/api/system/migrations", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("POST /api/system/migrations/run", () => { + it("fails with no internal api key", async () => { + const res = await api.migrations.runMigrations({ + headers: {}, + status: 403, + }) + expect(res.text).toBe("Unauthorized - no public worker access") + expect(migrateFn).toBeCalledTimes(0) + }) + + it("runs migrations", async () => { + const res = await api.migrations.runMigrations() + expect(res.text).toBe("OK") + expect(migrateFn).toBeCalledTimes(1) + }) + }) + + describe("DELETE /api/system/migrations/definitions", () => { + it("fails with no internal api key", async () => { + const res = await api.migrations.getMigrationDefinitions({ + headers: {}, + status: 403, + }) + expect(res.text).toBe("Unauthorized - no public worker access") + }) + + it("returns definitions", async () => { + const res = await api.migrations.getMigrationDefinitions() + expect(res.body).toEqual([ + { + name: "global_info_sync_users", + type: "global", + }, + ]) + }) + }) +}) diff --git a/packages/worker/src/api/routes/system/tests/restore.spec.ts b/packages/worker/src/api/routes/system/tests/restore.spec.ts new file mode 100644 index 0000000000..1da9389608 --- /dev/null +++ b/packages/worker/src/api/routes/system/tests/restore.spec.ts @@ -0,0 +1,37 @@ +import { TestConfiguration, API } from "../../../../tests" + +describe("/api/system/restore", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("POST /api/global/restore", () => { + it("doesn't allow restore in cloud", async () => { + const res = await api.restore.restored({ status: 405 }) + expect(res.body).toEqual({ + message: "This operation is not allowed in cloud.", + status: 405, + }) + }) + + it("restores in self host", async () => { + config.modeSelf() + const res = await api.restore.restored() + expect(res.body).toEqual({ + message: "System prepared after restore.", + }) + config.modeCloud() + }) + }) +}) diff --git a/packages/worker/src/api/routes/system/tests/status.spec.ts b/packages/worker/src/api/routes/system/tests/status.spec.ts new file mode 100644 index 0000000000..98308556d1 --- /dev/null +++ b/packages/worker/src/api/routes/system/tests/status.spec.ts @@ -0,0 +1,49 @@ +import { TestConfiguration, API } from "../../../../tests" +import { accounts } from "@budibase/backend-core" +import { mocks } from "@budibase/backend-core/tests" + +describe("/api/system/status", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("GET /api/system/status", () => { + it("returns status in self host", async () => { + config.modeSelf() + const res = await api.status.getStatus() + expect(res.body).toEqual({ + health: { + passing: true, + }, + }) + expect(accounts.getStatus).toBeCalledTimes(0) + config.modeCloud() + }) + + it("returns status in cloud", async () => { + const value = { + health: { + passing: false, + }, + } + + mocks.accounts.getStatus.mockReturnValueOnce(value) + + const res = await api.status.getStatus() + + expect(accounts.getStatus).toBeCalledTimes(1) + expect(res.body).toEqual(value) + }) + }) +}) diff --git a/packages/worker/src/api/routes/system/tests/tenants.spec.ts b/packages/worker/src/api/routes/system/tests/tenants.spec.ts new file mode 100644 index 0000000000..0491d43e8c --- /dev/null +++ b/packages/worker/src/api/routes/system/tests/tenants.spec.ts @@ -0,0 +1,32 @@ +import { TestConfiguration, API } from "../../../../tests" + +describe("/api/global/workspaces", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe("DELETE /api/system/tenants/:tenantId", () => { + it("allows deleting the current tenant", async () => { + const user = await config.createTenant() + await config.createSession(user) + const res = await api.tenants.delete(user.tenantId, { + headers: config.authHeaders(user), + }) + }) + + it("rejects deleting another tenant", () => {}) + + it("requires admin", () => {}) + }) +}) diff --git a/packages/worker/src/middleware/tests/tenancy.spec.ts b/packages/worker/src/middleware/tests/tenancy.spec.ts new file mode 100644 index 0000000000..04de41b9cf --- /dev/null +++ b/packages/worker/src/middleware/tests/tenancy.spec.ts @@ -0,0 +1,74 @@ +import { TestConfiguration, API, structures } from "../../tests" +import { constants } from "@budibase/backend-core" + +describe("tenancy middleware", () => { + const config = new TestConfiguration() + const api = new API(config) + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + it("should get tenant id from user", async () => { + const user = await config.createTenant() + await config.createSession(user) + const res = await api.self.getSelf(user) + expect(res.headers[constants.Headers.TENANT_ID]).toBe(user.tenantId) + }) + + it("should get tenant id from header", async () => { + const tenantId = structures.uuid() + const headers = { + [constants.Headers.TENANT_ID]: tenantId, + } + const res = await config.request + .get(`/api/global/configs/checklist`) + .set(headers) + expect(res.headers[constants.Headers.TENANT_ID]).toBe(tenantId) + }) + + it("should get tenant id from query param", async () => { + const tenantId = structures.uuid() + const res = await config.request.get( + `/api/global/configs/checklist?tenantId=${tenantId}` + ) + expect(res.headers[constants.Headers.TENANT_ID]).toBe(tenantId) + }) + + it("should get tenant id from subdomain", async () => { + const tenantId = structures.uuid() + const headers = { + host: `${tenantId}.localhost:10000`, + } + const res = await config.request + .get(`/api/global/configs/checklist`) + .set(headers) + expect(res.headers[constants.Headers.TENANT_ID]).toBe(tenantId) + }) + + it("should get tenant id from path variable", async () => { + const user = await config.createTenant() + const res = await config.request + .post(`/api/global/auth/${user.tenantId}/login`) + .send({ + username: user.email, + password: user.password, + }) + expect(res.headers[constants.Headers.TENANT_ID]).toBe(user.tenantId) + }) + + it("should throw when no tenant id is found", async () => { + const res = await config.request.get(`/api/global/configs/checklist`) + expect(res.status).toBe(403) + expect(res.text).toBe("Tenant id not set") + expect(res.headers[constants.Headers.TENANT_ID]).toBe(undefined) + }) +}) diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index 746e1ccf1b..31305066da 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -2,6 +2,7 @@ import "./mocks" import dbConfig from "../db" dbConfig.init() import env from "../environment" +import { env as coreEnv } from "@budibase/backend-core" import controllers from "./controllers" const supertest = require("supertest") import { Configs } from "../constants" @@ -12,13 +13,13 @@ import { Headers, sessions, auth, + constants, } from "@budibase/backend-core" -import { TENANT_ID, TENANT_1, CSRF_TOKEN } from "./structures" -import structures from "./structures" +import structures, { TENANT_ID, TENANT_1, CSRF_TOKEN } from "./structures" import { CreateUserResponse, User, AuthToken } from "@budibase/types" enum Mode { - ACCOUNT = "account", + CLOUD = "cloud", SELF = "self", } @@ -31,11 +32,11 @@ class TestConfiguration { constructor( opts: { openServer: boolean; mode: Mode } = { openServer: true, - mode: Mode.ACCOUNT, + mode: Mode.CLOUD, } ) { - if (opts.mode === Mode.ACCOUNT) { - this.modeAccount() + if (opts.mode === Mode.CLOUD) { + this.modeCloud() } else if (opts.mode === Mode.SELF) { this.modeSelf() } @@ -54,20 +55,24 @@ class TestConfiguration { // MODES - modeAccount = () => { - env.SELF_HOSTED = false - // @ts-ignore - env.MULTI_TENANCY = true - // @ts-ignore - env.DISABLE_ACCOUNT_PORTAL = false + setMultiTenancy = (value: boolean) => { + env._set("MULTI_TENANCY", value) + coreEnv._set("MULTI_TENANCY", value) + } + + setSelfHosted = (value: boolean) => { + env._set("SELF_HOSTED", value) + coreEnv._set("SELF_HOSTED", value) + } + + modeCloud = () => { + this.setSelfHosted(false) + this.setMultiTenancy(true) } modeSelf = () => { - env.SELF_HOSTED = true - // @ts-ignore - env.MULTI_TENANCY = false - // @ts-ignore - env.DISABLE_ACCOUNT_PORTAL = true + this.setSelfHosted(true) + this.setMultiTenancy(false) } // UTILS @@ -114,6 +119,16 @@ class TestConfiguration { // TENANCY + createTenant = async (tenantId?: string): Promise => { + // create user / new tenant + if (!tenantId) { + tenantId = structures.uuid() + } + return tenancy.doInTenant(tenantId, async () => { + return this.createUser() + }) + } + getTenantId() { try { return tenancy.getTenantId() @@ -179,6 +194,10 @@ class TestConfiguration { } } + internalAPIHeaders() { + return { [constants.Headers.API_KEY]: env.INTERNAL_API_KEY } + } + async getUser(email: string): Promise { return tenancy.doInTenant(this.getTenantId(), () => { return users.getGlobalUserByEmail(email) diff --git a/packages/worker/src/tests/api/base.ts b/packages/worker/src/tests/api/base.ts new file mode 100644 index 0000000000..c1263ed5cb --- /dev/null +++ b/packages/worker/src/tests/api/base.ts @@ -0,0 +1,16 @@ +import TestConfiguration from "../TestConfiguration" + +export interface TestAPIOpts { + headers?: any + status?: number +} + +export abstract class TestAPI { + config: TestConfiguration + request: any + + protected constructor(config: TestConfiguration) { + this.config = config + this.request = config.request + } +} diff --git a/packages/worker/src/tests/api/environment.ts b/packages/worker/src/tests/api/environment.ts new file mode 100644 index 0000000000..9847c2e369 --- /dev/null +++ b/packages/worker/src/tests/api/environment.ts @@ -0,0 +1,18 @@ +import TestConfiguration from "../TestConfiguration" + +export class EnvironmentAPI { + config: TestConfiguration + request: any + + constructor(config: TestConfiguration) { + this.config = config + this.request = config.request + } + + getEnvironment = () => { + return this.request + .get(`/api/system/environment`) + .expect("Content-Type", /json/) + .expect(200) + } +} diff --git a/packages/worker/src/tests/api/index.ts b/packages/worker/src/tests/api/index.ts index a12e489a78..bc0271b9c6 100644 --- a/packages/worker/src/tests/api/index.ts +++ b/packages/worker/src/tests/api/index.ts @@ -5,6 +5,11 @@ import { ConfigAPI } from "./configs" import { EmailAPI } from "./email" import { SelfAPI } from "./self" import { UserAPI } from "./users" +import { EnvironmentAPI } from "./environment" +import { MigrationAPI } from "./migrations" +import { StatusAPI } from "./status" +import { RestoreAPI } from "./restore" +import { TenantAPI } from "./tenants" export default class API { accounts: AccountAPI @@ -13,6 +18,11 @@ export default class API { emails: EmailAPI self: SelfAPI users: UserAPI + environment: EnvironmentAPI + migrations: MigrationAPI + status: StatusAPI + restore: RestoreAPI + tenants: TenantAPI constructor(config: TestConfiguration) { this.accounts = new AccountAPI(config) @@ -21,5 +31,10 @@ export default class API { this.emails = new EmailAPI(config) this.self = new SelfAPI(config) this.users = new UserAPI(config) + this.environment = new EnvironmentAPI(config) + this.migrations = new MigrationAPI(config) + this.status = new StatusAPI(config) + this.restore = new RestoreAPI(config) + this.tenants = new TenantAPI(config) } } diff --git a/packages/worker/src/tests/api/migrations.ts b/packages/worker/src/tests/api/migrations.ts new file mode 100644 index 0000000000..6038cbd5d8 --- /dev/null +++ b/packages/worker/src/tests/api/migrations.ts @@ -0,0 +1,22 @@ +import TestConfiguration from "../TestConfiguration" +import { TestAPI, TestAPIOpts } from "./base" + +export class MigrationAPI extends TestAPI { + constructor(config: TestConfiguration) { + super(config) + } + + runMigrations = (opts?: TestAPIOpts) => { + return this.request + .post(`/api/system/migrations/run`) + .set(opts?.headers ? opts.headers : this.config.internalAPIHeaders()) + .expect(opts?.status ? opts.status : 200) + } + + getMigrationDefinitions = (opts?: TestAPIOpts) => { + return this.request + .get(`/api/system/migrations/definitions`) + .set(opts?.headers ? opts.headers : this.config.internalAPIHeaders()) + .expect(opts?.status ? opts.status : 200) + } +} diff --git a/packages/worker/src/tests/api/restore.ts b/packages/worker/src/tests/api/restore.ts new file mode 100644 index 0000000000..6069c20185 --- /dev/null +++ b/packages/worker/src/tests/api/restore.ts @@ -0,0 +1,14 @@ +import TestConfiguration from "../TestConfiguration" +import { TestAPI, TestAPIOpts } from "./base" + +export class RestoreAPI extends TestAPI { + constructor(config: TestConfiguration) { + super(config) + } + + restored = (opts?: TestAPIOpts) => { + return this.request + .post(`/api/system/restored`) + .expect(opts?.status ? opts.status : 200) + } +} diff --git a/packages/worker/src/tests/api/self.ts b/packages/worker/src/tests/api/self.ts index b1cd4a48c6..da634e0db3 100644 --- a/packages/worker/src/tests/api/self.ts +++ b/packages/worker/src/tests/api/self.ts @@ -18,4 +18,12 @@ export class SelfAPI { .expect("Content-Type", /json/) .expect(200) } + + getSelf = (user: User) => { + return this.request + .get(`/api/global/self`) + .set(this.config.authHeaders(user)) + .expect("Content-Type", /json/) + .expect(200) + } } diff --git a/packages/worker/src/tests/api/status.ts b/packages/worker/src/tests/api/status.ts new file mode 100644 index 0000000000..c42060631f --- /dev/null +++ b/packages/worker/src/tests/api/status.ts @@ -0,0 +1,15 @@ +import TestConfiguration from "../TestConfiguration" + +export class StatusAPI { + config: TestConfiguration + request: any + + constructor(config: TestConfiguration) { + this.config = config + this.request = config.request + } + + getStatus = () => { + return this.request.get(`/api/system/status`).expect(200) + } +} diff --git a/packages/worker/src/tests/api/tenants.ts b/packages/worker/src/tests/api/tenants.ts new file mode 100644 index 0000000000..d4d47a6e4f --- /dev/null +++ b/packages/worker/src/tests/api/tenants.ts @@ -0,0 +1,15 @@ +import TestConfiguration from "../TestConfiguration" +import { TestAPI, TestAPIOpts } from "./base" + +export class TenantAPI extends TestAPI { + constructor(config: TestConfiguration) { + super(config) + } + + delete = (tenantId: string, opts?: TestAPIOpts) => { + return this.request + .delete(`/api/system/tenants/${tenantId}`) + .set(opts?.headers) + .expect(opts?.status ? opts.status : 200) + } +} diff --git a/packages/worker/src/tests/index.ts b/packages/worker/src/tests/index.ts index 6ab1e83955..e58040b468 100644 --- a/packages/worker/src/tests/index.ts +++ b/packages/worker/src/tests/index.ts @@ -1,10 +1,14 @@ +import { generator } from "@budibase/backend-core/tests" import TestConfiguration from "./TestConfiguration" import structures from "./structures" import mocks from "./mocks" import API from "./api" +import { v4 as uuid } from "uuid" const pkg = { structures, + generator, + uuid, TENANT_1: structures.TENANT_1, mocks, TestConfiguration, diff --git a/packages/worker/src/tests/structures/accounts.ts b/packages/worker/src/tests/structures/accounts.ts deleted file mode 100644 index df6b993684..0000000000 --- a/packages/worker/src/tests/structures/accounts.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Account, AuthType, Hosting, CloudAccount } from "@budibase/types" -import { v4 as uuid } from "uuid" -import { utils } from "@budibase/backend-core" - -export const account = (): Account => { - return { - email: `${uuid()}@test.com`, - tenantId: utils.newid(), - hosting: Hosting.SELF, - authType: AuthType.SSO, - accountId: uuid(), - createdAt: Date.now(), - verified: true, - verificationSent: true, - tier: "FREE", - } -} - -export const cloudAccount = (): CloudAccount => { - return { - ...account(), - budibaseUserId: uuid(), - } -} diff --git a/packages/worker/src/tests/structures/index.ts b/packages/worker/src/tests/structures/index.ts index a3029b0105..3a4c3324df 100644 --- a/packages/worker/src/tests/structures/index.ts +++ b/packages/worker/src/tests/structures/index.ts @@ -1,16 +1,18 @@ +import { structures } from "@budibase/backend-core/tests" import configs from "./configs" import * as users from "./users" import * as groups from "./groups" -import * as accounts from "./accounts" +import { v4 as uuid } from "uuid" const TENANT_ID = "default" const TENANT_1 = "tenant1" const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306" const pkg = { + ...structures, + uuid, configs, users, - accounts, TENANT_ID, TENANT_1, CSRF_TOKEN, diff --git a/packages/worker/src/tests/structures/users.ts b/packages/worker/src/tests/structures/users.ts index 4bf24ec780..bef9f38586 100644 --- a/packages/worker/src/tests/structures/users.ts +++ b/packages/worker/src/tests/structures/users.ts @@ -5,6 +5,7 @@ import { v4 as uuid } from "uuid" export const newEmail = () => { return `${uuid()}@test.com` } + export const user = (userProps?: any): User => { return { email: newEmail(), From 80b823f6cf92270c7d8e7cdca84f31293a995493 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 11 Nov 2022 15:43:41 +0000 Subject: [PATCH 03/15] Test fixes --- .../backend-core/src/middleware/matchers.ts | 1 - packages/backend-core/src/tenancy/tenancy.ts | 1 + .../tests/utilities/structures/common.ts | 6 ++ packages/types/src/api/web/user.ts | 6 ++ .../src/api/controllers/global/users.ts | 4 +- .../src/api/controllers/system/tenants.ts | 2 +- .../src/api/routes/global/tests/auth.spec.ts | 17 ++-- .../api/routes/global/tests/configs.spec.ts | 7 +- .../src/api/routes/global/tests/email.spec.ts | 7 +- .../api/routes/global/tests/license.spec.ts | 9 +- .../api/routes/global/tests/realEmail.spec.ts | 5 +- .../src/api/routes/global/tests/roles.spec.ts | 9 +- .../src/api/routes/global/tests/self.spec.ts | 7 +- .../api/routes/global/tests/templates.spec.ts | 9 +- .../src/api/routes/global/tests/users.spec.ts | 94 ++++++++++--------- .../routes/global/tests/workspaces.spec.ts | 9 +- .../api/routes/system/tests/accounts.spec.ts | 11 +-- .../routes/system/tests/environment.spec.ts | 5 +- .../routes/system/tests/migrations.spec.ts | 11 +-- .../api/routes/system/tests/restore.spec.ts | 7 +- .../api/routes/system/tests/status.spec.ts | 7 +- .../api/routes/system/tests/tenants.spec.ts | 41 ++++++-- .../src/middleware/tests/tenancy.spec.ts | 5 +- .../worker/src/tests/TestConfiguration.ts | 81 +++++++++++----- packages/worker/src/tests/api/accounts.ts | 13 +-- packages/worker/src/tests/api/auth.ts | 9 +- packages/worker/src/tests/api/configs.ts | 9 +- packages/worker/src/tests/api/email.ts | 9 +- packages/worker/src/tests/api/environment.ts | 9 +- packages/worker/src/tests/api/self.ts | 9 +- packages/worker/src/tests/api/status.ts | 9 +- packages/worker/src/tests/api/tenants.ts | 2 +- packages/worker/src/tests/api/users.ts | 44 +++++++-- 33 files changed, 285 insertions(+), 189 deletions(-) diff --git a/packages/backend-core/src/middleware/matchers.ts b/packages/backend-core/src/middleware/matchers.ts index e40a9a61b3..efbdec2dbe 100644 --- a/packages/backend-core/src/middleware/matchers.ts +++ b/packages/backend-core/src/middleware/matchers.ts @@ -21,7 +21,6 @@ export const buildMatcherRegex = ( const suffix = match.endsWith("/") ? "/" : "" const pattern = "/.*" + suffix route = route.replace(match, pattern) - console.log(route) } } diff --git a/packages/backend-core/src/tenancy/tenancy.ts b/packages/backend-core/src/tenancy/tenancy.ts index 66d170d3f8..99955ca321 100644 --- a/packages/backend-core/src/tenancy/tenancy.ts +++ b/packages/backend-core/src/tenancy/tenancy.ts @@ -225,6 +225,7 @@ export const getTenantIDFromCtx = ( } } + // path if (isAllowed(TenantResolutionStrategy.PATH)) { // params - have to parse manually due to koa-router not run yet const match = ctx.matched.find( diff --git a/packages/backend-core/tests/utilities/structures/common.ts b/packages/backend-core/tests/utilities/structures/common.ts index 51ae220254..05b879f36b 100644 --- a/packages/backend-core/tests/utilities/structures/common.ts +++ b/packages/backend-core/tests/utilities/structures/common.ts @@ -1 +1,7 @@ +import { v4 as uuid } from "uuid" + export { v4 as uuid } from "uuid" + +export const email = () => { + return `${uuid()}@test.com` +} diff --git a/packages/types/src/api/web/user.ts b/packages/types/src/api/web/user.ts index 98ffcdf360..0ebe4ccce8 100644 --- a/packages/types/src/api/web/user.ts +++ b/packages/types/src/api/web/user.ts @@ -51,3 +51,9 @@ export interface SearchUsersRequest { appId?: string userIds?: string[] } + +export interface CreateAdminUserRequest { + email: string + password: string + tenantId: string +} diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index ea1df5b45a..7edb1b710a 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -5,6 +5,7 @@ import { BulkUserRequest, BulkUserResponse, CloudAccount, + CreateAdminUserRequest, InviteUserRequest, InviteUsersRequest, SearchUsersRequest, @@ -67,7 +68,8 @@ const parseBooleanParam = (param: any) => { } export const adminUser = async (ctx: any) => { - const { email, password, tenantId } = ctx.request.body + const { email, password, tenantId } = ctx.request + .body as CreateAdminUserRequest await tenancy.doInTenant(tenantId, async () => { // account portal sends a pre-hashed password - honour param to prevent double hashing const hashPassword = parseBooleanParam(ctx.request.query.hashPassword) diff --git a/packages/worker/src/api/controllers/system/tenants.ts b/packages/worker/src/api/controllers/system/tenants.ts index 8fd1446bcb..6916049534 100644 --- a/packages/worker/src/api/controllers/system/tenants.ts +++ b/packages/worker/src/api/controllers/system/tenants.ts @@ -11,8 +11,8 @@ const _delete = async (ctx: BBContext) => { } try { - await deprovisioning.deleteTenant(tenantId) await quotas.bustCache() + await deprovisioning.deleteTenant(tenantId) ctx.status = 204 } catch (err) { ctx.log.error(err) diff --git a/packages/worker/src/api/routes/global/tests/auth.spec.ts b/packages/worker/src/api/routes/global/tests/auth.spec.ts index 69fa1b223c..45c8a62cc7 100644 --- a/packages/worker/src/api/routes/global/tests/auth.spec.ts +++ b/packages/worker/src/api/routes/global/tests/auth.spec.ts @@ -1,11 +1,10 @@ jest.mock("nodemailer") -import { TestConfiguration, mocks, API } from "../../../../tests" +import { TestConfiguration, mocks } from "../../../../tests" const sendMailMock = mocks.email.mock() import { events } from "@budibase/backend-core" describe("/api/global/auth", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -20,12 +19,14 @@ describe("/api/global/auth", () => { }) it("should logout", async () => { - await api.auth.logout() + await config.api.auth.logout() expect(events.auth.logout).toBeCalledTimes(1) }) it("should be able to generate password reset email", async () => { - const { res, code } = await api.auth.requestPasswordReset(sendMailMock) + const { res, code } = await config.api.auth.requestPasswordReset( + sendMailMock + ) const user = await config.getUser("test@test.com") expect(res.body).toEqual({ @@ -39,11 +40,11 @@ describe("/api/global/auth", () => { }) it("should allow resetting user password with code", async () => { - const { code } = await api.auth.requestPasswordReset(sendMailMock) + const { code } = await config.api.auth.requestPasswordReset(sendMailMock) const user = await config.getUser("test@test.com") delete user.password - const res = await api.auth.updatePassword(code) + const res = await config.api.auth.updatePassword(code) expect(res.body).toEqual({ message: "password reset successfully." }) expect(events.user.passwordReset).toBeCalledTimes(1) @@ -80,7 +81,7 @@ describe("/api/global/auth", () => { describe("oidc configs", () => { it("should load strategy and delegate to passport", async () => { - await api.configs.getOIDCConfig(configId) + await config.api.configs.getOIDCConfig(configId) expect(passportSpy).toBeCalledWith(mockStrategyReturn, { scope: ["profile", "email", "offline_access"], @@ -91,7 +92,7 @@ describe("/api/global/auth", () => { describe("oidc callback", () => { it("should load strategy and delegate to passport", async () => { - await api.configs.OIDCCallback(configId) + await config.api.configs.OIDCCallback(configId) expect(passportSpy).toBeCalledWith( mockStrategyReturn, diff --git a/packages/worker/src/api/routes/global/tests/configs.spec.ts b/packages/worker/src/api/routes/global/tests/configs.spec.ts index 99376988da..be4308c7b2 100644 --- a/packages/worker/src/api/routes/global/tests/configs.spec.ts +++ b/packages/worker/src/api/routes/global/tests/configs.spec.ts @@ -1,12 +1,11 @@ // mock the email system jest.mock("nodemailer") -import { TestConfiguration, structures, mocks, API } from "../../../../tests" +import { TestConfiguration, structures, mocks } from "../../../../tests" mocks.email.mock() import { Configs, events } from "@budibase/backend-core" describe("configs", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -28,7 +27,7 @@ describe("configs", () => { _rev, } - const res = await api.configs.saveConfig(data) + const res = await config.api.configs.saveConfig(data) return { ...data, @@ -266,7 +265,7 @@ describe("configs", () => { it("should return the correct checklist status based on the state of the budibase installation", async () => { await config.saveSmtpConfig() - const res = await api.configs.getConfigChecklist() + const res = await config.api.configs.getConfigChecklist() const checklist = res.body expect(checklist.apps.checked).toBeFalsy() diff --git a/packages/worker/src/api/routes/global/tests/email.spec.ts b/packages/worker/src/api/routes/global/tests/email.spec.ts index 608f4094f8..9e65cda3c5 100644 --- a/packages/worker/src/api/routes/global/tests/email.spec.ts +++ b/packages/worker/src/api/routes/global/tests/email.spec.ts @@ -1,11 +1,10 @@ jest.mock("nodemailer") -import { TestConfiguration, mocks, API } from "../../../../tests" +import { TestConfiguration, mocks } from "../../../../tests" const sendMailMock = mocks.email.mock() import { EmailTemplatePurpose } from "../../../../constants" describe("/api/global/email", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -20,7 +19,9 @@ describe("/api/global/email", () => { await config.saveSmtpConfig() await config.saveSettingsConfig() - const res = await api.emails.sendEmail(EmailTemplatePurpose.INVITATION) + const res = await config.api.emails.sendEmail( + EmailTemplatePurpose.INVITATION + ) expect(res.body.message).toBeDefined() expect(sendMailMock).toHaveBeenCalled() diff --git a/packages/worker/src/api/routes/global/tests/license.spec.ts b/packages/worker/src/api/routes/global/tests/license.spec.ts index a1566730ea..b25b41adb9 100644 --- a/packages/worker/src/api/routes/global/tests/license.spec.ts +++ b/packages/worker/src/api/routes/global/tests/license.spec.ts @@ -1,8 +1,9 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" + +// TODO describe("/api/global/license", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -16,7 +17,9 @@ describe("/api/global/license", () => { jest.clearAllMocks() }) - describe("POST /api/global/license/activate", () => {}) + describe("POST /api/global/license/activate", () => { + it("activates license", () => {}) + }) describe("POST /api/global/license/refresh", () => {}) diff --git a/packages/worker/src/api/routes/global/tests/realEmail.spec.ts b/packages/worker/src/api/routes/global/tests/realEmail.spec.ts index 135367e0d8..1c180be75d 100644 --- a/packages/worker/src/api/routes/global/tests/realEmail.spec.ts +++ b/packages/worker/src/api/routes/global/tests/realEmail.spec.ts @@ -1,4 +1,4 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" import { EmailTemplatePurpose } from "../../../../constants" const nodemailer = require("nodemailer") const fetch = require("node-fetch") @@ -8,7 +8,6 @@ jest.setTimeout(30000) describe("/api/global/email", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -35,7 +34,7 @@ describe("/api/global/email", () => { await Promise.race([config.saveEtherealSmtpConfig(), timeout()]) await Promise.race([config.saveSettingsConfig(), timeout()]) - const res = await api.emails.sendEmail(purpose).timeout(20000) + const res = await config.api.emails.sendEmail(purpose).timeout(20000) // ethereal hiccup, can't test right now if (res.status >= 300) { return diff --git a/packages/worker/src/api/routes/global/tests/roles.spec.ts b/packages/worker/src/api/routes/global/tests/roles.spec.ts index c9d6d085a5..516c3433ab 100644 --- a/packages/worker/src/api/routes/global/tests/roles.spec.ts +++ b/packages/worker/src/api/routes/global/tests/roles.spec.ts @@ -1,8 +1,9 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" + +// TODO describe("/api/global/roles", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -16,7 +17,9 @@ describe("/api/global/roles", () => { jest.clearAllMocks() }) - describe("GET /api/global/roles", () => {}) + describe("GET /api/global/roles", () => { + it("retrieves roles", () => {}) + }) describe("GET /api/global/roles/:appId", () => {}) diff --git a/packages/worker/src/api/routes/global/tests/self.spec.ts b/packages/worker/src/api/routes/global/tests/self.spec.ts index 5640bab3ce..d253a7f24e 100644 --- a/packages/worker/src/api/routes/global/tests/self.spec.ts +++ b/packages/worker/src/api/routes/global/tests/self.spec.ts @@ -1,10 +1,9 @@ jest.mock("nodemailer") -import { TestConfiguration, API, mocks } from "../../../../tests" +import { TestConfiguration, mocks } from "../../../../tests" import { events } from "@budibase/backend-core" describe("/api/global/self", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -24,7 +23,7 @@ describe("/api/global/self", () => { await config.createSession(user) delete user.password - const res = await api.self.updateSelf(user) + const res = await config.api.self.updateSelf(user) const dbUser = await config.getUser(user.email) user._rev = dbUser._rev @@ -40,7 +39,7 @@ describe("/api/global/self", () => { await config.createSession(user) user.password = "newPassword" - const res = await api.self.updateSelf(user) + const res = await config.api.self.updateSelf(user) const dbUser = await config.getUser(user.email) user._rev = dbUser._rev diff --git a/packages/worker/src/api/routes/global/tests/templates.spec.ts b/packages/worker/src/api/routes/global/tests/templates.spec.ts index 8986639637..d1c296643d 100644 --- a/packages/worker/src/api/routes/global/tests/templates.spec.ts +++ b/packages/worker/src/api/routes/global/tests/templates.spec.ts @@ -1,8 +1,9 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" + +// TODO describe("/api/global/template", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -16,7 +17,9 @@ describe("/api/global/template", () => { jest.clearAllMocks() }) - describe("GET /api/global/template/definitions", () => {}) + describe("GET /api/global/template/definitions", () => { + it("retrieves definitions", () => {}) + }) describe("POST /api/global/template", () => {}) diff --git a/packages/worker/src/api/routes/global/tests/users.spec.ts b/packages/worker/src/api/routes/global/tests/users.spec.ts index 218bc60800..3165cba315 100644 --- a/packages/worker/src/api/routes/global/tests/users.spec.ts +++ b/packages/worker/src/api/routes/global/tests/users.spec.ts @@ -6,14 +6,12 @@ import { mocks, structures, TENANT_1, - API, } from "../../../../tests" const sendMailMock = mocks.email.mock() import { events, tenancy } from "@budibase/backend-core" describe("/api/global/users", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -30,7 +28,10 @@ describe("/api/global/users", () => { describe("invite", () => { it("should be able to generate an invitation", async () => { const email = structures.users.newEmail() - const { code, res } = await api.users.sendUserInvite(sendMailMock, email) + const { code, res } = await config.api.users.sendUserInvite( + sendMailMock, + email + ) expect(res.body).toEqual({ message: "Invitation has been sent." }) expect(sendMailMock).toHaveBeenCalled() @@ -39,7 +40,7 @@ describe("/api/global/users", () => { }) it("should not be able to generate an invitation for existing user", async () => { - const { code, res } = await api.users.sendUserInvite( + const { code, res } = await config.api.users.sendUserInvite( sendMailMock, config.defaultUser!.email, 400 @@ -53,9 +54,12 @@ describe("/api/global/users", () => { it("should be able to create new user from invite", async () => { const email = structures.users.newEmail() - const { code } = await api.users.sendUserInvite(sendMailMock, email) + const { code } = await config.api.users.sendUserInvite( + sendMailMock, + email + ) - const res = await api.users.acceptInvite(code) + const res = await config.api.users.acceptInvite(code) expect(res.body._id).toBeDefined() const user = await config.getUser(email) @@ -74,7 +78,7 @@ describe("/api/global/users", () => { }) const request = [newUserInvite(), newUserInvite()] - const res = await api.users.sendMultiUserInvite(request) + const res = await config.api.users.sendMultiUserInvite(request) const body = res.body as InviteUsersResponse expect(body.successful.length).toBe(2) @@ -86,7 +90,7 @@ describe("/api/global/users", () => { it("should not be able to generate an invitation for existing user", async () => { const request = [{ email: config.defaultUser!.email, userInfo: {} }] - const res = await api.users.sendMultiUserInvite(request) + const res = await config.api.users.sendMultiUserInvite(request) const body = res.body as InviteUsersResponse expect(body.successful.length).toBe(0) @@ -102,7 +106,7 @@ describe("/api/global/users", () => { const user = await config.createUser() jest.clearAllMocks() - const response = await api.users.bulkCreateUsers([user]) + const response = await config.api.users.bulkCreateUsers([user]) expect(response.created?.successful.length).toBe(0) expect(response.created?.unsuccessful.length).toBe(1) @@ -115,7 +119,7 @@ describe("/api/global/users", () => { jest.resetAllMocks() await tenancy.doInTenant(TENANT_1, async () => { - const response = await api.users.bulkCreateUsers([user]) + const response = await config.api.users.bulkCreateUsers([user]) expect(response.created?.successful.length).toBe(0) expect(response.created?.unsuccessful.length).toBe(1) @@ -126,11 +130,11 @@ describe("/api/global/users", () => { it("should ignore accounts using the same email", async () => { const account = structures.accounts.account() - const resp = await api.accounts.saveMetadata(account) + const resp = await config.api.accounts.saveMetadata(account) const user = structures.users.user({ email: resp.email }) jest.clearAllMocks() - const response = await api.users.bulkCreateUsers([user]) + const response = await config.api.users.bulkCreateUsers([user]) expect(response.created?.successful.length).toBe(0) expect(response.created?.unsuccessful.length).toBe(1) @@ -143,7 +147,11 @@ describe("/api/global/users", () => { const admin = structures.users.adminUser() const user = structures.users.user() - const response = await api.users.bulkCreateUsers([builder, admin, user]) + const response = await config.api.users.bulkCreateUsers([ + builder, + admin, + user, + ]) expect(response.created?.successful.length).toBe(3) expect(response.created?.successful[0].email).toBe(builder.email) @@ -160,7 +168,7 @@ describe("/api/global/users", () => { it("should be able to create a basic user", async () => { const user = structures.users.user() - await api.users.saveUser(user) + await config.api.users.saveUser(user) expect(events.user.created).toBeCalledTimes(1) expect(events.user.updated).not.toBeCalled() @@ -171,7 +179,7 @@ describe("/api/global/users", () => { it("should be able to create an admin user", async () => { const user = structures.users.adminUser() - await api.users.saveUser(user) + await config.api.users.saveUser(user) expect(events.user.created).toBeCalledTimes(1) expect(events.user.updated).not.toBeCalled() @@ -182,7 +190,7 @@ describe("/api/global/users", () => { it("should be able to create a builder user", async () => { const user = structures.users.builderUser() - await api.users.saveUser(user) + await config.api.users.saveUser(user) expect(events.user.created).toBeCalledTimes(1) expect(events.user.updated).not.toBeCalled() @@ -197,7 +205,7 @@ describe("/api/global/users", () => { app_456: "role2", } - await api.users.saveUser(user) + await config.api.users.saveUser(user) const savedUser = await config.getUser(user.email) expect(events.user.created).toBeCalledTimes(1) @@ -213,7 +221,7 @@ describe("/api/global/users", () => { delete user._id delete user._rev - const response = await api.users.saveUser(user, 400) + const response = await config.api.users.saveUser(user, 400) expect(response.body.message).toBe(`Unavailable`) expect(events.user.created).toBeCalledTimes(0) @@ -225,7 +233,7 @@ describe("/api/global/users", () => { await tenancy.doInTenant(TENANT_1, async () => { delete user._id - const response = await api.users.saveUser(user, 400) + const response = await config.api.users.saveUser(user, 400) expect(response.body.message).toBe(`Unavailable`) expect(events.user.created).toBeCalledTimes(0) @@ -237,7 +245,7 @@ describe("/api/global/users", () => { const account = structures.accounts.cloudAccount() mocks.accounts.getAccount.mockReturnValueOnce(account) - const response = await api.users.saveUser(user, 400) + const response = await config.api.users.saveUser(user, 400) expect(response.body.message).toBe(`Unavailable`) expect(events.user.created).toBeCalledTimes(0) @@ -245,20 +253,20 @@ describe("/api/global/users", () => { it("should not be able to create a user with the same email and different casing", async () => { const user = structures.users.user() - await api.users.saveUser(user) + await config.api.users.saveUser(user) user.email = user.email.toUpperCase() - await api.users.saveUser(user, 400) + await config.api.users.saveUser(user, 400) expect(events.user.created).toBeCalledTimes(1) }) it("should not be able to bulk create a user with the same email and different casing", async () => { const user = structures.users.user() - await api.users.saveUser(user) + await config.api.users.saveUser(user) user.email = user.email.toUpperCase() - await api.users.bulkCreateUsers([user]) + await config.api.users.bulkCreateUsers([user]) expect(events.user.created).toBeCalledTimes(1) }) @@ -269,7 +277,7 @@ describe("/api/global/users", () => { const user = await config.createUser() jest.clearAllMocks() - await api.users.saveUser(user) + await config.api.users.saveUser(user) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) @@ -284,7 +292,7 @@ describe("/api/global/users", () => { user.forceResetPassword = true user.password = "tempPassword" - await api.users.saveUser(user) + await config.api.users.saveUser(user) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) @@ -297,7 +305,7 @@ describe("/api/global/users", () => { const user = await config.createUser() jest.clearAllMocks() - await api.users.saveUser(structures.users.adminUser(user)) + await config.api.users.saveUser(structures.users.adminUser(user)) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) @@ -309,7 +317,7 @@ describe("/api/global/users", () => { const user = await config.createUser() jest.clearAllMocks() - await api.users.saveUser(structures.users.builderUser(user)) + await config.api.users.saveUser(structures.users.builderUser(user)) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) @@ -323,7 +331,7 @@ describe("/api/global/users", () => { user.admin!.global = false user.builder!.global = false - await api.users.saveUser(user) + await config.api.users.saveUser(user) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) @@ -336,7 +344,7 @@ describe("/api/global/users", () => { jest.clearAllMocks() user.builder!.global = false - await api.users.saveUser(user) + await config.api.users.saveUser(user) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) @@ -352,7 +360,7 @@ describe("/api/global/users", () => { app_456: "role2", } - await api.users.saveUser(user) + await config.api.users.saveUser(user) const savedUser = await config.getUser(user.email) expect(events.user.created).not.toBeCalled() @@ -372,7 +380,7 @@ describe("/api/global/users", () => { jest.clearAllMocks() user.roles = {} - await api.users.saveUser(user) + await config.api.users.saveUser(user) const savedUser = await config.getUser(user.email) expect(events.user.created).not.toBeCalled() @@ -395,7 +403,7 @@ describe("/api/global/users", () => { app_456: "role2-edit", } - await api.users.saveUser(user) + await config.api.users.saveUser(user) const savedUser = await config.getUser(user.email) expect(events.user.created).not.toBeCalled() @@ -411,7 +419,7 @@ describe("/api/global/users", () => { const user = await config.createUser(structures.users.user({ email })) user.email = "new@test.com" - const response = await api.users.saveUser(user, 400) + const response = await config.api.users.saveUser(user, 400) const dbUser = await config.getUser(email) user.email = email @@ -424,7 +432,7 @@ describe("/api/global/users", () => { it("should not be able to bulk delete current user", async () => { const user = await config.defaultUser! - const response = await api.users.bulkDeleteUsers([user._id!], 400) + const response = await config.api.users.bulkDeleteUsers([user._id!], 400) expect(response.message).toBe("Unable to delete self.") expect(events.user.deleted).not.toBeCalled() @@ -436,7 +444,7 @@ describe("/api/global/users", () => { account.budibaseUserId = user._id! mocks.accounts.getAccountByTenantId.mockReturnValue(account) - const response = await api.users.bulkDeleteUsers([user._id!]) + const response = await config.api.users.bulkDeleteUsers([user._id!]) expect(response.deleted?.successful.length).toBe(0) expect(response.deleted?.unsuccessful.length).toBe(1) @@ -454,7 +462,7 @@ describe("/api/global/users", () => { const builder = structures.users.builderUser() const admin = structures.users.adminUser() const user = structures.users.user() - const createdUsers = await api.users.bulkCreateUsers([ + const createdUsers = await config.api.users.bulkCreateUsers([ builder, admin, user, @@ -463,7 +471,7 @@ describe("/api/global/users", () => { const toDelete = createdUsers.created?.successful.map( u => u._id! ) as string[] - const response = await api.users.bulkDeleteUsers(toDelete) + const response = await config.api.users.bulkDeleteUsers(toDelete) expect(response.deleted?.successful.length).toBe(3) expect(response.deleted?.unsuccessful.length).toBe(0) @@ -478,7 +486,7 @@ describe("/api/global/users", () => { const user = await config.createUser() jest.clearAllMocks() - await api.users.deleteUser(user._id!) + await config.api.users.deleteUser(user._id!) expect(events.user.deleted).toBeCalledTimes(1) expect(events.user.permissionBuilderRemoved).not.toBeCalled() @@ -489,7 +497,7 @@ describe("/api/global/users", () => { const user = await config.createUser(structures.users.adminUser()) jest.clearAllMocks() - await api.users.deleteUser(user._id!) + await config.api.users.deleteUser(user._id!) expect(events.user.deleted).toBeCalledTimes(1) expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1) @@ -500,7 +508,7 @@ describe("/api/global/users", () => { const user = await config.createUser(structures.users.builderUser()) jest.clearAllMocks() - await api.users.deleteUser(user._id!) + await config.api.users.deleteUser(user._id!) expect(events.user.deleted).toBeCalledTimes(1) expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1) @@ -512,7 +520,7 @@ describe("/api/global/users", () => { const account = structures.accounts.cloudAccount() mocks.accounts.getAccount.mockReturnValueOnce(account) - const response = await api.users.deleteUser(user._id!, 400) + const response = await config.api.users.deleteUser(user._id!, 400) expect(response.body.message).toBe("Account holder cannot be deleted") }) @@ -523,7 +531,7 @@ describe("/api/global/users", () => { account.email = user.email mocks.accounts.getAccount.mockReturnValueOnce(account) - const response = await api.users.deleteUser(user._id!, 400) + const response = await config.api.users.deleteUser(user._id!, 400) expect(response.body.message).toBe("Unable to delete self.") }) diff --git a/packages/worker/src/api/routes/global/tests/workspaces.spec.ts b/packages/worker/src/api/routes/global/tests/workspaces.spec.ts index f7ab1d9a7a..1a30c6525c 100644 --- a/packages/worker/src/api/routes/global/tests/workspaces.spec.ts +++ b/packages/worker/src/api/routes/global/tests/workspaces.spec.ts @@ -1,8 +1,9 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" + +// TODO describe("/api/global/workspaces", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -16,7 +17,9 @@ describe("/api/global/workspaces", () => { jest.clearAllMocks() }) - describe("GET /api/global/workspaces", () => {}) + describe("GET /api/global/workspaces", () => { + it("retrieves workspaces", () => {}) + }) describe("DELETE /api/global/workspaces/:id", () => {}) diff --git a/packages/worker/src/api/routes/system/tests/accounts.spec.ts b/packages/worker/src/api/routes/system/tests/accounts.spec.ts index f977d22cd9..fd54dd2b0a 100644 --- a/packages/worker/src/api/routes/system/tests/accounts.spec.ts +++ b/packages/worker/src/api/routes/system/tests/accounts.spec.ts @@ -1,10 +1,9 @@ import sdk from "../../../../sdk" -import { TestConfiguration, structures, API } from "../../../../tests" +import { TestConfiguration, structures } from "../../../../tests" import { v4 as uuid } from "uuid" describe("accounts", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -23,7 +22,7 @@ describe("accounts", () => { it("saves account metadata", async () => { let account = structures.accounts.account() - const response = await api.accounts.saveMetadata(account) + const response = await config.api.accounts.saveMetadata(account) const id = sdk.accounts.formatAccountMetadataId(account.accountId) const metadata = await sdk.accounts.getMetadata(id) @@ -34,9 +33,9 @@ describe("accounts", () => { describe("destroyMetadata", () => { it("destroys account metadata", async () => { const account = structures.accounts.account() - await api.accounts.saveMetadata(account) + await config.api.accounts.saveMetadata(account) - await api.accounts.destroyMetadata(account.accountId) + await config.api.accounts.destroyMetadata(account.accountId) const deleted = await sdk.accounts.getMetadata(account.accountId) expect(deleted).toBe(undefined) @@ -45,7 +44,7 @@ describe("accounts", () => { it("destroys account metadata that does not exist", async () => { const id = uuid() - const response = await api.accounts.destroyMetadata(id) + const response = await config.api.accounts.destroyMetadata(id) expect(response.status).toBe(204) }) diff --git a/packages/worker/src/api/routes/system/tests/environment.spec.ts b/packages/worker/src/api/routes/system/tests/environment.spec.ts index 5cc3b0c6f1..f18ae1ba91 100644 --- a/packages/worker/src/api/routes/system/tests/environment.spec.ts +++ b/packages/worker/src/api/routes/system/tests/environment.spec.ts @@ -1,8 +1,7 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" describe("/api/system/environment", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -18,7 +17,7 @@ describe("/api/system/environment", () => { describe("GET /api/system/environment", () => { it("returns the expected environment", async () => { - const env = await api.environment.getEnvironment() + const env = await config.api.environment.getEnvironment() expect(env.body).toEqual({ cloud: true, disableAccountPortal: false, diff --git a/packages/worker/src/api/routes/system/tests/migrations.spec.ts b/packages/worker/src/api/routes/system/tests/migrations.spec.ts index 0e3883f9ef..304a64761e 100644 --- a/packages/worker/src/api/routes/system/tests/migrations.spec.ts +++ b/packages/worker/src/api/routes/system/tests/migrations.spec.ts @@ -1,6 +1,6 @@ const migrateFn = jest.fn() -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" jest.mock("../../../../migrations", () => { return { @@ -11,7 +11,6 @@ jest.mock("../../../../migrations", () => { describe("/api/system/migrations", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -27,7 +26,7 @@ describe("/api/system/migrations", () => { describe("POST /api/system/migrations/run", () => { it("fails with no internal api key", async () => { - const res = await api.migrations.runMigrations({ + const res = await config.api.migrations.runMigrations({ headers: {}, status: 403, }) @@ -36,7 +35,7 @@ describe("/api/system/migrations", () => { }) it("runs migrations", async () => { - const res = await api.migrations.runMigrations() + const res = await config.api.migrations.runMigrations() expect(res.text).toBe("OK") expect(migrateFn).toBeCalledTimes(1) }) @@ -44,7 +43,7 @@ describe("/api/system/migrations", () => { describe("DELETE /api/system/migrations/definitions", () => { it("fails with no internal api key", async () => { - const res = await api.migrations.getMigrationDefinitions({ + const res = await config.api.migrations.getMigrationDefinitions({ headers: {}, status: 403, }) @@ -52,7 +51,7 @@ describe("/api/system/migrations", () => { }) it("returns definitions", async () => { - const res = await api.migrations.getMigrationDefinitions() + const res = await config.api.migrations.getMigrationDefinitions() expect(res.body).toEqual([ { name: "global_info_sync_users", diff --git a/packages/worker/src/api/routes/system/tests/restore.spec.ts b/packages/worker/src/api/routes/system/tests/restore.spec.ts index 1da9389608..4dd973270f 100644 --- a/packages/worker/src/api/routes/system/tests/restore.spec.ts +++ b/packages/worker/src/api/routes/system/tests/restore.spec.ts @@ -1,8 +1,7 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" describe("/api/system/restore", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -18,7 +17,7 @@ describe("/api/system/restore", () => { describe("POST /api/global/restore", () => { it("doesn't allow restore in cloud", async () => { - const res = await api.restore.restored({ status: 405 }) + const res = await config.api.restore.restored({ status: 405 }) expect(res.body).toEqual({ message: "This operation is not allowed in cloud.", status: 405, @@ -27,7 +26,7 @@ describe("/api/system/restore", () => { it("restores in self host", async () => { config.modeSelf() - const res = await api.restore.restored() + const res = await config.api.restore.restored() expect(res.body).toEqual({ message: "System prepared after restore.", }) diff --git a/packages/worker/src/api/routes/system/tests/status.spec.ts b/packages/worker/src/api/routes/system/tests/status.spec.ts index 98308556d1..afd3f8ac46 100644 --- a/packages/worker/src/api/routes/system/tests/status.spec.ts +++ b/packages/worker/src/api/routes/system/tests/status.spec.ts @@ -1,10 +1,9 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" import { accounts } from "@budibase/backend-core" import { mocks } from "@budibase/backend-core/tests" describe("/api/system/status", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -21,7 +20,7 @@ describe("/api/system/status", () => { describe("GET /api/system/status", () => { it("returns status in self host", async () => { config.modeSelf() - const res = await api.status.getStatus() + const res = await config.api.status.getStatus() expect(res.body).toEqual({ health: { passing: true, @@ -40,7 +39,7 @@ describe("/api/system/status", () => { mocks.accounts.getStatus.mockReturnValueOnce(value) - const res = await api.status.getStatus() + const res = await config.api.status.getStatus() expect(accounts.getStatus).toBeCalledTimes(1) expect(res.body).toEqual(value) diff --git a/packages/worker/src/api/routes/system/tests/tenants.spec.ts b/packages/worker/src/api/routes/system/tests/tenants.spec.ts index 0491d43e8c..8b3bcc99f5 100644 --- a/packages/worker/src/api/routes/system/tests/tenants.spec.ts +++ b/packages/worker/src/api/routes/system/tests/tenants.spec.ts @@ -1,8 +1,8 @@ -import { TestConfiguration, API } from "../../../../tests" +import { TestConfiguration } from "../../../../tests" +import { tenancy } from "@budibase/backend-core" describe("/api/global/workspaces", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -19,14 +19,43 @@ describe("/api/global/workspaces", () => { describe("DELETE /api/system/tenants/:tenantId", () => { it("allows deleting the current tenant", async () => { const user = await config.createTenant() - await config.createSession(user) - const res = await api.tenants.delete(user.tenantId, { + + await config.api.tenants.delete(user.tenantId, { headers: config.authHeaders(user), }) }) - it("rejects deleting another tenant", () => {}) + it("rejects deleting another tenant", async () => { + const user1 = await config.createTenant() + // create a second user in another tenant + const user2 = await config.createTenant() - it("requires admin", () => {}) + const status = 403 + const res = await config.api.tenants.delete(user1.tenantId, { + status, + headers: config.authHeaders(user2), + }) + + expect(res.body).toEqual({ + message: "Tenant ID does not match current user", + status, + }) + }) + + it("rejects non-admin", async () => { + const user1 = await config.createTenant() + // create an internal non-admin user + const user2 = await tenancy.doInTenant(user1.tenantId, () => { + return config.createUser() + }) + await config.createSession(user2) + + const res = await config.api.tenants.delete(user1.tenantId, { + status: 403, + headers: config.authHeaders(user2), + }) + + expect(res.body).toEqual(config.adminOnlyResponse()) + }) }) }) diff --git a/packages/worker/src/middleware/tests/tenancy.spec.ts b/packages/worker/src/middleware/tests/tenancy.spec.ts index 04de41b9cf..72c00fb6fb 100644 --- a/packages/worker/src/middleware/tests/tenancy.spec.ts +++ b/packages/worker/src/middleware/tests/tenancy.spec.ts @@ -1,9 +1,8 @@ -import { TestConfiguration, API, structures } from "../../tests" +import { TestConfiguration, structures } from "../../tests" import { constants } from "@budibase/backend-core" describe("tenancy middleware", () => { const config = new TestConfiguration() - const api = new API(config) beforeAll(async () => { await config.beforeAll() @@ -20,7 +19,7 @@ describe("tenancy middleware", () => { it("should get tenant id from user", async () => { const user = await config.createTenant() await config.createSession(user) - const res = await api.self.getSelf(user) + const res = await config.api.self.getSelf(user) expect(res.headers[constants.Headers.TENANT_ID]).toBe(user.tenantId) }) diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index 31305066da..ca02bda7a5 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -2,7 +2,6 @@ import "./mocks" import dbConfig from "../db" dbConfig.init() import env from "../environment" -import { env as coreEnv } from "@budibase/backend-core" import controllers from "./controllers" const supertest = require("supertest") import { Configs } from "../constants" @@ -14,9 +13,11 @@ import { sessions, auth, constants, + env as coreEnv, } from "@budibase/backend-core" import structures, { TENANT_ID, TENANT_1, CSRF_TOKEN } from "./structures" import { CreateUserResponse, User, AuthToken } from "@budibase/types" +import API from "./api" enum Mode { CLOUD = "cloud", @@ -26,6 +27,7 @@ enum Mode { class TestConfiguration { server: any request: any + api: API defaultUser?: User tenant1User?: User @@ -47,6 +49,8 @@ class TestConfiguration { // we need the request for logging in, involves cookies, hard to fake this.request = supertest(this.server) } + + this.api = new API(this) } getRequest() { @@ -119,14 +123,23 @@ class TestConfiguration { // TENANCY - createTenant = async (tenantId?: string): Promise => { + createTenant = async (): Promise => { // create user / new tenant - if (!tenantId) { - tenantId = structures.uuid() - } - return tenancy.doInTenant(tenantId, async () => { - return this.createUser() + const res = await this.api.users.createAdminUser() + + // return the created user + const userRes = await this.api.users.getUser(res.userId, { + headers: { + ...this.internalAPIHeaders(), + [constants.Headers.TENANT_ID]: res.tenantId, + }, }) + + // create a session for the new user + const user = userRes.body + await this.createSession(user) + + return user } getTenantId() { @@ -137,30 +150,24 @@ class TestConfiguration { } } - // USER / AUTH + // AUTH - async createDefaultUser() { - const user = structures.users.adminUser({ - email: "test@test.com", - password: "test", + async _createSession({ + userId, + tenantId, + }: { + userId: string + tenantId: string + }) { + await sessions.createASession(userId!, { + sessionId: "sessionid", + tenantId: tenantId, + csrfToken: CSRF_TOKEN, }) - this.defaultUser = await this.createUser(user) - } - - async createTenant1User() { - const user = structures.users.adminUser({ - email: "tenant1@test.com", - password: "test", - }) - this.tenant1User = await this.createUser(user) } async createSession(user: User) { - await sessions.createASession(user._id!, { - sessionId: "sessionid", - tenantId: user.tenantId, - csrfToken: CSRF_TOKEN, - }) + return this._createSession({ userId: user._id!, tenantId: user.tenantId }) } cookieHeader(cookies: any) { @@ -198,6 +205,28 @@ class TestConfiguration { return { [constants.Headers.API_KEY]: env.INTERNAL_API_KEY } } + adminOnlyResponse = () => { + return { message: "Admin user only endpoint.", status: 403 } + } + + // USERS + + async createDefaultUser() { + const user = structures.users.adminUser({ + email: "test@test.com", + password: "test", + }) + this.defaultUser = await this.createUser(user) + } + + async createTenant1User() { + const user = structures.users.adminUser({ + email: "tenant1@test.com", + password: "test", + }) + this.tenant1User = await this.createUser(user) + } + async getUser(email: string): Promise { return tenancy.doInTenant(this.getTenantId(), () => { return users.getGlobalUserByEmail(email) diff --git a/packages/worker/src/tests/api/accounts.ts b/packages/worker/src/tests/api/accounts.ts index fe6bf31192..bc6d055b77 100644 --- a/packages/worker/src/tests/api/accounts.ts +++ b/packages/worker/src/tests/api/accounts.ts @@ -1,20 +1,17 @@ import { Account, AccountMetadata } from "@budibase/types" import TestConfiguration from "../TestConfiguration" +import { TestAPI } from "./base" -export class AccountAPI { - config: TestConfiguration - request: any - +export class AccountAPI extends TestAPI { constructor(config: TestConfiguration) { - this.config = config - this.request = config.request + super(config) } saveMetadata = async (account: Account) => { const res = await this.request .put(`/api/system/accounts/${account.accountId}/metadata`) .send(account) - .set(this.config.defaultHeaders()) + .set(this.config.internalAPIHeaders()) .expect("Content-Type", /json/) .expect(200) return res.body as AccountMetadata @@ -23,6 +20,6 @@ export class AccountAPI { destroyMetadata = (accountId: string) => { return this.request .del(`/api/system/accounts/${accountId}/metadata`) - .set(this.config.defaultHeaders()) + .set(this.config.internalAPIHeaders()) } } diff --git a/packages/worker/src/tests/api/auth.ts b/packages/worker/src/tests/api/auth.ts index 204ae9f5dd..dda50976bd 100644 --- a/packages/worker/src/tests/api/auth.ts +++ b/packages/worker/src/tests/api/auth.ts @@ -1,12 +1,9 @@ import TestConfiguration from "../TestConfiguration" +import { TestAPI } from "./base" -export class AuthAPI { - config: TestConfiguration - request: any - +export class AuthAPI extends TestAPI { constructor(config: TestConfiguration) { - this.config = config - this.request = config.request + super(config) } updatePassword = (code: string) => { diff --git a/packages/worker/src/tests/api/configs.ts b/packages/worker/src/tests/api/configs.ts index 3a3c433fa0..6799229f58 100644 --- a/packages/worker/src/tests/api/configs.ts +++ b/packages/worker/src/tests/api/configs.ts @@ -1,12 +1,9 @@ import TestConfiguration from "../TestConfiguration" +import { TestAPI } from "./base" -export class ConfigAPI { - config: TestConfiguration - request: any - +export class ConfigAPI extends TestAPI { constructor(config: TestConfiguration) { - this.config = config - this.request = config.request + super(config) } getConfigChecklist = () => { diff --git a/packages/worker/src/tests/api/email.ts b/packages/worker/src/tests/api/email.ts index ea026c22ac..ba7c7dbec0 100644 --- a/packages/worker/src/tests/api/email.ts +++ b/packages/worker/src/tests/api/email.ts @@ -1,12 +1,9 @@ import TestConfiguration from "../TestConfiguration" +import { TestAPI } from "./base" -export class EmailAPI { - config: TestConfiguration - request: any - +export class EmailAPI extends TestAPI { constructor(config: TestConfiguration) { - this.config = config - this.request = config.request + super(config) } sendEmail = (purpose: string) => { diff --git a/packages/worker/src/tests/api/environment.ts b/packages/worker/src/tests/api/environment.ts index 9847c2e369..d9f82c5f0d 100644 --- a/packages/worker/src/tests/api/environment.ts +++ b/packages/worker/src/tests/api/environment.ts @@ -1,12 +1,9 @@ import TestConfiguration from "../TestConfiguration" +import { TestAPI } from "./base" -export class EnvironmentAPI { - config: TestConfiguration - request: any - +export class EnvironmentAPI extends TestAPI { constructor(config: TestConfiguration) { - this.config = config - this.request = config.request + super(config) } getEnvironment = () => { diff --git a/packages/worker/src/tests/api/self.ts b/packages/worker/src/tests/api/self.ts index da634e0db3..dcc6c1a98b 100644 --- a/packages/worker/src/tests/api/self.ts +++ b/packages/worker/src/tests/api/self.ts @@ -1,13 +1,10 @@ import TestConfiguration from "../TestConfiguration" import { User } from "@budibase/types" +import { TestAPI } from "./base" -export class SelfAPI { - config: TestConfiguration - request: any - +export class SelfAPI extends TestAPI { constructor(config: TestConfiguration) { - this.config = config - this.request = config.request + super(config) } updateSelf = (user: User) => { diff --git a/packages/worker/src/tests/api/status.ts b/packages/worker/src/tests/api/status.ts index c42060631f..5b0f77efc6 100644 --- a/packages/worker/src/tests/api/status.ts +++ b/packages/worker/src/tests/api/status.ts @@ -1,12 +1,9 @@ import TestConfiguration from "../TestConfiguration" +import { TestAPI } from "./base" -export class StatusAPI { - config: TestConfiguration - request: any - +export class StatusAPI extends TestAPI { constructor(config: TestConfiguration) { - this.config = config - this.request = config.request + super(config) } getStatus = () => { diff --git a/packages/worker/src/tests/api/tenants.ts b/packages/worker/src/tests/api/tenants.ts index d4d47a6e4f..b28b55697f 100644 --- a/packages/worker/src/tests/api/tenants.ts +++ b/packages/worker/src/tests/api/tenants.ts @@ -10,6 +10,6 @@ export class TenantAPI extends TestAPI { return this.request .delete(`/api/system/tenants/${tenantId}`) .set(opts?.headers) - .expect(opts?.status ? opts.status : 200) + .expect(opts?.status ? opts.status : 204) } } diff --git a/packages/worker/src/tests/api/users.ts b/packages/worker/src/tests/api/users.ts index 3677bfffc6..c9c5e33403 100644 --- a/packages/worker/src/tests/api/users.ts +++ b/packages/worker/src/tests/api/users.ts @@ -3,16 +3,16 @@ import { BulkUserRequest, InviteUsersRequest, User, + CreateAdminUserRequest, } from "@budibase/types" +import * as structures from "../structures" +import { generator } from "@budibase/backend-core/tests" import TestConfiguration from "../TestConfiguration" +import { TestAPI, TestAPIOpts } from "./base" -export class UserAPI { - config: TestConfiguration - request: any - +export class UserAPI extends TestAPI { constructor(config: TestConfiguration) { - this.config = config - this.request = config.request + super(config) } // INVITE @@ -91,6 +91,30 @@ export class UserAPI { // USER + createAdminUser = async ( + request?: CreateAdminUserRequest, + opts?: TestAPIOpts + ) => { + if (!request) { + request = { + email: structures.email(), + password: generator.string(), + tenantId: structures.uuid(), + } + } + const res = await this.request + .post(`/api/global/users/init`) + .send(request) + .set(this.config.internalAPIHeaders()) + .expect("Content-Type", /json/) + .expect(opts?.status ? opts.status : 200) + + return { + ...request, + userId: res.body._id, + } + } + saveUser = (user: User, status?: number) => { return this.request .post(`/api/global/users`) @@ -107,4 +131,12 @@ export class UserAPI { .expect("Content-Type", /json/) .expect(status ? status : 200) } + + getUser = (userId: string, opts?: TestAPIOpts) => { + return this.request + .get(`/api/global/users/${userId}`) + .set(opts?.headers ? opts.headers : this.config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(opts?.status ? opts.status : 200) + } } From 1246d25f0fae6e1f79fbb1e549bea3870e911ced Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Tue, 15 Nov 2022 16:20:40 +0000 Subject: [PATCH 04/15] Updating createAutomation.spec.js The + symbol button for creating an automation has been removed. It has been replaced with a button which says 'Add automation' I have updated the create automation test to now click on this button when creating a new automation. This test was failing due to the other button no longer existing --- packages/builder/cypress/integration/createAutomation.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/cypress/integration/createAutomation.spec.js b/packages/builder/cypress/integration/createAutomation.spec.js index b5ff406297..005f00cca6 100644 --- a/packages/builder/cypress/integration/createAutomation.spec.js +++ b/packages/builder/cypress/integration/createAutomation.spec.js @@ -12,7 +12,7 @@ filterTests(['smoke', 'all'], () => { cy.createTestTableWithData() cy.wait(2000) cy.contains("Automate").click() - cy.get(interact.ADD_BUTTON_SPECTRUM).click() + cy.get(interact.SPECTRUM_BUTTON_TEMPLATE).contains("Add automation").click({ force: true }) cy.get(interact.MODAL_INNER_WRAPPER).within(() => { cy.get("input").type("Add Row") cy.contains("Row Created").click({ force: true }) From 061f060aebb3afafad7aba36a49d7738d415c7ae Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 15 Nov 2022 16:49:11 +0000 Subject: [PATCH 05/15] v2.1.22-alpha.4 --- lerna.json | 2 +- packages/backend-core/package.json | 4 ++-- packages/bbui/package.json | 4 ++-- packages/builder/package.json | 10 +++++----- packages/cli/package.json | 8 ++++---- packages/client/package.json | 8 ++++---- packages/frontend-core/package.json | 4 ++-- packages/sdk/package.json | 2 +- packages/server/package.json | 10 +++++----- packages/string-templates/package.json | 2 +- packages/types/package.json | 2 +- packages/worker/package.json | 8 ++++---- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lerna.json b/lerna.json index 6bd7f8df89..b130e9cf1f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 63adcfc19c..ac1d68c6a1 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "2.1.22-alpha.3", + "@budibase/types": "2.1.22-alpha.4", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 767ccea8b0..827ff1a325 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "2.1.22-alpha.3", + "@budibase/string-templates": "2.1.22-alpha.4", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/builder/package.json b/packages/builder/package.json index 0e339a0363..168016d080 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "license": "GPL-3.0", "private": true, "scripts": { @@ -71,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "2.1.22-alpha.3", - "@budibase/client": "2.1.22-alpha.3", - "@budibase/frontend-core": "2.1.22-alpha.3", - "@budibase/string-templates": "2.1.22-alpha.3", + "@budibase/bbui": "2.1.22-alpha.4", + "@budibase/client": "2.1.22-alpha.4", + "@budibase/frontend-core": "2.1.22-alpha.4", + "@budibase/string-templates": "2.1.22-alpha.4", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 066ee70e87..cfe7f7cd2a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { @@ -26,9 +26,9 @@ "outputPath": "build" }, "dependencies": { - "@budibase/backend-core": "2.1.22-alpha.3", - "@budibase/string-templates": "2.1.22-alpha.3", - "@budibase/types": "2.1.22-alpha.3", + "@budibase/backend-core": "2.1.22-alpha.4", + "@budibase/string-templates": "2.1.22-alpha.4", + "@budibase/types": "2.1.22-alpha.4", "axios": "0.21.2", "chalk": "4.1.0", "cli-progress": "3.11.2", diff --git a/packages/client/package.json b/packages/client/package.json index be965071cd..143ff6b27b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "2.1.22-alpha.3", - "@budibase/frontend-core": "2.1.22-alpha.3", - "@budibase/string-templates": "2.1.22-alpha.3", + "@budibase/bbui": "2.1.22-alpha.4", + "@budibase/frontend-core": "2.1.22-alpha.4", + "@budibase/string-templates": "2.1.22-alpha.4", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index 8017e5b9d5..e60f3d3a29 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -1,12 +1,12 @@ { "name": "@budibase/frontend-core", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", "svelte": "src/index.js", "dependencies": { - "@budibase/bbui": "2.1.22-alpha.3", + "@budibase/bbui": "2.1.22-alpha.4", "lodash": "^4.17.21", "svelte": "^3.46.2" } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 75a7c5fc4d..22e5f7e65d 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/sdk", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "description": "Budibase Public API SDK", "author": "Budibase", "license": "MPL-2.0", diff --git a/packages/server/package.json b/packages/server/package.json index 8d16598dea..a235519aa4 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -43,11 +43,11 @@ "license": "GPL-3.0", "dependencies": { "@apidevtools/swagger-parser": "10.0.3", - "@budibase/backend-core": "2.1.22-alpha.3", - "@budibase/client": "2.1.22-alpha.3", + "@budibase/backend-core": "2.1.22-alpha.4", + "@budibase/client": "2.1.22-alpha.4", "@budibase/pro": "2.1.22-alpha.3", - "@budibase/string-templates": "2.1.22-alpha.3", - "@budibase/types": "2.1.22-alpha.3", + "@budibase/string-templates": "2.1.22-alpha.4", + "@budibase/types": "2.1.22-alpha.4", "@bull-board/api": "3.7.0", "@bull-board/koa": "3.9.4", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 557e446d6d..2fa6082232 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/types/package.json b/packages/types/package.json index f63fbf3480..55ac72c7d7 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "description": "Budibase types", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/worker/package.json b/packages/worker/package.json index a9a093e753..5d1ae31d49 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "2.1.22-alpha.3", + "version": "2.1.22-alpha.4", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -36,10 +36,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "2.1.22-alpha.3", + "@budibase/backend-core": "2.1.22-alpha.4", "@budibase/pro": "2.1.22-alpha.3", - "@budibase/string-templates": "2.1.22-alpha.3", - "@budibase/types": "2.1.22-alpha.3", + "@budibase/string-templates": "2.1.22-alpha.4", + "@budibase/types": "2.1.22-alpha.4", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", From faab0a84056b8bd8538d843d16b2aad4dc60f80b Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 15 Nov 2022 16:52:47 +0000 Subject: [PATCH 06/15] Update pro version to 2.1.22-alpha.4 --- packages/server/package.json | 2 +- packages/server/yarn.lock | 30 +++++++++++++++--------------- packages/worker/package.json | 2 +- packages/worker/yarn.lock | 30 +++++++++++++++--------------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index a235519aa4..0d43aee1e5 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -45,7 +45,7 @@ "@apidevtools/swagger-parser": "10.0.3", "@budibase/backend-core": "2.1.22-alpha.4", "@budibase/client": "2.1.22-alpha.4", - "@budibase/pro": "2.1.22-alpha.3", + "@budibase/pro": "2.1.22-alpha.4", "@budibase/string-templates": "2.1.22-alpha.4", "@budibase/types": "2.1.22-alpha.4", "@bull-board/api": "3.7.0", diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 6a945c226e..905af68259 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1273,12 +1273,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.1.22-alpha.3": - version "2.1.22-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.3.tgz#5cdff781cf6a448677304aa80a9658e9e51c36a5" - integrity sha512-je1mKTb1h9f+tyCAiFNJykg9O8ZM3smVQ3JJf24rXDYvLYN8GG8hxZ5fOJuWoeF/aGu8m+w3v3+EdWuTlQPcKg== +"@budibase/backend-core@2.1.22-alpha.4": + version "2.1.22-alpha.4" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.4.tgz#25e186273d2416f1fe8ed639146d3d8e977c8bc8" + integrity sha512-uii4hL/KvgMH8o713x54B2bGdtgYWE4AUxkzPs/2LCEa6Z3eeahziaElsRyc8oELqh0f71PYDdRC6gSJZZ1hdQ== dependencies: - "@budibase/types" "2.1.22-alpha.3" + "@budibase/types" "2.1.22-alpha.4" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -1360,13 +1360,13 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@2.1.22-alpha.3": - version "2.1.22-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.3.tgz#8f87ffc1c6c158ad467216d5cfcb7844e9a08363" - integrity sha512-IE0eHPswBycPYzvduZCAp4T6U8t1qTcT1A9jVJ8TeX2jiL22hPOQ+8nVNIaqTGfQvJ7foGiqtDcugQbk3QLGOg== +"@budibase/pro@2.1.22-alpha.4": + version "2.1.22-alpha.4" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.4.tgz#860edf8e6f022ea4bf33440d1c3173cc3f0ee9a1" + integrity sha512-L1EezM8ZdUWb+XlBgrslwCZ67wxUBRgTH3S/uGTZqGL1jGvEm7w4IQTyH55AqTABx5ZyNyAlOYKCipGjb5fplw== dependencies: - "@budibase/backend-core" "2.1.22-alpha.3" - "@budibase/types" "2.1.22-alpha.3" + "@budibase/backend-core" "2.1.22-alpha.4" + "@budibase/types" "2.1.22-alpha.4" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" @@ -1390,10 +1390,10 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/types@2.1.22-alpha.3": - version "2.1.22-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.3.tgz#30cfcb6a58989a94dfba43ec7f9052e4855b6803" - integrity sha512-3WKZ5DVkygUi9H3KJTL8geQf9cjmssM8tsbDEFi8KJHogfszJPia9di/DnN8rd/CXbsx3Zbsbe8LHiAAADz5og== +"@budibase/types@2.1.22-alpha.4": + version "2.1.22-alpha.4" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.4.tgz#cadcafae414610e0bec101380dc0300ad6ad0702" + integrity sha512-xkrgqzkd4a9xb94Z05yh91fD7/2LSzrvvkS7eNV5rL7+9ddMWHUFVCCm/ggK+qnXKPhqIMq5TmGeLu28HjcJJQ== "@bull-board/api@3.7.0": version "3.7.0" diff --git a/packages/worker/package.json b/packages/worker/package.json index 5d1ae31d49..fdadff86a6 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -37,7 +37,7 @@ "license": "GPL-3.0", "dependencies": { "@budibase/backend-core": "2.1.22-alpha.4", - "@budibase/pro": "2.1.22-alpha.3", + "@budibase/pro": "2.1.22-alpha.4", "@budibase/string-templates": "2.1.22-alpha.4", "@budibase/types": "2.1.22-alpha.4", "@koa/router": "8.0.8", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index cfdba32e7a..3dae0b88ea 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -470,12 +470,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.1.22-alpha.3": - version "2.1.22-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.3.tgz#5cdff781cf6a448677304aa80a9658e9e51c36a5" - integrity sha512-je1mKTb1h9f+tyCAiFNJykg9O8ZM3smVQ3JJf24rXDYvLYN8GG8hxZ5fOJuWoeF/aGu8m+w3v3+EdWuTlQPcKg== +"@budibase/backend-core@2.1.22-alpha.4": + version "2.1.22-alpha.4" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.4.tgz#25e186273d2416f1fe8ed639146d3d8e977c8bc8" + integrity sha512-uii4hL/KvgMH8o713x54B2bGdtgYWE4AUxkzPs/2LCEa6Z3eeahziaElsRyc8oELqh0f71PYDdRC6gSJZZ1hdQ== dependencies: - "@budibase/types" "2.1.22-alpha.3" + "@budibase/types" "2.1.22-alpha.4" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -507,22 +507,22 @@ uuid "8.3.2" zlib "1.0.5" -"@budibase/pro@2.1.22-alpha.3": - version "2.1.22-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.3.tgz#8f87ffc1c6c158ad467216d5cfcb7844e9a08363" - integrity sha512-IE0eHPswBycPYzvduZCAp4T6U8t1qTcT1A9jVJ8TeX2jiL22hPOQ+8nVNIaqTGfQvJ7foGiqtDcugQbk3QLGOg== +"@budibase/pro@2.1.22-alpha.4": + version "2.1.22-alpha.4" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.4.tgz#860edf8e6f022ea4bf33440d1c3173cc3f0ee9a1" + integrity sha512-L1EezM8ZdUWb+XlBgrslwCZ67wxUBRgTH3S/uGTZqGL1jGvEm7w4IQTyH55AqTABx5ZyNyAlOYKCipGjb5fplw== dependencies: - "@budibase/backend-core" "2.1.22-alpha.3" - "@budibase/types" "2.1.22-alpha.3" + "@budibase/backend-core" "2.1.22-alpha.4" + "@budibase/types" "2.1.22-alpha.4" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" node-fetch "^2.6.1" -"@budibase/types@2.1.22-alpha.3": - version "2.1.22-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.3.tgz#30cfcb6a58989a94dfba43ec7f9052e4855b6803" - integrity sha512-3WKZ5DVkygUi9H3KJTL8geQf9cjmssM8tsbDEFi8KJHogfszJPia9di/DnN8rd/CXbsx3Zbsbe8LHiAAADz5og== +"@budibase/types@2.1.22-alpha.4": + version "2.1.22-alpha.4" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.4.tgz#cadcafae414610e0bec101380dc0300ad6ad0702" + integrity sha512-xkrgqzkd4a9xb94Z05yh91fD7/2LSzrvvkS7eNV5rL7+9ddMWHUFVCCm/ggK+qnXKPhqIMq5TmGeLu28HjcJJQ== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" From 5692fca1e5f2e2175529f1d7d51c580c2637d0c3 Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Tue, 15 Nov 2022 17:59:31 +0000 Subject: [PATCH 07/15] Skipping Screen Tests There are 4 tests in this file - Skipped create and delete screen tests (covered by API) - Update URL test remains (will eventually be covered via E2E with QA Wolf) - Create and filter screens by access level test remains (This is due for API coverage within another ticket) --- packages/builder/cypress/integration/createScreen.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/builder/cypress/integration/createScreen.spec.js b/packages/builder/cypress/integration/createScreen.spec.js index a516e279f4..c4b237279d 100644 --- a/packages/builder/cypress/integration/createScreen.spec.js +++ b/packages/builder/cypress/integration/createScreen.spec.js @@ -9,7 +9,7 @@ filterTests(["smoke", "all"], () => { cy.navigateToFrontend() }) - it("Should successfully create a screen", () => { + it.skip("Should successfully create a screen", () => { cy.createScreen("test") cy.get(interact.BODY).within(() => { cy.contains("/test").should("exist") @@ -23,7 +23,7 @@ filterTests(["smoke", "all"], () => { }) }) - it("should delete all screens then create first screen via button", () => { + it.skip("should delete all screens then create first screen via button", () => { cy.deleteAllScreens() cy.contains("Create first screen").click() From 0a5839a82bb5156ba1a1e5eb98ae0b34f755cb64 Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Wed, 16 Nov 2022 11:29:59 +0000 Subject: [PATCH 08/15] Skipping createApp Tests & createAutomation Test Fix Skipping createApp tests covered via API Automation. Also updated the createAutomation test to fix an issue with the nightly build --- packages/builder/cypress/integration/createApp.spec.js | 6 +++--- .../builder/cypress/integration/createAutomation.spec.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/builder/cypress/integration/createApp.spec.js b/packages/builder/cypress/integration/createApp.spec.js index 179741e21a..d37b0806c4 100644 --- a/packages/builder/cypress/integration/createApp.spec.js +++ b/packages/builder/cypress/integration/createApp.spec.js @@ -10,7 +10,7 @@ filterTests(['smoke', 'all'], () => { }) if (!(Cypress.env("TEST_ENV"))) { - it("should show the new user UI/UX", () => { + it.skip("should show the new user UI/UX", () => { cy.visit(`${Cypress.config().baseUrl}/builder/portal/apps/create`, { timeout: 5000 }) //added /portal/apps/create cy.wait(1000) cy.get(interact.CREATE_APP_BUTTON, { timeout: 10000 }).contains('Start from scratch').should("exist") @@ -83,7 +83,7 @@ filterTests(['smoke', 'all'], () => { }) }) - it("should create the first application from scratch", () => { + it.skip("should create the first application from scratch", () => { const appName = "Cypress Tests" cy.createApp(appName, false) @@ -93,7 +93,7 @@ filterTests(['smoke', 'all'], () => { cy.deleteApp(appName) }) - it("should create the first application from scratch with a default name", () => { + it.skip("should create the first application from scratch with a default name", () => { cy.updateUserInformation("", "") cy.createApp("", false) cy.applicationInAppTable("My app") diff --git a/packages/builder/cypress/integration/createAutomation.spec.js b/packages/builder/cypress/integration/createAutomation.spec.js index 005f00cca6..8c16f4bd22 100644 --- a/packages/builder/cypress/integration/createAutomation.spec.js +++ b/packages/builder/cypress/integration/createAutomation.spec.js @@ -24,7 +24,7 @@ filterTests(['smoke', 'all'], () => { cy.wait(500) cy.contains("dog").click() // Create action - cy.get('[aria-label="AddCircle"]', { timeout: 2000 }).eq(1).click() + cy.get('[aria-label="AddCircle"]', { timeout: 2000 }).click() cy.get(interact.MODAL_INNER_WRAPPER).within(() => { cy.wait(1000) cy.contains("Create Row").trigger('mouseover').click().click() From 32a12eff00112579ed2f08016179da029fcf1d76 Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Wed, 16 Nov 2022 11:33:30 +0000 Subject: [PATCH 09/15] Skip revertApp Cypress Spec File We have API Automation coverage for reverting applications --- packages/builder/cypress/integration/revertApp.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/cypress/integration/revertApp.spec.js b/packages/builder/cypress/integration/revertApp.spec.js index 0fb58e89e9..2cd806b02c 100644 --- a/packages/builder/cypress/integration/revertApp.spec.js +++ b/packages/builder/cypress/integration/revertApp.spec.js @@ -2,7 +2,7 @@ import filterTests from "../support/filterTests" const interact = require('../support/interact') filterTests(['smoke', 'all'], () => { - context("Revert apps", () => { + xcontext("Revert apps", () => { before(() => { cy.login() cy.createTestApp() From 6017f6be474820eaf3fed2aaeb5252e37ab4dddc Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 16 Nov 2022 11:34:16 +0000 Subject: [PATCH 10/15] Add copy button to sso callback urls, e2e unit testing for OIDC, stub out other auth tests --- .eslintrc.json | 3 +- packages/backend-core/__mocks__/node-fetch.ts | 1 - packages/backend-core/package.json | 4 +- .../src/middleware/tests/matchers.spec.ts | 134 ++++++++++++ packages/backend-core/src/tenancy/tenancy.ts | 16 +- packages/backend-core/src/utils.ts | 4 +- .../tests/utilities/mocks/fetch.ts | 4 + .../tests/utilities/mocks/index.ts | 1 + .../tests/utilities/structures/koa.ts | 12 +- packages/backend-core/yarn.lock | 103 +-------- .../builder/portal/manage/auth/index.svelte | 75 +++++-- packages/types/src/sdk/auth.ts | 2 +- packages/worker/__mocks__/node-fetch.ts | 1 - packages/worker/__mocks__/oauth.ts | 57 +++++ packages/worker/package.json | 2 + .../worker/src/api/controllers/global/auth.ts | 2 +- packages/worker/src/api/routes/global/auth.js | 39 ++-- .../src/api/routes/global/tests/auth.spec.ts | 203 ++++++++++++------ .../api/routes/system/tests/tenants.spec.ts | 2 +- .../worker/src/tests/TestConfiguration.ts | 10 +- packages/worker/src/tests/api/configs.ts | 14 +- packages/worker/yarn.lock | 24 +++ .../TestConfiguration/applications.ts | 9 +- .../internal-api/TestConfiguration/tables.ts | 4 +- .../internal-api/fixtures/applications.ts | 1 - .../applications/applications.spec.ts | 29 +-- .../internal-api/screens/screens.spec.ts | 5 +- .../tests/internal-api/tables/tables.spec.ts | 150 +++++++------ 28 files changed, 578 insertions(+), 333 deletions(-) delete mode 100644 packages/backend-core/__mocks__/node-fetch.ts create mode 100644 packages/backend-core/src/middleware/tests/matchers.spec.ts create mode 100644 packages/backend-core/tests/utilities/mocks/fetch.ts delete mode 100644 packages/worker/__mocks__/node-fetch.ts create mode 100644 packages/worker/__mocks__/oauth.ts diff --git a/.eslintrc.json b/.eslintrc.json index 8eccb147a5..87f8269c50 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,8 +16,7 @@ "dist", "public", "*.spec.js", - "bundle.js", - "coverage" + "bundle.js" ], "plugins": ["svelte3"], "extends": ["eslint:recommended"], diff --git a/packages/backend-core/__mocks__/node-fetch.ts b/packages/backend-core/__mocks__/node-fetch.ts deleted file mode 100644 index 4c7127ee48..0000000000 --- a/packages/backend-core/__mocks__/node-fetch.ts +++ /dev/null @@ -1 +0,0 @@ -jest.mock("node-fetch", () => jest.fn()) diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 63adcfc19c..0239acb9a1 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -56,7 +56,7 @@ "@types/chance": "1.1.3", "@types/ioredis": "4.28.0", "@types/jest": "27.5.1", - "@types/koa": "2.0.52", + "@types/koa": "2.13.4", "@types/lodash": "4.14.180", "@types/node": "14.18.20", "@types/node-fetch": "2.6.1", @@ -68,7 +68,7 @@ "chance": "1.1.3", "ioredis-mock": "5.8.0", "jest": "28.1.1", - "koa": "2.7.0", + "koa": "2.13.4", "nodemon": "2.0.16", "pouchdb-adapter-memory": "7.2.2", "timekeeper": "2.2.0", diff --git a/packages/backend-core/src/middleware/tests/matchers.spec.ts b/packages/backend-core/src/middleware/tests/matchers.spec.ts new file mode 100644 index 0000000000..c39bbb6dd3 --- /dev/null +++ b/packages/backend-core/src/middleware/tests/matchers.spec.ts @@ -0,0 +1,134 @@ +import * as matchers from "../matchers" +import { structures } from "../../../tests" + +describe("matchers", () => { + it("matches by path and method", () => { + const pattern = [ + { + route: "/api/tests", + method: "POST", + }, + ] + const ctx = structures.koa.newContext() + ctx.request.url = "/api/tests" + ctx.request.method = "POST" + + const built = matchers.buildMatcherRegex(pattern) + + expect(!!matchers.matches(ctx, built)).toBe(true) + }) + + it("wildcards path", () => { + const pattern = [ + { + route: "/api/tests", + method: "POST", + }, + ] + const ctx = structures.koa.newContext() + ctx.request.url = "/api/tests/id/something/else" + ctx.request.method = "POST" + + const built = matchers.buildMatcherRegex(pattern) + + expect(!!matchers.matches(ctx, built)).toBe(true) + }) + + it("doesn't wildcard path with strict", () => { + const pattern = [ + { + route: "/api/tests", + method: "POST", + strict: true, + }, + ] + const ctx = structures.koa.newContext() + ctx.request.url = "/api/tests/id/something/else" + ctx.request.method = "POST" + + const built = matchers.buildMatcherRegex(pattern) + + expect(!!matchers.matches(ctx, built)).toBe(false) + }) + + it("matches with param", () => { + const pattern = [ + { + route: "/api/tests/:testId", + method: "GET", + }, + ] + const ctx = structures.koa.newContext() + ctx.request.url = "/api/tests/id" + ctx.request.method = "GET" + + const built = matchers.buildMatcherRegex(pattern) + + expect(!!matchers.matches(ctx, built)).toBe(true) + }) + + // TODO: Support the below behaviour + // Strict does not work when a param is present + // it("matches with param with strict", () => { + // const pattern = [{ + // route: "/api/tests/:testId", + // method: "GET", + // strict: true + // }] + // const ctx = structures.koa.newContext() + // ctx.request.url = "/api/tests/id" + // ctx.request.method = "GET" + // + // const built = matchers.buildMatcherRegex(pattern) + // + // expect(!!matchers.matches(ctx, built)).toBe(true) + // }) + + it("doesn't match by path", () => { + const pattern = [ + { + route: "/api/tests", + method: "POST", + }, + ] + const ctx = structures.koa.newContext() + ctx.request.url = "/api/unknown" + ctx.request.method = "POST" + + const built = matchers.buildMatcherRegex(pattern) + + expect(!!matchers.matches(ctx, built)).toBe(false) + }) + + it("doesn't match by method", () => { + const pattern = [ + { + route: "/api/tests", + method: "POST", + }, + ] + const ctx = structures.koa.newContext() + ctx.request.url = "/api/tests" + ctx.request.method = "GET" + + const built = matchers.buildMatcherRegex(pattern) + + expect(!!matchers.matches(ctx, built)).toBe(false) + }) + + it("matches by path and wildcard method", () => { + const pattern = [ + { + route: "/api/tests", + method: "ALL", + }, + ] + const ctx = structures.koa.newContext() + ctx.request.url = "/api/tests" + ctx.request.method = "GET" + + const built = matchers.buildMatcherRegex(pattern) + + expect(!!matchers.matches(ctx, built)).toBe(true) + }) +}) diff --git a/packages/backend-core/src/tenancy/tenancy.ts b/packages/backend-core/src/tenancy/tenancy.ts index 99955ca321..3ac0f5c314 100644 --- a/packages/backend-core/src/tenancy/tenancy.ts +++ b/packages/backend-core/src/tenancy/tenancy.ts @@ -231,12 +231,18 @@ export const getTenantIDFromCtx = ( const match = ctx.matched.find( (m: any) => !!m.paramNames.find((p: any) => p.name === "tenantId") ) + + // get the raw path url - without any query params + const ctxUrl = ctx.originalUrl + let url + if (ctxUrl.includes("?")) { + url = ctxUrl.split("?")[0] + } else { + url = ctxUrl + } + if (match) { - const params = match.params( - ctx.originalUrl, - match.captures(ctx.originalUrl), - {} - ) + const params = match.params(url, match.captures(url), {}) if (params.tenantId) { return params.tenantId } diff --git a/packages/backend-core/src/utils.ts b/packages/backend-core/src/utils.ts index 215288b93c..3b9bd611d4 100644 --- a/packages/backend-core/src/utils.ts +++ b/packages/backend-core/src/utils.ts @@ -30,8 +30,8 @@ async function resolveAppUrl(ctx: BBContext) { let possibleAppUrl = `/${appUrl.toLowerCase()}` let tenantId = tenancy.getTenantId() - if (!env.SELF_HOSTED) { - // always use the tenant id from the subdomain in cloud + if (env.MULTI_TENANCY) { + // always use the tenant id from the subdomain in multi tenancy // this ensures the logged-in user tenant id doesn't overwrite // e.g. in the case of viewing a public app while already logged-in to another tenant tenantId = tenancy.getTenantIDFromCtx(ctx, { diff --git a/packages/backend-core/tests/utilities/mocks/fetch.ts b/packages/backend-core/tests/utilities/mocks/fetch.ts new file mode 100644 index 0000000000..573b47db9f --- /dev/null +++ b/packages/backend-core/tests/utilities/mocks/fetch.ts @@ -0,0 +1,4 @@ +const mockFetch = jest.fn() +jest.mock("node-fetch", () => mockFetch) + +export default mockFetch diff --git a/packages/backend-core/tests/utilities/mocks/index.ts b/packages/backend-core/tests/utilities/mocks/index.ts index 7031b225ec..e71c739e26 100644 --- a/packages/backend-core/tests/utilities/mocks/index.ts +++ b/packages/backend-core/tests/utilities/mocks/index.ts @@ -2,3 +2,4 @@ import "./posthog" import "./events" export * as accounts from "./accounts" export * as date from "./date" +export { default as fetch } from "./fetch" diff --git a/packages/backend-core/tests/utilities/structures/koa.ts b/packages/backend-core/tests/utilities/structures/koa.ts index 6f0f7866e6..7084c90360 100644 --- a/packages/backend-core/tests/utilities/structures/koa.ts +++ b/packages/backend-core/tests/utilities/structures/koa.ts @@ -1,5 +1,13 @@ import { createMockContext } from "@shopify/jest-koa-mocks" +import { BBContext } from "@budibase/types" -export const newContext = () => { - return createMockContext() +export const newContext = (): BBContext => { + const ctx = createMockContext() + return { + ...ctx, + request: { + ...ctx.request, + body: {}, + }, + } } diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index 77216a9069..fd48d574b7 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -1079,7 +1079,7 @@ dependencies: "@types/koa" "*" -"@types/koa@*": +"@types/koa@*", "@types/koa@2.13.4": version "2.13.4" resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b" integrity sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw== @@ -1093,18 +1093,6 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/koa@2.0.52": - version "2.0.52" - resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.0.52.tgz#7dd11de4189ab339ad66c4ccad153716b14e525f" - integrity sha512-cp/GTOhOYwomlSKqEoG0kaVEVJEzP4ojYmfa7EKaGkmkkRwJ4B/1VBLbQZ49Z+WJNvzXejQB/9GIKqMo9XLgFQ== - dependencies: - "@types/accepts" "*" - "@types/cookies" "*" - "@types/http-assert" "*" - "@types/keygrip" "*" - "@types/koa-compose" "*" - "@types/node" "*" - "@types/lodash@4.14.180": version "4.14.180" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" @@ -1478,11 +1466,6 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -any-promise@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== - anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -2056,14 +2039,6 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" -cookies@~0.7.1: - version "0.7.3" - resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.3.tgz#7912ce21fbf2e8c2da70cf1c3f351aecf59dadfa" - integrity sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A== - dependencies: - depd "~1.1.2" - keygrip "~1.0.3" - cookies@~0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" @@ -2134,13 +2109,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - debuglog@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -2201,7 +2169,7 @@ denque@^1.1.0: resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== -depd@^1.1.0, depd@^1.1.2, depd@~1.1.2: +depd@^1.1.0, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== @@ -2353,11 +2321,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error-inject@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" - integrity sha512-JM8N6PytDbmIYm1IhPWlo8vr3NtfjhDY/1MhD/a5b/aad/USE8a0+NsqE9d5n+GVGmuNkPQWm4bFQWv18d8tMg== - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -3602,11 +3565,6 @@ jws@^3.0.0, jws@^3.1.4, jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" -keygrip@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.3.tgz#399d709f0aed2bab0a059e0cdd3a5023a053e1dc" - integrity sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g== - keygrip@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" @@ -3626,26 +3584,11 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -koa-compose@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" - integrity sha512-8gen2cvKHIZ35eDEik5WOo8zbVp9t4cP8p4hW4uE55waxolLRexKKrqfCpwhGVppnB40jWeF8bZeTVg99eZgPw== - dependencies: - any-promise "^1.1.0" - koa-compose@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== -koa-convert@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" - integrity sha512-K9XqjmEDStGX09v3oxR7t5uPRy0jqJdvodHa6wxWTHrTfDq0WUNnYTOOUZN6g8OM8oZQXprQASbiIXG2Ez8ehA== - dependencies: - co "^4.6.0" - koa-compose "^3.0.0" - koa-convert@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" @@ -3654,11 +3597,6 @@ koa-convert@^2.0.0: co "^4.6.0" koa-compose "^4.1.0" -koa-is-json@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" - integrity sha512-+97CtHAlWDx0ndt0J8y3P12EWLwTLMXIfMnYDev3wOTwH/RpBGMlfn4bDXlMEg1u73K6XRE9BbUp+5ZAYoRYWw== - koa-passport@4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/koa-passport/-/koa-passport-4.1.4.tgz#5f1665c1c2a37ace79af9f970b770885ca30ccfa" @@ -3666,37 +3604,7 @@ koa-passport@4.1.4: dependencies: passport "^0.4.0" -koa@2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/koa/-/koa-2.7.0.tgz#7e00843506942b9d82c6cc33749f657c6e5e7adf" - integrity sha512-7ojD05s2Q+hFudF8tDLZ1CpCdVZw8JQELWSkcfG9bdtoTDzMmkRF6BQBU7JzIzCCOY3xd3tftiy/loHBUYaY2Q== - dependencies: - accepts "^1.3.5" - cache-content-type "^1.0.0" - content-disposition "~0.5.2" - content-type "^1.0.4" - cookies "~0.7.1" - debug "~3.1.0" - delegates "^1.0.0" - depd "^1.1.2" - destroy "^1.0.4" - error-inject "^1.0.0" - escape-html "^1.0.3" - fresh "~0.5.2" - http-assert "^1.3.0" - http-errors "^1.6.3" - is-generator-function "^1.0.7" - koa-compose "^4.1.0" - koa-convert "^1.2.0" - koa-is-json "^1.0.0" - on-finished "^2.3.0" - only "~0.0.2" - parseurl "^1.3.2" - statuses "^1.5.0" - type-is "^1.6.16" - vary "^1.1.2" - -koa@^2.13.4: +koa@2.13.4, koa@^2.13.4: version "2.13.4" resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e" integrity sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g== @@ -4079,11 +3987,6 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" diff --git a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte index dab0bfdd90..116fdeff28 100644 --- a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte @@ -20,11 +20,12 @@ Toggle, Tag, Tags, + Icon, + Helpers, } from "@budibase/bbui" import { onMount } from "svelte" import { API } from "api" import { organisation, admin } from "stores/portal" - import { Helpers } from "@budibase/bbui" const ConfigTypes = { Google: "google", @@ -40,7 +41,9 @@ // Indicate to user that callback is based on platform url // If there is an existing value, indicate that it may be removed to return to default behaviour - $: googleCallbackTooltip = googleCallbackReadonly + $: googleCallbackTooltip = $admin.cloud + ? null + : googleCallbackReadonly ? "Vist the organisation page to update the platform URL" : "Leave blank to use the default callback URL" @@ -54,6 +57,7 @@ readonly: googleCallbackReadonly, tooltip: googleCallbackTooltip, placeholder: $organisation.googleCallbackUrl, + copyButton: true, }, ], } @@ -66,9 +70,12 @@ { name: "callbackURL", readonly: true, - tooltip: "Vist the organisation page to update the platform URL", + tooltip: $admin.cloud + ? null + : "Vist the organisation page to update the platform URL", label: "Callback URL", placeholder: $organisation.oidcCallbackUrl, + copyButton: true, }, ], } @@ -231,6 +238,11 @@ }, ] + const copyToClipboard = async value => { + await Helpers.copyToClipboard(value) + notifications.success("Copied") + } + onMount(async () => { try { await organisation.init() @@ -336,11 +348,23 @@ {#each GoogleConfigFields.Google as field}
- +
+
+ +
+ {#if field.copyButton} +
copyToClipboard(field.placeholder)} + > + +
+ {/if} +
{/each}
@@ -375,12 +399,23 @@ {#each OIDCConfigFields.Oidc as field}
- +
+
+ +
+ {#if field.copyButton} +
copyToClipboard(field.placeholder)} + > + +
+ {/if} +
{/each} @@ -557,4 +592,16 @@ .provider-title span { flex: 1 1 auto; } + .inputContainer { + display: flex; + flex-direction: row; + } + .input { + flex: 1; + } + .copy { + display: flex; + align-items: center; + margin-left: 10px; + } diff --git a/packages/types/src/sdk/auth.ts b/packages/types/src/sdk/auth.ts index d61b679c62..766d18a606 100644 --- a/packages/types/src/sdk/auth.ts +++ b/packages/types/src/sdk/auth.ts @@ -31,5 +31,5 @@ export interface ScannedSession { export interface PlatformLogoutOpts { ctx: BBContext userId: string - keepActiveSession: boolean + keepActiveSession?: boolean } diff --git a/packages/worker/__mocks__/node-fetch.ts b/packages/worker/__mocks__/node-fetch.ts deleted file mode 100644 index 4c7127ee48..0000000000 --- a/packages/worker/__mocks__/node-fetch.ts +++ /dev/null @@ -1 +0,0 @@ -jest.mock("node-fetch", () => jest.fn()) diff --git a/packages/worker/__mocks__/oauth.ts b/packages/worker/__mocks__/oauth.ts new file mode 100644 index 0000000000..8e8122a9e0 --- /dev/null +++ b/packages/worker/__mocks__/oauth.ts @@ -0,0 +1,57 @@ +import * as jwt from "jsonwebtoken" + +const mockOAuth2 = { + getOAuthAccessToken: (code: string, p: any, cb: any) => { + const err = null + const accessToken = "access_token" + const refreshToken = "refresh_token" + + const exp = new Date() + exp.setDate(exp.getDate() + 1) + + const iat = new Date() + iat.setDate(iat.getDate() - 1) + + const claims = { + iss: "test", + sub: "sub", + aud: "clientId", + exp: exp.getTime() / 1000, + iat: iat.getTime() / 1000, + email: "oauth@example.com", + } + + const idToken = jwt.sign(claims, "secret") + + const params = { + id_token: idToken, + } + return cb(err, accessToken, refreshToken, params) + }, + _request: ( + method: string, + url: string, + headers: any, + postBody: any, + accessToken: string, + cb: any + ) => { + const err = null + const body = { + sub: "sub", + user_id: "userId", + name: "OAuth", + family_name: "2", + given_name: "OAuth", + middle_name: "", + } + const res = {} + return cb(err, JSON.stringify(body), res) + }, +} + +const oauth = { + OAuth2: jest.fn(() => mockOAuth2), +} + +export = oauth diff --git a/packages/worker/package.json b/packages/worker/package.json index a9a093e753..c925b9d5d6 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -71,9 +71,11 @@ }, "devDependencies": { "@types/jest": "26.0.23", + "@types/jsonwebtoken": "8.5.1", "@types/koa": "2.13.4", "@types/koa__router": "8.0.11", "@types/node": "14.18.20", + "@types/node-fetch": "2.6.1", "@types/pouchdb": "6.4.0", "@types/uuid": "8.3.4", "@typescript-eslint/parser": "5.12.0", diff --git a/packages/worker/src/api/controllers/global/auth.ts b/packages/worker/src/api/controllers/global/auth.ts index c27fe17ee7..9065267658 100644 --- a/packages/worker/src/api/controllers/global/auth.ts +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -1,4 +1,4 @@ -const core = require("@budibase/backend-core") +import core from "@budibase/backend-core" const { Configs, EmailTemplatePurpose } = require("../../../constants") const { sendEmail, isEmailConfigured } = require("../../../utilities/email") const { setCookie, getCookie, clearCookie, hash, platformLogout } = core.utils diff --git a/packages/worker/src/api/routes/global/auth.js b/packages/worker/src/api/routes/global/auth.js index 674279a6f4..2bf6bb68bf 100644 --- a/packages/worker/src/api/routes/global/auth.js +++ b/packages/worker/src/api/routes/global/auth.js @@ -29,11 +29,13 @@ function buildResetUpdateValidation() { } router + // PASSWORD .post( "/api/global/auth/:tenantId/login", buildAuthValidation(), authController.authenticate ) + .post("/api/global/auth/logout", authController.logout) .post( "/api/global/auth/:tenantId/reset", buildResetValidation(), @@ -44,36 +46,43 @@ router buildResetUpdateValidation(), authController.resetUpdate ) - .post("/api/global/auth/logout", authController.logout) + // INIT .post("/api/global/auth/init", authController.setInitInfo) .get("/api/global/auth/init", authController.getInitInfo) - .get("/api/global/auth/:tenantId/google", authController.googlePreAuth) + + // DATASOURCE - MULTI TENANT .get( "/api/global/auth/:tenantId/datasource/:provider", authController.datasourcePreAuth ) - // single tenancy endpoint - .get("/api/global/auth/google/callback", authController.googleAuth) - .get( - "/api/global/auth/datasource/:provider/callback", - authController.datasourceAuth - ) - // multi-tenancy endpoint - .get("/api/global/auth/:tenantId/google/callback", authController.googleAuth) .get( "/api/global/auth/:tenantId/datasource/:provider/callback", authController.datasourceAuth ) + + // DATASOURCE - SINGLE TENANT - DEPRECATED + .get( + "/api/global/auth/datasource/:provider/callback", + authController.datasourceAuth + ) + + // GOOGLE - MULTI TENANT + .get("/api/global/auth/:tenantId/google", authController.googlePreAuth) + .get("/api/global/auth/:tenantId/google/callback", authController.googleAuth) + + // GOOGLE - SINGLE TENANT - DEPRECATED + .get("/api/global/auth/google/callback", authController.googleAuth) + .get("/api/admin/auth/google/callback", authController.googleAuth) + + // OIDC - MULTI TENANT .get( "/api/global/auth/:tenantId/oidc/configs/:configId", authController.oidcPreAuth ) - // single tenancy endpoint - .get("/api/global/auth/oidc/callback", authController.oidcAuth) - // multi-tenancy endpoint .get("/api/global/auth/:tenantId/oidc/callback", authController.oidcAuth) - // deprecated - used by the default system before tenancy - .get("/api/admin/auth/google/callback", authController.googleAuth) + + // OIDC - SINGLE TENANT - DEPRECATED + .get("/api/global/auth/oidc/callback", authController.oidcAuth) .get("/api/admin/auth/oidc/callback", authController.oidcAuth) module.exports = router diff --git a/packages/worker/src/api/routes/global/tests/auth.spec.ts b/packages/worker/src/api/routes/global/tests/auth.spec.ts index 45c8a62cc7..0d47857ac1 100644 --- a/packages/worker/src/api/routes/global/tests/auth.spec.ts +++ b/packages/worker/src/api/routes/global/tests/auth.spec.ts @@ -3,6 +3,12 @@ import { TestConfiguration, mocks } from "../../../../tests" const sendMailMock = mocks.email.mock() import { events } from "@budibase/backend-core" +const expectSetAuthCookie = (res: any) => { + expect( + res.get("Set-Cookie").find((c: string) => c.startsWith("budibase:auth")) + ).toBeDefined() +} + describe("/api/global/auth", () => { const config = new TestConfiguration() @@ -18,92 +24,155 @@ describe("/api/global/auth", () => { jest.clearAllMocks() }) - it("should logout", async () => { - await config.api.auth.logout() - expect(events.auth.logout).toBeCalledTimes(1) - }) - - it("should be able to generate password reset email", async () => { - const { res, code } = await config.api.auth.requestPasswordReset( - sendMailMock - ) - const user = await config.getUser("test@test.com") - - expect(res.body).toEqual({ - message: "Please check your email for a reset link.", + describe("password", () => { + describe("POST /api/global/auth/:tenantId/login", () => { + it("should login", () => {}) }) - expect(sendMailMock).toHaveBeenCalled() - expect(code).toBeDefined() - expect(events.user.passwordResetRequested).toBeCalledTimes(1) - expect(events.user.passwordResetRequested).toBeCalledWith(user) + describe("POST /api/global/auth/logout", () => { + it("should logout", async () => { + await config.api.auth.logout() + expect(events.auth.logout).toBeCalledTimes(1) + + // TODO: Verify sessions deleted + }) + }) + + describe("POST /api/global/auth/:tenantId/reset", () => { + it("should generate password reset email", async () => { + const { res, code } = await config.api.auth.requestPasswordReset( + sendMailMock + ) + const user = await config.getUser("test@test.com") + + expect(res.body).toEqual({ + message: "Please check your email for a reset link.", + }) + expect(sendMailMock).toHaveBeenCalled() + + expect(code).toBeDefined() + expect(events.user.passwordResetRequested).toBeCalledTimes(1) + expect(events.user.passwordResetRequested).toBeCalledWith(user) + }) + }) + + describe("POST /api/global/auth/:tenantId/reset/update", () => { + it("should reset password", async () => { + const { code } = await config.api.auth.requestPasswordReset( + sendMailMock + ) + const user = await config.getUser("test@test.com") + delete user.password + + const res = await config.api.auth.updatePassword(code) + + expect(res.body).toEqual({ message: "password reset successfully." }) + expect(events.user.passwordReset).toBeCalledTimes(1) + expect(events.user.passwordReset).toBeCalledWith(user) + + // TODO: Login using new password + }) + }) }) - it("should allow resetting user password with code", async () => { - const { code } = await config.api.auth.requestPasswordReset(sendMailMock) - const user = await config.getUser("test@test.com") - delete user.password + describe("init", () => { + describe("POST /api/global/auth/init", () => {}) - const res = await config.api.auth.updatePassword(code) + describe("GET /api/global/auth/init", () => {}) + }) - expect(res.body).toEqual({ message: "password reset successfully." }) - expect(events.user.passwordReset).toBeCalledTimes(1) - expect(events.user.passwordReset).toBeCalledWith(user) + describe("datasource", () => { + // MULTI TENANT + + describe("GET /api/global/auth/:tenantId/datasource/:provider", () => {}) + + describe("GET /api/global/auth/:tenantId/datasource/:provider/callback", () => {}) + + // SINGLE TENANT + + describe("GET /api/global/auth/datasource/:provider/callback", () => {}) + }) + + describe("google", () => { + // MULTI TENANT + + describe("GET /api/global/auth/:tenantId/google", () => {}) + + describe("GET /api/global/auth/:tenantId/google/callback", () => {}) + + // SINGLE TENANT + + describe("GET /api/global/auth/google/callback", () => {}) + + describe("GET /api/admin/auth/google/callback", () => {}) }) describe("oidc", () => { - const auth = require("@budibase/backend-core/auth") - - const passportSpy = jest.spyOn(auth.passport, "authenticate") - let oidcConf - let chosenConfig: any - let configId: string - - // mock the oidc strategy implementation and return value - let strategyFactory = jest.fn() - let mockStrategyReturn = jest.fn() - let mockStrategyConfig = jest.fn() - auth.oidc.fetchStrategyConfig = mockStrategyConfig - - strategyFactory.mockReturnValue(mockStrategyReturn) - auth.oidc.strategyFactory = strategyFactory - beforeEach(async () => { - oidcConf = await config.saveOIDCConfig() - chosenConfig = oidcConf.config.configs[0] - configId = chosenConfig.uuid - mockStrategyConfig.mockReturnValue(chosenConfig) + jest.clearAllMocks() + mockGetWellKnownConfig() + + // see: __mocks__/oauth + // for associated mocking inside passport }) - afterEach(() => { - expect(strategyFactory).toBeCalledWith(chosenConfig, expect.any(Function)) - }) + const generateOidcConfig = async () => { + const oidcConf = await config.saveOIDCConfig() + const chosenConfig = oidcConf.config.configs[0] + return chosenConfig.uuid + } - describe("oidc configs", () => { - it("should load strategy and delegate to passport", async () => { - await config.api.configs.getOIDCConfig(configId) + const mockGetWellKnownConfig = () => { + mocks.fetch.mockReturnValue({ + ok: true, + json: () => ({ + issuer: "test", + authorization_endpoint: "http://localhost/auth", + token_endpoint: "http://localhost/token", + userinfo_endpoint: "http://localhost/userinfo", + }), + }) + } - expect(passportSpy).toBeCalledWith(mockStrategyReturn, { - scope: ["profile", "email", "offline_access"], - }) - expect(passportSpy.mock.calls.length).toBe(1) + // MULTI TENANT + describe("GET /api/global/auth/:tenantId/oidc/configs/:configId", () => { + it("redirects to auth provider", async () => { + const configId = await generateOidcConfig() + + const res = await config.api.configs.getOIDCConfig(configId) + + expect(res.status).toBe(302) + const location: string = res.get("location") + expect( + location.startsWith( + "http://localhost/auth?response_type=code&client_id=clientId&redirect_uri=http%3A%2F%2Flocalhost%3A10000%2Fapi%2Fglobal%2Fauth%2Fdefault%2Foidc%2Fcallback&scope=openid%20profile%20email%20offline_access" + ) + ).toBe(true) }) }) - describe("oidc callback", () => { - it("should load strategy and delegate to passport", async () => { - await config.api.configs.OIDCCallback(configId) + describe("GET /api/global/auth/:tenantId/oidc/callback", () => { + it("logs in", async () => { + const configId = await generateOidcConfig() + const preAuthRes = await config.api.configs.getOIDCConfig(configId) - expect(passportSpy).toBeCalledWith( - mockStrategyReturn, - { - successRedirect: "/", - failureRedirect: "/error", - }, - expect.anything() - ) - expect(passportSpy.mock.calls.length).toBe(1) + const res = await config.api.configs.OIDCCallback(configId, preAuthRes) + + expect(events.auth.login).toBeCalledWith("oidc") + expect(events.auth.login).toBeCalledTimes(1) + expect(res.status).toBe(302) + const location: string = res.get("location") + expect(location).toBe("/") + expectSetAuthCookie(res) }) }) + + // SINGLE TENANT + + describe("GET /api/global/auth/oidc/callback", () => {}) + + describe("GET /api/global/auth/oidc/callback", () => {}) + + describe("GET /api/admin/auth/oidc/callback", () => {}) }) }) diff --git a/packages/worker/src/api/routes/system/tests/tenants.spec.ts b/packages/worker/src/api/routes/system/tests/tenants.spec.ts index 8b3bcc99f5..af509b402e 100644 --- a/packages/worker/src/api/routes/system/tests/tenants.spec.ts +++ b/packages/worker/src/api/routes/system/tests/tenants.spec.ts @@ -1,7 +1,7 @@ import { TestConfiguration } from "../../../../tests" import { tenancy } from "@budibase/backend-core" -describe("/api/global/workspaces", () => { +describe("/api/global/tenants", () => { const config = new TestConfiguration() beforeAll(async () => { diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index 2927915284..11da7c2b03 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -171,8 +171,11 @@ class TestConfiguration { } cookieHeader(cookies: any) { + if (!Array.isArray(cookies)) { + cookies = [cookies] + } return { - Cookie: [cookies], + Cookie: cookies, } } @@ -288,11 +291,6 @@ class TestConfiguration { // CONFIGS - OIDC - getOIDConfigCookie(configId: string) { - const token = auth.jwt.sign(configId, env.JWT_SECRET) - return this.cookieHeader([[`${Cookies.OIDC_CONFIG}=${token}`]]) - } - async saveOIDCConfig() { await this.deleteConfig(Configs.OIDC) const config = structures.configs.oidc() diff --git a/packages/worker/src/tests/api/configs.ts b/packages/worker/src/tests/api/configs.ts index 6799229f58..10413dfdd6 100644 --- a/packages/worker/src/tests/api/configs.ts +++ b/packages/worker/src/tests/api/configs.ts @@ -23,10 +23,20 @@ export class ConfigAPI extends TestAPI { .expect(200) } - OIDCCallback = (configId: string) => { + OIDCCallback = (configId: string, preAuthRes: any) => { + const cookie = this.config.cookieHeader(preAuthRes.get("set-cookie")) + const setKoaSession = cookie.Cookie.find((c: string) => + c.includes("koa:sess") + ) + const koaSession = setKoaSession.split("=")[1] + "==" + const sessionContent = JSON.parse( + Buffer.from(koaSession, "base64").toString("utf-8") + ) + const handle = sessionContent["openidconnect:localhost"].state.handle return this.request .get(`/api/global/auth/${this.config.getTenantId()}/oidc/callback`) - .set(this.config.getOIDConfigCookie(configId)) + .query({ code: "test", state: handle }) + .set(cookie) } getOIDCConfig = (configId: string) => { diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index cfdba32e7a..23f54ae770 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -1284,6 +1284,13 @@ resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== +"@types/jsonwebtoken@8.5.1": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#56958cb2d80f6d74352bd2e501a018e2506a8a84" + integrity sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw== + dependencies: + "@types/node" "*" + "@types/keygrip@*": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" @@ -1334,6 +1341,14 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== +"@types/node-fetch@2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" + integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*": version "17.0.41" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.41.tgz#1607b2fd3da014ae5d4d1b31bc792a39348dfb9b" @@ -3412,6 +3427,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" diff --git a/qa-core/src/config/internal-api/TestConfiguration/applications.ts b/qa-core/src/config/internal-api/TestConfiguration/applications.ts index cb0558222e..13d0969854 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/applications.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/applications.ts @@ -114,8 +114,6 @@ export default class AppApi { return [response, json] } - - async delete(appId: string): Promise<[Response, any]> { const response = await this.api.del(`/applications/${appId}`) const json = await response.json() @@ -123,7 +121,11 @@ export default class AppApi { return [response, json] } - async update(appId: string, oldName: string, body: any): Promise<[Response, Application]> { + async update( + appId: string, + oldName: string, + body: any + ): Promise<[Response, Application]> { const response = await this.api.put(`/applications/${appId}`, { body }) const json = await response.json() expect(response).toHaveStatusCode(200) @@ -142,7 +144,6 @@ export default class AppApi { const json = await response.json() expect(response).toHaveStatusCode(200) if (screenExists) { - expect(json.routes["/test"]).toBeTruthy() } else { expect(json.routes["/test"]).toBeUndefined() diff --git a/qa-core/src/config/internal-api/TestConfiguration/tables.ts b/qa-core/src/config/internal-api/TestConfiguration/tables.ts index ed0ab78cad..5b7e1648a0 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/tables.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/tables.ts @@ -46,9 +46,7 @@ export default class TablesApi { const response = await this.api.del(`/tables/${id}/${revId}`) const json = await response.json() expect(response).toHaveStatusCode(200) - expect(json.message).toEqual( - `Table ${id} deleted.` - ) + expect(json.message).toEqual(`Table ${id} deleted.`) return [response, json] } } diff --git a/qa-core/src/config/internal-api/fixtures/applications.ts b/qa-core/src/config/internal-api/fixtures/applications.ts index abdd674577..200aa9abff 100644 --- a/qa-core/src/config/internal-api/fixtures/applications.ts +++ b/qa-core/src/config/internal-api/fixtures/applications.ts @@ -1,7 +1,6 @@ import generator from "../../generator" import { Application } from "@budibase/server/api/controllers/public/mapping/types" - const generate = ( overrides: Partial = {} ): Partial => ({ diff --git a/qa-core/src/tests/internal-api/applications/applications.spec.ts b/qa-core/src/tests/internal-api/applications/applications.spec.ts index 4b9b66ec65..4b3208ee10 100644 --- a/qa-core/src/tests/internal-api/applications/applications.spec.ts +++ b/qa-core/src/tests/internal-api/applications/applications.spec.ts @@ -48,7 +48,8 @@ describe("Internal API - Application creation, update, publish and delete", () = }) config.applications.api.appId = app.appId - const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(app.appId) + const [appPackageResponse, appPackageJson] = + await config.applications.getAppPackage(app.appId) expect(appPackageJson.application.name).toEqual(app.name) expect(appPackageJson.application.version).toEqual(app.version) expect(appPackageJson.application.url).toEqual(app.url) @@ -72,7 +73,6 @@ describe("Internal API - Application creation, update, publish and delete", () = config.applications.api.appId = db.getProdAppID(app.appId) await config.applications.canRender() - // unpublish app await config.applications.unpublish(app.appId) }) @@ -109,22 +109,16 @@ describe("Internal API - Application creation, update, publish and delete", () = config.applications.api.appId = app.appId - await config.applications.update( - app.appId, - app.name, - { - name: generator.word(), - } - ) + await config.applications.update(app.appId, app.name, { + name: generator.word(), + }) }) it("POST - Revert Changes without changes", async () => { const app = await config.applications.create(generateApp()) config.applications.api.appId = app.appId - await config.applications.revertUnpublished( - app.appId - ) + await config.applications.revertUnpublished(app.appId) }) it("POST - Revert Changes", async () => { @@ -134,20 +128,14 @@ describe("Internal API - Application creation, update, publish and delete", () = // publish app await config.applications.publish(app.url) - // Change/add component to the app - await config.screen.create( - generateScreen("BASIC") - ) + await config.screen.create(generateScreen("BASIC")) // // Revert the app to published state - await config.applications.revertPublished( - app.appId - ) + await config.applications.revertPublished(app.appId) // Check screen is removed await config.applications.getRoutes() - }) it("DELETE - Delete an application", async () => { @@ -155,5 +143,4 @@ describe("Internal API - Application creation, update, publish and delete", () = await config.applications.delete(app.appId) }) - }) diff --git a/qa-core/src/tests/internal-api/screens/screens.spec.ts b/qa-core/src/tests/internal-api/screens/screens.spec.ts index 2dc7962914..218d71cb0d 100644 --- a/qa-core/src/tests/internal-api/screens/screens.spec.ts +++ b/qa-core/src/tests/internal-api/screens/screens.spec.ts @@ -38,9 +38,7 @@ describe("Internal API - /screens endpoints", () => { // Create Screen appConfig.applications.api.appId = app.appId - await config.screen.create( - generateScreen("BASIC") - ) + await config.screen.create(generateScreen("BASIC")) // Check screen exists await appConfig.applications.getRoutes(true) @@ -58,6 +56,5 @@ describe("Internal API - /screens endpoints", () => { // Delete Screen await config.screen.delete(screen._id!, screen._rev!) - }) }) diff --git a/qa-core/src/tests/internal-api/tables/tables.spec.ts b/qa-core/src/tests/internal-api/tables/tables.spec.ts index 69ad0fed7b..4f9e4299cf 100644 --- a/qa-core/src/tests/internal-api/tables/tables.spec.ts +++ b/qa-core/src/tests/internal-api/tables/tables.spec.ts @@ -3,93 +3,87 @@ import { Application } from "@budibase/server/api/controllers/public/mapping/typ import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient" import generator from "../../../config/generator" import { - generateTable, - generateNewColumnForTable, + generateTable, + generateNewColumnForTable, } from "../../../config/internal-api/fixtures/table" import { generateNewRowForTable } from "../../../config/internal-api/fixtures/rows" describe("Internal API - Application creation, update, publish and delete", () => { - const api = new InternalAPIClient() - const config = new TestConfiguration(api) + const api = new InternalAPIClient() + const config = new TestConfiguration(api) - beforeAll(async () => { - await config.beforeAll() + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + async function createAppFromTemplate() { + return config.applications.create({ + name: generator.word(), + url: `/${generator.word()}`, + useTemplate: "true", + templateName: "Near Miss Register", + templateKey: "app/near-miss-register", + templateFile: undefined, }) + } - afterAll(async () => { - await config.afterAll() - }) + it("Operations on Tables", async () => { + // create the app + const appName = generator.word() + const app = await createAppFromTemplate() + config.applications.api.appId = app.appId - async function createAppFromTemplate() { - return config.applications.create({ - name: generator.word(), - url: `/${generator.word()}`, - useTemplate: "true", - templateName: "Near Miss Register", - templateKey: "app/near-miss-register", - templateFile: undefined, - }) + // Get current tables: expect 2 in this template + await config.tables.getAll(2) + + // Add new table + const [createdTableResponse, createdTableData] = await config.tables.save( + generateTable() + ) + + //Table was added + await config.tables.getAll(3) + + //Get information about the table + await config.tables.getTableById(createdTableData._id) + + //Add Column to table + const newColumn = generateNewColumnForTable(createdTableData) + const [addColumnResponse, addColumnData] = await config.tables.save( + newColumn, + true + ) + + //Add Row to table + const newRow = generateNewRowForTable(addColumnData._id) + await config.rows.add(addColumnData._id, newRow) + + //Get Row from table + const [getRowResponse, getRowData] = await config.rows.getAll( + addColumnData._id + ) + + //Delete Row from table + const rowToDelete = { + rows: [getRowData[0]], } + const [deleteRowResponse, deleteRowData] = await config.rows.delete( + addColumnData._id, + rowToDelete + ) + expect(deleteRowData[0]._id).toEqual(getRowData[0]._id) - it("Operations on Tables", async () => { - // create the app - const appName = generator.word() - const app = await createAppFromTemplate() - config.applications.api.appId = app.appId + //Delete the table + const [deleteTableResponse, deleteTable] = await config.tables.delete( + addColumnData._id, + addColumnData._rev + ) - // Get current tables: expect 2 in this template - await config.tables.getAll(2) - - // Add new table - const [createdTableResponse, createdTableData] = await config.tables.save( - generateTable() - ) - - //Table was added - await config.tables.getAll(3) - - //Get information about the table - await config.tables.getTableById( - createdTableData._id - ) - - //Add Column to table - const newColumn = generateNewColumnForTable(createdTableData) - const [addColumnResponse, addColumnData] = await config.tables.save( - newColumn, - true - ) - - //Add Row to table - const newRow = generateNewRowForTable(addColumnData._id) - await config.rows.add( - addColumnData._id, - newRow - ) - - //Get Row from table - const [getRowResponse, getRowData] = await config.rows.getAll( - addColumnData._id - ) - - //Delete Row from table - const rowToDelete = { - rows: [getRowData[0]], - } - const [deleteRowResponse, deleteRowData] = await config.rows.delete( - addColumnData._id, - rowToDelete - ) - expect(deleteRowData[0]._id).toEqual(getRowData[0]._id) - - //Delete the table - const [deleteTableResponse, deleteTable] = await config.tables.delete( - addColumnData._id, - addColumnData._rev - ) - - - //Table was deleted - await config.tables.getAll(2) - }) + //Table was deleted + await config.tables.getAll(2) + }) }) From 0367d466c3c566be54d45d040654929966c836ff Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 16 Nov 2022 12:04:59 +0000 Subject: [PATCH 11/15] v2.1.22-alpha.5 --- lerna.json | 2 +- packages/backend-core/package.json | 4 ++-- packages/bbui/package.json | 4 ++-- packages/builder/package.json | 10 +++++----- packages/cli/package.json | 8 ++++---- packages/client/package.json | 8 ++++---- packages/frontend-core/package.json | 4 ++-- packages/sdk/package.json | 2 +- packages/server/package.json | 10 +++++----- packages/string-templates/package.json | 2 +- packages/types/package.json | 2 +- packages/worker/package.json | 8 ++++---- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lerna.json b/lerna.json index b130e9cf1f..80ba94078e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index ac1d68c6a1..e695b65303 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "2.1.22-alpha.4", + "@budibase/types": "2.1.22-alpha.5", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 827ff1a325..5746d369cd 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "2.1.22-alpha.4", + "@budibase/string-templates": "2.1.22-alpha.5", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/builder/package.json b/packages/builder/package.json index 168016d080..48b92a2f1a 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "license": "GPL-3.0", "private": true, "scripts": { @@ -71,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "2.1.22-alpha.4", - "@budibase/client": "2.1.22-alpha.4", - "@budibase/frontend-core": "2.1.22-alpha.4", - "@budibase/string-templates": "2.1.22-alpha.4", + "@budibase/bbui": "2.1.22-alpha.5", + "@budibase/client": "2.1.22-alpha.5", + "@budibase/frontend-core": "2.1.22-alpha.5", + "@budibase/string-templates": "2.1.22-alpha.5", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index cfe7f7cd2a..3ef135ce0d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { @@ -26,9 +26,9 @@ "outputPath": "build" }, "dependencies": { - "@budibase/backend-core": "2.1.22-alpha.4", - "@budibase/string-templates": "2.1.22-alpha.4", - "@budibase/types": "2.1.22-alpha.4", + "@budibase/backend-core": "2.1.22-alpha.5", + "@budibase/string-templates": "2.1.22-alpha.5", + "@budibase/types": "2.1.22-alpha.5", "axios": "0.21.2", "chalk": "4.1.0", "cli-progress": "3.11.2", diff --git a/packages/client/package.json b/packages/client/package.json index 143ff6b27b..09299de447 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "2.1.22-alpha.4", - "@budibase/frontend-core": "2.1.22-alpha.4", - "@budibase/string-templates": "2.1.22-alpha.4", + "@budibase/bbui": "2.1.22-alpha.5", + "@budibase/frontend-core": "2.1.22-alpha.5", + "@budibase/string-templates": "2.1.22-alpha.5", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index e60f3d3a29..f1f56fa5a1 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -1,12 +1,12 @@ { "name": "@budibase/frontend-core", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", "svelte": "src/index.js", "dependencies": { - "@budibase/bbui": "2.1.22-alpha.4", + "@budibase/bbui": "2.1.22-alpha.5", "lodash": "^4.17.21", "svelte": "^3.46.2" } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 22e5f7e65d..805d8c7493 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/sdk", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "description": "Budibase Public API SDK", "author": "Budibase", "license": "MPL-2.0", diff --git a/packages/server/package.json b/packages/server/package.json index 0d43aee1e5..12d8c2bc56 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -43,11 +43,11 @@ "license": "GPL-3.0", "dependencies": { "@apidevtools/swagger-parser": "10.0.3", - "@budibase/backend-core": "2.1.22-alpha.4", - "@budibase/client": "2.1.22-alpha.4", + "@budibase/backend-core": "2.1.22-alpha.5", + "@budibase/client": "2.1.22-alpha.5", "@budibase/pro": "2.1.22-alpha.4", - "@budibase/string-templates": "2.1.22-alpha.4", - "@budibase/types": "2.1.22-alpha.4", + "@budibase/string-templates": "2.1.22-alpha.5", + "@budibase/types": "2.1.22-alpha.5", "@bull-board/api": "3.7.0", "@bull-board/koa": "3.9.4", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 2fa6082232..f644de4da3 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/types/package.json b/packages/types/package.json index 55ac72c7d7..9653472795 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "description": "Budibase types", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/worker/package.json b/packages/worker/package.json index fdadff86a6..6d3cfefdba 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "2.1.22-alpha.4", + "version": "2.1.22-alpha.5", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -36,10 +36,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "2.1.22-alpha.4", + "@budibase/backend-core": "2.1.22-alpha.5", "@budibase/pro": "2.1.22-alpha.4", - "@budibase/string-templates": "2.1.22-alpha.4", - "@budibase/types": "2.1.22-alpha.4", + "@budibase/string-templates": "2.1.22-alpha.5", + "@budibase/types": "2.1.22-alpha.5", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", From ec178a95d18048388dc38de5fd3f386cff208911 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 16 Nov 2022 12:08:51 +0000 Subject: [PATCH 12/15] Update pro version to 2.1.22-alpha.5 --- packages/server/package.json | 2 +- packages/server/yarn.lock | 30 +++++++++++++++--------------- packages/worker/package.json | 2 +- packages/worker/yarn.lock | 30 +++++++++++++++--------------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index 12d8c2bc56..edce54787c 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -45,7 +45,7 @@ "@apidevtools/swagger-parser": "10.0.3", "@budibase/backend-core": "2.1.22-alpha.5", "@budibase/client": "2.1.22-alpha.5", - "@budibase/pro": "2.1.22-alpha.4", + "@budibase/pro": "2.1.22-alpha.5", "@budibase/string-templates": "2.1.22-alpha.5", "@budibase/types": "2.1.22-alpha.5", "@bull-board/api": "3.7.0", diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 905af68259..33fe32d3c5 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1273,12 +1273,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.1.22-alpha.4": - version "2.1.22-alpha.4" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.4.tgz#25e186273d2416f1fe8ed639146d3d8e977c8bc8" - integrity sha512-uii4hL/KvgMH8o713x54B2bGdtgYWE4AUxkzPs/2LCEa6Z3eeahziaElsRyc8oELqh0f71PYDdRC6gSJZZ1hdQ== +"@budibase/backend-core@2.1.22-alpha.5": + version "2.1.22-alpha.5" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.5.tgz#171bff9affca8a372c08aa5007f6e55bfb202f9c" + integrity sha512-N/+w/nsUgYXUE9zSMJsKb3lrV3gWhXfyMgl+5cKMEi9o9wT/Zvi20nBtkBb2kxQE8RrL8C37+5531tdj0adJZA== dependencies: - "@budibase/types" "2.1.22-alpha.4" + "@budibase/types" "2.1.22-alpha.5" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -1360,13 +1360,13 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@2.1.22-alpha.4": - version "2.1.22-alpha.4" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.4.tgz#860edf8e6f022ea4bf33440d1c3173cc3f0ee9a1" - integrity sha512-L1EezM8ZdUWb+XlBgrslwCZ67wxUBRgTH3S/uGTZqGL1jGvEm7w4IQTyH55AqTABx5ZyNyAlOYKCipGjb5fplw== +"@budibase/pro@2.1.22-alpha.5": + version "2.1.22-alpha.5" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.5.tgz#d86f850bc35d81b5f41807b995d5896acca36aaa" + integrity sha512-Mv3uIq3kJYuUdj0Hdz6toTShgWdGpf1k7962cGds5vrTtzESbXpB8HkL7WhgDnbOCH92AP3mKmuhkxe9T1On7A== dependencies: - "@budibase/backend-core" "2.1.22-alpha.4" - "@budibase/types" "2.1.22-alpha.4" + "@budibase/backend-core" "2.1.22-alpha.5" + "@budibase/types" "2.1.22-alpha.5" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" @@ -1390,10 +1390,10 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/types@2.1.22-alpha.4": - version "2.1.22-alpha.4" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.4.tgz#cadcafae414610e0bec101380dc0300ad6ad0702" - integrity sha512-xkrgqzkd4a9xb94Z05yh91fD7/2LSzrvvkS7eNV5rL7+9ddMWHUFVCCm/ggK+qnXKPhqIMq5TmGeLu28HjcJJQ== +"@budibase/types@2.1.22-alpha.5": + version "2.1.22-alpha.5" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.5.tgz#b90116da2fbccbd5f20acfe5189c1fb970cb5d3b" + integrity sha512-VRkjc5rtpZlj6iBDgQx/L4EGqSRupcqG/tu+tGZNJjEV92T/yiRt6+7UC+uIQjEUawcK1RDSqXLdk3g7+kFv0g== "@bull-board/api@3.7.0": version "3.7.0" diff --git a/packages/worker/package.json b/packages/worker/package.json index 6d3cfefdba..c87dfd4e3c 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -37,7 +37,7 @@ "license": "GPL-3.0", "dependencies": { "@budibase/backend-core": "2.1.22-alpha.5", - "@budibase/pro": "2.1.22-alpha.4", + "@budibase/pro": "2.1.22-alpha.5", "@budibase/string-templates": "2.1.22-alpha.5", "@budibase/types": "2.1.22-alpha.5", "@koa/router": "8.0.8", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 3dae0b88ea..ba04eb0cca 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -470,12 +470,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.1.22-alpha.4": - version "2.1.22-alpha.4" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.4.tgz#25e186273d2416f1fe8ed639146d3d8e977c8bc8" - integrity sha512-uii4hL/KvgMH8o713x54B2bGdtgYWE4AUxkzPs/2LCEa6Z3eeahziaElsRyc8oELqh0f71PYDdRC6gSJZZ1hdQ== +"@budibase/backend-core@2.1.22-alpha.5": + version "2.1.22-alpha.5" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.5.tgz#171bff9affca8a372c08aa5007f6e55bfb202f9c" + integrity sha512-N/+w/nsUgYXUE9zSMJsKb3lrV3gWhXfyMgl+5cKMEi9o9wT/Zvi20nBtkBb2kxQE8RrL8C37+5531tdj0adJZA== dependencies: - "@budibase/types" "2.1.22-alpha.4" + "@budibase/types" "2.1.22-alpha.5" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -507,22 +507,22 @@ uuid "8.3.2" zlib "1.0.5" -"@budibase/pro@2.1.22-alpha.4": - version "2.1.22-alpha.4" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.4.tgz#860edf8e6f022ea4bf33440d1c3173cc3f0ee9a1" - integrity sha512-L1EezM8ZdUWb+XlBgrslwCZ67wxUBRgTH3S/uGTZqGL1jGvEm7w4IQTyH55AqTABx5ZyNyAlOYKCipGjb5fplw== +"@budibase/pro@2.1.22-alpha.5": + version "2.1.22-alpha.5" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.5.tgz#d86f850bc35d81b5f41807b995d5896acca36aaa" + integrity sha512-Mv3uIq3kJYuUdj0Hdz6toTShgWdGpf1k7962cGds5vrTtzESbXpB8HkL7WhgDnbOCH92AP3mKmuhkxe9T1On7A== dependencies: - "@budibase/backend-core" "2.1.22-alpha.4" - "@budibase/types" "2.1.22-alpha.4" + "@budibase/backend-core" "2.1.22-alpha.5" + "@budibase/types" "2.1.22-alpha.5" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" node-fetch "^2.6.1" -"@budibase/types@2.1.22-alpha.4": - version "2.1.22-alpha.4" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.4.tgz#cadcafae414610e0bec101380dc0300ad6ad0702" - integrity sha512-xkrgqzkd4a9xb94Z05yh91fD7/2LSzrvvkS7eNV5rL7+9ddMWHUFVCCm/ggK+qnXKPhqIMq5TmGeLu28HjcJJQ== +"@budibase/types@2.1.22-alpha.5": + version "2.1.22-alpha.5" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.5.tgz#b90116da2fbccbd5f20acfe5189c1fb970cb5d3b" + integrity sha512-VRkjc5rtpZlj6iBDgQx/L4EGqSRupcqG/tu+tGZNJjEV92T/yiRt6+7UC+uIQjEUawcK1RDSqXLdk3g7+kFv0g== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" From dc7002270874327719384e05684a554c2d2e918f Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 16 Nov 2022 13:06:30 +0000 Subject: [PATCH 13/15] Test fixes --- packages/backend-core/tests/jestSetup.ts | 3 +++ packages/backend-core/tests/utilities/mocks/fetch.ts | 10 ++++++++-- .../backend-core/tests/utilities/structures/koa.ts | 3 ++- packages/worker/src/api/index.ts | 4 ++-- packages/worker/src/tests/jestSetup.ts | 3 +++ 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/backend-core/tests/jestSetup.ts b/packages/backend-core/tests/jestSetup.ts index 30b645d0e5..7870a721aa 100644 --- a/packages/backend-core/tests/jestSetup.ts +++ b/packages/backend-core/tests/jestSetup.ts @@ -1,6 +1,9 @@ import env from "../src/environment" import { mocks } from "./utilities" +// must explicitly enable fetch mock +mocks.fetch.enable() + // mock all dates to 2020-01-01T00:00:00.000Z // use tk.reset() to use real dates in individual tests import tk from "timekeeper" diff --git a/packages/backend-core/tests/utilities/mocks/fetch.ts b/packages/backend-core/tests/utilities/mocks/fetch.ts index 573b47db9f..eeb0ccda45 100644 --- a/packages/backend-core/tests/utilities/mocks/fetch.ts +++ b/packages/backend-core/tests/utilities/mocks/fetch.ts @@ -1,4 +1,10 @@ const mockFetch = jest.fn() -jest.mock("node-fetch", () => mockFetch) -export default mockFetch +const enable = () => { + jest.mock("node-fetch", () => mockFetch) +} + +export default { + ...mockFetch, + enable, +} diff --git a/packages/backend-core/tests/utilities/structures/koa.ts b/packages/backend-core/tests/utilities/structures/koa.ts index 7084c90360..a33dca1546 100644 --- a/packages/backend-core/tests/utilities/structures/koa.ts +++ b/packages/backend-core/tests/utilities/structures/koa.ts @@ -1,10 +1,11 @@ -import { createMockContext } from "@shopify/jest-koa-mocks" +import { createMockContext, createMockCookies } from "@shopify/jest-koa-mocks" import { BBContext } from "@budibase/types" export const newContext = (): BBContext => { const ctx = createMockContext() return { ...ctx, + cookies: createMockCookies(), request: { ...ctx.request, body: {}, diff --git a/packages/worker/src/api/index.ts b/packages/worker/src/api/index.ts index 28fbbdb4b5..9a32792691 100644 --- a/packages/worker/src/api/index.ts +++ b/packages/worker/src/api/index.ts @@ -45,11 +45,11 @@ const PUBLIC_ENDPOINTS = [ method: "POST", }, { - route: "api/system/environment", + route: "/api/system/environment", method: "GET", }, { - route: "api/system/status", + route: "/api/system/status", method: "GET", }, // TODO: This should be an internal api diff --git a/packages/worker/src/tests/jestSetup.ts b/packages/worker/src/tests/jestSetup.ts index 4cbc91eaf8..fee704ae45 100644 --- a/packages/worker/src/tests/jestSetup.ts +++ b/packages/worker/src/tests/jestSetup.ts @@ -14,6 +14,9 @@ env._set("DISABLE_ACCOUNT_PORTAL", false) import { mocks } from "@budibase/backend-core/tests" +// must explicitly enable fetch mock +mocks.fetch.enable() + // mock all dates to 2020-01-01T00:00:00.000Z // use tk.reset() to use real dates in individual tests const tk = require("timekeeper") From 395a9a21c27c534fd2b4297f5d0d19a341089993 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 16 Nov 2022 13:33:51 +0000 Subject: [PATCH 14/15] v2.1.22-alpha.6 --- lerna.json | 2 +- packages/backend-core/package.json | 4 ++-- packages/bbui/package.json | 4 ++-- packages/builder/package.json | 10 +++++----- packages/cli/package.json | 8 ++++---- packages/client/package.json | 8 ++++---- packages/frontend-core/package.json | 4 ++-- packages/sdk/package.json | 2 +- packages/server/package.json | 10 +++++----- packages/string-templates/package.json | 2 +- packages/types/package.json | 2 +- packages/worker/package.json | 8 ++++---- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lerna.json b/lerna.json index 80ba94078e..55f768ac0c 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 48f7b10020..fa1d1e92e9 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "2.1.22-alpha.5", + "@budibase/types": "2.1.22-alpha.6", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 5746d369cd..169d51a6ca 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "2.1.22-alpha.5", + "@budibase/string-templates": "2.1.22-alpha.6", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/builder/package.json b/packages/builder/package.json index 48b92a2f1a..bcda888beb 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "license": "GPL-3.0", "private": true, "scripts": { @@ -71,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "2.1.22-alpha.5", - "@budibase/client": "2.1.22-alpha.5", - "@budibase/frontend-core": "2.1.22-alpha.5", - "@budibase/string-templates": "2.1.22-alpha.5", + "@budibase/bbui": "2.1.22-alpha.6", + "@budibase/client": "2.1.22-alpha.6", + "@budibase/frontend-core": "2.1.22-alpha.6", + "@budibase/string-templates": "2.1.22-alpha.6", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 3ef135ce0d..b63744ac38 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { @@ -26,9 +26,9 @@ "outputPath": "build" }, "dependencies": { - "@budibase/backend-core": "2.1.22-alpha.5", - "@budibase/string-templates": "2.1.22-alpha.5", - "@budibase/types": "2.1.22-alpha.5", + "@budibase/backend-core": "2.1.22-alpha.6", + "@budibase/string-templates": "2.1.22-alpha.6", + "@budibase/types": "2.1.22-alpha.6", "axios": "0.21.2", "chalk": "4.1.0", "cli-progress": "3.11.2", diff --git a/packages/client/package.json b/packages/client/package.json index 09299de447..eaa2eaac67 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "2.1.22-alpha.5", - "@budibase/frontend-core": "2.1.22-alpha.5", - "@budibase/string-templates": "2.1.22-alpha.5", + "@budibase/bbui": "2.1.22-alpha.6", + "@budibase/frontend-core": "2.1.22-alpha.6", + "@budibase/string-templates": "2.1.22-alpha.6", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index f1f56fa5a1..21d0e27230 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -1,12 +1,12 @@ { "name": "@budibase/frontend-core", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", "svelte": "src/index.js", "dependencies": { - "@budibase/bbui": "2.1.22-alpha.5", + "@budibase/bbui": "2.1.22-alpha.6", "lodash": "^4.17.21", "svelte": "^3.46.2" } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 805d8c7493..56c609c303 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/sdk", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "description": "Budibase Public API SDK", "author": "Budibase", "license": "MPL-2.0", diff --git a/packages/server/package.json b/packages/server/package.json index edce54787c..dcae3d7002 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -43,11 +43,11 @@ "license": "GPL-3.0", "dependencies": { "@apidevtools/swagger-parser": "10.0.3", - "@budibase/backend-core": "2.1.22-alpha.5", - "@budibase/client": "2.1.22-alpha.5", + "@budibase/backend-core": "2.1.22-alpha.6", + "@budibase/client": "2.1.22-alpha.6", "@budibase/pro": "2.1.22-alpha.5", - "@budibase/string-templates": "2.1.22-alpha.5", - "@budibase/types": "2.1.22-alpha.5", + "@budibase/string-templates": "2.1.22-alpha.6", + "@budibase/types": "2.1.22-alpha.6", "@bull-board/api": "3.7.0", "@bull-board/koa": "3.9.4", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index f644de4da3..7655fe4417 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/types/package.json b/packages/types/package.json index 9653472795..2978e8b6c1 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "description": "Budibase types", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/worker/package.json b/packages/worker/package.json index 7bec499494..dfb09c2750 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "2.1.22-alpha.5", + "version": "2.1.22-alpha.6", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -36,10 +36,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "2.1.22-alpha.5", + "@budibase/backend-core": "2.1.22-alpha.6", "@budibase/pro": "2.1.22-alpha.5", - "@budibase/string-templates": "2.1.22-alpha.5", - "@budibase/types": "2.1.22-alpha.5", + "@budibase/string-templates": "2.1.22-alpha.6", + "@budibase/types": "2.1.22-alpha.6", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", From f81c20811a96eb04393dd34c7b3499a8d8ab7ed5 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 16 Nov 2022 13:37:23 +0000 Subject: [PATCH 15/15] Update pro version to 2.1.22-alpha.6 --- packages/server/package.json | 2 +- packages/server/yarn.lock | 30 +++++++++++++++--------------- packages/worker/package.json | 2 +- packages/worker/yarn.lock | 30 +++++++++++++++--------------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index dcae3d7002..188811523a 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -45,7 +45,7 @@ "@apidevtools/swagger-parser": "10.0.3", "@budibase/backend-core": "2.1.22-alpha.6", "@budibase/client": "2.1.22-alpha.6", - "@budibase/pro": "2.1.22-alpha.5", + "@budibase/pro": "2.1.22-alpha.6", "@budibase/string-templates": "2.1.22-alpha.6", "@budibase/types": "2.1.22-alpha.6", "@bull-board/api": "3.7.0", diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 33fe32d3c5..2e54c41cf5 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1273,12 +1273,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.1.22-alpha.5": - version "2.1.22-alpha.5" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.5.tgz#171bff9affca8a372c08aa5007f6e55bfb202f9c" - integrity sha512-N/+w/nsUgYXUE9zSMJsKb3lrV3gWhXfyMgl+5cKMEi9o9wT/Zvi20nBtkBb2kxQE8RrL8C37+5531tdj0adJZA== +"@budibase/backend-core@2.1.22-alpha.6": + version "2.1.22-alpha.6" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.6.tgz#e9886620fcbee6fe0348365cb96ac6484fc5f8fd" + integrity sha512-P3NSgNuQXKmdeT8MLfeCji3ibRSeIIMSOQeNSQBWpaOTA69rpXQk753lHRwUWMpqil/ybsOuE/h1/Y3eTa+/UA== dependencies: - "@budibase/types" "2.1.22-alpha.5" + "@budibase/types" "2.1.22-alpha.6" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -1360,13 +1360,13 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@2.1.22-alpha.5": - version "2.1.22-alpha.5" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.5.tgz#d86f850bc35d81b5f41807b995d5896acca36aaa" - integrity sha512-Mv3uIq3kJYuUdj0Hdz6toTShgWdGpf1k7962cGds5vrTtzESbXpB8HkL7WhgDnbOCH92AP3mKmuhkxe9T1On7A== +"@budibase/pro@2.1.22-alpha.6": + version "2.1.22-alpha.6" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.6.tgz#e4d8238f1727eab85ac72b3ce2fcaec2a5c18456" + integrity sha512-1CKqQ2HMX+/5p24aHpPlUgxoMjKRZxRoyK5fPD/X35Z0mDj+9Ohny3oqbF1fC20pl/20bmcaE4J+q2ph/pbxdQ== dependencies: - "@budibase/backend-core" "2.1.22-alpha.5" - "@budibase/types" "2.1.22-alpha.5" + "@budibase/backend-core" "2.1.22-alpha.6" + "@budibase/types" "2.1.22-alpha.6" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" @@ -1390,10 +1390,10 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/types@2.1.22-alpha.5": - version "2.1.22-alpha.5" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.5.tgz#b90116da2fbccbd5f20acfe5189c1fb970cb5d3b" - integrity sha512-VRkjc5rtpZlj6iBDgQx/L4EGqSRupcqG/tu+tGZNJjEV92T/yiRt6+7UC+uIQjEUawcK1RDSqXLdk3g7+kFv0g== +"@budibase/types@2.1.22-alpha.6": + version "2.1.22-alpha.6" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.6.tgz#a280321373c26a5bf1a52ed119de9178292893c0" + integrity sha512-rVrhs9u7OTzlCxgUFqBu5H6jsMHhwH8uduPhT8Eo7J+Wr4J/0Io7WeuAt1egK6t83JiEPxDBH3WLzAH+04hPVA== "@bull-board/api@3.7.0": version "3.7.0" diff --git a/packages/worker/package.json b/packages/worker/package.json index dfb09c2750..0ad9fd6758 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -37,7 +37,7 @@ "license": "GPL-3.0", "dependencies": { "@budibase/backend-core": "2.1.22-alpha.6", - "@budibase/pro": "2.1.22-alpha.5", + "@budibase/pro": "2.1.22-alpha.6", "@budibase/string-templates": "2.1.22-alpha.6", "@budibase/types": "2.1.22-alpha.6", "@koa/router": "8.0.8", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index a44960276f..1bcc1f0534 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -470,12 +470,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.1.22-alpha.5": - version "2.1.22-alpha.5" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.5.tgz#171bff9affca8a372c08aa5007f6e55bfb202f9c" - integrity sha512-N/+w/nsUgYXUE9zSMJsKb3lrV3gWhXfyMgl+5cKMEi9o9wT/Zvi20nBtkBb2kxQE8RrL8C37+5531tdj0adJZA== +"@budibase/backend-core@2.1.22-alpha.6": + version "2.1.22-alpha.6" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.22-alpha.6.tgz#e9886620fcbee6fe0348365cb96ac6484fc5f8fd" + integrity sha512-P3NSgNuQXKmdeT8MLfeCji3ibRSeIIMSOQeNSQBWpaOTA69rpXQk753lHRwUWMpqil/ybsOuE/h1/Y3eTa+/UA== dependencies: - "@budibase/types" "2.1.22-alpha.5" + "@budibase/types" "2.1.22-alpha.6" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -507,22 +507,22 @@ uuid "8.3.2" zlib "1.0.5" -"@budibase/pro@2.1.22-alpha.5": - version "2.1.22-alpha.5" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.5.tgz#d86f850bc35d81b5f41807b995d5896acca36aaa" - integrity sha512-Mv3uIq3kJYuUdj0Hdz6toTShgWdGpf1k7962cGds5vrTtzESbXpB8HkL7WhgDnbOCH92AP3mKmuhkxe9T1On7A== +"@budibase/pro@2.1.22-alpha.6": + version "2.1.22-alpha.6" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.22-alpha.6.tgz#e4d8238f1727eab85ac72b3ce2fcaec2a5c18456" + integrity sha512-1CKqQ2HMX+/5p24aHpPlUgxoMjKRZxRoyK5fPD/X35Z0mDj+9Ohny3oqbF1fC20pl/20bmcaE4J+q2ph/pbxdQ== dependencies: - "@budibase/backend-core" "2.1.22-alpha.5" - "@budibase/types" "2.1.22-alpha.5" + "@budibase/backend-core" "2.1.22-alpha.6" + "@budibase/types" "2.1.22-alpha.6" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" node-fetch "^2.6.1" -"@budibase/types@2.1.22-alpha.5": - version "2.1.22-alpha.5" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.5.tgz#b90116da2fbccbd5f20acfe5189c1fb970cb5d3b" - integrity sha512-VRkjc5rtpZlj6iBDgQx/L4EGqSRupcqG/tu+tGZNJjEV92T/yiRt6+7UC+uIQjEUawcK1RDSqXLdk3g7+kFv0g== +"@budibase/types@2.1.22-alpha.6": + version "2.1.22-alpha.6" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.22-alpha.6.tgz#a280321373c26a5bf1a52ed119de9178292893c0" + integrity sha512-rVrhs9u7OTzlCxgUFqBu5H6jsMHhwH8uduPhT8Eo7J+Wr4J/0Io7WeuAt1egK6t83JiEPxDBH3WLzAH+04hPVA== "@cspotcode/source-map-support@^0.8.0": version "0.8.1"