budibase/packages/backend-core/src/events/identification.ts

298 lines
7.5 KiB
TypeScript
Raw Normal View History

2022-05-23 23:14:44 +02:00
import * as context from "../context"
import * as identityCtx from "../context/identity"
2022-05-23 23:14:44 +02:00
import env from "../environment"
import {
Hosting,
User,
Identity,
IdentityType,
Account,
2022-05-24 10:54:36 +02:00
isCloudAccount,
isSSOAccount,
TenantGroup,
CloudAccount,
UserIdentity,
InstallationGroup,
UserContext,
Group,
isSSOUser,
2022-05-23 23:14:44 +02:00
} from "@budibase/types"
import { processors } from "./processors"
import { newid } from "../utils"
import * as installation from "../installation"
import * as configs from "../configs"
import * as users from "../users"
import { withCache, TTL, CacheKey } from "../cache/generic"
2022-05-23 23:14:44 +02:00
/**
* An identity can be:
* - account user (Self host)
* - budibase user
* - tenant
* - installation
*/
const getCurrentIdentity = async (): Promise<Identity> => {
let identityContext = identityCtx.getIdentity()
const environment = getDeploymentEnvironment()
let identityType
2022-05-23 23:14:44 +02:00
if (!identityContext) {
identityType = IdentityType.TENANT
2022-05-23 23:14:44 +02:00
} else {
identityType = identityContext.type
2022-05-23 23:14:44 +02:00
}
if (identityType === IdentityType.INSTALLATION) {
const installationId = await getInstallationId()
const hosting = getHostingFromEnv()
return {
id: formatDistinctId(installationId, identityType),
hosting,
type: identityType,
installationId,
environment,
}
} else if (identityType === IdentityType.TENANT) {
const installationId = await getInstallationId()
2022-05-30 22:46:08 +02:00
const tenantId = await getEventTenantId(context.getTenantId())
const hosting = getHostingFromEnv()
return {
id: formatDistinctId(tenantId, identityType),
type: identityType,
hosting,
installationId,
tenantId,
realTenantId: context.getTenantId(),
environment,
}
} else if (identityType === IdentityType.USER) {
const userContext = identityContext as UserContext
2022-05-30 22:46:08 +02:00
const tenantId = await getEventTenantId(context.getTenantId())
const installationId = await getInstallationId()
2022-05-23 23:14:44 +02:00
const account = userContext.account
let hosting
if (account) {
hosting = account.hosting
} else {
hosting = getHostingFromEnv()
}
return {
id: userContext._id,
type: identityType,
hosting,
installationId,
tenantId,
environment,
realTenantId: context.getTenantId(),
hostInfo: userContext.hostInfo,
}
2022-05-24 21:01:13 +02:00
} else {
throw new Error("Unknown identity type")
2022-05-24 21:01:13 +02:00
}
}
const identifyInstallationGroup = async (
installId: string,
timestamp?: string | number
): Promise<void> => {
const id = installId
const type = IdentityType.INSTALLATION
const hosting = getHostingFromEnv()
2023-04-17 14:53:00 +02:00
const version = env.VERSION
const environment = getDeploymentEnvironment()
const group: InstallationGroup = {
id,
type,
hosting,
version,
environment,
}
await identifyGroup(group, timestamp)
// need to create a normal identity for the group to be able to query it globally
// match the posthog syntax to link this identity to the empty auto generated one
await identify({ ...group, id: `$${type}_${id}` }, timestamp)
}
const identifyTenantGroup = async (
2022-05-25 22:32:08 +02:00
tenantId: string,
hosting: Hosting,
2022-05-25 22:32:08 +02:00
timestamp?: string | number
): Promise<void> => {
2022-05-30 22:46:08 +02:00
const id = await getEventTenantId(tenantId)
const type = IdentityType.TENANT
const installationId = await getInstallationId()
const environment = getDeploymentEnvironment()
2022-05-24 21:01:13 +02:00
const group: TenantGroup = {
id,
type,
hosting,
environment,
installationId,
2022-05-24 21:01:13 +02:00
}
await identifyGroup(group, timestamp)
// need to create a normal identity for the group to be able to query it globally
// match the posthog syntax to link this identity to the auto generated one
await identify({ ...group, id: `$${type}_${id}` }, timestamp)
2022-05-24 21:01:13 +02:00
}
const identifyUser = async (
user: User,
account: CloudAccount | undefined,
timestamp?: string | number
) => {
2022-05-23 23:14:44 +02:00
const id = user._id as string
2022-05-30 22:46:08 +02:00
const tenantId = await getEventTenantId(user.tenantId)
2022-05-23 23:14:44 +02:00
const type = IdentityType.USER
let builder = users.hasBuilderPermissions(user)
let admin = users.hasAdminPermissions(user)
let providerType
if (isSSOUser(user)) {
providerType = user.providerType
}
const accountHolder = await users.getExistingAccounts([user.email])
const isAccountHolder = accountHolder.length > 0
const verified = !!account && isAccountHolder && account.verified
const installationId = await getInstallationId()
const hosting = account ? account.hosting : getHostingFromEnv()
const environment = getDeploymentEnvironment()
2022-05-23 23:14:44 +02:00
const identity: UserIdentity = {
2022-05-23 23:14:44 +02:00
id,
type,
hosting,
installationId,
tenantId,
verified,
accountHolder: isAccountHolder,
providerType,
2022-05-23 23:14:44 +02:00
builder,
admin,
environment,
2022-05-23 23:14:44 +02:00
}
2022-05-25 22:32:08 +02:00
await identify(identity, timestamp)
2022-05-23 23:14:44 +02:00
}
const identifyAccount = async (account: Account) => {
2022-05-23 23:14:44 +02:00
let id = account.accountId
const tenantId = account.tenantId
let type = IdentityType.USER
2022-05-24 21:01:13 +02:00
let providerType = isSSOAccount(account) ? account.providerType : undefined
const verified = account.verified
const accountHolder = true
const hosting = account.hosting
const installationId = await getInstallationId()
const environment = getDeploymentEnvironment()
2022-05-23 23:14:44 +02:00
if (isCloudAccount(account)) {
const user = await users.getGlobalUserByEmail(account.email)
if (user?._id) {
2022-05-23 23:14:44 +02:00
// use the budibase user as the id if set
id = user._id
2022-05-23 23:14:44 +02:00
}
}
const identity: UserIdentity = {
2022-05-23 23:14:44 +02:00
id,
type,
hosting,
installationId,
tenantId,
2022-05-24 21:01:13 +02:00
providerType,
verified,
accountHolder,
environment,
2022-05-23 23:14:44 +02:00
}
await identify(identity)
}
const identify = async (identity: Identity, timestamp?: string | number) => {
await processors.identify(identity, timestamp)
2022-05-23 23:14:44 +02:00
}
const identifyGroup = async (group: Group, timestamp?: string | number) => {
await processors.identifyGroup(group, timestamp)
}
const getDeploymentEnvironment = () => {
if (env.isDev()) {
return "development"
} else {
return env.DEPLOYMENT_ENVIRONMENT
}
}
const getHostingFromEnv = () => {
return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD
}
const getInstallationId = async () => {
if (isAccountPortal()) {
return "account-portal"
}
const install = await installation.getInstall()
return install.installId
}
2022-05-30 22:46:08 +02:00
const getEventTenantId = async (tenantId: string): Promise<string> => {
if (env.SELF_HOSTED) {
2022-05-30 22:46:08 +02:00
return getUniqueTenantId(tenantId)
} else {
// tenant id's in the cloud are already unique
return tenantId
}
}
2023-07-07 21:07:15 +02:00
export const getUniqueTenantId = async (tenantId: string): Promise<string> => {
2022-05-30 22:46:08 +02:00
// make sure this tenantId always matches the tenantId in context
return context.doInTenant(tenantId, () => {
return withCache(CacheKey.UNIQUE_TENANT_ID, TTL.ONE_DAY, async () => {
2022-05-30 22:46:08 +02:00
const db = context.getGlobalDB()
const config = await configs.getSettingsConfigDoc()
2022-05-30 22:46:08 +02:00
let uniqueTenantId: string
if (config.config.uniqueTenantId) {
return config.config.uniqueTenantId
} else {
uniqueTenantId = `${newid()}_${tenantId}`
2022-05-30 22:46:08 +02:00
config.config.uniqueTenantId = uniqueTenantId
await db.put(config)
return uniqueTenantId
}
})
})
}
const isAccountPortal = () => {
return env.SERVICE === "account-portal"
}
const formatDistinctId = (id: string, type: IdentityType) => {
if (type === IdentityType.INSTALLATION || type === IdentityType.TENANT) {
return `$${type}_${id}`
} else {
return id
}
}
export default {
getCurrentIdentity,
identifyInstallationGroup,
identifyTenantGroup,
identifyUser,
identifyAccount,
identify,
identifyGroup,
getInstallationId,
Per user pricing (#10378) * Update pro version to 2.4.44-alpha.9 (#10231) Co-authored-by: Budibase Staging Release Bot <> * Track installation and unique tenant id on licence activate (#10146) * changes and exports * removing the extend * Lint + tidy * Update account.ts --------- Co-authored-by: Rory Powell <rory.codes@gmail.com> Co-authored-by: mike12345567 <me@michaeldrury.co.uk> * Type updates for loading new plans (#10245) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` (#10247) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete + migration (#10250) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Lint * Types and structures for user subscription quantity sync (#10280) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Prevent old installs from activating, track install info via get license request instead of on activation. * Add usesInvoicing to PurchasedPlan * Add min/max users to PurchasedPlan * Additional test structures for generating a license, remove maxUsers from PurchasedPlan - this is already present in the license quotas * Stripe integration for monthly prorations on annual plans * Integrate annual prorations with test clocks * Updated types, test utils and date processing for licensing (#10346) * Add new quota for max users on free plan * Split available vs purchased plan & price type definitions. Update usages of available prices and plans * Type fixes * Add types for minimums * New `PlanModel` type for `PER_USER` and `DAY_PASS` * Add loadEnvFiles to lerna config for run command to prevent local test failures * Fix types in license test structure * Add quotas integration to user create / delete * Always sync user count from view total_rows value for accuracy * Add migration to sync users * Add syncUsers.spec.ts * Prevent old installs from activating, track install info via get license request instead of on activation. * Add usesInvoicing to PurchasedPlan * Add min/max users to PurchasedPlan * Additional test structures for generating a license, remove maxUsers from PurchasedPlan - this is already present in the license quotas * Stripe integration for monthly prorations on annual plans * Integrate annual prorations with test clocks * Updated types, test utils and date processing * Lint * Pricing/billing page (#10353) * bbui updates for billing page * Require all PlanTypes in PlanMinimums for compile time safety * fix test package utils * Incoming user limits warnings (#10379) * incoming user limits warning * fix inlinealert button * add corretc button link and text to user alert * pr comments * simplify limit check * Types and test updates for subscription quantity changes in account-portal (#10372) * Add chance extensions for `arrayOf`. Update events spies with license events * Add generics to doInTenant response * Update account structure with quota usage * User count limits (#10385) * incoming user limits warning * fix inlinealert button * add corretc button link and text to user alert * pr comments * simplify limit check * user limit messaging on add users modal * user limit messaging on import users modal * update licensing store to be more generic * some styling updates * remove console log * Store tweaks * Add startDate to Quota type --------- Co-authored-by: Rory Powell <rory.codes@gmail.com> * Lint * Support custom lock options * Reactivity fixes for add user modals * Update ethereal email creds * Add warn for getting invite from code error * Extract disabling user import condition * Handling unlimited users in modals logic and adding start date processing to store * Lint * Integration testing fixes (#10389) * lint --------- Co-authored-by: Mateus Badan de Pieri <mateuspieri@gmail.com> Co-authored-by: mike12345567 <me@michaeldrury.co.uk> Co-authored-by: Peter Clement <PClmnt@users.noreply.github.com>
2023-04-24 10:31:48 +02:00
getUniqueTenantId,
}