Implementing some changes to how context gets set for tenancy, after testing, as well as updating server.

This commit is contained in:
mike12345567 2021-08-03 15:32:25 +01:00
parent f3ce979230
commit e7974f7e86
18 changed files with 194 additions and 152 deletions

View File

@ -1,22 +1,25 @@
const redis = require("../redis/authRedis") const redis = require("../redis/authRedis")
const { const {
updateTenantId, getTenantId,
lookupTenantId, lookupTenantId,
getGlobalDB, getGlobalDB,
isTenantIdSet,
} = require("../tenancy") } = require("../tenancy")
const EXPIRY_SECONDS = 3600 const EXPIRY_SECONDS = 3600
exports.getUser = async userId => { exports.getUser = async (userId, tenantId = null) => {
if (!isTenantIdSet()) { if (!tenantId) {
updateTenantId(await lookupTenantId(userId)) try {
tenantId = getTenantId()
} catch (err) {
tenantId = await lookupTenantId(userId)
}
} }
const client = await redis.getUserClient() const client = await redis.getUserClient()
// try cache // try cache
let user = await client.get(userId) let user = await client.get(userId)
if (!user) { if (!user) {
user = await getGlobalDB().get(userId) user = await getGlobalDB(tenantId).get(userId)
client.store(userId, user, EXPIRY_SECONDS) client.store(userId, user, EXPIRY_SECONDS)
} }
return user return user

View File

@ -3,7 +3,6 @@ const { getCookie, clearCookie } = require("../utils")
const { getUser } = require("../cache/user") const { getUser } = require("../cache/user")
const { getSession, updateSessionTTL } = require("../security/sessions") const { getSession, updateSessionTTL } = require("../security/sessions")
const { buildMatcherRegex, matches } = require("./matchers") const { buildMatcherRegex, matches } = require("./matchers")
const { isTenantIdSet, updateTenantId } = require("../tenancy")
const env = require("../environment") const env = require("../environment")
function finalise( function finalise(
@ -17,6 +16,11 @@ function finalise(
ctx.version = version ctx.version = version
} }
/**
* This middleware is tenancy aware, so that it does not depend on other middlewares being used.
* The tenancy modules should not be used here and it should be assumed that the tenancy context
* has not yet been populated.
*/
module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => {
const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : [] const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : []
return async (ctx, next) => { return async (ctx, next) => {
@ -42,10 +46,7 @@ module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => {
error = "No session found" error = "No session found"
} else { } else {
try { try {
if (session.tenantId && !isTenantIdSet()) { user = await getUser(userId, session.tenantId)
updateTenantId(session.tenantId)
}
user = await getUser(userId)
delete user.password delete user.password
authenticated = true authenticated = true
} catch (err) { } catch (err) {

View File

@ -1,6 +1,9 @@
const PARAM_REGEX = /\/:(.*?)(\/.*)?$/g const PARAM_REGEX = /\/:(.*?)(\/.*)?$/g
exports.buildMatcherRegex = patterns => { exports.buildMatcherRegex = patterns => {
if (!patterns) {
return []
}
return patterns.map(pattern => { return patterns.map(pattern => {
const isObj = typeof pattern === "object" && pattern.route const isObj = typeof pattern === "object" && pattern.route
const method = isObj ? pattern.method : "GET" const method = isObj ? pattern.method : "GET"

View File

@ -1,20 +1,14 @@
const { createTenancyContext, setTenantId } = require("../tenancy") const { setTenantId } = require("../tenancy")
const ContextFactory = require("../tenancy/FunctionContext")
const { buildMatcherRegex, matches } = require("./matchers") const { buildMatcherRegex, matches } = require("./matchers")
module.exports = (allowQueryStringPatterns, noTenancyPatterns) => { module.exports = (allowQueryStringPatterns, noTenancyPatterns) => {
const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns) const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns)
const noTenancyOptions = buildMatcherRegex(noTenancyPatterns) const noTenancyOptions = buildMatcherRegex(noTenancyPatterns)
return (ctx, next) => { return ContextFactory.getMiddleware(ctx => {
// always run in context const allowNoTenant = !!matches(ctx, noTenancyOptions)
return createTenancyContext().runAndReturn(() => { const allowQs = !!matches(ctx, allowQsOptions)
if (matches(ctx, noTenancyOptions)) { setTenantId(ctx, { allowQs, allowNoTenant })
return next() })
}
const allowQs = !!matches(ctx, allowQsOptions)
setTenantId(ctx, { allowQs })
return next()
})
}
} }

View File

@ -0,0 +1,69 @@
const cls = require("cls-hooked")
const { newid } = require("../hashing")
const REQUEST_ID_KEY = "requestId"
class FunctionContext {
static getMiddleware(updateCtxFn = null) {
const namespace = this.createNamespace()
return async function(ctx, next) {
await new Promise(namespace.bind(function(resolve, reject) {
// store a contextual request ID that can be used anywhere (audit logs)
namespace.set(REQUEST_ID_KEY, newid())
namespace.bindEmitter(ctx.req)
namespace.bindEmitter(ctx.res)
if (updateCtxFn) {
updateCtxFn(ctx)
}
next().then(resolve).catch(reject)
}))
}
}
static run(callback) {
const namespace = this.createNamespace()
return namespace.runAndReturn(callback)
}
static setOnContext(key, value) {
const namespace = this.createNamespace()
namespace.set(key, value)
}
static getContextStorage() {
if (this._namespace && this._namespace.active) {
const { id, _ns_name, ...contextData } = this._namespace.active
return contextData
}
return {}
}
static getFromContext(key) {
const context = this.getContextStorage()
if (context) {
return context[key]
} else {
return null
}
}
static destroyNamespace() {
if (this._namespace) {
cls.destroyNamespace("session")
this._namespace = null
}
}
static createNamespace() {
if (!this._namespace) {
this._namespace = cls.createNamespace("session")
}
return this._namespace
}
}
module.exports = FunctionContext

View File

@ -1,6 +1,6 @@
const cls = require("cls-hooked")
const env = require("../environment") const env = require("../environment")
const { Headers } = require("../../constants") const { Headers } = require("../../constants")
const cls = require("./FunctionContext")
exports.DEFAULT_TENANT_ID = "default" exports.DEFAULT_TENANT_ID = "default"
@ -12,66 +12,61 @@ exports.isMultiTenant = () => {
return env.MULTI_TENANCY return env.MULTI_TENANCY
} }
// continuation local storage
const CONTEXT_NAME = "tenancy"
const TENANT_ID = "tenantId" const TENANT_ID = "tenantId"
exports.createTenancyContext = () => {
return cls.createNamespace(CONTEXT_NAME)
}
const getTenancyContext = () => {
return cls.getNamespace(CONTEXT_NAME)
}
// used for automations, API endpoints should always be in context already // used for automations, API endpoints should always be in context already
exports.doInTenant = (tenantId, task) => { exports.doInTenant = (tenantId, task) => {
const context = getTenancyContext() return cls.run(() => {
return getTenancyContext().runAndReturn(() => {
// set the tenant id // set the tenant id
context.set(TENANT_ID, tenantId) cls.setOnContext(TENANT_ID, tenantId)
// invoke the task // invoke the task
const result = task() const result = task()
// clear down the tenant id manually for extra safety // clear down the tenant id manually for extra safety
// this should also happen automatically when the call exits // this should also happen automatically when the call exits
context.set(TENANT_ID, null) cls.setOnContext(TENANT_ID, null)
return result return result
}) })
} }
exports.updateTenantId = tenantId => { exports.updateTenantId = tenantId => {
getTenancyContext().set(TENANT_ID, tenantId) cls.setOnContext(TENANT_ID, tenantId)
} }
exports.setTenantId = (ctx, opts = { allowQs: false }) => { exports.setTenantId = (ctx, opts = { allowQs: false, allowNoTenant: false }) => {
let tenantId let tenantId
// exit early if not multi-tenant // exit early if not multi-tenant
if (!exports.isMultiTenant()) { if (!exports.isMultiTenant()) {
getTenancyContext().set(TENANT_ID, this.DEFAULT_TENANT_ID) cls.setOnContext(TENANT_ID, this.DEFAULT_TENANT_ID)
return return
} }
const params = ctx.request.params || {} const allowQs = opts && opts.allowQs
const allowNoTenant = opts && opts.allowNoTenant
const header = ctx.request.headers[Headers.TENANT_ID] const header = ctx.request.headers[Headers.TENANT_ID]
const user = ctx.request.user || {} const user = ctx.user || {}
tenantId = user.tenantId || params.tenantId || header if (allowQs) {
if (opts.allowQs && !tenantId) {
const query = ctx.request.query || {} const query = ctx.request.query || {}
tenantId = query.tenantId tenantId = query.tenantId
} }
// override query string (if allowed) by user, or header
// URL params cannot be used in a middleware, as they are
// processed later in the chain
tenantId = user.tenantId || header || tenantId
if (!tenantId) { if (!tenantId && !allowNoTenant) {
ctx.throw(403, "Tenant id not set") ctx.throw(403, "Tenant id not set")
} }
// check tenant ID just incase no tenant was allowed
getTenancyContext().set(TENANT_ID, tenantId) if (tenantId) {
cls.setOnContext(TENANT_ID, tenantId)
}
} }
exports.isTenantIdSet = () => { exports.isTenantIdSet = () => {
const tenantId = getTenancyContext().get(TENANT_ID) const tenantId = cls.getFromContext(TENANT_ID)
return !!tenantId return !!tenantId
} }
@ -79,7 +74,7 @@ exports.getTenantId = () => {
if (!exports.isMultiTenant()) { if (!exports.isMultiTenant()) {
return exports.DEFAULT_TENANT_ID return exports.DEFAULT_TENANT_ID
} }
const tenantId = getTenancyContext().get(TENANT_ID) const tenantId = cls.getFromContext(TENANT_ID)
if (!tenantId) { if (!tenantId) {
throw Error("Tenant id not found") throw Error("Tenant id not found")
} }

View File

@ -1,4 +1,4 @@
const { getDB } = require("../../db") const { getDB } = require("../db")
const { SEPARATOR, StaticDatabases } = require("../db/constants") const { SEPARATOR, StaticDatabases } = require("../db/constants")
const { getTenantId, DEFAULT_TENANT_ID, isMultiTenant } = require("./context") const { getTenantId, DEFAULT_TENANT_ID, isMultiTenant } = require("./context")
const env = require("../environment") const env = require("../environment")

View File

@ -94,6 +94,9 @@
requireAuth = smtpConfig.config.auth != null requireAuth = smtpConfig.config.auth != null
// always attach the auth for the forms purpose - // always attach the auth for the forms purpose -
// this will be removed later if required // this will be removed later if required
if (!smtpDoc.config) {
smtpDoc.config = {}
}
if (!smtpDoc.config.auth) { if (!smtpDoc.config.auth) {
smtpConfig.config.auth = { smtpConfig.config.auth = {
type: "login", type: "login",

View File

@ -38,6 +38,7 @@ const {
backupClientLibrary, backupClientLibrary,
revertClientLibrary, revertClientLibrary,
} = require("../../utilities/fileSystem/clientLibrary") } = require("../../utilities/fileSystem/clientLibrary")
const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy")
const URL_REGEX_SLASH = /\/|\\/g const URL_REGEX_SLASH = /\/|\\/g
@ -92,8 +93,9 @@ async function getAppUrlIfNotInUse(ctx) {
return url return url
} }
async function createInstance(tenantId, template) { async function createInstance(template) {
const baseAppId = generateAppID(env.MULTI_TENANCY ? tenantId : null) const tenantId = isMultiTenant() ? getTenantId() : null
const baseAppId = generateAppID(tenantId)
const appId = generateDevAppID(baseAppId) const appId = generateDevAppID(baseAppId)
const db = new CouchDB(appId) const db = new CouchDB(appId)
@ -128,8 +130,7 @@ async function createInstance(tenantId, template) {
exports.fetch = async function (ctx) { exports.fetch = async function (ctx) {
const dev = ctx.query && ctx.query.status === AppStatus.DEV const dev = ctx.query && ctx.query.status === AppStatus.DEV
const all = ctx.query && ctx.query.status === AppStatus.ALL const all = ctx.query && ctx.query.status === AppStatus.ALL
const tenantId = ctx.user.tenantId const apps = await getAllApps(CouchDB, { dev, all })
const apps = await getAllApps(CouchDB, { tenantId, dev, all })
// get the locks for all the dev apps // get the locks for all the dev apps
if (dev || all) { if (dev || all) {
@ -189,7 +190,6 @@ exports.fetchAppPackage = async function (ctx) {
} }
exports.create = async function (ctx) { exports.create = async function (ctx) {
const tenantId = ctx.user.tenantId
const { useTemplate, templateKey } = ctx.request.body const { useTemplate, templateKey } = ctx.request.body
const instanceConfig = { const instanceConfig = {
useTemplate, useTemplate,
@ -198,7 +198,7 @@ exports.create = async function (ctx) {
if (ctx.request.files && ctx.request.files.templateFile) { if (ctx.request.files && ctx.request.files.templateFile) {
instanceConfig.file = ctx.request.files.templateFile instanceConfig.file = ctx.request.files.templateFile
} }
const instance = await createInstance(tenantId, instanceConfig) const instance = await createInstance(instanceConfig)
const appId = instance._id const appId = instance._id
const url = await getAppUrlIfNotInUse(ctx) const url = await getAppUrlIfNotInUse(ctx)
@ -222,7 +222,7 @@ exports.create = async function (ctx) {
url: url, url: url,
template: ctx.request.body.template, template: ctx.request.body.template,
instance: instance, instance: instance,
tenantId, tenantId: getTenantId(),
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
} }

View File

@ -10,6 +10,13 @@ const env = require("../environment")
const router = new Router() const router = new Router()
const NO_TENANCY_ENDPOINTS = [
{
route: "/api/analytics",
method: "GET",
},
]
router router
.use( .use(
compress({ compress({
@ -32,12 +39,13 @@ router
}) })
.use("/health", ctx => (ctx.status = 200)) .use("/health", ctx => (ctx.status = 200))
.use("/version", ctx => (ctx.body = pkg.version)) .use("/version", ctx => (ctx.body = pkg.version))
.use(buildTenancyMiddleware())
.use( .use(
buildAuthMiddleware(null, { buildAuthMiddleware(null, {
publicAllowed: true, publicAllowed: true,
}) })
) )
// nothing in the server should allow query string tenants
.use(buildTenancyMiddleware(null, NO_TENANCY_ENDPOINTS))
.use(currentApp) .use(currentApp)
.use(auditLog) .use(auditLog)

View File

@ -46,13 +46,13 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs, tenantId }) { module.exports.run = async function ({ inputs }) {
let { to, from, subject, contents } = inputs let { to, from, subject, contents } = inputs
if (!contents) { if (!contents) {
contents = "<h1>No content</h1>" contents = "<h1>No content</h1>"
} }
try { try {
let response = await sendSmtpEmail(tenantId, to, from, subject, contents) let response = await sendSmtpEmail(to, from, subject, contents)
return { return {
success: true, success: true,
response, response,

View File

@ -6,6 +6,7 @@ const { processObject } = require("@budibase/string-templates")
const { DEFAULT_TENANT_ID } = require("@budibase/auth").constants const { DEFAULT_TENANT_ID } = require("@budibase/auth").constants
const CouchDB = require("../db") const CouchDB = require("../db")
const { DocumentTypes } = require("../db/utils") const { DocumentTypes } = require("../db/utils")
const { doInTenant } = require("@budibase/auth/tenancy")
const FILTER_STEP_ID = logic.BUILTIN_DEFINITIONS.FILTER.stepId const FILTER_STEP_ID = logic.BUILTIN_DEFINITIONS.FILTER.stepId
@ -56,7 +57,7 @@ class Orchestrator {
async execute() { async execute() {
let automation = this._automation let automation = this._automation
const app = this.getApp() const app = await this.getApp()
for (let step of automation.definition.steps) { for (let step of automation.definition.steps) {
let stepFn = await this.getStepFunctionality(step.type, step.stepId) let stepFn = await this.getStepFunctionality(step.type, step.stepId)
step.inputs = await processObject(step.inputs, this._context) step.inputs = await processObject(step.inputs, this._context)
@ -66,13 +67,15 @@ class Orchestrator {
) )
// appId is always passed // appId is always passed
try { try {
const outputs = await stepFn({ let tenantId = app.tenantId || DEFAULT_TENANT_ID
inputs: step.inputs, const outputs = await doInTenant(tenantId, () => {
appId: this._appId, return stepFn({
apiKey: automation.apiKey, inputs: step.inputs,
emitter: this._emitter, appId: this._appId,
context: this._context, apiKey: automation.apiKey,
tenantId: app.tenantId || DEFAULT_TENANT_ID, emitter: this._emitter,
context: this._context,
})
}) })
if (step.stepId === FILTER_STEP_ID && !outputs.success) { if (step.stepId === FILTER_STEP_ID && !outputs.success) {
break break

View File

@ -68,5 +68,6 @@ module.exports = async (ctx, next) => {
) { ) {
setCookie(ctx, { appId }, Cookies.CurrentApp) setCookie(ctx, { appId }, Cookies.CurrentApp)
} }
return next() return next()
} }

View File

@ -33,7 +33,9 @@ function processUser(appId, user) {
} }
exports.getCachedSelf = async (ctx, appId) => { exports.getCachedSelf = async (ctx, appId) => {
const user = await userCache.getUser(ctx.user._id, ctx.user.tenantId) // this has to be tenant aware, can't depend on the context to find it out
// running some middlewares before the tenancy causes context to break
const user = await userCache.getUser(ctx.user._id)
return processUser(appId, user) return processUser(appId, user)
} }

View File

@ -4,6 +4,7 @@ const { checkSlashesInUrl } = require("./index")
const { getDeployedAppID } = require("@budibase/auth/db") const { getDeployedAppID } = require("@budibase/auth/db")
const { updateAppRole, getGlobalUser } = require("./global") const { updateAppRole, getGlobalUser } = require("./global")
const { Headers } = require("@budibase/auth/constants") const { Headers } = require("@budibase/auth/constants")
const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy")
function request(ctx, request) { function request(ctx, request) {
if (!request.headers) { if (!request.headers) {
@ -11,6 +12,9 @@ function request(ctx, request) {
} }
if (!ctx) { if (!ctx) {
request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY
if (isTenantIdSet()) {
request.headers[Headers.TENANT_ID] = getTenantId()
}
} }
if (request.body && Object.keys(request.body).length > 0) { if (request.body && Object.keys(request.body).length > 0) {
request.headers["Content-Type"] = "application/json" request.headers["Content-Type"] = "application/json"
@ -29,13 +33,14 @@ function request(ctx, request) {
exports.request = request exports.request = request
exports.sendSmtpEmail = async (tenantId, to, from, subject, contents) => { // have to pass in the tenant ID as this could be coming from an automation
exports.sendSmtpEmail = async (to, from, subject, contents) => {
// tenant ID will be set in header
const response = await fetch( const response = await fetch(
checkSlashesInUrl(env.WORKER_URL + `/api/global/email/send`), checkSlashesInUrl(env.WORKER_URL + `/api/global/email/send`),
request(null, { request(null, {
method: "POST", method: "POST",
body: { body: {
tenantId,
email: to, email: to,
from, from,
contents, contents,

View File

@ -13,65 +13,9 @@ const { user: userCache } = require("@budibase/auth/cache")
const { invalidateSessions } = require("@budibase/auth/sessions") const { invalidateSessions } = require("@budibase/auth/sessions")
const CouchDB = require("../../../db") const CouchDB = require("../../../db")
const env = require("../../../environment") const env = require("../../../environment")
const { getGlobalDB, getTenantId } = require("@budibase/auth/tenancy") const { getGlobalDB, getTenantId, doesTenantExist, tryAddTenant, updateTenantId } = require("@budibase/auth/tenancy")
const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name
const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants
async function tryAddTenant(tenantId, userId, email) {
const db = new CouchDB(PLATFORM_INFO_DB)
const getDoc = async id => {
if (!id) {
return null
}
try {
return await db.get(id)
} catch (err) {
return { _id: id }
}
}
let [tenants, userIdDoc, emailDoc] = await Promise.all([
getDoc(TENANT_DOC),
getDoc(userId),
getDoc(email),
])
if (!Array.isArray(tenants.tenantIds)) {
tenants = {
_id: TENANT_DOC,
tenantIds: [],
}
}
let promises = []
if (userIdDoc) {
userIdDoc.tenantId = tenantId
promises.push(db.put(userIdDoc))
}
if (emailDoc) {
emailDoc.tenantId = tenantId
promises.push(db.put(emailDoc))
}
if (tenants.tenantIds.indexOf(tenantId) === -1) {
tenants.tenantIds.push(tenantId)
promises.push(db.put(tenants))
}
await Promise.all(promises)
}
async function doesTenantExist(tenantId) {
const db = new CouchDB(PLATFORM_INFO_DB)
let tenants
try {
tenants = await db.get(TENANT_DOC)
} catch (err) {
// if theres an error the doc doesn't exist, no tenants exist
return false
}
return (
tenants &&
Array.isArray(tenants.tenantIds) &&
tenants.tenantIds.indexOf(tenantId) !== -1
)
}
async function allUsers() { async function allUsers() {
const db = getGlobalDB() const db = getGlobalDB()
@ -87,6 +31,8 @@ async function saveUser(user, tenantId) {
if (!tenantId) { if (!tenantId) {
throw "No tenancy specified." throw "No tenancy specified."
} }
// need to set the context for this request, as specified
updateTenantId(tenantId)
// specify the tenancy incase we're making a new admin user (public) // specify the tenancy incase we're making a new admin user (public)
const db = getGlobalDB(tenantId) const db = getGlobalDB(tenantId)
let { email, password, _id } = user let { email, password, _id } = user
@ -162,7 +108,7 @@ exports.adminUser = async ctx => {
ctx.throw(403, "Organisation already exists.") ctx.throw(403, "Organisation already exists.")
} }
const db = getGlobalDB() const db = getGlobalDB(tenantId)
const response = await db.allDocs( const response = await db.allDocs(
getGlobalUserParams(null, { getGlobalUserParams(null, {
include_docs: true, include_docs: true,

View File

@ -5,17 +5,6 @@ const { routes } = require("./routes")
const { buildAuthMiddleware, auditLog, buildTenancyMiddleware } = const { buildAuthMiddleware, auditLog, buildTenancyMiddleware } =
require("@budibase/auth").auth require("@budibase/auth").auth
const NO_TENANCY_ENDPOINTS = [
{
route: "/api/system",
method: "ALL",
},
{
route: "/api/global/users/self",
method: "GET",
},
]
const PUBLIC_ENDPOINTS = [ const PUBLIC_ENDPOINTS = [
{ {
// this covers all of the POST auth routes // this covers all of the POST auth routes
@ -32,10 +21,6 @@ const PUBLIC_ENDPOINTS = [
route: "/api/global/configs/public", route: "/api/global/configs/public",
method: "GET", method: "GET",
}, },
{
route: "api/global/flags",
method: "GET",
},
{ {
route: "/api/global/configs/checklist", route: "/api/global/configs/checklist",
method: "GET", method: "GET",
@ -48,6 +33,22 @@ const PUBLIC_ENDPOINTS = [
route: "/api/global/users/invite/accept", route: "/api/global/users/invite/accept",
method: "POST", method: "POST",
}, },
{
route: "api/system/flags",
method: "GET",
},
]
const NO_TENANCY_ENDPOINTS = [
...PUBLIC_ENDPOINTS,
{
route: "/api/system",
method: "ALL",
},
{
route: "/api/global/users/self",
method: "GET",
},
] ]
const router = new Router() const router = new Router()
@ -65,8 +66,8 @@ router
}) })
) )
.use("/health", ctx => (ctx.status = 200)) .use("/health", ctx => (ctx.status = 200))
.use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS))
.use(buildAuthMiddleware(PUBLIC_ENDPOINTS)) .use(buildAuthMiddleware(PUBLIC_ENDPOINTS))
.use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS))
// 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.isAuthenticated && !ctx.publicEndpoint) { if (!ctx.isAuthenticated && !ctx.publicEndpoint) {

View File

@ -2,6 +2,7 @@ const Router = require("@koa/router")
const authController = require("../../controllers/global/auth") const authController = require("../../controllers/global/auth")
const joiValidator = require("../../../middleware/joi-validator") const joiValidator = require("../../../middleware/joi-validator")
const Joi = require("joi") const Joi = require("joi")
const { updateTenantId } = require("@budibase/auth/tenancy")
const router = Router() const router = Router()
@ -28,34 +29,41 @@ function buildResetUpdateValidation() {
}).required().unknown(false)) }).required().unknown(false))
} }
function updateTenant(ctx, next) {
updateTenantId(ctx.params.tenantId)
return next()
}
router router
.post( .post(
"/api/global/auth/:tenantId/login", "/api/global/auth/:tenantId/login",
buildAuthValidation(), buildAuthValidation(),
updateTenant,
authController.authenticate authController.authenticate
) )
.post( .post(
"/api/global/auth/:tenantId/reset", "/api/global/auth/:tenantId/reset",
buildResetValidation(), buildResetValidation(),
updateTenant,
authController.reset authController.reset
) )
.post( .post(
"/api/global/auth/:tenantId/reset/update", "/api/global/auth/:tenantId/reset/update",
buildResetUpdateValidation(), buildResetUpdateValidation(),
updateTenant,
authController.resetUpdate authController.resetUpdate
) )
.post("/api/global/auth/logout", authController.logout) .post("/api/global/auth/logout", authController.logout)
.get("/api/global/auth/:tenantId/google", authController.googlePreAuth) .get("/api/global/auth/:tenantId/google", updateTenant, authController.googlePreAuth)
.get("/api/global/auth/:tenantId/google/callback", authController.googleAuth) .get("/api/global/auth/:tenantId/google/callback", updateTenant, authController.googleAuth)
.get( .get(
"/api/global/auth/:tenantId/oidc/configs/:configId", "/api/global/auth/:tenantId/oidc/configs/:configId",
updateTenant,
authController.oidcPreAuth authController.oidcPreAuth
) )
.get("/api/global/auth/:tenantId/oidc/callback", authController.oidcAuth) .get("/api/global/auth/:tenantId/oidc/callback", updateTenant, authController.oidcAuth)
// deprecated - used by the default system before tenancy // deprecated - used by the default system before tenancy
.get("/api/admin/auth/google/callback", authController.googleAuth) .get("/api/admin/auth/google/callback", authController.googleAuth)
.get("/api/global/auth/google/callback", authController.googleAuth)
.get("/api/admin/auth/oidc/callback", authController.oidcAuth) .get("/api/admin/auth/oidc/callback", authController.oidcAuth)
.get("/api/global/auth/oidc/callback", authController.oidcAuth)
module.exports = router module.exports = router