diff --git a/ci/snowflake/pom.xml b/ci/snowflake/pom.xml index ad39d9646..265a8fb2d 100644 --- a/ci/snowflake/pom.xml +++ b/ci/snowflake/pom.xml @@ -19,6 +19,16 @@ slf4j-api 1.7.2 + + org.bouncycastle + bcprov-jdk18on + 1.80 + + + org.bouncycastle + bcpkix-jdk18on + 1.80 + diff --git a/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb b/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb index e17f11c19..304a9780d 100644 --- a/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb +++ b/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb @@ -15,6 +15,14 @@ require file unless file.start_with?('lcm-snowflake-driver') end +java_import 'java.io.StringReader' +java_import 'org.bouncycastle.openssl.PEMParser' +java_import 'org.bouncycastle.jce.provider.BouncyCastleProvider' +java_import 'org.bouncycastle.asn1.pkcs.PrivateKeyInfo' +java_import 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo' +java_import 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder' +java_import 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter' + module GoodData module CloudResources class SnowflakeClient < CloudResourceClient @@ -75,11 +83,19 @@ def connect GoodData.logger.info "Setting up connection to Snowflake #{@url}" prop = java.util.Properties.new - prop.setProperty('user', @authentication['basic']['userName']) - prop.setProperty('password', @authentication['basic']['password']) prop.setProperty('schema', @schema) prop.setProperty('warehouse', @warehouse) prop.setProperty('db', @database) + + if @authentication['keyPair'] + prop.setProperty('user', @authentication['keyPair']['userName']) + private_key_str = build_private_key(@authentication['keyPair']['privateKey'], @authentication['keyPair']['passPhrase']) + prop.setProperty('private_key_base64', private_key_str) + else + prop.setProperty('user', @authentication['basic']['userName']) + prop.setProperty('password', @authentication['basic']['password']) + end + # Add JDBC_QUERY_RESULT_FORMAT parameter to fix unsafe memory issue of Snowflake JDBC driver prop.setProperty('JDBC_QUERY_RESULT_FORMAT', 'JSON') @@ -99,6 +115,42 @@ def build_url(url) url end + + private + + def build_private_key(private_key_string, pass_phrase) + java.security.Security.removeProvider("BC") + java.security.Security.addProvider(BouncyCastleProvider.new) + + begin + pem_parser = PEMParser.new(StringReader.new(private_key_string)) + pem_object = pem_parser.readObject + + if pem_object.is_a?(PKCS8EncryptedPrivateKeyInfo) + builder = JceOpenSSLPKCS8DecryptorProviderBuilder.new + decryptor = builder.build(pass_phrase.to_java.to_char_array) + private_key_info = pem_object.decryptPrivateKeyInfo(decryptor) + else pem_object.is_a?(PrivateKeyInfo) + private_key_info = pem_object + end + + ensure + pem_parser&.close + end + + converter = JcaPEMKeyConverter.new + private_key = converter.getPrivateKey(private_key_info) + pem_str = convert_private_key(private_key) + java.util.Base64.getEncoder.encodeToString(pem_str.encode('UTF-8').bytes) + end + + def convert_private_key(private_key) + pem = "-----BEGIN PRIVATE KEY-----\n" + encoder = java.util.Base64.getMimeEncoder(64, "\n".to_java_bytes) + base64 = encoder.encodeToString(private_key.getEncoded) + "#{pem}#{base64}\n-----END PRIVATE KEY-----" + end + end end end