From 62b360b9eaea6a6cf05b6255a47a770e698c4cfb Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 18 Mar 2022 08:01:31 +0000 Subject: [PATCH] Add developer usage restrictions to SSO user creation --- .../src/middleware/passport/google.js | 46 +++++----- .../src/middleware/passport/oidc.js | 88 ++++++++++--------- .../middleware/passport/third-party-common.js | 6 +- packages/backend-core/src/utils.js | 6 -- .../src/api/controllers/table/internal.ts | 8 +- .../server/src/api/controllers/table/utils.ts | 20 ++--- packages/server/src/api/index.js | 4 +- .../server/src/automations/steps/createRow.ts | 18 ++-- .../server/src/automations/steps/deleteRow.ts | 8 +- packages/server/src/middleware/usageQuota.ts | 40 ++++----- .../migrations/functions/usageQuotas/index.ts | 4 +- .../functions/usageQuotas/syncApps.ts | 8 +- .../functions/usageQuotas/syncDevelopers.ts | 8 +- .../usageQuotas/syncPublishedApps.ts | 8 +- .../functions/usageQuotas/syncRows.ts | 8 +- packages/server/src/pro-poc.ts | 7 -- .../controllers/global/{auth.js => auth.ts} | 56 ++++++------ .../src/api/controllers/global/license.ts | 10 +-- .../src/api/controllers/global/users.ts | 11 ++- packages/worker/src/api/index.js | 4 +- 20 files changed, 168 insertions(+), 200 deletions(-) delete mode 100644 packages/server/src/pro-poc.ts rename packages/worker/src/api/controllers/global/{auth.js => auth.ts} (81%) diff --git a/packages/backend-core/src/middleware/passport/google.js b/packages/backend-core/src/middleware/passport/google.js index cb93844c31..04d5d23f47 100644 --- a/packages/backend-core/src/middleware/passport/google.js +++ b/packages/backend-core/src/middleware/passport/google.js @@ -2,24 +2,27 @@ const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy const { authenticateThirdParty } = require("./third-party-common") -async function authenticate(accessToken, refreshToken, profile, done) { - const thirdPartyUser = { - provider: profile.provider, // should always be 'google' - providerType: "google", - userId: profile.id, - profile: profile, - email: profile._json.email, - oauth2: { - accessToken: accessToken, - refreshToken: refreshToken, - }, - } +const buildVerifyFn = async saveUserFn => { + return (accessToken, refreshToken, profile, done) => { + const thirdPartyUser = { + provider: profile.provider, // should always be 'google' + providerType: "google", + userId: profile.id, + profile: profile, + email: profile._json.email, + oauth2: { + accessToken: accessToken, + refreshToken: refreshToken, + }, + } - return authenticateThirdParty( - thirdPartyUser, - true, // require local accounts to exist - done - ) + return authenticateThirdParty( + thirdPartyUser, + true, // require local accounts to exist + done, + saveUserFn + ) + } } /** @@ -27,11 +30,7 @@ async function authenticate(accessToken, refreshToken, profile, done) { * from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport. * @returns Dynamically configured Passport Google Strategy */ -exports.strategyFactory = async function ( - config, - callbackUrl, - verify = authenticate -) { +exports.strategyFactory = async function (config, callbackUrl, saveUserFn) { try { const { clientID, clientSecret } = config @@ -41,6 +40,7 @@ exports.strategyFactory = async function ( ) } + const verify = buildVerifyFn(saveUserFn) return new GoogleStrategy( { clientID: config.clientID, @@ -55,4 +55,4 @@ exports.strategyFactory = async function ( } } // expose for testing -exports.authenticate = authenticate +exports.authenticate = buildVerifyFn diff --git a/packages/backend-core/src/middleware/passport/oidc.js b/packages/backend-core/src/middleware/passport/oidc.js index 3a75dfcf8e..d7e6df6514 100644 --- a/packages/backend-core/src/middleware/passport/oidc.js +++ b/packages/backend-core/src/middleware/passport/oidc.js @@ -2,46 +2,49 @@ const fetch = require("node-fetch") const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy const { authenticateThirdParty } = require("./third-party-common") -/** - * @param {*} issuer The identity provider base URL - * @param {*} sub The user ID - * @param {*} profile The user profile information. Created by passport from the /userinfo response - * @param {*} jwtClaims The parsed id_token claims - * @param {*} accessToken The access_token for contacting the identity provider - may or may not be a JWT - * @param {*} refreshToken The refresh_token for obtaining a new access_token - usually not a JWT - * @param {*} idToken The id_token - always a JWT - * @param {*} params The response body from requesting an access_token - * @param {*} done The passport callback: err, user, info - */ -async function authenticate( - issuer, - sub, - profile, - jwtClaims, - accessToken, - refreshToken, - idToken, - params, - done -) { - const thirdPartyUser = { - // store the issuer info to enable sync in future - provider: issuer, - providerType: "oidc", - userId: profile.id, - profile: profile, - email: getEmail(profile, jwtClaims), - oauth2: { - accessToken: accessToken, - refreshToken: refreshToken, - }, - } - - return authenticateThirdParty( - thirdPartyUser, - false, // don't require local accounts to exist +const buildVerifyFn = saveUserFn => { + /** + * @param {*} issuer The identity provider base URL + * @param {*} sub The user ID + * @param {*} profile The user profile information. Created by passport from the /userinfo response + * @param {*} jwtClaims The parsed id_token claims + * @param {*} accessToken The access_token for contacting the identity provider - may or may not be a JWT + * @param {*} refreshToken The refresh_token for obtaining a new access_token - usually not a JWT + * @param {*} idToken The id_token - always a JWT + * @param {*} params The response body from requesting an access_token + * @param {*} done The passport callback: err, user, info + */ + return async ( + issuer, + sub, + profile, + jwtClaims, + accessToken, + refreshToken, + idToken, + params, done - ) + ) => { + const thirdPartyUser = { + // store the issuer info to enable sync in future + provider: issuer, + providerType: "oidc", + userId: profile.id, + profile: profile, + email: getEmail(profile, jwtClaims), + oauth2: { + accessToken: accessToken, + refreshToken: refreshToken, + }, + } + + return authenticateThirdParty( + thirdPartyUser, + false, // don't require local accounts to exist + done, + saveUserFn + ) + } } /** @@ -86,7 +89,7 @@ function validEmail(value) { * from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport. * @returns Dynamically configured Passport OIDC Strategy */ -exports.strategyFactory = async function (config, callbackUrl) { +exports.strategyFactory = async function (config, callbackUrl, saveUserFn) { try { const { clientID, clientSecret, configUrl } = config @@ -106,6 +109,7 @@ exports.strategyFactory = async function (config, callbackUrl) { const body = await response.json() + const verify = buildVerifyFn(saveUserFn) return new OIDCStrategy( { issuer: body.issuer, @@ -116,7 +120,7 @@ exports.strategyFactory = async function (config, callbackUrl) { clientSecret: clientSecret, callbackURL: callbackUrl, }, - authenticate + verify ) } catch (err) { console.error(err) @@ -125,4 +129,4 @@ exports.strategyFactory = async function (config, callbackUrl) { } // expose for testing -exports.authenticate = authenticate +exports.authenticate = buildVerifyFn diff --git a/packages/backend-core/src/middleware/passport/third-party-common.js b/packages/backend-core/src/middleware/passport/third-party-common.js index b467c0b10b..3fbfb145bc 100644 --- a/packages/backend-core/src/middleware/passport/third-party-common.js +++ b/packages/backend-core/src/middleware/passport/third-party-common.js @@ -1,7 +1,6 @@ const env = require("../../environment") const jwt = require("jsonwebtoken") const { generateGlobalUserID } = require("../../db/utils") -const { saveUser } = require("../../utils") const { authError } = require("./utils") const { newid } = require("../../hashing") const { createASession } = require("../../security/sessions") @@ -16,8 +15,11 @@ exports.authenticateThirdParty = async function ( thirdPartyUser, requireLocalAccount = true, done, - saveUserFn = saveUser + saveUserFn ) { + if (!saveUserFn) { + throw new Error("Save user function must be provided") + } if (!thirdPartyUser.provider) { return authError(done, "third party user provider required") } diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.js index 41d6d0de4a..bfd2b49b32 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.js @@ -181,12 +181,6 @@ exports.saveUser = async ( hashPassword = true, requirePassword = true ) => { - // // new user - // // check license restrictions - // if (!user._id && user.builder) { - // await limits.checkMaxDevelopers() - // } - if (!tenantId) { throw "No tenancy specified." } diff --git a/packages/server/src/api/controllers/table/internal.ts b/packages/server/src/api/controllers/table/internal.ts index 2e215c0868..4166066557 100644 --- a/packages/server/src/api/controllers/table/internal.ts +++ b/packages/server/src/api/controllers/table/internal.ts @@ -11,7 +11,7 @@ const { getAppDB } = require("@budibase/backend-core/context") import { isTest } from "../../../environment" import { cleanupAttachments } from "../../../utilities/rowProcessor" import { runStaticFormulaChecks } from "./bulkFormula" -import * as Pro from "@budibase/pro" +import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" export async function save(ctx: any) { const db = getAppDB() @@ -120,10 +120,10 @@ export async function destroy(ctx: any) { await db.bulkDocs( rows.rows.map((row: any) => ({ ...row.doc, _deleted: true })) ) - await Pro.Licensing.Quotas.updateUsage( + await quotas.updateUsage( -rows.rows.length, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC + StaticQuotaName.ROWS, + QuotaUsageType.STATIC ) // update linked rows diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index 49ad8e4ee1..1086621202 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -26,7 +26,7 @@ import { getViews, saveView } from "../view/utils" import viewTemplate from "../view/viewBuilder" const { getAppDB } = require("@budibase/backend-core/context") import { cloneDeep } from "lodash/fp" -import * as Pro from "@budibase/pro" +import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" export async function clearColumns(table: any, columnNames: any) { const db = getAppDB() @@ -117,7 +117,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) { existingTable: table, }) - let finalData = [] + let finalData: any = [] for (let i = 0; i < data.length; i++) { let row = data[i] row._id = generateRowID(table._id) @@ -147,19 +147,11 @@ export async function handleDataImport(user: any, table: any, dataImport: any) { finalData.push(row) } - await Pro.Licensing.Quotas.updateUsage( + await quotas.tryUpdateUsage( + () => db.bulkDocs(finalData), finalData.length, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC, - { - dryRun: true, - } - ) - await db.bulkDocs(finalData) - await Pro.Licensing.Quotas.updateUsage( - finalData.length, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC + StaticQuotaName.ROWS, + QuotaUsageType.STATIC ) let response = await db.put(table) table._rev = response._rev diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index 0a1509d3cf..9f20e76c02 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -12,7 +12,7 @@ const zlib = require("zlib") const { mainRoutes, staticRoutes } = require("./routes") const pkg = require("../../package.json") const env = require("../environment") -const Pro = require("@budibase/pro") +const { middleware: pro } = require("@budibase/pro") const router = new Router() @@ -56,7 +56,7 @@ router .use(currentApp) // this middleware will try to use the app ID to determine the tenancy .use(buildAppTenancyMiddleware()) - .use(Pro.Middleware.Licensing()) + .use(pro.licensing()) .use(auditLog) // error handling middleware diff --git a/packages/server/src/automations/steps/createRow.ts b/packages/server/src/automations/steps/createRow.ts index 2c83bf6fec..6c5e5a87b2 100644 --- a/packages/server/src/automations/steps/createRow.ts +++ b/packages/server/src/automations/steps/createRow.ts @@ -1,6 +1,6 @@ +import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" import { save } from "../../api/controllers/row" import { cleanUpRow, getError } from "../automationUtils" -import * as Pro from "@budibase/pro" import { buildCtx } from "./utils" export const definition = { @@ -78,19 +78,11 @@ export async function run({ inputs, appId, emitter }: any) { try { inputs.row = await cleanUpRow(inputs.row.tableId, inputs.row) - await Pro.Licensing.Quotas.updateUsage( + await quotas.tryUpdateUsage( + () => save(ctx), 1, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC, - { - dryRun: true, - } - ) - await save(ctx) - await Pro.Licensing.Quotas.updateUsage( - 1, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC + StaticQuotaName.ROWS, + QuotaUsageType.STATIC ) return { row: inputs.row, diff --git a/packages/server/src/automations/steps/deleteRow.ts b/packages/server/src/automations/steps/deleteRow.ts index abf115bcff..0261f80ea6 100644 --- a/packages/server/src/automations/steps/deleteRow.ts +++ b/packages/server/src/automations/steps/deleteRow.ts @@ -1,7 +1,7 @@ import { destroy } from "../../api/controllers/row" -import * as Pro from "@budibase/pro" import { buildCtx } from "./utils" import { getError } from "../automationUtils" +import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" export const definition = { description: "Delete a row from your database", @@ -73,11 +73,7 @@ export async function run({ inputs, appId, emitter }: any) { }) try { - await Pro.Licensing.Quotas.updateUsage( - -1, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC - ) + await quotas.updateUsage(-1, StaticQuotaName.ROWS, QuotaUsageType.STATIC) await destroy(ctx) return { response: ctx.body, diff --git a/packages/server/src/middleware/usageQuota.ts b/packages/server/src/middleware/usageQuota.ts index 835f669a58..7347635e78 100644 --- a/packages/server/src/middleware/usageQuota.ts +++ b/packages/server/src/middleware/usageQuota.ts @@ -1,4 +1,4 @@ -import * as Pro from "@budibase/pro" +import { quotas, StaticQuotaName, QuotaUsageType } from "@budibase/pro" const { getUniqueRows } = require("../utilities/usageQuota/rows") const { isExternalTable, @@ -14,12 +14,12 @@ const METHOD_MAP: any = { const DOMAIN_MAP: any = { rows: { - name: Pro.StaticQuotaName.ROWS, - type: Pro.QuotaUsageType.STATIC, + name: StaticQuotaName.ROWS, + type: QuotaUsageType.STATIC, }, applications: { - name: Pro.StaticQuotaName.APPS, - type: Pro.QuotaUsageType.STATIC, + name: StaticQuotaName.APPS, + type: QuotaUsageType.STATIC, }, } @@ -32,7 +32,7 @@ function getQuotaInfo(url: string) { } module.exports = async (ctx: any, next: any) => { - if (!Pro.Licensing.Quotas.useQuotas()) { + if (!quotas.useQuotas()) { return next() } @@ -79,7 +79,7 @@ const performRequest = async ( const usageContext = { skipNext: false, skipUsage: false, - [Pro.StaticQuotaName.APPS]: {}, + [StaticQuotaName.APPS]: {}, } const quotaName = quotaInfo.name @@ -96,7 +96,7 @@ const performRequest = async ( // run the request if (!usageContext.skipNext) { - await Pro.Licensing.Quotas.updateUsage(usage, quotaName, quotaInfo.type, { + await quotas.updateUsage(usage, quotaName, quotaInfo.type, { dryRun: true, }) await next() @@ -114,7 +114,7 @@ const performRequest = async ( // update the usage if (!usageContext.skipUsage) { - await Pro.Licensing.Quotas.updateUsage(usage, quotaName, quotaInfo.type) + await quotas.updateUsage(usage, quotaName, quotaInfo.type) } } @@ -128,18 +128,18 @@ const appPreDelete = async (ctx: any, usageContext: any) => { // store the row count to delete const rows = await getUniqueRows([ctx.appId]) if (rows.length) { - usageContext[Pro.StaticQuotaName.APPS] = { rowCount: rows.length } + usageContext[StaticQuotaName.APPS] = { rowCount: rows.length } } } const appPostDelete = async (ctx: any, usageContext: any) => { // delete the app rows from usage - const rowCount = usageContext[Pro.StaticQuotaName.APPS].rowCount + const rowCount = usageContext[StaticQuotaName.APPS].rowCount if (rowCount) { - await Pro.Licensing.Quotas.updateUsage( + await quotas.updateUsage( -rowCount, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC + StaticQuotaName.ROWS, + QuotaUsageType.STATIC ) } } @@ -149,24 +149,24 @@ const appPostCreate = async (ctx: any) => { if (ctx.request.body.useTemplate === "true") { const rows = await getUniqueRows([ctx.response.body.appId]) const rowCount = rows ? rows.length : 0 - await Pro.Licensing.Quotas.updateUsage( + await quotas.updateUsage( rowCount, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC + StaticQuotaName.ROWS, + QuotaUsageType.STATIC ) } } const PRE_DELETE: any = { - [Pro.StaticQuotaName.APPS]: appPreDelete, + [StaticQuotaName.APPS]: appPreDelete, } const POST_DELETE: any = { - [Pro.StaticQuotaName.APPS]: appPostDelete, + [StaticQuotaName.APPS]: appPostDelete, } const PRE_CREATE: any = {} const POST_CREATE: any = { - [Pro.StaticQuotaName.APPS]: appPostCreate, + [StaticQuotaName.APPS]: appPostCreate, } diff --git a/packages/server/src/migrations/functions/usageQuotas/index.ts b/packages/server/src/migrations/functions/usageQuotas/index.ts index 740ee819cf..4e3fe436db 100644 --- a/packages/server/src/migrations/functions/usageQuotas/index.ts +++ b/packages/server/src/migrations/functions/usageQuotas/index.ts @@ -1,7 +1,7 @@ -import * as Pro from "@budibase/pro" +import { quotas } from "@budibase/pro" export const runQuotaMigration = async (migration: Function) => { - if (!Pro.Licensing.Quotas.useQuotas()) { + if (!quotas.useQuotas()) { return } await migration() diff --git a/packages/server/src/migrations/functions/usageQuotas/syncApps.ts b/packages/server/src/migrations/functions/usageQuotas/syncApps.ts index 13f14fda36..24e4c21969 100644 --- a/packages/server/src/migrations/functions/usageQuotas/syncApps.ts +++ b/packages/server/src/migrations/functions/usageQuotas/syncApps.ts @@ -1,6 +1,6 @@ import { getTenantId } from "@budibase/backend-core/tenancy" import { getAllApps } from "@budibase/backend-core/db" -import * as Pro from "@budibase/pro" +import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" export const run = async () => { // get app count @@ -11,9 +11,5 @@ export const run = async () => { // sync app count const tenantId = getTenantId() console.log(`[Tenant: ${tenantId}] Syncing app count: ${appCount}`) - await Pro.Licensing.Quotas.setUsage( - appCount, - Pro.StaticQuotaName.APPS, - Pro.QuotaUsageType.STATIC - ) + await quotas.setUsage(appCount, StaticQuotaName.APPS, QuotaUsageType.STATIC) } diff --git a/packages/server/src/migrations/functions/usageQuotas/syncDevelopers.ts b/packages/server/src/migrations/functions/usageQuotas/syncDevelopers.ts index 216fd6e26a..c13a095b23 100644 --- a/packages/server/src/migrations/functions/usageQuotas/syncDevelopers.ts +++ b/packages/server/src/migrations/functions/usageQuotas/syncDevelopers.ts @@ -1,6 +1,6 @@ import { getTenantId } from "@budibase/backend-core/tenancy" import { utils } from "@budibase/backend-core" -import * as Pro from "@budibase/pro" +import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" export const run = async () => { // get developer count @@ -11,9 +11,9 @@ export const run = async () => { console.log( `[Tenant: ${tenantId}] Syncing developer count: ${developerCount}` ) - await Pro.Licensing.Quotas.setUsage( + await quotas.setUsage( developerCount, - Pro.StaticQuotaName.DEVELOPERS, - Pro.QuotaUsageType.STATIC + StaticQuotaName.DEVELOPERS, + QuotaUsageType.STATIC ) } diff --git a/packages/server/src/migrations/functions/usageQuotas/syncPublishedApps.ts b/packages/server/src/migrations/functions/usageQuotas/syncPublishedApps.ts index 65d276282f..550a3006b3 100644 --- a/packages/server/src/migrations/functions/usageQuotas/syncPublishedApps.ts +++ b/packages/server/src/migrations/functions/usageQuotas/syncPublishedApps.ts @@ -1,6 +1,6 @@ import { getTenantId } from "@budibase/backend-core/tenancy" import { getAllApps } from "@budibase/backend-core/db" -import * as Pro from "@budibase/pro" +import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" export const run = async () => { // get app count @@ -13,9 +13,9 @@ export const run = async () => { console.log( `[Tenant: ${tenantId}] Syncing published app count: ${prodAppCount}` ) - await Pro.Licensing.Quotas.setUsage( + await quotas.setUsage( prodAppCount, - Pro.StaticQuotaName.PUBLISHED_APPS, - Pro.QuotaUsageType.STATIC + StaticQuotaName.PUBLISHED_APPS, + QuotaUsageType.STATIC ) } diff --git a/packages/server/src/migrations/functions/usageQuotas/syncRows.ts b/packages/server/src/migrations/functions/usageQuotas/syncRows.ts index 9fdd27bcef..b92d880f7a 100644 --- a/packages/server/src/migrations/functions/usageQuotas/syncRows.ts +++ b/packages/server/src/migrations/functions/usageQuotas/syncRows.ts @@ -1,7 +1,7 @@ import { getTenantId } from "@budibase/backend-core/tenancy" import { getAllApps } from "@budibase/backend-core/db" -import * as Pro from "@budibase/pro" import { getUniqueRows } from "../../../utilities/usageQuota/rows" +import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" export const run = async () => { // get all rows in all apps @@ -15,9 +15,5 @@ export const run = async () => { // sync row count const tenantId = getTenantId() console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`) - await Pro.Licensing.Quotas.setUsage( - rowCount, - Pro.StaticQuotaName.ROWS, - Pro.QuotaUsageType.STATIC - ) + await quotas.setUsage(rowCount, StaticQuotaName.ROWS, QuotaUsageType.STATIC) } diff --git a/packages/server/src/pro-poc.ts b/packages/server/src/pro-poc.ts deleted file mode 100644 index f3f5bfe29d..0000000000 --- a/packages/server/src/pro-poc.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as pro from "@budibase/pro" - -export const run = () => { - if (process.env.PRO) { - console.log(pro) - } -} diff --git a/packages/worker/src/api/controllers/global/auth.js b/packages/worker/src/api/controllers/global/auth.ts similarity index 81% rename from packages/worker/src/api/controllers/global/auth.js rename to packages/worker/src/api/controllers/global/auth.ts index 7b0e50c099..9c2047a8e3 100644 --- a/packages/worker/src/api/controllers/global/auth.js +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -21,8 +21,9 @@ const { isMultiTenant, } = require("@budibase/backend-core/tenancy") const env = require("../../../environment") +import { users } from "@budibase/pro" -const ssoCallbackUrl = async (config, type) => { +const ssoCallbackUrl = async (config: any, type: any) => { // incase there is a callback URL from before if (config && config.callbackURL) { return config.callbackURL @@ -42,15 +43,15 @@ const ssoCallbackUrl = async (config, type) => { return `${publicConfig.platformUrl}${callbackUrl}` } -exports.googleCallbackUrl = async config => { +export const googleCallbackUrl = async (config: any) => { return ssoCallbackUrl(config, "google") } -exports.oidcCallbackUrl = async config => { +export const oidcCallbackUrl = async (config: any) => { return ssoCallbackUrl(config, "oidc") } -async function authInternal(ctx, user, err = null, info = null) { +async function authInternal(ctx: any, user: any, err = null, info = null) { if (err) { console.error("Authentication error", err) return ctx.throw(403, info ? info : "Unauthorized") @@ -71,27 +72,30 @@ async function authInternal(ctx, user, err = null, info = null) { } } -exports.authenticate = async (ctx, next) => { - return passport.authenticate("local", async (err, user, info) => { - await authInternal(ctx, user, err, info) - ctx.status = 200 - })(ctx, next) +export const authenticate = async (ctx: any, next: any) => { + return passport.authenticate( + "local", + async (err: any, user: any, info: any) => { + await authInternal(ctx, user, err, info) + ctx.status = 200 + } + )(ctx, next) } -exports.setInitInfo = ctx => { +export const setInitInfo = (ctx: any) => { const initInfo = ctx.request.body setCookie(ctx, initInfo, Cookies.Init) ctx.status = 200 } -exports.getInitInfo = ctx => { +export const getInitInfo = (ctx: any) => { ctx.body = getCookie(ctx, Cookies.Init) || {} } /** * Reset the user password, used as part of a forgotten password flow. */ -exports.reset = async ctx => { +export const reset = async (ctx: any) => { const { email } = ctx.request.body const configured = await isEmailConfigured() if (!configured) { @@ -121,7 +125,7 @@ exports.reset = async ctx => { /** * Perform the user password update if the provided reset code is valid. */ -exports.resetUpdate = async ctx => { +export const resetUpdate = async (ctx: any) => { const { resetCode, password } = ctx.request.body try { const { userId } = await checkResetPasswordCode(resetCode) @@ -137,14 +141,14 @@ exports.resetUpdate = async ctx => { } } -exports.logout = async ctx => { +export const logout = async (ctx: any) => { if (ctx.user && ctx.user._id) { await platformLogout({ ctx, userId: ctx.user._id }) } ctx.body = { message: "User logged out." } } -exports.datasourcePreAuth = async (ctx, next) => { +export const datasourcePreAuth = async (ctx: any, next: any) => { const provider = ctx.params.provider const middleware = require(`@budibase/backend-core/middleware`) const handler = middleware.datasource[provider] @@ -162,7 +166,7 @@ exports.datasourcePreAuth = async (ctx, next) => { return handler.preAuth(passport, ctx, next) } -exports.datasourceAuth = async (ctx, next) => { +export const datasourceAuth = async (ctx: any, next: any) => { const authStateCookie = getCookie(ctx, Cookies.DatasourceAuth) const provider = authStateCookie.provider const middleware = require(`@budibase/backend-core/middleware`) @@ -174,7 +178,7 @@ exports.datasourceAuth = async (ctx, next) => { * The initial call that google authentication makes to take you to the google login screen. * On a successful login, you will be redirected to the googleAuth callback route. */ -exports.googlePreAuth = async (ctx, next) => { +export const googlePreAuth = async (ctx: any, next: any) => { const db = getGlobalDB() const config = await core.db.getScopedConfig(db, { @@ -182,14 +186,14 @@ exports.googlePreAuth = async (ctx, next) => { workspace: ctx.query.workspace, }) let callbackUrl = await exports.googleCallbackUrl(config) - const strategy = await google.strategyFactory(config, callbackUrl) + const strategy = await google.strategyFactory(config, callbackUrl, users.save) return passport.authenticate(strategy, { scope: ["profile", "email"], })(ctx, next) } -exports.googleAuth = async (ctx, next) => { +export const googleAuth = async (ctx: any, next: any) => { const db = getGlobalDB() const config = await core.db.getScopedConfig(db, { @@ -197,12 +201,12 @@ exports.googleAuth = async (ctx, next) => { workspace: ctx.query.workspace, }) const callbackUrl = await exports.googleCallbackUrl(config) - const strategy = await google.strategyFactory(config, callbackUrl) + const strategy = await google.strategyFactory(config, callbackUrl, users.save) return passport.authenticate( strategy, { successRedirect: "/", failureRedirect: "/error" }, - async (err, user, info) => { + async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) ctx.redirect("/") @@ -210,14 +214,14 @@ exports.googleAuth = async (ctx, next) => { )(ctx, next) } -async function oidcStrategyFactory(ctx, configId) { +async function oidcStrategyFactory(ctx: any, configId: any) { const db = getGlobalDB() const config = await core.db.getScopedConfig(db, { type: Configs.OIDC, group: ctx.query.group, }) - const chosenConfig = config.configs.filter(c => c.uuid === configId)[0] + const chosenConfig = config.configs.filter((c: any) => c.uuid === configId)[0] let callbackUrl = await exports.oidcCallbackUrl(chosenConfig) return oidc.strategyFactory(chosenConfig, callbackUrl) @@ -227,7 +231,7 @@ async function oidcStrategyFactory(ctx, configId) { * The initial call that OIDC authentication makes to take you to the configured OIDC login screen. * On a successful login, you will be redirected to the oidcAuth callback route. */ -exports.oidcPreAuth = async (ctx, next) => { +export const oidcPreAuth = async (ctx: any, next: any) => { const { configId } = ctx.params const strategy = await oidcStrategyFactory(ctx, configId) @@ -239,14 +243,14 @@ exports.oidcPreAuth = async (ctx, next) => { })(ctx, next) } -exports.oidcAuth = async (ctx, next) => { +export const oidcAuth = async (ctx: any, next: any) => { const configId = getCookie(ctx, Cookies.OIDC_CONFIG) const strategy = await oidcStrategyFactory(ctx, configId) return passport.authenticate( strategy, { successRedirect: "/", failureRedirect: "/error" }, - async (err, user, info) => { + async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) ctx.redirect("/") diff --git a/packages/worker/src/api/controllers/global/license.ts b/packages/worker/src/api/controllers/global/license.ts index 6864f0b0f8..62d6004258 100644 --- a/packages/worker/src/api/controllers/global/license.ts +++ b/packages/worker/src/api/controllers/global/license.ts @@ -1,4 +1,4 @@ -import * as Pro from "@budibase/pro" +import { licensing, quotas } from "@budibase/pro" export const activate = async (ctx: any) => { const { licenseKey } = ctx.request.body @@ -6,17 +6,17 @@ export const activate = async (ctx: any) => { ctx.throw(400, "licenseKey is required") } - await Pro.Licensing.activateLicenseKey(licenseKey) + await licensing.activateLicenseKey(licenseKey) ctx.status = 200 } export const refresh = async (ctx: any) => { - await Pro.Licensing.Cache.refresh() + await licensing.cache.refresh() ctx.status = 200 } export const getInfo = async (ctx: any) => { - const licenseInfo = await Pro.Licensing.getLicenseInfo() + const licenseInfo = await licensing.getLicenseInfo() if (licenseInfo) { licenseInfo.licenseKey = "*" ctx.body = licenseInfo @@ -25,6 +25,6 @@ export const getInfo = async (ctx: any) => { } export const getQuotaUsage = async (ctx: any) => { - const usage = await Pro.Licensing.Quotas.getQuotaUsage() + const usage = await quotas.getQuotaUsage() ctx.body = usage } diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 963ec829f2..ddba7fd9d4 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -5,7 +5,6 @@ const { const { hash, getGlobalUserByEmail, - saveUser, platformLogout, } = require("@budibase/backend-core/utils") import { EmailTemplatePurpose } from "../../../constants" @@ -23,8 +22,8 @@ const { const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision") import env from "../../../environment" import { syncUserInApps } from "../../../utilities/appService" +import { quotas, users } from "@budibase/pro" const { errors } = require("@budibase/backend-core") -import * as Pro from "@budibase/pro" const allUsers = async () => { const db = getGlobalDB() @@ -38,7 +37,7 @@ const allUsers = async () => { export const save = async (ctx: any) => { try { - const user = await saveUser(ctx.request.body, getTenantId()) + const user = await users.save(ctx.request.body, getTenantId()) // let server know to sync user await syncUserInApps(user._id) ctx.body = user @@ -81,7 +80,7 @@ export const adminUser = async (ctx: any) => { } catch (err) { // don't worry about errors } - await db.put(Pro.Licensing.Quotas.generateNewQuotaUsage()) + await db.put(quotas.generateNewQuotaUsage()) } if (response.rows.some((row: any) => row.doc.admin)) { @@ -105,7 +104,7 @@ export const adminUser = async (ctx: any) => { tenantId, } try { - ctx.body = await saveUser(user, tenantId, hashPassword, requirePassword) + ctx.body = await users.save(user, tenantId, hashPassword, requirePassword) } catch (err: any) { ctx.throw(err.status || 400, err) } @@ -286,7 +285,7 @@ export const inviteAccept = async (ctx: any) => { try { // info is an extension of the user object that was stored by global const { email, info }: any = await checkInviteCode(inviteCode) - ctx.body = await saveUser( + ctx.body = await users.save( { firstName, lastName, diff --git a/packages/worker/src/api/index.js b/packages/worker/src/api/index.js index 25899e2245..00b7e52cc0 100644 --- a/packages/worker/src/api/index.js +++ b/packages/worker/src/api/index.js @@ -8,7 +8,7 @@ const { buildTenancyMiddleware, buildCsrfMiddleware, } = require("@budibase/backend-core/auth") -const Pro = require("@budibase/pro") +const { middleware: pro } = require("@budibase/pro") const { errors } = require("@budibase/backend-core") const PUBLIC_ENDPOINTS = [ @@ -93,7 +93,7 @@ router .use(buildAuthMiddleware(PUBLIC_ENDPOINTS)) .use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS)) .use(buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_ENDPOINTS })) - .use(Pro.Middleware.Licensing()) + .use(pro.licensing()) // for now no public access is allowed to worker (bar health check) .use((ctx, next) => { if (ctx.publicEndpoint) {