From 00c67289b0d7eed413f4e2477b390297a136d156 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Apr 2022 16:56:30 +0100 Subject: [PATCH] Fixing an issue with cookie auth. --- packages/backend-core/src/db/pouch.js | 63 ++++++++----------- packages/backend-core/src/db/utils.js | 14 +++-- .../src/api/controllers/row/internalSearch.js | 15 +++-- 3 files changed, 44 insertions(+), 48 deletions(-) diff --git a/packages/backend-core/src/db/pouch.js b/packages/backend-core/src/db/pouch.js index 722913aa67..a04fd60a02 100644 --- a/packages/backend-core/src/db/pouch.js +++ b/packages/backend-core/src/db/pouch.js @@ -1,35 +1,36 @@ const PouchDB = require("pouchdb") const env = require("../environment") -exports.getCouchUrl = () => { - if (!env.COUCH_DB_URL) return - - // username and password already exist in URL - if (env.COUCH_DB_URL.includes("@")) { - return env.COUCH_DB_URL +exports.getCouchInfo = () => { + let url = "http://localhost:4005" + if (env.COUCH_DB_URL && env.COUCH_DB_URL.includes("@")) { + url = env.COUCH_DB_URL + } else if (env.COUCH_DB_URL) { + const [protocol, ...rest] = env.COUCH_DB_URL.split("://") + url = `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` + if (!env.COUCH_DB_USERNAME || !env.COUCH_DB_PASSWORD) { + throw new Error( + "CouchDB configuration invalid. You must provide a fully qualified CouchDB url, or the COUCH_DB_USER and COUCH_DB_PASSWORD environment variables." + ) + } } - - const [protocol, ...rest] = env.COUCH_DB_URL.split("://") - - if (!env.COUCH_DB_USERNAME || !env.COUCH_DB_PASSWORD) { - throw new Error( - "CouchDB configuration invalid. You must provide a fully qualified CouchDB url, or the COUCH_DB_USER and COUCH_DB_PASSWORD environment variables." - ) - } - - return `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` -} - -exports.splitCouchUrl = url => { const [protocol, rest] = url.split("://") const [auth, host] = rest.split("@") - const [username, password] = auth.split(":") + let [username, password] = auth.split(":") + if (!username && env.COUCH_DB_USERNAME) { + username = env.COUCH_DB_USERNAME + } + if (!password && env.COUCH_DB_PASSWORD) { + password = env.COUCH_DB_PASSWORD + } + const authCookie = Buffer.from(`${username}:${password}`).toString("base64") return { url: `${protocol}://${host}`, auth: { - username, - password, + username: username, + password: password, }, + cookie: `Basic ${authCookie}`, } } @@ -39,26 +40,12 @@ exports.splitCouchUrl = url => { * Exposed for exceptional cases such as in-memory views. */ exports.getPouch = (opts = {}) => { - let auth = { - username: env.COUCH_DB_USERNAME, - password: env.COUCH_DB_PASSWORD, - } - let url = exports.getCouchUrl() || "http://localhost:4005" - // need to update security settings - if (!auth.username || !auth.password || url.includes("@")) { - const split = exports.splitCouchUrl(url) - url = split.url - auth = split.auth - } - - const authCookie = Buffer.from(`${auth.username}:${auth.password}`).toString( - "base64" - ) + let { url, cookie } = exports.getCouchInfo() let POUCH_DB_DEFAULTS = { prefix: url, fetch: (url, opts) => { // use a specific authorization cookie - be very explicit about how we authenticate - opts.headers.set("Authorization", `Basic ${authCookie}`) + opts.headers.set("Authorization", cookie) return PouchDB.fetch(url, opts) }, } diff --git a/packages/backend-core/src/db/utils.js b/packages/backend-core/src/db/utils.js index 9e2a06d065..1e2ba846de 100644 --- a/packages/backend-core/src/db/utils.js +++ b/packages/backend-core/src/db/utils.js @@ -12,7 +12,7 @@ const { const { getTenantId, getGlobalDBName } = require("../tenancy") const fetch = require("node-fetch") const { doWithDB, allDbs } = require("./index") -const { getCouchUrl } = require("./pouch") +const { getCouchInfo } = require("./pouch") const { getAppMetadata } = require("../cache/appMetadata") const { checkSlashesInUrl } = require("../helpers") const { @@ -169,8 +169,14 @@ exports.getAllDbs = async (opts = { efficient: false }) => { return allDbs() } let dbs = [] - async function addDbs(url) { - const response = await fetch(checkSlashesInUrl(encodeURI(url))) + let { url, cookie } = getCouchInfo() + async function addDbs(couchUrl) { + const response = await fetch(checkSlashesInUrl(encodeURI(couchUrl)), { + method: "GET", + headers: { + Authorization: cookie, + }, + }) if (response.status === 200) { let json = await response.json() dbs = dbs.concat(json) @@ -178,7 +184,7 @@ exports.getAllDbs = async (opts = { efficient: false }) => { throw "Cannot connect to CouchDB instance" } } - let couchUrl = `${getCouchUrl()}/_all_dbs` + let couchUrl = `${url}/_all_dbs` let tenantId = getTenantId() if (!env.MULTI_TENANCY || (!efficient && tenantId === DEFAULT_TENANT_ID)) { // just get all DBs when: diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index ad95a25fc5..5f1dc25faa 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -1,6 +1,6 @@ const { SearchIndexes } = require("../../../db/utils") const fetch = require("node-fetch") -const { getCouchUrl } = require("@budibase/backend-core/db") +const { getCouchInfo } = require("@budibase/backend-core/db") const { getAppId } = require("@budibase/backend-core/context") /** @@ -242,11 +242,10 @@ class QueryBuilder { async run() { const appId = getAppId() - const url = `${getCouchUrl()}/${appId}/_design/database/_search/${ - SearchIndexes.ROWS - }` + const { url, cookie } = getCouchInfo() + const fullPath = `${url}/${appId}/_design/database/_search/${SearchIndexes.ROWS}` const body = this.buildSearchBody() - return await runQuery(url, body) + return await runQuery(fullPath, body, cookie) } } @@ -254,12 +253,16 @@ class QueryBuilder { * Executes a lucene search query. * @param url The query URL * @param body The request body defining search criteria + * @param cookie The auth cookie for CouchDB * @returns {Promise<{rows: []}>} */ -const runQuery = async (url, body) => { +const runQuery = async (url, body, cookie) => { const response = await fetch(url, { body: JSON.stringify(body), method: "POST", + headers: { + Authorization: cookie, + }, }) const json = await response.json()