Add developer usage restrictions to SSO user creation

This commit is contained in:
Rory Powell 2022-03-18 08:01:31 +00:00
parent e695a57853
commit 661367333d
20 changed files with 168 additions and 200 deletions

View File

@ -2,7 +2,8 @@ const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy
const { authenticateThirdParty } = require("./third-party-common") const { authenticateThirdParty } = require("./third-party-common")
async function authenticate(accessToken, refreshToken, profile, done) { const buildVerifyFn = async saveUserFn => {
return (accessToken, refreshToken, profile, done) => {
const thirdPartyUser = { const thirdPartyUser = {
provider: profile.provider, // should always be 'google' provider: profile.provider, // should always be 'google'
providerType: "google", providerType: "google",
@ -18,20 +19,18 @@ async function authenticate(accessToken, refreshToken, profile, done) {
return authenticateThirdParty( return authenticateThirdParty(
thirdPartyUser, thirdPartyUser,
true, // require local accounts to exist true, // require local accounts to exist
done done,
saveUserFn
) )
} }
}
/** /**
* Create an instance of the google passport strategy. This wrapper fetches the configuration * Create an instance of the google passport strategy. This wrapper fetches the configuration
* from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport. * from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport.
* @returns Dynamically configured Passport Google Strategy * @returns Dynamically configured Passport Google Strategy
*/ */
exports.strategyFactory = async function ( exports.strategyFactory = async function (config, callbackUrl, saveUserFn) {
config,
callbackUrl,
verify = authenticate
) {
try { try {
const { clientID, clientSecret } = config const { clientID, clientSecret } = config
@ -41,6 +40,7 @@ exports.strategyFactory = async function (
) )
} }
const verify = buildVerifyFn(saveUserFn)
return new GoogleStrategy( return new GoogleStrategy(
{ {
clientID: config.clientID, clientID: config.clientID,
@ -55,4 +55,4 @@ exports.strategyFactory = async function (
} }
} }
// expose for testing // expose for testing
exports.authenticate = authenticate exports.authenticate = buildVerifyFn

View File

@ -2,6 +2,7 @@ const fetch = require("node-fetch")
const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy
const { authenticateThirdParty } = require("./third-party-common") const { authenticateThirdParty } = require("./third-party-common")
const buildVerifyFn = saveUserFn => {
/** /**
* @param {*} issuer The identity provider base URL * @param {*} issuer The identity provider base URL
* @param {*} sub The user ID * @param {*} sub The user ID
@ -13,7 +14,7 @@ const { authenticateThirdParty } = require("./third-party-common")
* @param {*} params The response body from requesting an access_token * @param {*} params The response body from requesting an access_token
* @param {*} done The passport callback: err, user, info * @param {*} done The passport callback: err, user, info
*/ */
async function authenticate( return async (
issuer, issuer,
sub, sub,
profile, profile,
@ -23,7 +24,7 @@ async function authenticate(
idToken, idToken,
params, params,
done done
) { ) => {
const thirdPartyUser = { const thirdPartyUser = {
// store the issuer info to enable sync in future // store the issuer info to enable sync in future
provider: issuer, provider: issuer,
@ -40,9 +41,11 @@ async function authenticate(
return authenticateThirdParty( return authenticateThirdParty(
thirdPartyUser, thirdPartyUser,
false, // don't require local accounts to exist false, // don't require local accounts to exist
done done,
saveUserFn
) )
} }
}
/** /**
* @param {*} profile The structured profile created by passport using the user info endpoint * @param {*} profile The structured profile created by passport using the user info endpoint
@ -86,7 +89,7 @@ function validEmail(value) {
* from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport. * from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport.
* @returns Dynamically configured Passport OIDC Strategy * @returns Dynamically configured Passport OIDC Strategy
*/ */
exports.strategyFactory = async function (config, callbackUrl) { exports.strategyFactory = async function (config, callbackUrl, saveUserFn) {
try { try {
const { clientID, clientSecret, configUrl } = config const { clientID, clientSecret, configUrl } = config
@ -106,6 +109,7 @@ exports.strategyFactory = async function (config, callbackUrl) {
const body = await response.json() const body = await response.json()
const verify = buildVerifyFn(saveUserFn)
return new OIDCStrategy( return new OIDCStrategy(
{ {
issuer: body.issuer, issuer: body.issuer,
@ -116,7 +120,7 @@ exports.strategyFactory = async function (config, callbackUrl) {
clientSecret: clientSecret, clientSecret: clientSecret,
callbackURL: callbackUrl, callbackURL: callbackUrl,
}, },
authenticate verify
) )
} catch (err) { } catch (err) {
console.error(err) console.error(err)
@ -125,4 +129,4 @@ exports.strategyFactory = async function (config, callbackUrl) {
} }
// expose for testing // expose for testing
exports.authenticate = authenticate exports.authenticate = buildVerifyFn

View File

@ -1,7 +1,6 @@
const env = require("../../environment") const env = require("../../environment")
const jwt = require("jsonwebtoken") const jwt = require("jsonwebtoken")
const { generateGlobalUserID } = require("../../db/utils") const { generateGlobalUserID } = require("../../db/utils")
const { saveUser } = require("../../utils")
const { authError } = require("./utils") const { authError } = require("./utils")
const { newid } = require("../../hashing") const { newid } = require("../../hashing")
const { createASession } = require("../../security/sessions") const { createASession } = require("../../security/sessions")
@ -16,8 +15,11 @@ exports.authenticateThirdParty = async function (
thirdPartyUser, thirdPartyUser,
requireLocalAccount = true, requireLocalAccount = true,
done, done,
saveUserFn = saveUser saveUserFn
) { ) {
if (!saveUserFn) {
throw new Error("Save user function must be provided")
}
if (!thirdPartyUser.provider) { if (!thirdPartyUser.provider) {
return authError(done, "third party user provider required") return authError(done, "third party user provider required")
} }

View File

@ -181,12 +181,6 @@ exports.saveUser = async (
hashPassword = true, hashPassword = true,
requirePassword = true requirePassword = true
) => { ) => {
// // new user
// // check license restrictions
// if (!user._id && user.builder) {
// await limits.checkMaxDevelopers()
// }
if (!tenantId) { if (!tenantId) {
throw "No tenancy specified." throw "No tenancy specified."
} }

View File

@ -11,7 +11,7 @@ const { getAppDB } = require("@budibase/backend-core/context")
import { isTest } from "../../../environment" import { isTest } from "../../../environment"
import { cleanupAttachments } from "../../../utilities/rowProcessor" import { cleanupAttachments } from "../../../utilities/rowProcessor"
import { runStaticFormulaChecks } from "./bulkFormula" import { runStaticFormulaChecks } from "./bulkFormula"
import * as Pro from "@budibase/pro" import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
export async function save(ctx: any) { export async function save(ctx: any) {
const db = getAppDB() const db = getAppDB()
@ -120,10 +120,10 @@ export async function destroy(ctx: any) {
await db.bulkDocs( await db.bulkDocs(
rows.rows.map((row: any) => ({ ...row.doc, _deleted: true })) rows.rows.map((row: any) => ({ ...row.doc, _deleted: true }))
) )
await Pro.Licensing.Quotas.updateUsage( await quotas.updateUsage(
-rows.rows.length, -rows.rows.length,
Pro.StaticQuotaName.ROWS, StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC QuotaUsageType.STATIC
) )
// update linked rows // update linked rows

View File

@ -26,7 +26,7 @@ import { getViews, saveView } from "../view/utils"
import viewTemplate from "../view/viewBuilder" import viewTemplate from "../view/viewBuilder"
const { getAppDB } = require("@budibase/backend-core/context") const { getAppDB } = require("@budibase/backend-core/context")
import { cloneDeep } from "lodash/fp" 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) { export async function clearColumns(table: any, columnNames: any) {
const db = getAppDB() const db = getAppDB()
@ -117,7 +117,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) {
existingTable: table, existingTable: table,
}) })
let finalData = [] let finalData: any = []
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
let row = data[i] let row = data[i]
row._id = generateRowID(table._id) row._id = generateRowID(table._id)
@ -147,19 +147,11 @@ export async function handleDataImport(user: any, table: any, dataImport: any) {
finalData.push(row) finalData.push(row)
} }
await Pro.Licensing.Quotas.updateUsage( await quotas.tryUpdateUsage(
() => db.bulkDocs(finalData),
finalData.length, finalData.length,
Pro.StaticQuotaName.ROWS, StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC, QuotaUsageType.STATIC
{
dryRun: true,
}
)
await db.bulkDocs(finalData)
await Pro.Licensing.Quotas.updateUsage(
finalData.length,
Pro.StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC
) )
let response = await db.put(table) let response = await db.put(table)
table._rev = response._rev table._rev = response._rev

View File

@ -12,7 +12,7 @@ const zlib = require("zlib")
const { mainRoutes, staticRoutes } = require("./routes") const { mainRoutes, staticRoutes } = require("./routes")
const pkg = require("../../package.json") const pkg = require("../../package.json")
const env = require("../environment") const env = require("../environment")
const Pro = require("@budibase/pro") const { middleware: pro } = require("@budibase/pro")
const router = new Router() const router = new Router()
@ -56,7 +56,7 @@ router
.use(currentApp) .use(currentApp)
// this middleware will try to use the app ID to determine the tenancy // this middleware will try to use the app ID to determine the tenancy
.use(buildAppTenancyMiddleware()) .use(buildAppTenancyMiddleware())
.use(Pro.Middleware.Licensing()) .use(pro.licensing())
.use(auditLog) .use(auditLog)
// error handling middleware // error handling middleware

View File

@ -1,6 +1,6 @@
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
import { save } from "../../api/controllers/row" import { save } from "../../api/controllers/row"
import { cleanUpRow, getError } from "../automationUtils" import { cleanUpRow, getError } from "../automationUtils"
import * as Pro from "@budibase/pro"
import { buildCtx } from "./utils" import { buildCtx } from "./utils"
export const definition = { export const definition = {
@ -78,19 +78,11 @@ export async function run({ inputs, appId, emitter }: any) {
try { try {
inputs.row = await cleanUpRow(inputs.row.tableId, inputs.row) inputs.row = await cleanUpRow(inputs.row.tableId, inputs.row)
await Pro.Licensing.Quotas.updateUsage( await quotas.tryUpdateUsage(
() => save(ctx),
1, 1,
Pro.StaticQuotaName.ROWS, StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC, QuotaUsageType.STATIC
{
dryRun: true,
}
)
await save(ctx)
await Pro.Licensing.Quotas.updateUsage(
1,
Pro.StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC
) )
return { return {
row: inputs.row, row: inputs.row,

View File

@ -1,7 +1,7 @@
import { destroy } from "../../api/controllers/row" import { destroy } from "../../api/controllers/row"
import * as Pro from "@budibase/pro"
import { buildCtx } from "./utils" import { buildCtx } from "./utils"
import { getError } from "../automationUtils" import { getError } from "../automationUtils"
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
export const definition = { export const definition = {
description: "Delete a row from your database", description: "Delete a row from your database",
@ -73,11 +73,7 @@ export async function run({ inputs, appId, emitter }: any) {
}) })
try { try {
await Pro.Licensing.Quotas.updateUsage( await quotas.updateUsage(-1, StaticQuotaName.ROWS, QuotaUsageType.STATIC)
-1,
Pro.StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC
)
await destroy(ctx) await destroy(ctx)
return { return {
response: ctx.body, response: ctx.body,

View File

@ -1,4 +1,4 @@
import * as Pro from "@budibase/pro" import { quotas, StaticQuotaName, QuotaUsageType } from "@budibase/pro"
const { getUniqueRows } = require("../utilities/usageQuota/rows") const { getUniqueRows } = require("../utilities/usageQuota/rows")
const { const {
isExternalTable, isExternalTable,
@ -14,12 +14,12 @@ const METHOD_MAP: any = {
const DOMAIN_MAP: any = { const DOMAIN_MAP: any = {
rows: { rows: {
name: Pro.StaticQuotaName.ROWS, name: StaticQuotaName.ROWS,
type: Pro.QuotaUsageType.STATIC, type: QuotaUsageType.STATIC,
}, },
applications: { applications: {
name: Pro.StaticQuotaName.APPS, name: StaticQuotaName.APPS,
type: Pro.QuotaUsageType.STATIC, type: QuotaUsageType.STATIC,
}, },
} }
@ -32,7 +32,7 @@ function getQuotaInfo(url: string) {
} }
module.exports = async (ctx: any, next: any) => { module.exports = async (ctx: any, next: any) => {
if (!Pro.Licensing.Quotas.useQuotas()) { if (!quotas.useQuotas()) {
return next() return next()
} }
@ -79,7 +79,7 @@ const performRequest = async (
const usageContext = { const usageContext = {
skipNext: false, skipNext: false,
skipUsage: false, skipUsage: false,
[Pro.StaticQuotaName.APPS]: {}, [StaticQuotaName.APPS]: {},
} }
const quotaName = quotaInfo.name const quotaName = quotaInfo.name
@ -96,7 +96,7 @@ const performRequest = async (
// run the request // run the request
if (!usageContext.skipNext) { if (!usageContext.skipNext) {
await Pro.Licensing.Quotas.updateUsage(usage, quotaName, quotaInfo.type, { await quotas.updateUsage(usage, quotaName, quotaInfo.type, {
dryRun: true, dryRun: true,
}) })
await next() await next()
@ -114,7 +114,7 @@ const performRequest = async (
// update the usage // update the usage
if (!usageContext.skipUsage) { 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 // store the row count to delete
const rows = await getUniqueRows([ctx.appId]) const rows = await getUniqueRows([ctx.appId])
if (rows.length) { if (rows.length) {
usageContext[Pro.StaticQuotaName.APPS] = { rowCount: rows.length } usageContext[StaticQuotaName.APPS] = { rowCount: rows.length }
} }
} }
const appPostDelete = async (ctx: any, usageContext: any) => { const appPostDelete = async (ctx: any, usageContext: any) => {
// delete the app rows from usage // delete the app rows from usage
const rowCount = usageContext[Pro.StaticQuotaName.APPS].rowCount const rowCount = usageContext[StaticQuotaName.APPS].rowCount
if (rowCount) { if (rowCount) {
await Pro.Licensing.Quotas.updateUsage( await quotas.updateUsage(
-rowCount, -rowCount,
Pro.StaticQuotaName.ROWS, StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC QuotaUsageType.STATIC
) )
} }
} }
@ -149,24 +149,24 @@ const appPostCreate = async (ctx: any) => {
if (ctx.request.body.useTemplate === "true") { if (ctx.request.body.useTemplate === "true") {
const rows = await getUniqueRows([ctx.response.body.appId]) const rows = await getUniqueRows([ctx.response.body.appId])
const rowCount = rows ? rows.length : 0 const rowCount = rows ? rows.length : 0
await Pro.Licensing.Quotas.updateUsage( await quotas.updateUsage(
rowCount, rowCount,
Pro.StaticQuotaName.ROWS, StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC QuotaUsageType.STATIC
) )
} }
} }
const PRE_DELETE: any = { const PRE_DELETE: any = {
[Pro.StaticQuotaName.APPS]: appPreDelete, [StaticQuotaName.APPS]: appPreDelete,
} }
const POST_DELETE: any = { const POST_DELETE: any = {
[Pro.StaticQuotaName.APPS]: appPostDelete, [StaticQuotaName.APPS]: appPostDelete,
} }
const PRE_CREATE: any = {} const PRE_CREATE: any = {}
const POST_CREATE: any = { const POST_CREATE: any = {
[Pro.StaticQuotaName.APPS]: appPostCreate, [StaticQuotaName.APPS]: appPostCreate,
} }

View File

@ -1,7 +1,7 @@
import * as Pro from "@budibase/pro" import { quotas } from "@budibase/pro"
export const runQuotaMigration = async (migration: Function) => { export const runQuotaMigration = async (migration: Function) => {
if (!Pro.Licensing.Quotas.useQuotas()) { if (!quotas.useQuotas()) {
return return
} }
await migration() await migration()

View File

@ -1,6 +1,6 @@
import { getTenantId } from "@budibase/backend-core/tenancy" import { getTenantId } from "@budibase/backend-core/tenancy"
import { getAllApps } from "@budibase/backend-core/db" import { getAllApps } from "@budibase/backend-core/db"
import * as Pro from "@budibase/pro" import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
export const run = async () => { export const run = async () => {
// get app count // get app count
@ -11,9 +11,5 @@ export const run = async () => {
// sync app count // sync app count
const tenantId = getTenantId() const tenantId = getTenantId()
console.log(`[Tenant: ${tenantId}] Syncing app count: ${appCount}`) console.log(`[Tenant: ${tenantId}] Syncing app count: ${appCount}`)
await Pro.Licensing.Quotas.setUsage( await quotas.setUsage(appCount, StaticQuotaName.APPS, QuotaUsageType.STATIC)
appCount,
Pro.StaticQuotaName.APPS,
Pro.QuotaUsageType.STATIC
)
} }

View File

@ -1,6 +1,6 @@
import { getTenantId } from "@budibase/backend-core/tenancy" import { getTenantId } from "@budibase/backend-core/tenancy"
import { utils } from "@budibase/backend-core" import { utils } from "@budibase/backend-core"
import * as Pro from "@budibase/pro" import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
export const run = async () => { export const run = async () => {
// get developer count // get developer count
@ -11,9 +11,9 @@ export const run = async () => {
console.log( console.log(
`[Tenant: ${tenantId}] Syncing developer count: ${developerCount}` `[Tenant: ${tenantId}] Syncing developer count: ${developerCount}`
) )
await Pro.Licensing.Quotas.setUsage( await quotas.setUsage(
developerCount, developerCount,
Pro.StaticQuotaName.DEVELOPERS, StaticQuotaName.DEVELOPERS,
Pro.QuotaUsageType.STATIC QuotaUsageType.STATIC
) )
} }

View File

@ -1,6 +1,6 @@
import { getTenantId } from "@budibase/backend-core/tenancy" import { getTenantId } from "@budibase/backend-core/tenancy"
import { getAllApps } from "@budibase/backend-core/db" import { getAllApps } from "@budibase/backend-core/db"
import * as Pro from "@budibase/pro" import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
export const run = async () => { export const run = async () => {
// get app count // get app count
@ -13,9 +13,9 @@ export const run = async () => {
console.log( console.log(
`[Tenant: ${tenantId}] Syncing published app count: ${prodAppCount}` `[Tenant: ${tenantId}] Syncing published app count: ${prodAppCount}`
) )
await Pro.Licensing.Quotas.setUsage( await quotas.setUsage(
prodAppCount, prodAppCount,
Pro.StaticQuotaName.PUBLISHED_APPS, StaticQuotaName.PUBLISHED_APPS,
Pro.QuotaUsageType.STATIC QuotaUsageType.STATIC
) )
} }

View File

@ -1,7 +1,7 @@
import { getTenantId } from "@budibase/backend-core/tenancy" import { getTenantId } from "@budibase/backend-core/tenancy"
import { getAllApps } from "@budibase/backend-core/db" import { getAllApps } from "@budibase/backend-core/db"
import * as Pro from "@budibase/pro"
import { getUniqueRows } from "../../../utilities/usageQuota/rows" import { getUniqueRows } from "../../../utilities/usageQuota/rows"
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
export const run = async () => { export const run = async () => {
// get all rows in all apps // get all rows in all apps
@ -15,9 +15,5 @@ export const run = async () => {
// sync row count // sync row count
const tenantId = getTenantId() const tenantId = getTenantId()
console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`) console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`)
await Pro.Licensing.Quotas.setUsage( await quotas.setUsage(rowCount, StaticQuotaName.ROWS, QuotaUsageType.STATIC)
rowCount,
Pro.StaticQuotaName.ROWS,
Pro.QuotaUsageType.STATIC
)
} }

View File

@ -1,7 +0,0 @@
import * as pro from "@budibase/pro"
export const run = () => {
if (process.env.PRO) {
console.log(pro)
}
}

View File

@ -21,8 +21,9 @@ const {
isMultiTenant, isMultiTenant,
} = require("@budibase/backend-core/tenancy") } = require("@budibase/backend-core/tenancy")
const env = require("../../../environment") 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 // incase there is a callback URL from before
if (config && config.callbackURL) { if (config && config.callbackURL) {
return config.callbackURL return config.callbackURL
@ -42,15 +43,15 @@ const ssoCallbackUrl = async (config, type) => {
return `${publicConfig.platformUrl}${callbackUrl}` return `${publicConfig.platformUrl}${callbackUrl}`
} }
exports.googleCallbackUrl = async config => { export const googleCallbackUrl = async (config: any) => {
return ssoCallbackUrl(config, "google") return ssoCallbackUrl(config, "google")
} }
exports.oidcCallbackUrl = async config => { export const oidcCallbackUrl = async (config: any) => {
return ssoCallbackUrl(config, "oidc") 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) { if (err) {
console.error("Authentication error", err) console.error("Authentication error", err)
return ctx.throw(403, info ? info : "Unauthorized") 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) => { export const authenticate = async (ctx: any, next: any) => {
return passport.authenticate("local", async (err, user, info) => { return passport.authenticate(
"local",
async (err: any, user: any, info: any) => {
await authInternal(ctx, user, err, info) await authInternal(ctx, user, err, info)
ctx.status = 200 ctx.status = 200
})(ctx, next) }
)(ctx, next)
} }
exports.setInitInfo = ctx => { export const setInitInfo = (ctx: any) => {
const initInfo = ctx.request.body const initInfo = ctx.request.body
setCookie(ctx, initInfo, Cookies.Init) setCookie(ctx, initInfo, Cookies.Init)
ctx.status = 200 ctx.status = 200
} }
exports.getInitInfo = ctx => { export const getInitInfo = (ctx: any) => {
ctx.body = getCookie(ctx, Cookies.Init) || {} ctx.body = getCookie(ctx, Cookies.Init) || {}
} }
/** /**
* Reset the user password, used as part of a forgotten password flow. * 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 { email } = ctx.request.body
const configured = await isEmailConfigured() const configured = await isEmailConfigured()
if (!configured) { if (!configured) {
@ -121,7 +125,7 @@ exports.reset = async ctx => {
/** /**
* Perform the user password update if the provided reset code is valid. * 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 const { resetCode, password } = ctx.request.body
try { try {
const { userId } = await checkResetPasswordCode(resetCode) 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) { if (ctx.user && ctx.user._id) {
await platformLogout({ ctx, userId: ctx.user._id }) await platformLogout({ ctx, userId: ctx.user._id })
} }
ctx.body = { message: "User logged out." } 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 provider = ctx.params.provider
const middleware = require(`@budibase/backend-core/middleware`) const middleware = require(`@budibase/backend-core/middleware`)
const handler = middleware.datasource[provider] const handler = middleware.datasource[provider]
@ -162,7 +166,7 @@ exports.datasourcePreAuth = async (ctx, next) => {
return handler.preAuth(passport, 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 authStateCookie = getCookie(ctx, Cookies.DatasourceAuth)
const provider = authStateCookie.provider const provider = authStateCookie.provider
const middleware = require(`@budibase/backend-core/middleware`) 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. * 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. * 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 db = getGlobalDB()
const config = await core.db.getScopedConfig(db, { const config = await core.db.getScopedConfig(db, {
@ -182,14 +186,14 @@ exports.googlePreAuth = async (ctx, next) => {
workspace: ctx.query.workspace, workspace: ctx.query.workspace,
}) })
let callbackUrl = await exports.googleCallbackUrl(config) 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, { return passport.authenticate(strategy, {
scope: ["profile", "email"], scope: ["profile", "email"],
})(ctx, next) })(ctx, next)
} }
exports.googleAuth = async (ctx, next) => { export const googleAuth = async (ctx: any, next: any) => {
const db = getGlobalDB() const db = getGlobalDB()
const config = await core.db.getScopedConfig(db, { const config = await core.db.getScopedConfig(db, {
@ -197,12 +201,12 @@ exports.googleAuth = async (ctx, next) => {
workspace: ctx.query.workspace, workspace: ctx.query.workspace,
}) })
const callbackUrl = await exports.googleCallbackUrl(config) 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( return passport.authenticate(
strategy, strategy,
{ successRedirect: "/", failureRedirect: "/error" }, { successRedirect: "/", failureRedirect: "/error" },
async (err, user, info) => { async (err: any, user: any, info: any) => {
await authInternal(ctx, user, err, info) await authInternal(ctx, user, err, info)
ctx.redirect("/") ctx.redirect("/")
@ -210,14 +214,14 @@ exports.googleAuth = async (ctx, next) => {
)(ctx, next) )(ctx, next)
} }
async function oidcStrategyFactory(ctx, configId) { async function oidcStrategyFactory(ctx: any, configId: any) {
const db = getGlobalDB() const db = getGlobalDB()
const config = await core.db.getScopedConfig(db, { const config = await core.db.getScopedConfig(db, {
type: Configs.OIDC, type: Configs.OIDC,
group: ctx.query.group, 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) let callbackUrl = await exports.oidcCallbackUrl(chosenConfig)
return oidc.strategyFactory(chosenConfig, callbackUrl) 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. * 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. * 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 { configId } = ctx.params
const strategy = await oidcStrategyFactory(ctx, configId) const strategy = await oidcStrategyFactory(ctx, configId)
@ -239,14 +243,14 @@ exports.oidcPreAuth = async (ctx, next) => {
})(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 configId = getCookie(ctx, Cookies.OIDC_CONFIG)
const strategy = await oidcStrategyFactory(ctx, configId) const strategy = await oidcStrategyFactory(ctx, configId)
return passport.authenticate( return passport.authenticate(
strategy, strategy,
{ successRedirect: "/", failureRedirect: "/error" }, { successRedirect: "/", failureRedirect: "/error" },
async (err, user, info) => { async (err: any, user: any, info: any) => {
await authInternal(ctx, user, err, info) await authInternal(ctx, user, err, info)
ctx.redirect("/") ctx.redirect("/")

View File

@ -1,4 +1,4 @@
import * as Pro from "@budibase/pro" import { licensing, quotas } from "@budibase/pro"
export const activate = async (ctx: any) => { export const activate = async (ctx: any) => {
const { licenseKey } = ctx.request.body const { licenseKey } = ctx.request.body
@ -6,17 +6,17 @@ export const activate = async (ctx: any) => {
ctx.throw(400, "licenseKey is required") ctx.throw(400, "licenseKey is required")
} }
await Pro.Licensing.activateLicenseKey(licenseKey) await licensing.activateLicenseKey(licenseKey)
ctx.status = 200 ctx.status = 200
} }
export const refresh = async (ctx: any) => { export const refresh = async (ctx: any) => {
await Pro.Licensing.Cache.refresh() await licensing.cache.refresh()
ctx.status = 200 ctx.status = 200
} }
export const getInfo = async (ctx: any) => { export const getInfo = async (ctx: any) => {
const licenseInfo = await Pro.Licensing.getLicenseInfo() const licenseInfo = await licensing.getLicenseInfo()
if (licenseInfo) { if (licenseInfo) {
licenseInfo.licenseKey = "*" licenseInfo.licenseKey = "*"
ctx.body = licenseInfo ctx.body = licenseInfo
@ -25,6 +25,6 @@ export const getInfo = async (ctx: any) => {
} }
export const getQuotaUsage = async (ctx: any) => { export const getQuotaUsage = async (ctx: any) => {
const usage = await Pro.Licensing.Quotas.getQuotaUsage() const usage = await quotas.getQuotaUsage()
ctx.body = usage ctx.body = usage
} }

View File

@ -5,7 +5,6 @@ const {
const { const {
hash, hash,
getGlobalUserByEmail, getGlobalUserByEmail,
saveUser,
platformLogout, platformLogout,
} = require("@budibase/backend-core/utils") } = require("@budibase/backend-core/utils")
import { EmailTemplatePurpose } from "../../../constants" import { EmailTemplatePurpose } from "../../../constants"
@ -23,8 +22,8 @@ const {
const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision") const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision")
import env from "../../../environment" import env from "../../../environment"
import { syncUserInApps } from "../../../utilities/appService" import { syncUserInApps } from "../../../utilities/appService"
import { quotas, users } from "@budibase/pro"
const { errors } = require("@budibase/backend-core") const { errors } = require("@budibase/backend-core")
import * as Pro from "@budibase/pro"
const allUsers = async () => { const allUsers = async () => {
const db = getGlobalDB() const db = getGlobalDB()
@ -38,7 +37,7 @@ const allUsers = async () => {
export const save = async (ctx: any) => { export const save = async (ctx: any) => {
try { try {
const user = await saveUser(ctx.request.body, getTenantId()) const user = await users.save(ctx.request.body, getTenantId())
// let server know to sync user // let server know to sync user
await syncUserInApps(user._id) await syncUserInApps(user._id)
ctx.body = user ctx.body = user
@ -81,7 +80,7 @@ export const adminUser = async (ctx: any) => {
} catch (err) { } catch (err) {
// don't worry about errors // 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)) { if (response.rows.some((row: any) => row.doc.admin)) {
@ -105,7 +104,7 @@ export const adminUser = async (ctx: any) => {
tenantId, tenantId,
} }
try { try {
ctx.body = await saveUser(user, tenantId, hashPassword, requirePassword) ctx.body = await users.save(user, tenantId, hashPassword, requirePassword)
} catch (err: any) { } catch (err: any) {
ctx.throw(err.status || 400, err) ctx.throw(err.status || 400, err)
} }
@ -286,7 +285,7 @@ export const inviteAccept = async (ctx: any) => {
try { try {
// info is an extension of the user object that was stored by global // info is an extension of the user object that was stored by global
const { email, info }: any = await checkInviteCode(inviteCode) const { email, info }: any = await checkInviteCode(inviteCode)
ctx.body = await saveUser( ctx.body = await users.save(
{ {
firstName, firstName,
lastName, lastName,

View File

@ -8,7 +8,7 @@ const {
buildTenancyMiddleware, buildTenancyMiddleware,
buildCsrfMiddleware, buildCsrfMiddleware,
} = require("@budibase/backend-core/auth") } = require("@budibase/backend-core/auth")
const Pro = require("@budibase/pro") const { middleware: pro } = require("@budibase/pro")
const { errors } = require("@budibase/backend-core") const { errors } = require("@budibase/backend-core")
const PUBLIC_ENDPOINTS = [ const PUBLIC_ENDPOINTS = [
@ -93,7 +93,7 @@ router
.use(buildAuthMiddleware(PUBLIC_ENDPOINTS)) .use(buildAuthMiddleware(PUBLIC_ENDPOINTS))
.use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS)) .use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS))
.use(buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_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) // for now no public access is allowed to worker (bar health check)
.use((ctx, next) => { .use((ctx, next) => {
if (ctx.publicEndpoint) { if (ctx.publicEndpoint) {