User context updates and misc fixes
This commit is contained in:
parent
28b48a88fe
commit
ff48aaec6e
|
@ -5,6 +5,7 @@ const {
|
|||
getAppId,
|
||||
updateAppId,
|
||||
doInAppContext,
|
||||
doInUserContext,
|
||||
} = require("./src/context")
|
||||
|
||||
module.exports = {
|
||||
|
@ -14,4 +15,5 @@ module.exports = {
|
|||
getAppId,
|
||||
updateAppId,
|
||||
doInAppContext,
|
||||
doInUserContext,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const env = require("../environment")
|
||||
const { Headers } = require("../../constants")
|
||||
const { SEPARATOR, DocumentTypes } = require("../db/constants")
|
||||
const { DEFAULT_TENANT_ID } = require("../constants")
|
||||
const cls = require("./FunctionContext")
|
||||
|
@ -77,7 +76,11 @@ exports.isMultiTenant = () => {
|
|||
exports.doInTenant = (tenantId, task) => {
|
||||
// the internal function is so that we can re-use an existing
|
||||
// context - don't want to close DB on a parent context
|
||||
async function internal(opts = { existing: false }) {
|
||||
async function internal(opts = { existing: false, user: undefined }) {
|
||||
// preserve the user
|
||||
if (user) {
|
||||
exports.setUser(user)
|
||||
}
|
||||
// set the tenant id
|
||||
if (!opts.existing) {
|
||||
cls.setOnContext(ContextKeys.TENANT_ID, tenantId)
|
||||
|
@ -98,14 +101,16 @@ exports.doInTenant = (tenantId, task) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const user = cls.getFromContext(ContextKeys.USER)
|
||||
const using = cls.getFromContext(ContextKeys.IN_USE)
|
||||
if (using && cls.getFromContext(ContextKeys.TENANT_ID) === tenantId) {
|
||||
cls.setOnContext(ContextKeys.IN_USE, using + 1)
|
||||
return internal({ existing: true })
|
||||
return internal({ existing: true, user })
|
||||
} else {
|
||||
return cls.run(async () => {
|
||||
cls.setOnContext(ContextKeys.IN_USE, 1)
|
||||
return internal()
|
||||
return internal({ existing: false, user })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +148,11 @@ exports.doInAppContext = (appId, task) => {
|
|||
|
||||
// the internal function is so that we can re-use an existing
|
||||
// context - don't want to close DB on a parent context
|
||||
async function internal(opts = { existing: false }) {
|
||||
async function internal(opts = { existing: false, user: undefined }) {
|
||||
// preserve the user
|
||||
if (user) {
|
||||
exports.setUser(user)
|
||||
}
|
||||
// set the app tenant id
|
||||
if (!opts.existing) {
|
||||
setAppTenantId(appId)
|
||||
|
@ -162,28 +171,39 @@ exports.doInAppContext = (appId, task) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
const user = cls.getFromContext(ContextKeys.USER)
|
||||
const using = cls.getFromContext(ContextKeys.IN_USE)
|
||||
if (using && cls.getFromContext(ContextKeys.APP_ID) === appId) {
|
||||
cls.setOnContext(ContextKeys.IN_USE, using + 1)
|
||||
return internal({ existing: true })
|
||||
return internal({ existing: true, user })
|
||||
} else {
|
||||
return cls.run(async () => {
|
||||
cls.setOnContext(ContextKeys.IN_USE, 1)
|
||||
return internal()
|
||||
return internal({ existing: false, user })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.doInUserContext = (user, task) => {
|
||||
return cls.run(async () => {
|
||||
cls.setOnContext(ContextKeys.USER, user)
|
||||
return cls.run(() => {
|
||||
let tenantId = user.tenantId
|
||||
if (!tenantId) {
|
||||
tenantId = exports.getTenantId()
|
||||
}
|
||||
cls.setOnContext(ContextKeys.TENANT_ID, tenantId)
|
||||
exports.setUser(user)
|
||||
return task()
|
||||
})
|
||||
}
|
||||
|
||||
exports.setUser = user => {
|
||||
cls.setOnContext(ContextKeys.USER, user)
|
||||
}
|
||||
|
||||
exports.getUser = () => {
|
||||
try {
|
||||
return cls.getFromContext(ContextKeys.USER)
|
||||
const user = cls.getFromContext(ContextKeys.USER)
|
||||
return user
|
||||
} catch (e) {
|
||||
// do nothing - user is not in context
|
||||
}
|
||||
|
@ -208,45 +228,6 @@ exports.updateAppId = async appId => {
|
|||
}
|
||||
}
|
||||
|
||||
exports.setTenantId = (
|
||||
ctx,
|
||||
opts = { allowQs: false, allowNoTenant: false }
|
||||
) => {
|
||||
let tenantId
|
||||
// exit early if not multi-tenant
|
||||
if (!exports.isMultiTenant()) {
|
||||
cls.setOnContext(ContextKeys.TENANT_ID, exports.DEFAULT_TENANT_ID)
|
||||
return exports.DEFAULT_TENANT_ID
|
||||
}
|
||||
|
||||
const allowQs = opts && opts.allowQs
|
||||
const allowNoTenant = opts && opts.allowNoTenant
|
||||
const header = ctx.request.headers[Headers.TENANT_ID]
|
||||
const user = ctx.user || {}
|
||||
if (allowQs) {
|
||||
const query = ctx.request.query || {}
|
||||
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
|
||||
|
||||
// Set the tenantId from the subdomain
|
||||
if (!tenantId) {
|
||||
tenantId = ctx.subdomains && ctx.subdomains[0]
|
||||
}
|
||||
|
||||
if (!tenantId && !allowNoTenant) {
|
||||
ctx.throw(403, "Tenant id not set")
|
||||
}
|
||||
// check tenant ID just incase no tenant was allowed
|
||||
if (tenantId) {
|
||||
cls.setOnContext(ContextKeys.TENANT_ID, tenantId)
|
||||
}
|
||||
return tenantId
|
||||
}
|
||||
|
||||
exports.setGlobalDB = tenantId => {
|
||||
const dbName = baseGlobalDBName(tenantId)
|
||||
const db = dangerousGetDB(dbName)
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
import {
|
||||
isCloudAccount,
|
||||
isSSOAccount,
|
||||
} from "./../../../types/src/documents/account/account"
|
||||
import * as context from "../context"
|
||||
import env from "../environment"
|
||||
import {
|
||||
|
@ -13,6 +9,8 @@ import {
|
|||
Account,
|
||||
AccountIdentity,
|
||||
BudibaseIdentity,
|
||||
isCloudAccount,
|
||||
isSSOAccount,
|
||||
} from "@budibase/types"
|
||||
import { analyticsProcessor } from "./processors"
|
||||
|
||||
|
@ -26,7 +24,7 @@ export const getCurrentIdentity = (): Identity => {
|
|||
} else if (env.SELF_HOSTED) {
|
||||
id = "installationId" // TODO
|
||||
} else {
|
||||
id = context.getTenantId()
|
||||
id = tenantId
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -137,7 +137,7 @@ module.exports = (
|
|||
if (user && user.email) {
|
||||
return context.doInUserContext(user, next)
|
||||
} else {
|
||||
return next
|
||||
return next()
|
||||
}
|
||||
} catch (err) {
|
||||
// invalid token, clear the cookie
|
||||
|
|
|
@ -1,6 +1,38 @@
|
|||
const { setTenantId, setGlobalDB, closeTenancy } = require("../tenancy")
|
||||
const cls = require("../context/FunctionContext")
|
||||
const { doInTenant, isMultiTenant, DEFAULT_TENANT_ID } = require("../tenancy")
|
||||
const { buildMatcherRegex, matches } = require("./matchers")
|
||||
const { Headers } = require("../../constants")
|
||||
|
||||
const getTenantID = (ctx, opts = { allowQs: false, allowNoTenant: false }) => {
|
||||
// exit early if not multi-tenant
|
||||
if (!isMultiTenant()) {
|
||||
return DEFAULT_TENANT_ID
|
||||
}
|
||||
|
||||
let tenantId
|
||||
const allowQs = opts && opts.allowQs
|
||||
const allowNoTenant = opts && opts.allowNoTenant
|
||||
const header = ctx.request.headers[Headers.TENANT_ID]
|
||||
const user = ctx.user || {}
|
||||
if (allowQs) {
|
||||
const query = ctx.request.query || {}
|
||||
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
|
||||
|
||||
// Set the tenantId from the subdomain
|
||||
if (!tenantId) {
|
||||
tenantId = ctx.subdomains && ctx.subdomains[0]
|
||||
}
|
||||
|
||||
if (!tenantId && !allowNoTenant) {
|
||||
ctx.throw(403, "Tenant id not set")
|
||||
}
|
||||
|
||||
return tenantId
|
||||
}
|
||||
|
||||
module.exports = (
|
||||
allowQueryStringPatterns,
|
||||
|
@ -11,15 +43,10 @@ module.exports = (
|
|||
const noTenancyOptions = buildMatcherRegex(noTenancyPatterns)
|
||||
|
||||
return async function (ctx, next) {
|
||||
return cls.run(async () => {
|
||||
const allowNoTenant =
|
||||
opts.noTenancyRequired || !!matches(ctx, noTenancyOptions)
|
||||
const allowQs = !!matches(ctx, allowQsOptions)
|
||||
const tenantId = setTenantId(ctx, { allowQs, allowNoTenant })
|
||||
setGlobalDB(tenantId)
|
||||
const res = await next()
|
||||
await closeTenancy()
|
||||
return res
|
||||
})
|
||||
const tenantId = getTenantID(ctx, { allowQs, allowNoTenant })
|
||||
return doInTenant(tenantId, next)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
"extends": "./tsconfig.json",
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist/**/*",
|
||||
"**/*.json",
|
||||
"**/*.spec.js",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist/**/*",
|
||||
"**/*.spec.js",
|
||||
// "**/*.spec.ts" // don't exclude spec.ts files for editor support
|
||||
]
|
||||
|
|
|
@ -131,5 +131,5 @@ exports.getBudibaseVersion = async ctx => {
|
|||
ctx.body = {
|
||||
version,
|
||||
}
|
||||
events.org.versionChecked(version)
|
||||
await events.org.versionChecked(version)
|
||||
}
|
||||
|
|
|
@ -315,6 +315,6 @@ exports.bulkImport = async function (ctx) {
|
|||
await handleRequest(DataSourceOperation.BULK_CREATE, table._id, {
|
||||
rows,
|
||||
})
|
||||
events.row.import(table, "csv", rows.length)
|
||||
await events.rows.imported(table, "csv", rows.length)
|
||||
return table
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
// TODO: Add migrations to account portal
|
||||
|
||||
import { events, db } from "@budibase/backend-core"
|
||||
import { Account } from "@budibase/types"
|
||||
|
||||
export const backfill = async (appDb: any) => {
|
||||
const accounts: Account[] = []
|
||||
|
||||
for (const account of accounts) {
|
||||
events.account.created(account)
|
||||
|
||||
if (account.verified) {
|
||||
events.account.verified(account)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ export const backfill = async (globalDb: any) => {
|
|||
if (isOIDCConfig(config)) {
|
||||
await events.auth.SSOCreated("oidc")
|
||||
if (config.config.configs[0].activated) {
|
||||
events.auth.SSOActivated("oidc")
|
||||
await events.auth.SSOActivated("oidc")
|
||||
}
|
||||
}
|
||||
if (isSettingsConfig(config)) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export type LoginSource = "local" | "sso"
|
||||
export type LoginSource = "local" | "google" | "oidc"
|
||||
export type SSOType = "oidc" | "google"
|
||||
|
||||
export interface LoginEvent {
|
||||
|
|
|
@ -14,8 +14,9 @@ const {
|
|||
isMultiTenant,
|
||||
} = require("@budibase/backend-core/tenancy")
|
||||
const env = require("../../../environment")
|
||||
const { events, users: usersCore } = require("@budibase/backend-core")
|
||||
import { events, users as usersCore, context } from "@budibase/backend-core"
|
||||
import { users } from "../../../sdk"
|
||||
import { User } from "@budibase/types"
|
||||
|
||||
const ssoCallbackUrl = async (config: any, type: any) => {
|
||||
// incase there is a callback URL from before
|
||||
|
@ -71,7 +72,9 @@ export const authenticate = async (ctx: any, next: any) => {
|
|||
"local",
|
||||
async (err: any, user: any, info: any) => {
|
||||
await authInternal(ctx, user, err, info)
|
||||
await context.doInUserContext(user, async () => {
|
||||
await events.auth.login("local")
|
||||
})
|
||||
ctx.status = 200
|
||||
}
|
||||
)(ctx, next)
|
||||
|
@ -105,7 +108,7 @@ export const reset = async (ctx: any) => {
|
|||
)
|
||||
}
|
||||
try {
|
||||
const user = await usersCore.getGlobalUserByEmail(email)
|
||||
const user = (await usersCore.getGlobalUserByEmail(email)) as User
|
||||
// only if user exists, don't error though if they don't
|
||||
if (user) {
|
||||
await sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, {
|
||||
|
@ -212,7 +215,9 @@ export const googleAuth = async (ctx: any, next: any) => {
|
|||
{ successRedirect: "/", failureRedirect: "/error" },
|
||||
async (err: any, user: any, info: any) => {
|
||||
await authInternal(ctx, user, err, info)
|
||||
await context.doInUserContext(user, async () => {
|
||||
await events.auth.login("google")
|
||||
})
|
||||
ctx.redirect("/")
|
||||
}
|
||||
)(ctx, next)
|
||||
|
@ -256,7 +261,9 @@ export const oidcAuth = async (ctx: any, next: any) => {
|
|||
{ successRedirect: "/", failureRedirect: "/error" },
|
||||
async (err: any, user: any, info: any) => {
|
||||
await authInternal(ctx, user, err, info)
|
||||
await context.doInUserContext(user, async () => {
|
||||
await events.auth.login("oidc")
|
||||
})
|
||||
ctx.redirect("/")
|
||||
}
|
||||
)(ctx, next)
|
||||
|
|
Loading…
Reference in New Issue