Browse Source

Merge pull request #9661 from nocodb/feat/9653-add-fallback-mechanism

Add fallback mechanism in decrypt upgrader
pull/9665/head
Pranav C 1 month ago committed by GitHub
parent
commit
67f0948e5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      packages/noco-docs/docs/100.data-sources/050.updating-secret.md
  2. 71
      packages/nocodb/src/version-upgrader/upgraders/0225002_ncDatasourceDecrypt.ts

2
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. 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: 2. Run the executable using the following command:
```bash ```bash

71
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; if (!encryptedConfig) return encryptedConfig;
try {
const decryptedVal = CryptoJS.AES.decrypt(encryptedConfig, secret).toString( const decryptedVal = CryptoJS.AES.decrypt(encryptedConfig, secret).toString(
CryptoJS.enc.Utf8, CryptoJS.enc.Utf8,
); );
let parsedVal;
// validate by parsing JSON // validate by parsing JSON
try { try {
JSON.parse(decryptedVal); parsedVal = JSON.parse(decryptedVal);
} catch { } catch (parseError) {
throw new Error('Config decryption failed'); 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 // 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'); logger.log('Starting decryption of sources and integrations');
let encryptionKey = process.env.NC_AUTH_JWT_SECRET; let encryptionKey = process.env.NC_AUTH_JWT_SECRET;
let fallbackEncryptionKey: string | null = null;
if (!encryptionKey) { const encryptionKeyFromMeta = (
encryptionKey = (
await ncMeta.metaGet(RootScopes.ROOT, RootScopes.ROOT, MetaTable.STORE, { await ncMeta.metaGet(RootScopes.ROOT, RootScopes.ROOT, MetaTable.STORE, {
key: 'nc_auth_jwt_secret', key: 'nc_auth_jwt_secret',
}) })
)?.value; )?.value;
if (!encryptionKey) {
encryptionKey = encryptionKeyFromMeta;
} else {
fallbackEncryptionKey = encryptionKeyFromMeta;
} }
// if encryption key is same as previous, just update is_encrypted flag and return // 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'); throw Error('Encryption key not found');
} }
// get all external sources // get all sources
const sources = await ncMeta.knexConnection(MetaTable.SOURCES); const sources = await ncMeta.knexConnection(MetaTable.SOURCES);
const passed = []; const passed = [];
@ -70,7 +104,13 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
for (const source of sources) { for (const source of sources) {
if (source?.config) { if (source?.config) {
try { 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 await ncMeta
.knexConnection(MetaTable.SOURCES) .knexConnection(MetaTable.SOURCES)
.update({ .update({
@ -78,7 +118,11 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
}) })
.where('id', source.id); .where('id', source.id);
logger.log(`Decrypted source ${source.id}`); logger.log(`Decrypted source ${source.id}`);
// skip pushing to passed if it is meta source
if (!source.is_meta) {
passed.push(true); passed.push(true);
}
} catch (e) { } catch (e) {
logger.error(`Failed to decrypt source ${source.id}`); logger.error(`Failed to decrypt source ${source.id}`);
passed.push(false); passed.push(false);
@ -93,10 +137,11 @@ export default async function ({ ncMeta }: NcUpgraderCtx) {
for (const integration of integrations) { for (const integration of integrations) {
if (integration?.config) { if (integration?.config) {
try { try {
const decrypted = await decryptConfig( const decrypted = await decryptConfigWithFallbackKey({
integration.config, encryptedConfig: integration.config,
encryptionKey, secret: encryptionKey,
); fallbackSecret: fallbackEncryptionKey,
});
await ncMeta await ncMeta
.knexConnection(MetaTable.INTEGRATIONS) .knexConnection(MetaTable.INTEGRATIONS)
.update({ .update({

Loading…
Cancel
Save