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. 89
      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.
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

89
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({

Loading…
Cancel
Save