diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index df55bedd37..650798f23e 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -71,6 +71,7 @@ jest.mock("../../../events", () => { }, rows: { imported: jest.fn(), + created: jest.fn(), }, screen: { created: jest.fn(), diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index b28cf48d99..a723e03a75 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -1,3 +1,7 @@ +import * as users from "./global/users" +import * as rows from "./global/rows" +import * as configs from "./global/configs" + /** * Date: * May 2022 @@ -6,4 +10,8 @@ * Backfill global events. */ -export const run = async (db: any) => {} +export const run = async (db: any) => { + await users.backfill(db) + await rows.backfill() + await configs.backfill(db) +} diff --git a/packages/server/src/migrations/functions/backfill/global/config.ts b/packages/server/src/migrations/functions/backfill/global/config.ts deleted file mode 100644 index 2c058952ca..0000000000 --- a/packages/server/src/migrations/functions/backfill/global/config.ts +++ /dev/null @@ -1,7 +0,0 @@ -// EMAIL_SMTP_CREATED = "email:smtp:created", -// AUTH_SSO_CREATED = "auth:sso:created", -// AUTH_SSO_ACTIVATED = "auth:sso:activated", -// AUTH_SSO_DEACTIVATED = "auth:sso:deactivated", -// ORG_NAME_UPDATED = "org:info:name:updated", -// ORG_LOGO_UPDATED = "org:info:logo:updated", -// ORG_PLATFORM_URL_UPDATED = "org:platformurl:updated", diff --git a/packages/server/src/migrations/functions/backfill/global/configs.ts b/packages/server/src/migrations/functions/backfill/global/configs.ts new file mode 100644 index 0000000000..7b04cbc05f --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/global/configs.ts @@ -0,0 +1,63 @@ +import { events, db } from "@budibase/backend-core" +import { + Config, + isSMTPConfig, + isGoogleConfig, + isOIDCConfig, + isSettingsConfig, +} from "@budibase/types" +import env from "./../../../../environment" + +const getConfigs = async (globalDb: any): Promise => { + const response = await globalDb.allDocs( + db.getConfigParams( + {}, + { + include_docs: true, + } + ) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (globalDb: any) => { + const configs = await getConfigs(globalDb) + + for (const config of configs) { + if (isSMTPConfig(config)) { + events.email.SMTPCreated(config) + } + if (isGoogleConfig(config)) { + events.auth.SSOCreated("google") + if (config.config.activated) { + events.auth.SSOActivated("google") + } + } + if (isOIDCConfig(config)) { + events.auth.SSOCreated("oidc") + if (config.config.configs[0].activated) { + events.auth.SSOActivated("oidc") + } + } + if (isSettingsConfig(config)) { + const company = config.config.company + if (company && company !== "Budibase") { + events.org.nameUpdated() + } + + const logoUrl = config.config.logoUrl + if (logoUrl) { + events.org.logoUpdated() + } + + const platformUrl = config.config.platformUrl + if ( + platformUrl && + platformUrl !== "http://localhost:10000" && + env.SELF_HOSTED + ) { + events.org.platformURLUpdated() + } + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/global/rows.ts b/packages/server/src/migrations/functions/backfill/global/rows.ts index 07cc1cdaf8..593b0516d0 100644 --- a/packages/server/src/migrations/functions/backfill/global/rows.ts +++ b/packages/server/src/migrations/functions/backfill/global/rows.ts @@ -10,5 +10,7 @@ export const backfill = async () => { const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : [] const rows: Row[] = await getUniqueRows(appIds) const rowCount = rows ? rows.length : 0 - events.rows.created(rowCount) + if (rowCount) { + events.rows.created(rowCount) + } } diff --git a/packages/server/src/migrations/functions/backfill/global/user.ts b/packages/server/src/migrations/functions/backfill/global/user.ts deleted file mode 100644 index 52eadeb3be..0000000000 --- a/packages/server/src/migrations/functions/backfill/global/user.ts +++ /dev/null @@ -1,3 +0,0 @@ -// USER_CREATED = "user:created", -// USER_PERMISSION_ADMIN_ASSIGNED = "user:admin:assigned", -// USER_PERMISSION_BUILDER_ASSIGNED = "user:builder:assigned", diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts new file mode 100644 index 0000000000..a3c8ce52cc --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -0,0 +1,38 @@ +import { events, db } from "@budibase/backend-core" +import { User } from "@budibase/types" + +// manually define user doc params - normally server doesn't read users from the db +const getUserParams = (props: any) => { + return db.getDocParams(db.DocumentTypes.USER, null, props) +} + +const getUsers = async (globalDb: any): Promise => { + const response = await globalDb.allDocs( + getUserParams({ + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (globalDb: any) => { + const users = await getUsers(globalDb) + + for (const user of users) { + events.user.created(user) + + if (user.admin?.global) { + events.user.permissionAdminAssigned(user) + } + + if (user.builder?.global) { + events.user.permissionBuilderAssigned(user) + } + + if (user.roles) { + for (const [appId, role] of Object.entries(user.roles)) { + events.role.assigned(user, role) + } + } + } +} diff --git a/packages/server/src/migrations/tests/helpers.ts b/packages/server/src/migrations/tests/helpers.ts new file mode 100644 index 0000000000..a2e45fbb26 --- /dev/null +++ b/packages/server/src/migrations/tests/helpers.ts @@ -0,0 +1,30 @@ +// Mimic configs test configuration from worker, creation configs directly in database + +import * as structures from "./structures" +import { db } from "@budibase/backend-core" +import { Config } from "@budibase/types" + +export const saveSettingsConfig = async (globalDb: any) => { + const config = structures.settings() + await saveConfig(config, globalDb) +} + +export const saveGoogleConfig = async (globalDb: any) => { + const config = structures.google() + await saveConfig(config, globalDb) +} + +export const saveOIDCConfig = async (globalDb: any) => { + const config = structures.oidc() + await saveConfig(config, globalDb) +} + +export const saveSmtpConfig = async (globalDb: any) => { + const config = structures.smtp() + await saveConfig(config, globalDb) +} + +const saveConfig = async (config: Config, globalDb: any) => { + config._id = db.generateConfigID({ type: config.type }) + await globalDb.put(config) +} diff --git a/packages/server/src/migrations/tests/index.spec.ts b/packages/server/src/migrations/tests/index.spec.ts index dd5f130ae4..908c67efdf 100644 --- a/packages/server/src/migrations/tests/index.spec.ts +++ b/packages/server/src/migrations/tests/index.spec.ts @@ -1,7 +1,8 @@ -import { events, migrations } from "@budibase/backend-core" +import { events, migrations, tenancy } from "@budibase/backend-core" import TestConfig from "../../tests/utilities/TestConfiguration" import structures from "../../tests/utilities/structures" import { MIGRATIONS } from "../" +import * as helpers from "./helpers" jest.setTimeout(100000) @@ -57,4 +58,39 @@ describe("migrations", () => { }) }) }) + + it("runs global db migration", async () => { + await config.doInContext(null, async () => { + await config.createUser(undefined, undefined, false, true) // admin only + await config.createUser(undefined, undefined, false, false) // non admin non builder + await config.createTable() + await config.createRow() + await config.createRow() + + const db = tenancy.getGlobalDB() + await helpers.saveGoogleConfig(db) + await helpers.saveOIDCConfig(db) + await helpers.saveSettingsConfig(db) + await helpers.saveSmtpConfig(db) + + jest.clearAllMocks() + const migration = MIGRATIONS.filter( + m => m.name === "event_global_backfill" + )[0] + await migrations.runMigration(migration) + + expect(events.user.created).toBeCalledTimes(3) + expect(events.role.assigned).toBeCalledTimes(2) + expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1) // default test user + expect(events.user.permissionAdminAssigned).toBeCalledTimes(1) // admin from above + expect(events.rows.created).toBeCalledTimes(1) + expect(events.rows.created).toBeCalledWith(2) + expect(events.email.SMTPCreated).toBeCalledTimes(1) + expect(events.auth.SSOCreated).toBeCalledTimes(2) + expect(events.auth.SSOActivated).toBeCalledTimes(2) + expect(events.org.logoUpdated).toBeCalledTimes(1) + expect(events.org.nameUpdated).toBeCalledTimes(1) + expect(events.org.platformURLUpdated).toBeCalledTimes(1) + }) + }) }) diff --git a/packages/server/src/migrations/tests/structures.ts b/packages/server/src/migrations/tests/structures.ts new file mode 100644 index 0000000000..bd48bf63cd --- /dev/null +++ b/packages/server/src/migrations/tests/structures.ts @@ -0,0 +1,66 @@ +import { utils } from "@budibase/backend-core" +import { + SMTPConfig, + OIDCConfig, + GoogleConfig, + SettingsConfig, + ConfigType, +} from "@budibase/types" + +export const oidc = (conf?: OIDCConfig): OIDCConfig => { + return { + type: ConfigType.OIDC, + config: { + configs: [ + { + configUrl: "http://someconfigurl", + clientID: "clientId", + clientSecret: "clientSecret", + logo: "Microsoft", + name: "Active Directory", + uuid: utils.newid(), + activated: true, + ...conf, + }, + ], + }, + } +} + +export const google = (conf?: GoogleConfig): GoogleConfig => { + return { + type: ConfigType.GOOGLE, + config: { + clientID: "clientId", + clientSecret: "clientSecret", + activated: true, + ...conf, + }, + } +} + +export const smtp = (conf?: SMTPConfig): SMTPConfig => { + return { + type: ConfigType.SMTP, + config: { + port: 12345, + host: "smtptesthost.com", + from: "testfrom@test.com", + subject: "Hello!", + secure: false, + ...conf, + }, + } +} + +export const settings = (conf?: SettingsConfig): SettingsConfig => { + return { + type: ConfigType.SETTINGS, + config: { + platformUrl: "http://mycustomdomain.com", + logoUrl: "http://mylogourl,com", + company: "mycompany", + ...conf, + }, + } +} diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 8155a27940..d1e7a9cac1 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -121,6 +121,7 @@ class TestConfiguration { async globalUser({ id = GLOBAL_USER_ID, builder = true, + admin = false, email = EMAIL, roles, } = {}) { @@ -147,6 +148,11 @@ class TestConfiguration { } else { user.builder = { global: false } } + if (admin) { + user.admin = { global: true } + } else { + user.admin = { global: false } + } const resp = await db.put(user) return { _rev: resp._rev, @@ -155,9 +161,17 @@ class TestConfiguration { }) } - async createUser(id = null, email = EMAIL) { + async createUser(id = null, email = EMAIL, builder = true, admin = false) { const globalId = !id ? `us_${Math.random()}` : `us_${id}` - const resp = await this.globalUser({ id: globalId, email }) + const appId = this.prodAppId + const roles = { [appId]: "role_12345" } + const resp = await this.globalUser({ + id: globalId, + email, + builder, + admin, + roles, + }) await userCache.invalidateUser(globalId) return { ...resp, @@ -277,7 +291,7 @@ class TestConfiguration { // APP - async createApp(appName, opts = { deploy: true }) { + async createApp(appName) { // create dev app // clear any old app this.appId = null @@ -287,11 +301,9 @@ class TestConfiguration { await context.updateAppId(this.appId) // create production app - if (opts.deploy) { - this.prodApp = await this.deploy() - this.prodAppId = this.prodApp.appId - this.allApps.push(this.prodApp) - } + this.prodApp = await this.deploy() + this.prodAppId = this.prodApp.appId + this.allApps.push(this.prodApp) this.allApps.push(this.app) diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index 358c2afbf1..344c4e36d3 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" export interface App extends Document { appId: string diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts index de584581b0..478ef0b7de 100644 --- a/packages/types/src/documents/app/automation.ts +++ b/packages/types/src/documents/app/automation.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Automation extends Document { definition: { diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts index 1e3a712efa..63499cd02b 100644 --- a/packages/types/src/documents/app/datasource.ts +++ b/packages/types/src/documents/app/datasource.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Datasource extends Document {} diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts index 26f498979d..e8b29257fc 100644 --- a/packages/types/src/documents/app/index.ts +++ b/packages/types/src/documents/app/index.ts @@ -7,6 +7,6 @@ export * from "./role" export * from "./table" export * from "./screen" export * from "./view" -export * from "./document" +export * from "../document" export * from "./row" export * from "./user" diff --git a/packages/types/src/documents/app/layout.ts b/packages/types/src/documents/app/layout.ts index de7b61ed97..85ca4b7e94 100644 --- a/packages/types/src/documents/app/layout.ts +++ b/packages/types/src/documents/app/layout.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Layout extends Document {} diff --git a/packages/types/src/documents/app/query.ts b/packages/types/src/documents/app/query.ts index 6cdda0306f..72b6c288a5 100644 --- a/packages/types/src/documents/app/query.ts +++ b/packages/types/src/documents/app/query.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Query extends Document { datasourceId: string diff --git a/packages/types/src/documents/app/role.ts b/packages/types/src/documents/app/role.ts index 292ab71806..0248fe733d 100644 --- a/packages/types/src/documents/app/role.ts +++ b/packages/types/src/documents/app/role.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Role extends Document {} diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index d921beae24..d053d3d938 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Row extends Document {} diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index ab6e76eaa0..01d3f02746 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Screen extends Document {} diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts index 1b3a523d26..4ee7afd03c 100644 --- a/packages/types/src/documents/app/table.ts +++ b/packages/types/src/documents/app/table.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" import { View } from "./view" export interface Table extends Document { diff --git a/packages/types/src/documents/app/user.ts b/packages/types/src/documents/app/user.ts index a3fcc8ece3..b5f31ca349 100644 --- a/packages/types/src/documents/app/user.ts +++ b/packages/types/src/documents/app/user.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" export interface UserMetadata extends Document { roleId: string diff --git a/packages/types/src/documents/app/document.ts b/packages/types/src/documents/document.ts similarity index 100% rename from packages/types/src/documents/app/document.ts rename to packages/types/src/documents/document.ts diff --git a/packages/types/src/documents/global/config.ts b/packages/types/src/documents/global/config.ts index 6828cabc30..b37e09bb9d 100644 --- a/packages/types/src/documents/global/config.ts +++ b/packages/types/src/documents/global/config.ts @@ -1 +1,67 @@ -export interface SMTPConfig {} +import { Document } from "../document" + +export interface Config extends Document { + type: ConfigType +} + +export interface SMTPConfig extends Config { + config: { + port: number + host: string + from: string + subject: string + secure: boolean + } +} + +export interface SettingsConfig extends Config { + config: { + company: string + logoUrl: string + platformUrl: string + } +} + +export interface GoogleConfig extends Config { + config: { + clientID: string + clientSecret: string + activated: boolean + } +} + +export interface OIDCConfig extends Config { + config: { + configs: { + configUrl: string + clientID: string + clientSecret: string + logo: string + name: string + uuid: string + activated: boolean + }[] + } +} + +export type NestedConfig = + | SMTPConfig + | SettingsConfig + | GoogleConfig + | OIDCConfig + +export const isSettingsConfig = (config: Config): config is SettingsConfig => + config.type === ConfigType.SETTINGS +export const isSMTPConfig = (config: Config): config is SMTPConfig => + config.type === ConfigType.SMTP +export const isGoogleConfig = (config: Config): config is GoogleConfig => + config.type === ConfigType.GOOGLE +export const isOIDCConfig = (config: Config): config is OIDCConfig => + config.type === ConfigType.OIDC + +export enum ConfigType { + SETTINGS = "settings", + SMTP = "smtp", + GOOGLE = "google", + OIDC = "oidc", +} diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 43592582c7..37155dcb73 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -1,5 +1,13 @@ -export interface User { +import { Document } from "../document" + +export interface User extends Document { roles: UserRoles + builder?: { + global: boolean + } + admin?: { + global: boolean + } } export interface UserRoles {