diff --git a/packages/noco-docs/docs/100.data-sources/050.updating-secret.md b/packages/noco-docs/docs/100.data-sources/050.updating-secret.md index 3f348c841b..739bc57abc 100644 --- a/packages/noco-docs/docs/100.data-sources/050.updating-secret.md +++ b/packages/noco-docs/docs/100.data-sources/050.updating-secret.md @@ -37,7 +37,7 @@ To update a secret in NocoDB, you can use the `nc-secret-mgr` package. Follow th Alternatively, you can use the `nc-secret-mgr` executable to update secrets. -1. Download the `nc-secret-mgr` executable from the [NocoDB website](https://github.com/nocodb/nc-secret-mgr/releases/latest). +1. Download the `nc-secret-mgr` executable from the [NocoDB Github](https://github.com/nocodb/nc-secret-mgr/releases/latest). 2. Run the executable using the following command: ```bash diff --git a/packages/nocodb/src/version-upgrader/upgraders/0225002_ncDatasourceDecrypt.ts b/packages/nocodb/src/version-upgrader/upgraders/0225002_ncDatasourceDecrypt.ts index bd991b105d..fb879b5251 100644 --- a/packages/nocodb/src/version-upgrader/upgraders/0225002_ncDatasourceDecrypt.ts +++ b/packages/nocodb/src/version-upgrader/upgraders/0225002_ncDatasourceDecrypt.ts @@ -11,20 +11,49 @@ const logger = { }, }; -const decryptConfig = async (encryptedConfig: string, secret: string) => { +const decryptConfigWithFallbackKey = async ({ + encryptedConfig, + secret, + fallbackSecret, + fallbackToNullIfFailed = false, +}: { + encryptedConfig: string; + secret: string; + fallbackSecret?: string; + fallbackToNullIfFailed?: boolean; +}) => { if (!encryptedConfig) return encryptedConfig; - const decryptedVal = CryptoJS.AES.decrypt(encryptedConfig, secret).toString( - CryptoJS.enc.Utf8, - ); - - // validate by parsing JSON try { - JSON.parse(decryptedVal); - } catch { - throw new Error('Config decryption failed'); + const decryptedVal = CryptoJS.AES.decrypt(encryptedConfig, secret).toString( + CryptoJS.enc.Utf8, + ); + + let parsedVal; + + // validate by parsing JSON + try { + parsedVal = JSON.parse(decryptedVal); + } catch (parseError) { + throw new Error(`JSON parse failed: ${parseError.message}`); + } + // if parsed value is null, return null + return parsedVal === null ? null : decryptedVal; + } catch (e) { + if (fallbackSecret) { + logger.log('Retrying decryption with a fallback mechanism'); + return decryptConfigWithFallbackKey({ + encryptedConfig, + secret: fallbackSecret, + }); + } + + if (fallbackToNullIfFailed) { + return null; + } + + throw e; } - return decryptedVal; }; // decrypt datasource details in source table and integration table @@ -32,13 +61,18 @@ export default async function ({ ncMeta }: NcUpgraderCtx) { logger.log('Starting decryption of sources and integrations'); let encryptionKey = process.env.NC_AUTH_JWT_SECRET; + let fallbackEncryptionKey: string | null = null; + + const encryptionKeyFromMeta = ( + await ncMeta.metaGet(RootScopes.ROOT, RootScopes.ROOT, MetaTable.STORE, { + key: 'nc_auth_jwt_secret', + }) + )?.value; if (!encryptionKey) { - encryptionKey = ( - await ncMeta.metaGet(RootScopes.ROOT, RootScopes.ROOT, MetaTable.STORE, { - key: 'nc_auth_jwt_secret', - }) - )?.value; + encryptionKey = encryptionKeyFromMeta; + } else { + fallbackEncryptionKey = encryptionKeyFromMeta; } // if encryption key is same as previous, just update is_encrypted flag and return @@ -61,7 +95,7 @@ export default async function ({ ncMeta }: NcUpgraderCtx) { throw Error('Encryption key not found'); } - // get all external sources + // get all sources const sources = await ncMeta.knexConnection(MetaTable.SOURCES); const passed = []; @@ -70,7 +104,13 @@ export default async function ({ ncMeta }: NcUpgraderCtx) { for (const source of sources) { if (source?.config) { try { - const decrypted = await decryptConfig(source.config, encryptionKey); + const decrypted = await decryptConfigWithFallbackKey({ + encryptedConfig: source.config, + secret: encryptionKey, + fallbackSecret: fallbackEncryptionKey, + // if source is meta, fallback to null if decryption failed as it is not required and the actual value is JSON `null` string + fallbackToNullIfFailed: source.is_meta, + }); await ncMeta .knexConnection(MetaTable.SOURCES) .update({ @@ -78,7 +118,11 @@ export default async function ({ ncMeta }: NcUpgraderCtx) { }) .where('id', source.id); logger.log(`Decrypted source ${source.id}`); - passed.push(true); + + // skip pushing to passed if it is meta source + if (!source.is_meta) { + passed.push(true); + } } catch (e) { logger.error(`Failed to decrypt source ${source.id}`); passed.push(false); @@ -93,10 +137,11 @@ export default async function ({ ncMeta }: NcUpgraderCtx) { for (const integration of integrations) { if (integration?.config) { try { - const decrypted = await decryptConfig( - integration.config, - encryptionKey, - ); + const decrypted = await decryptConfigWithFallbackKey({ + encryptedConfig: integration.config, + secret: encryptionKey, + fallbackSecret: fallbackEncryptionKey, + }); await ncMeta .knexConnection(MetaTable.INTEGRATIONS) .update({