From dd657d3877b0540627d07bab64d237ab425222e2 Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Thu, 6 Jul 2023 13:47:14 +0800 Subject: [PATCH] feat(schemas): drop m2m credentials in existing logto email connector config (#4126) --- ...m-existing-logto-email-connector-config.ts | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 packages/schemas/alterations/next-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.ts diff --git a/packages/schemas/alterations/next-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.ts b/packages/schemas/alterations/next-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.ts new file mode 100644 index 000000000..b649ce99f --- /dev/null +++ b/packages/schemas/alterations/next-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.ts @@ -0,0 +1,88 @@ +import { GlobalValues } from '@logto/shared'; +import { appendPath } from '@silverhand/essentials'; +import { sql } from 'slonik'; + +import type { AlterationScript } from '../lib/types/alteration.js'; + +type M2mCredentials = { + appSecret: string; + appId: string; + endpoint: string; + tokenEndpoint: string; + resource: string; +}; + +type EmailServiceConnector = { + tenantId: string; + config: Partial & Record; +}; +type CloudConnectionData = { + tenantId: string; + value: { appSecret: string; appId: string; resource: string }; +}; + +enum ServiceConnector { + Email = 'logto-email', +} + +const cloudConnectionKey = 'cloudConnection'; + +const alteration: AlterationScript = { + up: async (pool) => { + const { rows: rawConnectors } = await pool.query(sql` + select tenant_id, config from connectors where connector_id = ${ServiceConnector.Email}; + `); + const connectors = rawConnectors.map((rawConnector) => { + const { + tenantId, + config: { appSecret, appId, endpoint, tokenEndpoint, resource, ...rest }, + } = rawConnector; + return { tenantId, config: rest }; + }); + for (const connector of connectors) { + const { tenantId, config } = connector; + // eslint-disable-next-line no-await-in-loop + await pool.query(sql` + update connectors set config = ${JSON.stringify( + config + )} where tenant_id = ${tenantId} and connector_id = ${ServiceConnector.Email}; + `); + } + }, + down: async (pool) => { + const { rows: cloudConnections } = await pool.query(sql` + select tenant_id, value from logto_configs where key = ${cloudConnectionKey}; + `); + + /** Get `endpoint` and `tokenEndpoints` */ + const globalValues = new GlobalValues(); + const { cloudUrlSet, adminUrlSet } = globalValues; + const endpoint = appendPath(cloudUrlSet.endpoint, 'api').toString(); + const tokenEndpoint = appendPath(adminUrlSet.endpoint, 'oidc/token').toString(); + + const { rows: rawEmailServiceConnectors } = await pool.query(sql` + select tenant_id, config from connectors where connector_id = ${ServiceConnector.Email}; + `); + const tenantIdsWithM2mCredentials = new Set(cloudConnections.map(({ tenantId }) => tenantId)); + const emailServiceConnectors = rawEmailServiceConnectors.filter(({ tenantId }) => + tenantIdsWithM2mCredentials.has(tenantId) + ); + for (const emailServiceConnector of emailServiceConnectors) { + const { tenantId: currentTenantId, config } = emailServiceConnector; + const newConfig = { + ...config, + endpoint, + tokenEndpoint, + ...cloudConnections.find(({ tenantId }) => tenantId === currentTenantId)?.value, + }; + // eslint-disable-next-line no-await-in-loop + await pool.query(sql` + update connectors set config = ${JSON.stringify( + newConfig + )} where tenant_id = ${currentTenantId} and connector_id = ${ServiceConnector.Email}; + `); + } + }, +}; + +export default alteration;