fix(Snowflake Node): Fix key-pair credentials (#16635)

Co-authored-by: Elias Meire <elias@meire.dev>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
Michael Kret
2025-06-25 21:23:01 +03:00
committed by GitHub
parent 7c33292483
commit 8e6de34bc3
3 changed files with 72 additions and 7 deletions

View File

@@ -52,11 +52,6 @@ export class Snowflake implements ICredentialType {
name: 'username',
type: 'string',
default: '',
displayOptions: {
show: {
authentication: ['password'],
},
},
},
{
displayName: 'Password',
@@ -87,7 +82,19 @@ export class Snowflake implements ICredentialType {
authentication: ['keyPair'],
},
},
description: 'Private PEM key for Key-pair authentication with Snowflake',
description:
'Private PEM key for Key-pair authentication with Snowflake, follow guide <a href="https://docs.snowflake.com/en/user-guide/key-pair-auth" target="_blank">here</a>',
},
{
displayName: 'Passphrase',
name: 'passphrase',
type: 'string',
default: '',
description:
'If the private key is encrypted, you must provide the passphrase used to encrypt it',
typeOptions: {
password: true,
},
},
{
displayName: 'Schema',

View File

@@ -1,6 +1,9 @@
import { createPrivateKey } from 'crypto';
import pick from 'lodash/pick';
import type snowflake from 'snowflake-sdk';
import { formatPrivateKey } from '@utils/utilities';
const commonConnectionFields = [
'account',
'database',
@@ -22,15 +25,35 @@ export type SnowflakeCredential = Pick<
}
| {
authentication: 'keyPair';
username: string;
privateKey: string;
passphrase?: string;
}
);
const extractPrivateKey = (credential: { privateKey: string; passphrase?: string }) => {
const key = formatPrivateKey(credential.privateKey as string);
if (!credential.passphrase) return key;
const privateKeyObject = createPrivateKey({
key,
format: 'pem',
passphrase: credential.passphrase as string,
});
return privateKeyObject.export({
format: 'pem',
type: 'pkcs8',
}) as string;
};
export const getConnectionOptions = (credential: SnowflakeCredential) => {
const connectionOptions: snowflake.ConnectionOptions = pick(credential, commonConnectionFields);
if (credential.authentication === 'keyPair') {
connectionOptions.authenticator = 'SNOWFLAKE_JWT';
connectionOptions.privateKey = credential.privateKey;
connectionOptions.username = credential.username;
connectionOptions.privateKey = extractPrivateKey(credential);
} else {
connectionOptions.username = credential.username;
connectionOptions.password = credential.password;

View File

@@ -1,5 +1,9 @@
import crypto from 'crypto';
import { getConnectionOptions } from '../GenericFunctions';
jest.mock('crypto');
describe('getConnectionOptions', () => {
const commonOptions = {
account: 'test-account',
@@ -29,12 +33,43 @@ describe('getConnectionOptions', () => {
it('with private key for keyPair authentication', () => {
const result = getConnectionOptions({
...commonOptions,
username: 'test-username',
authentication: 'keyPair',
privateKey: 'test-private-key',
});
expect(result).toEqual({
...commonOptions,
username: 'test-username',
authenticator: 'SNOWFLAKE_JWT',
privateKey: 'test-private-key',
});
});
it('with private key for keyPair authentication and passphrase', () => {
const createPrivateKeySpy = jest.spyOn(crypto, 'createPrivateKey').mockImplementation(
() =>
({
export: () => 'test-private-key',
}) as unknown as crypto.KeyObject,
);
const result = getConnectionOptions({
...commonOptions,
username: 'test-username',
authentication: 'keyPair',
privateKey: 'encrypted-private-key',
passphrase: 'test-passphrase',
});
expect(createPrivateKeySpy).toHaveBeenCalledWith({
key: 'encrypted-private-key',
format: 'pem',
passphrase: 'test-passphrase',
});
expect(result).toEqual({
...commonOptions,
username: 'test-username',
authenticator: 'SNOWFLAKE_JWT',
privateKey: 'test-private-key',
});