From f0f9736c0b91d290841e248f0ec4eb7383b0d24c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 16 Jul 2021 15:08:58 +0100 Subject: [PATCH] Updating auth to utilise the tenant system. --- packages/auth/src/cache/user.js | 2 +- packages/auth/src/db/utils.js | 12 +++++++----- packages/auth/src/index.js | 2 +- .../auth/src/middleware/passport/google.js | 2 +- .../auth/src/middleware/passport/local.js | 19 +++++++++++++------ packages/auth/src/utils.js | 12 ++++-------- .../auth/_components/GoogleButton.svelte | 5 +++-- packages/builder/src/stores/portal/auth.js | 13 ++++++++++--- .../builder/src/stores/portal/organisation.js | 2 +- .../src/tests/utilities/TestConfiguration.js | 2 +- .../src/api/controllers/admin/configs.js | 4 +++- .../worker/src/api/controllers/admin/users.js | 2 +- packages/worker/src/api/controllers/app.js | 11 ++--------- packages/worker/src/api/index.js | 16 ++++++++-------- packages/worker/src/api/routes/admin/auth.js | 2 +- .../tests/utilities/TestConfiguration.js | 5 ++++- 16 files changed, 61 insertions(+), 50 deletions(-) diff --git a/packages/auth/src/cache/user.js b/packages/auth/src/cache/user.js index b49721a541..616612a588 100644 --- a/packages/auth/src/cache/user.js +++ b/packages/auth/src/cache/user.js @@ -6,7 +6,7 @@ const EXPIRY_SECONDS = 3600 exports.getUser = async (userId, tenantId = null) => { if (!tenantId) { - tenantId = await lookupTenantId({ userId }) + tenantId = await lookupTenantId(userId) } const client = await redis.getUserClient() // try cache diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index f305d18d0d..1a69bb5517 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -4,6 +4,7 @@ const { getDB } = require("./index") const UNICODE_MAX = "\ufff0" const SEPARATOR = "_" +const DEFAULT_TENANT = "default" exports.ViewNames = { USER_BY_EMAIL: "by_email", @@ -72,19 +73,20 @@ function getDocParams(docType, docId = null, otherProps = {}) { * Gets the name of the global DB to connect to in a multi-tenancy system. */ exports.getGlobalDB = tenantId => { - const globalName = exports.StaticDatabases.GLOBAL.name // fallback for system pre multi-tenancy - if (!tenantId) { - return globalName + let dbName = exports.StaticDatabases.GLOBAL.name + if (tenantId && tenantId !== DEFAULT_TENANT) { + dbName = `${tenantId}${SEPARATOR}${dbName}` } - return getDB(`${tenantId}${SEPARATOR}${globalName}`) + return getDB(dbName) } /** * Given a koa context this tries to find the correct tenant Global DB. */ exports.getGlobalDBFromCtx = ctx => { - return exports.getGlobalDB(ctx.user.tenantId) + const user = ctx.user || {} + return exports.getGlobalDB(user.tenantId) } /** diff --git a/packages/auth/src/index.js b/packages/auth/src/index.js index 2e398d8c55..e8eeb6c8d0 100644 --- a/packages/auth/src/index.js +++ b/packages/auth/src/index.js @@ -1,7 +1,7 @@ const passport = require("koa-passport") const LocalStrategy = require("passport-local").Strategy const JwtStrategy = require("passport-jwt").Strategy -const { getGlobalDB } = require("./db/utils") +const { getGlobalDB, StaticDatabases } = require("./db/utils") const { jwt, local, authenticated, google, auditLog } = require("./middleware") const { setDB } = require("./db") const userCache = require("./cache/user") diff --git a/packages/auth/src/middleware/passport/google.js b/packages/auth/src/middleware/passport/google.js index bc68121577..446e9b5cba 100644 --- a/packages/auth/src/middleware/passport/google.js +++ b/packages/auth/src/middleware/passport/google.js @@ -13,7 +13,7 @@ const { lookupTenantId } = require("../../utils") async function authenticate(token, tokenSecret, profile, done) { // Check the user exists in the instance DB by email const userId = generateGlobalUserID(profile.id) - const tenantId = await lookupTenantId({ userId }) + const tenantId = await lookupTenantId(userId) const db = getGlobalDB(tenantId) let dbUser diff --git a/packages/auth/src/middleware/passport/local.js b/packages/auth/src/middleware/passport/local.js index 147305e318..9ed837e1fe 100644 --- a/packages/auth/src/middleware/passport/local.js +++ b/packages/auth/src/middleware/passport/local.js @@ -8,20 +8,27 @@ const { createASession } = require("../../security/sessions") const INVALID_ERR = "Invalid Credentials" -exports.options = {} +exports.options = { + passReqToCallback: true, +} /** * Passport Local Authentication Middleware. - * @param {*} email - username to login with - * @param {*} password - plain text password to log in with - * @param {*} done - callback from passport to return user information and errors + * @param {*} ctx the request structure + * @param {*} email username to login with + * @param {*} password plain text password to log in with + * @param {*} done callback from passport to return user information and errors * @returns The authenticated user, or errors if they occur */ -exports.authenticate = async function (email, password, done) { +exports.authenticate = async function (ctx, email, password, done) { if (!email) return done(null, false, "Email Required.") if (!password) return done(null, false, "Password Required.") + const params = ctx.params || {} + const query = ctx.query || {} - const dbUser = await getGlobalUserByEmail(email) + // use the request to find the tenantId + const tenantId = params.tenantId || query.tenantId + const dbUser = await getGlobalUserByEmail(email, tenantId) if (dbUser == null) { return done(null, false, { message: "User not found" }) } diff --git a/packages/auth/src/utils.js b/packages/auth/src/utils.js index b5225881da..fe9230ea29 100644 --- a/packages/auth/src/utils.js +++ b/packages/auth/src/utils.js @@ -101,10 +101,9 @@ exports.isClient = ctx => { return ctx.headers["x-budibase-type"] === "client" } -exports.lookupTenantId = async ({ email, userId }) => { - const toQuery = email || userId +exports.lookupTenantId = async userId => { const db = getDB(StaticDatabases.PLATFORM_INFO.name) - const doc = await db.get(toQuery) + const doc = await db.get(userId) if (!doc || !doc.tenantId) { throw "Unable to find tenant" } @@ -118,13 +117,10 @@ exports.lookupTenantId = async ({ email, userId }) => { * @param {string|null} tenantId If tenant ID is known it can be specified * @return {Promise} */ -exports.getGlobalUserByEmail = async (email, tenantId = null) => { +exports.getGlobalUserByEmail = async (email, tenantId) => { if (email == null) { throw "Must supply an email address to view" } - if (!tenantId) { - tenantId = await exports.lookupTenantId({ email }) - } const db = getGlobalDB(tenantId) try { let users = ( @@ -138,7 +134,7 @@ exports.getGlobalUserByEmail = async (email, tenantId = null) => { } catch (err) { if (err != null && err.name === "not_found") { await createUserEmailView(db) - return exports.getGlobalUserByEmail(email) + return exports.getGlobalUserByEmail(email, tenantId) } else { throw err } diff --git a/packages/builder/src/pages/builder/auth/_components/GoogleButton.svelte b/packages/builder/src/pages/builder/auth/_components/GoogleButton.svelte index ffd870c213..b5d3394fd1 100644 --- a/packages/builder/src/pages/builder/auth/_components/GoogleButton.svelte +++ b/packages/builder/src/pages/builder/auth/_components/GoogleButton.svelte @@ -1,16 +1,17 @@ {#if show} window.open("/api/admin/auth/google", "_blank")} + on:click={() => window.open(`/api/admin/auth/${tenantId}/google`, "_blank")} >
google icon diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index ef91c114f6..e34fd2584e 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -7,6 +7,7 @@ export function createAuthStore() { let initials = null let isAdmin = false let isBuilder = false + let tenantId = "default" if ($user) { if ($user.firstName) { initials = $user.firstName[0] @@ -20,18 +21,21 @@ export function createAuthStore() { } isAdmin = !!$user.admin?.global isBuilder = !!$user.builder?.global + tenantId = $user.tenantId || "default" } return { user: $user, initials, isAdmin, isBuilder, + tenantId, } }) return { subscribe: store.subscribe, checkAuth: async () => { + const response = await api.get("/api/admin/users/self") if (response.status !== 200) { user.set(null) @@ -41,7 +45,8 @@ export function createAuthStore() { } }, login: async creds => { - const response = await api.post(`/api/admin/auth`, creds) + const tenantId = get(store).tenantId + const response = await api.post(`/api/admin/auth/${tenantId}/login`, creds) const json = await response.json() if (response.status === 200) { user.set(json.user) @@ -68,7 +73,8 @@ export function createAuthStore() { } }, forgotPassword: async email => { - const response = await api.post(`/api/admin/auth/reset`, { + const tenantId = get(store).tenantId + const response = await api.post(`/api/admin/auth/${tenantId}/reset`, { email, }) if (response.status !== 200) { @@ -77,7 +83,8 @@ export function createAuthStore() { await response.json() }, resetPassword: async (password, code) => { - const response = await api.post(`/api/admin/auth/reset/update`, { + const tenantId = get(store).tenantId + const response = await api.post(`/api/admin/auth/${tenantId}/reset/update`, { password, resetCode: code, }) diff --git a/packages/builder/src/stores/portal/organisation.js b/packages/builder/src/stores/portal/organisation.js index 7e6a777cd4..6c0d5412a7 100644 --- a/packages/builder/src/stores/portal/organisation.js +++ b/packages/builder/src/stores/portal/organisation.js @@ -2,7 +2,7 @@ import { writable, get } from "svelte/store" import api from "builderStore/api" const DEFAULT_CONFIG = { - platformUrl: "http://localhost:1000", + platformUrl: "http://localhost:10000", logoUrl: undefined, docsUrl: undefined, company: "Budibase", diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 3a883b4a71..ceef2a3e91 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -23,7 +23,7 @@ const { user: userCache } = require("@budibase/auth/cache") const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" const PASSWORD = "babs_password" -const TENANT_ID = "tenant1" +const TENANT_ID = "default" class TestConfiguration { constructor(openServer = true) { diff --git a/packages/worker/src/api/controllers/admin/configs.js b/packages/worker/src/api/controllers/admin/configs.js index bf4ded0a29..02dd18360a 100644 --- a/packages/worker/src/api/controllers/admin/configs.js +++ b/packages/worker/src/api/controllers/admin/configs.js @@ -5,6 +5,7 @@ const { getGlobalUserParams, getScopedFullConfig, getGlobalDBFromCtx, + getGlobalDB, getAllApps, } = require("@budibase/auth/db") const { Configs } = require("../../../constants") @@ -168,7 +169,8 @@ exports.destroy = async function (ctx) { } exports.configChecklist = async function (ctx) { - const db = getGlobalDBFromCtx(ctx) + const tenantId = ctx.query.tenantId + const db = tenantId ? getGlobalDB(tenantId) : getGlobalDBFromCtx(ctx) try { // TODO: Watch get started video diff --git a/packages/worker/src/api/controllers/admin/users.js b/packages/worker/src/api/controllers/admin/users.js index 73c10d007d..a27b42d59a 100644 --- a/packages/worker/src/api/controllers/admin/users.js +++ b/packages/worker/src/api/controllers/admin/users.js @@ -56,7 +56,7 @@ async function saveUser(user, tenantId) { // make sure another user isn't using the same email let dbUser if (email) { - dbUser = await getGlobalUserByEmail(email) + dbUser = await getGlobalUserByEmail(email, tenantId) if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) { throw "Email address already in use." } diff --git a/packages/worker/src/api/controllers/app.js b/packages/worker/src/api/controllers/app.js index ff9692a5ec..782c45bc16 100644 --- a/packages/worker/src/api/controllers/app.js +++ b/packages/worker/src/api/controllers/app.js @@ -1,18 +1,11 @@ -const { DocumentTypes } = require("@budibase/auth").db +const { getAllApps } = require("@budibase/auth/db") const CouchDB = require("../../db") -const APP_PREFIX = "app_" const URL_REGEX_SLASH = /\/|\\/g exports.getApps = async ctx => { - // allDbs call of CouchDB is very inaccurate in production - const allDbs = await CouchDB.allDbs() - const appDbNames = allDbs.filter(dbName => dbName.startsWith(APP_PREFIX)) - const appPromises = appDbNames.map(db => - new CouchDB(db).get(DocumentTypes.APP_METADATA) - ) + const apps = await getAllApps({ CouchDB }) - const apps = await Promise.allSettled(appPromises) const body = {} for (let app of apps) { if (app.status !== "fulfilled") { diff --git a/packages/worker/src/api/index.js b/packages/worker/src/api/index.js index bda57863f6..a62cd4db58 100644 --- a/packages/worker/src/api/index.js +++ b/packages/worker/src/api/index.js @@ -14,29 +14,29 @@ const PUBLIC_ENDPOINTS = [ method: "POST", }, { - route: "/api/admin/auth", + route: "/api/admin/auth/:tenantId/login", method: "POST", }, { - route: "/api/admin/auth/google", + route: "/api/admin/auth/:tenantId/google", method: "GET", }, { - route: "/api/admin/auth/google/callback", + route: "/api/admin/auth/:tenantId/google/callback", method: "GET", }, { - route: "/api/admin/auth/reset", + route: "/api/admin/auth/:tenantId/reset", + method: "POST", + }, + { + route: "/api/admin/auth/:tenantId/reset/update", method: "POST", }, { route: "/api/admin/configs/checklist", method: "GET", }, - { - route: "/api/apps", - method: "GET", - }, { route: "/api/admin/configs/public", method: "GET", diff --git a/packages/worker/src/api/routes/admin/auth.js b/packages/worker/src/api/routes/admin/auth.js index c26acec6c4..3fef6d7233 100644 --- a/packages/worker/src/api/routes/admin/auth.js +++ b/packages/worker/src/api/routes/admin/auth.js @@ -29,7 +29,7 @@ function buildResetUpdateValidation() { } router - .post("/api/admin/auth", buildAuthValidation(), authController.authenticate) + .post("/api/admin/auth/:tenantId/login", buildAuthValidation(), authController.authenticate) .post("/api/admin/auth/:tenantId/reset", buildResetValidation(), authController.reset) .post( "/api/admin/auth/:tenantId/reset/update", diff --git a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js index c205a45e38..593c0edd5a 100644 --- a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js @@ -7,6 +7,8 @@ const { Configs, LOGO_URL } = require("../../../../constants") const { getGlobalUserByEmail } = require("@budibase/auth").utils const { createASession } = require("@budibase/auth/sessions") +const TENANT_ID = "default" + class TestConfiguration { constructor(openServer = true) { if (openServer) { @@ -72,6 +74,7 @@ class TestConfiguration { _id: "us_uuid1", userId: "us_uuid1", sessionId: "sessionid", + tenantId: TENANT_ID, } const authToken = jwt.sign(user, env.JWT_SECRET) return { @@ -81,7 +84,7 @@ class TestConfiguration { } async getUser(email) { - return getGlobalUserByEmail(email) + return getGlobalUserByEmail(email, TENANT_ID) } async createUser(email = "test@test.com", password = "test") {