From 9663806bfc512845981fe7a01ea107c9d82accd8 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 17 Jan 2022 15:52:10 +0100 Subject: [PATCH] more flexible datasource auth config --- packages/backend-core/src/constants.js | 1 + packages/backend-core/src/middleware/index.js | 4 + .../middleware/passport/datasource/google.js | 77 +++++++++++++++++++ .../_components/GoogleButton.svelte | 3 +- .../server/src/api/controllers/query/index.js | 5 +- .../worker/src/api/controllers/global/auth.js | 75 ++++-------------- packages/worker/src/api/routes/global/auth.js | 10 ++- 7 files changed, 106 insertions(+), 69 deletions(-) create mode 100644 packages/backend-core/src/middleware/passport/datasource/google.js diff --git a/packages/backend-core/src/constants.js b/packages/backend-core/src/constants.js index 28b9ced49b..091e4337cf 100644 --- a/packages/backend-core/src/constants.js +++ b/packages/backend-core/src/constants.js @@ -7,6 +7,7 @@ exports.Cookies = { CurrentApp: "budibase:currentapp", Auth: "budibase:auth", Init: "budibase:init", + DatasourceAuth: "budibase:datasourceauth", OIDC_CONFIG: "budibase:oidc:config", } diff --git a/packages/backend-core/src/middleware/index.js b/packages/backend-core/src/middleware/index.js index cf8676a2bc..7ea07a21ce 100644 --- a/packages/backend-core/src/middleware/index.js +++ b/packages/backend-core/src/middleware/index.js @@ -7,6 +7,7 @@ const authenticated = require("./authenticated") const auditLog = require("./auditLog") const tenancy = require("./tenancy") const appTenancy = require("./appTenancy") +const datasourceGoogle = require("./passport/datasource/google") module.exports = { google, @@ -18,4 +19,7 @@ module.exports = { tenancy, appTenancy, authError, + datasource: { + google: datasourceGoogle, + }, } diff --git a/packages/backend-core/src/middleware/passport/datasource/google.js b/packages/backend-core/src/middleware/passport/datasource/google.js new file mode 100644 index 0000000000..e7f8de52c4 --- /dev/null +++ b/packages/backend-core/src/middleware/passport/datasource/google.js @@ -0,0 +1,77 @@ +const { getScopedConfig } = require("../../../db/utils") +const { getGlobalDB } = require("../../../tenancy") +const google = require("../google") +const { Configs, Cookies } = require("../../../constants") +const { clearCookie, getCookie } = require("../../../utils") +const { getDB } = require("../../../db") + +async function preAuth(passport, ctx, next) { + const db = getGlobalDB() + // get the relevant config + const config = await getScopedConfig(db, { + type: Configs.GOOGLE, + workspace: ctx.query.workspace, + }) + const publicConfig = await getScopedConfig(db, { + type: Configs.SETTINGS, + }) + let callbackUrl = `${publicConfig.platformUrl}/api/global/auth/datasource/google/callback` + const strategy = await google.strategyFactory(config, callbackUrl) + + if (!ctx.query.appId || !ctx.query.datasourceId) { + ctx.throw(400, "appId and datasourceId query params not present.") + } + + // TODO: prob update - shouldn't include the google sheets scopes here + return passport.authenticate(strategy, { + scope: ["profile", "email", "https://www.googleapis.com/auth/spreadsheets"], + })(ctx, next) +} + +async function postAuth(passport, ctx, next) { + const db = getGlobalDB() + + const config = await getScopedConfig(db, { + type: Configs.GOOGLE, + workspace: ctx.query.workspace, + }) + + const publicConfig = await getScopedConfig(db, { + type: Configs.SETTINGS, + }) + + let callbackUrl = `${publicConfig.platformUrl}/api/global/auth/datasource/google/callback` + const strategy = await google.strategyFactory( + config, + callbackUrl, + (accessToken, refreshToken, profile, done) => { + clearCookie(ctx, Cookies.DatasourceAuth) + done(null, { accessToken, refreshToken }) + } + ) + + const authStateCookie = getCookie(ctx, Cookies.DatasourceAuth) + + return passport.authenticate( + strategy, + { successRedirect: "/", failureRedirect: "/error" }, + async (err, tokens) => { + // update the DB for the datasource with all the user info + const db = getDB(authStateCookie.appId) + const datasource = await db.get(authStateCookie.datasourceId) + datasource.config = { + auth: { + type: "google", + ...tokens, + }, + } + await db.put(datasource) + ctx.redirect( + `/builder/app/${authStateCookie.appId}/data/datasource/${authStateCookie.datasourceId}` + ) + } + )(ctx, next) +} + +exports.preAuth = preAuth +exports.postAuth = postAuth diff --git a/packages/builder/src/components/backend/DatasourceNavigator/_components/GoogleButton.svelte b/packages/builder/src/components/backend/DatasourceNavigator/_components/GoogleButton.svelte index ea9c5cff2a..5aca42b173 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/_components/GoogleButton.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/_components/GoogleButton.svelte @@ -12,9 +12,8 @@ { const datasource = await preAuthStep() - console.log(datasource) window.open( - `/api/global/auth/${tenantId}/google2?datasourceId=${datasource._id}&appId=${$store.appId}`, + `/api/global/auth/${tenantId}/datasource/google?datasourceId=${datasource._id}&appId=${$store.appId}`, "_blank" ) }} diff --git a/packages/server/src/api/controllers/query/index.js b/packages/server/src/api/controllers/query/index.js index 4096dd433f..4ff35ece34 100644 --- a/packages/server/src/api/controllers/query/index.js +++ b/packages/server/src/api/controllers/query/index.js @@ -9,8 +9,11 @@ const { Thread, ThreadType } = require("../../../threads") const { save: saveDatasource } = require("../datasource") const { RestImporter } = require("./import") const { invalidateDynamicVariables } = require("../../../threads/utils") +const environment = require("../../../environment") -const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 }) +const Runner = new Thread(ThreadType.QUERY, { + timeoutMs: 10000 || environment.QUERY_THREAD_TIMEOUT, +}) // simple function to append "readable" to all read queries function enrichQueries(input) { diff --git a/packages/worker/src/api/controllers/global/auth.js b/packages/worker/src/api/controllers/global/auth.js index c9243f944b..3dc4841f00 100644 --- a/packages/worker/src/api/controllers/global/auth.js +++ b/packages/worker/src/api/controllers/global/auth.js @@ -21,7 +21,6 @@ const { isMultiTenant, } = require("@budibase/backend-core/tenancy") const env = require("../../../environment") -const CouchDB = require("../../../db") const ssoCallbackUrl = async (config, type) => { // incase there is a callback URL from before @@ -146,78 +145,30 @@ exports.logout = async ctx => { ctx.body = { message: "User logged out." } } -/** - * 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.googlePreAuth2 = async (ctx, next) => { - const db = getGlobalDB() +exports.datasourcePreAuth = async (ctx, next) => { + const provider = ctx.params.provider + const middleware = require(`@budibase/backend-core/middleware`) + const handler = middleware.datasource[provider] - const config = await core.db.getScopedConfig(db, { - type: Configs.GOOGLE, - workspace: ctx.query.workspace, - }) - // let callbackUrl = await exports.googleCallbackUrl(config) - // TODO: Hardcoded - fix - let callbackUrl = "http://localhost:10000/api/global/auth/google2/callback" - const strategy = await google.strategyFactory(config, callbackUrl) - - // TODO: fix setCookie( ctx, { + provider, appId: ctx.query.appId, datasourceId: ctx.query.datasourceId, }, - "AuthCallback" + Cookies.DatasourceAuth ) - // TODO: prob update - shouldn't include the google sheets scopes here - return passport.authenticate(strategy, { - scope: ["profile", "email", "https://www.googleapis.com/auth/spreadsheets"], - })(ctx, next) + return handler.preAuth(passport, ctx, next) } -exports.googleAuth2 = async (ctx, next) => { - const db = getGlobalDB() - - const config = await core.db.getScopedConfig(db, { - type: Configs.GOOGLE, - workspace: ctx.query.workspace, - }) - // const callbackUrl = await exports.googleCallbackUrl(config) - const authStateCookie = getCookie(ctx, "AuthCallback") - - // TODO: correct callback URL - let callbackUrl = "http://localhost:10000/api/global/auth/google2/callback" - const strategy = await google.strategyFactory( - config, - callbackUrl, - (accessToken, refreshToken, profile, done) => { - clearCookie(ctx, "AuthCallback") - done(null, { accessToken, refreshToken }) - } - ) - - return passport.authenticate( - strategy, - { successRedirect: "/", failureRedirect: "/error" }, - async (err, tokens) => { - // update the DB for the datasource with all the user info - const db = new CouchDB(authStateCookie.appId) - const datasource = await db.get(authStateCookie.datasourceId) - datasource.config = { - auth: { - type: "google", - ...tokens, - }, - } - await db.put(datasource) - ctx.redirect( - `/builder/app/${authStateCookie.appId}/data/datasource/${authStateCookie.datasourceId}` - ) - } - )(ctx, next) +exports.datasourceAuth = async (ctx, next) => { + const authStateCookie = getCookie(ctx, Cookies.DatasourceAuth) + const provider = authStateCookie.provider + const middleware = require(`@budibase/backend-core/middleware`) + const handler = middleware.datasource[provider] + return handler.postAuth(passport, ctx, next) } /** diff --git a/packages/worker/src/api/routes/global/auth.js b/packages/worker/src/api/routes/global/auth.js index 34cc397966..373bf5736a 100644 --- a/packages/worker/src/api/routes/global/auth.js +++ b/packages/worker/src/api/routes/global/auth.js @@ -64,14 +64,16 @@ router authController.googlePreAuth ) .get( - "/api/global/auth/:tenantId/google2", + "/api/global/auth/:tenantId/datasource/:provider", updateTenant, - authController.googlePreAuth2 + authController.datasourcePreAuth ) // single tenancy endpoint .get("/api/global/auth/google/callback", authController.googleAuth) - // TODO: Remove - .get("/api/global/auth/google2/callback", authController.googleAuth2) + .get( + "/api/global/auth/datasource/:provider/callback", + authController.datasourceAuth + ) // multi-tenancy endpoint .get( "/api/global/auth/:tenantId/google/callback",