2022-05-23 23:14:44 +02:00
|
|
|
import * as context from "../context"
|
2022-05-28 22:38:22 +02:00
|
|
|
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,
|
2022-05-28 22:38:22 +02:00
|
|
|
TenantGroup,
|
2022-05-24 21:01:13 +02:00
|
|
|
SettingsConfig,
|
2022-05-26 11:13:26 +02:00
|
|
|
CloudAccount,
|
|
|
|
UserIdentity,
|
2022-05-28 22:38:22 +02:00
|
|
|
InstallationGroup,
|
|
|
|
UserContext,
|
|
|
|
Group,
|
2022-05-23 23:14:44 +02:00
|
|
|
} from "@budibase/types"
|
2022-05-27 00:57:14 +02:00
|
|
|
import { processors } from "./processors"
|
2022-05-24 21:01:13 +02:00
|
|
|
import * as dbUtils from "../db/utils"
|
|
|
|
import { Configs } from "../constants"
|
|
|
|
import * as hashing from "../hashing"
|
2022-05-28 22:38:22 +02:00
|
|
|
import * as installation from "../installation"
|
2022-05-30 22:46:08 +02:00
|
|
|
import { withCache, TTL, CacheKeys } from "../cache/generic"
|
2022-05-23 23:14:44 +02:00
|
|
|
|
2022-05-27 00:57:14 +02:00
|
|
|
const pkg = require("../../package.json")
|
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
/**
|
|
|
|
* An identity can be:
|
|
|
|
* - account user (Self host)
|
|
|
|
* - budibase user
|
|
|
|
* - tenant
|
|
|
|
* - installation
|
|
|
|
*/
|
2022-05-24 21:01:13 +02:00
|
|
|
export const getCurrentIdentity = async (): Promise<Identity> => {
|
2022-05-28 22:38:22 +02:00
|
|
|
let identityContext = identityCtx.getIdentity()
|
2022-05-27 00:57:14 +02:00
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
let identityType
|
2022-05-23 23:14:44 +02:00
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
if (!identityContext) {
|
|
|
|
identityType = IdentityType.TENANT
|
2022-05-23 23:14:44 +02:00
|
|
|
} else {
|
2022-05-28 22:38:22 +02:00
|
|
|
identityType = identityContext.type
|
2022-05-23 23:14:44 +02:00
|
|
|
}
|
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
if (identityType === IdentityType.INSTALLATION) {
|
|
|
|
const installationId = await getInstallationId()
|
2022-06-14 18:34:32 +02:00
|
|
|
const hosting = getHostingFromEnv()
|
2022-05-28 22:38:22 +02:00
|
|
|
return {
|
|
|
|
id: formatDistinctId(installationId, identityType),
|
2022-06-14 18:34:32 +02:00
|
|
|
hosting,
|
2022-05-28 22:38:22 +02:00
|
|
|
type: identityType,
|
|
|
|
installationId,
|
|
|
|
}
|
|
|
|
} else if (identityType === IdentityType.TENANT) {
|
|
|
|
const installationId = await getInstallationId()
|
2022-05-30 22:46:08 +02:00
|
|
|
const tenantId = await getEventTenantId(context.getTenantId())
|
2022-06-14 18:34:32 +02:00
|
|
|
const hosting = getHostingFromEnv()
|
2022-05-27 00:57:14 +02:00
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
return {
|
|
|
|
id: formatDistinctId(tenantId, identityType),
|
|
|
|
type: identityType,
|
2022-06-14 18:34:32 +02:00
|
|
|
hosting,
|
2022-05-28 22:38:22 +02:00
|
|
|
installationId,
|
|
|
|
tenantId,
|
|
|
|
}
|
|
|
|
} else if (identityType === IdentityType.USER) {
|
|
|
|
const userContext = identityContext as UserContext
|
2022-05-30 22:46:08 +02:00
|
|
|
const tenantId = await getEventTenantId(context.getTenantId())
|
2022-06-14 18:34:32 +02:00
|
|
|
const installationId = await getInstallationId()
|
2022-05-23 23:14:44 +02:00
|
|
|
|
2022-06-14 18:34:32 +02:00
|
|
|
const account = userContext.account
|
|
|
|
let hosting
|
|
|
|
if (account) {
|
|
|
|
hosting = account.hosting
|
|
|
|
} else {
|
|
|
|
hosting = getHostingFromEnv()
|
2022-05-28 22:38:22 +02:00
|
|
|
}
|
2022-05-27 00:57:14 +02:00
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
return {
|
|
|
|
id: userContext._id,
|
|
|
|
type: identityType,
|
2022-06-14 18:34:32 +02:00
|
|
|
hosting,
|
2022-05-28 22:38:22 +02:00
|
|
|
installationId,
|
|
|
|
tenantId,
|
|
|
|
}
|
2022-05-24 21:01:13 +02:00
|
|
|
} else {
|
2022-05-28 22:38:22 +02:00
|
|
|
throw new Error("Unknown identity type")
|
2022-05-24 21:01:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
export const identifyInstallationGroup = async (
|
|
|
|
installId: string,
|
|
|
|
timestamp?: string | number
|
|
|
|
): Promise<void> => {
|
|
|
|
const id = installId
|
2022-05-27 00:57:14 +02:00
|
|
|
const type = IdentityType.INSTALLATION
|
|
|
|
const hosting = getHostingFromEnv()
|
2022-05-28 22:38:22 +02:00
|
|
|
const version = pkg.version
|
2022-05-27 00:57:14 +02:00
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
const group: InstallationGroup = {
|
2022-05-27 00:57:14 +02:00
|
|
|
id,
|
|
|
|
type,
|
|
|
|
hosting,
|
2022-05-28 22:38:22 +02:00
|
|
|
version,
|
2022-05-27 00:57:14 +02:00
|
|
|
}
|
2022-05-28 22:38:22 +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 empty auto generated one
|
|
|
|
await identify({ ...group, id: `$${type}_${id}` }, timestamp)
|
2022-05-27 00:57:14 +02:00
|
|
|
}
|
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
export const identifyTenantGroup = async (
|
2022-05-25 22:32:08 +02:00
|
|
|
tenantId: string,
|
2022-05-28 22:38:22 +02:00
|
|
|
account: Account | undefined,
|
2022-05-25 22:32:08 +02:00
|
|
|
timestamp?: string | number
|
2022-05-28 22:38:22 +02:00
|
|
|
): Promise<void> => {
|
2022-05-30 22:46:08 +02:00
|
|
|
const id = await getEventTenantId(tenantId)
|
2022-05-26 11:13:26 +02:00
|
|
|
const type = IdentityType.TENANT
|
2022-06-14 18:34:32 +02:00
|
|
|
const installationId = await getInstallationId()
|
2022-05-24 21:01:13 +02:00
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
let hosting: Hosting
|
|
|
|
let profession: string | undefined
|
|
|
|
let companySize: string | undefined
|
|
|
|
|
|
|
|
if (account) {
|
|
|
|
profession = account.profession
|
|
|
|
companySize = account.size
|
|
|
|
hosting = account.hosting
|
|
|
|
} else {
|
|
|
|
hosting = getHostingFromEnv()
|
|
|
|
}
|
|
|
|
|
|
|
|
const group: TenantGroup = {
|
2022-05-26 11:13:26 +02:00
|
|
|
id,
|
|
|
|
type,
|
2022-05-28 22:38:22 +02:00
|
|
|
hosting,
|
2022-06-14 18:34:32 +02:00
|
|
|
installationId,
|
2022-05-26 11:13:26 +02:00
|
|
|
profession,
|
|
|
|
companySize,
|
2022-05-24 21:01:13 +02:00
|
|
|
}
|
2022-05-28 22:38:22 +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
|
|
|
}
|
|
|
|
|
2022-05-26 11:13:26 +02:00
|
|
|
export 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
|
2022-05-28 22:38:22 +02:00
|
|
|
let builder = user.builder?.global || false
|
|
|
|
let admin = user.admin?.global || false
|
2022-05-24 21:01:13 +02:00
|
|
|
let providerType = user.providerType
|
2022-05-28 22:38:22 +02:00
|
|
|
const accountHolder = account?.budibaseUserId === user._id || false
|
2022-05-27 00:57:14 +02:00
|
|
|
const verified =
|
|
|
|
account && account?.budibaseUserId === user._id ? account.verified : false
|
2022-05-28 22:38:22 +02:00
|
|
|
const installationId = await getInstallationId()
|
2022-06-14 18:34:32 +02:00
|
|
|
const hosting = account ? account.hosting : getHostingFromEnv()
|
2022-05-23 23:14:44 +02:00
|
|
|
|
2022-05-28 22:38:22 +02:00
|
|
|
const identity: UserIdentity = {
|
2022-05-23 23:14:44 +02:00
|
|
|
id,
|
|
|
|
type,
|
2022-06-14 18:34:32 +02:00
|
|
|
hosting,
|
2022-05-28 22:38:22 +02:00
|
|
|
installationId,
|
|
|
|
tenantId,
|
|
|
|
verified,
|
|
|
|
accountHolder,
|
|
|
|
providerType,
|
2022-05-23 23:14:44 +02:00
|
|
|
builder,
|
|
|
|
admin,
|
|
|
|
}
|
|
|
|
|
2022-05-25 22:32:08 +02:00
|
|
|
await identify(identity, timestamp)
|
2022-05-23 23:14:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export const identifyAccount = async (account: Account) => {
|
|
|
|
let id = account.accountId
|
|
|
|
const tenantId = account.tenantId
|
2022-05-25 01:15:52 +02:00
|
|
|
let type = IdentityType.USER
|
2022-05-24 21:01:13 +02:00
|
|
|
let providerType = isSSOAccount(account) ? account.providerType : undefined
|
2022-05-26 11:13:26 +02:00
|
|
|
const verified = account.verified
|
|
|
|
const accountHolder = true
|
2022-06-14 18:34:32 +02:00
|
|
|
const hosting = account.hosting
|
|
|
|
const installationId = await getInstallationId()
|
2022-05-23 23:14:44 +02:00
|
|
|
|
|
|
|
if (isCloudAccount(account)) {
|
|
|
|
if (account.budibaseUserId) {
|
|
|
|
// use the budibase user as the id if set
|
|
|
|
id = account.budibaseUserId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-26 11:13:26 +02:00
|
|
|
const identity: UserIdentity = {
|
2022-05-23 23:14:44 +02:00
|
|
|
id,
|
|
|
|
type,
|
2022-06-14 18:34:32 +02:00
|
|
|
hosting,
|
|
|
|
installationId,
|
2022-05-28 22:38:22 +02:00
|
|
|
tenantId,
|
2022-05-24 21:01:13 +02:00
|
|
|
providerType,
|
2022-05-26 11:13:26 +02:00
|
|
|
verified,
|
|
|
|
accountHolder,
|
2022-05-23 23:14:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
await identify(identity)
|
|
|
|
}
|
|
|
|
|
2022-05-27 00:57:14 +02:00
|
|
|
export const identify = async (
|
|
|
|
identity: Identity,
|
|
|
|
timestamp?: string | number
|
|
|
|
) => {
|
|
|
|
await processors.identify(identity, timestamp)
|
2022-05-23 23:14:44 +02:00
|
|
|
}
|
2022-05-28 22:38:22 +02:00
|
|
|
|
|
|
|
export const identifyGroup = async (
|
|
|
|
group: Group,
|
|
|
|
timestamp?: string | number
|
|
|
|
) => {
|
|
|
|
await processors.identifyGroup(group, timestamp)
|
|
|
|
}
|
|
|
|
|
|
|
|
const getHostingFromEnv = () => {
|
|
|
|
return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD
|
|
|
|
}
|
|
|
|
|
|
|
|
export 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> => {
|
2022-05-28 22:38:22 +02:00
|
|
|
if (env.SELF_HOSTED) {
|
2022-05-30 22:46:08 +02:00
|
|
|
return getUniqueTenantId(tenantId)
|
2022-05-28 22:38:22 +02:00
|
|
|
} else {
|
|
|
|
// tenant id's in the cloud are already unique
|
|
|
|
return tenantId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-30 22:46:08 +02:00
|
|
|
const getUniqueTenantId = async (tenantId: string): Promise<string> => {
|
|
|
|
// make sure this tenantId always matches the tenantId in context
|
|
|
|
return context.doInTenant(tenantId, () => {
|
|
|
|
return withCache(CacheKeys.UNIQUE_TENANT_ID, TTL.ONE_DAY, async () => {
|
|
|
|
const db = context.getGlobalDB()
|
|
|
|
const config: SettingsConfig = await dbUtils.getScopedFullConfig(db, {
|
|
|
|
type: Configs.SETTINGS,
|
|
|
|
})
|
|
|
|
|
|
|
|
let uniqueTenantId: string
|
|
|
|
if (config.config.uniqueTenantId) {
|
|
|
|
return config.config.uniqueTenantId
|
|
|
|
} else {
|
|
|
|
uniqueTenantId = `${hashing.newid()}_${tenantId}`
|
|
|
|
config.config.uniqueTenantId = uniqueTenantId
|
|
|
|
await db.put(config)
|
|
|
|
return uniqueTenantId
|
|
|
|
}
|
|
|
|
})
|
2022-05-28 22:38:22 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|