Skip to content

Commit

Permalink
#43 TLS fingerprint support for ODBC (#45)
Browse files Browse the repository at this point in the history
* Update plugin version to 0.5.0

* Cleanup connector manifest

* Allow starting tests for a specified connector

* Add fingerprint support to ODBC connector

* Add unit tests for fingerprint

* Apply suggestions from code review

Co-authored-by: jakobbraun <[email protected]>
  • Loading branch information
kaklakariada and jakobbraun authored Feb 2, 2022
1 parent f7a1d12 commit e7b9f2f
Show file tree
Hide file tree
Showing 18 changed files with 178 additions and 94 deletions.
8 changes: 5 additions & 3 deletions doc/changes/changes_0.5.0.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Exasol Tableau Connector 0.5.0, released 2022-02-??
# Exasol Tableau Connector 0.5.0, released 2022-02-02

Code name:
Code name: TLS certificate fingerprint support for ODBC connector

## Summary


This release allows the user to enter a TLS certificate fingerprint for an ODBC connection. Entering a fingerprint is required when the Exasol database uses a self-signed TLS certificate.

## Features

* #43: Added TLS certificate fingerprint support for ODBC connector

## Tests

* #42: Added unit tests for ODBC Connector
15 changes: 14 additions & 1 deletion doc/developer_guide/developer_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ To use the connectors, copy them to

As the connectors are not signed, you need to start Tableau Desktop with argument `-DDisableVerifyConnectorPluginSignature`.

### Signing the connectors
### Signing the Connectors

To sign the built connectors you will need the certificate as a `.pfx` file and the keystore password.

Expand Down Expand Up @@ -332,3 +332,16 @@ const enableDebugging = true;
```
This will add value `jdbc-driver-debug` with a debug message to the JDBC driver properties. The JDBC driver will write this to its log file when debugging is enabled in `connectionBuilder.js`.
## Building a Release
To build a release of the JDBC and ODBC connectors follow these steps:
1. Update version number of the `plugin-version` element in the manifest files for JDBC and ODBC connectors:
* [src\exasol_jdbc\manifest.xml](../../src/exasol_jdbc/manifest.xml)
* [src\exasol_odbc\manifest.xml](../../src/exasol_odbc/manifest.xml)
2. [Run the TDVT tests](#running-tdvt-tests) for JDBC and ODBC connectors.
3. [Package](#packaging-the-connectors) and [sign](#signing-the-connectors) the JDBC and ODBC connectors.
4. [Create a new GitHub release](https://github.com/exasol/tableau-connector/releases/new) and upload files
* `target/tableau-exasol-connector-jdbc-<version>.taco`
* `target/tableau-exasol-connector-odbc-<version>.taco`
12 changes: 12 additions & 0 deletions javascript-test/jdbc.connectionBuilder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ test('no fingerprint', () => {
expect(getJdbcUrl({'v-fingerprint': undefined})).toEqual("jdbc:exa:exasoldb.example.com:8563;validateservercertificate=1;feedbackinterval=1;clientname=Tableau Desktop;kerberoshostname=exasoldb.example.com;kerberosservicename=exasol");
});

test('empty fingerprint', () => {
expect(getJdbcUrl({'v-fingerprint': ""})).toEqual("jdbc:exa:exasoldb.example.com:8563;validateservercertificate=1;feedbackinterval=1;clientname=Tableau Desktop;kerberoshostname=exasoldb.example.com;kerberosservicename=exasol");
});

test('blank fingerprint', () => {
expect(getJdbcUrl({'v-fingerprint': " "})).toEqual("jdbc:exa:exasoldb.example.com:8563;validateservercertificate=1;feedbackinterval=1;clientname=Tableau Desktop;kerberoshostname=exasoldb.example.com;kerberosservicename=exasol");
});

test('fingerprint is trimmed', () => {
expect(getJdbcUrl({'v-fingerprint': " abc "})).toEqual("jdbc:exa:exasoldb.example.com:8563;validateservercertificate=1;fingerprint=abc;feedbackinterval=1;clientname=Tableau Desktop;kerberoshostname=exasoldb.example.com;kerberosservicename=exasol");
});

test('custom hostname', () => {
expect(getJdbcUrl({server: "db"})).toEqual("jdbc:exa:db:8563;validateservercertificate=1;fingerprint=15F9CA9;feedbackinterval=1;clientname=Tableau Desktop;kerberoshostname=db;kerberosservicename=exasol");
});
Expand Down
46 changes: 35 additions & 11 deletions javascript-test/odbc.connectionBuilder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ function createDefaultOdbcAttr(customAttributes) {
':subclass': 'exasol_odbc',
':dialect-xml': '<dialect class="exasol_odbc"...',
':driver-bitness': '64',
'v-fingerprint': '15F9CA9',
'v-validateservercertificate': '1'
'v-fingerprint': '15F9CA9'
};
customAttributes = customAttributes || {};
return { ...defaultAttr, ...customAttributes };
Expand All @@ -30,37 +29,62 @@ function getParameters(attr) {
}

test('default parameters', () => {
expect(getParameters({})).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd"]);
expect(getParameters({})).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd", "FINGERPRINT=15F9CA9"]);
});

test('schema defined', () => {
expect(getParameters({ schema: "dbSchema" })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "EXASCHEMA=dbSchema", "UID=user", "PWD=passwd"]);
expect(getParameters({ schema: "dbSchema" })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "EXASCHEMA=dbSchema", "UID=user", "PWD=passwd", "FINGERPRINT=15F9CA9"]);
});

test('schema undefined', () => {
expect(getParameters({ schema: undefined })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd"]);
expect(getParameters({ schema: undefined })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd", "FINGERPRINT=15F9CA9"]);
});

test('schema empty string', () => {
expect(getParameters({ schema: "" })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd"]);
expect(getParameters({ schema: "" })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd", "FINGERPRINT=15F9CA9"]);
});

test('schema blank', () => {
expect(getParameters({ schema: " " })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "EXASCHEMA= ", "UID=user", "PWD=passwd"]);
expect(getParameters({ schema: " " })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "EXASCHEMA= ", "UID=user", "PWD=passwd", "FINGERPRINT=15F9CA9"]);
});

test('non-default server', () => {
expect(getParameters({ server: "myserver" })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=myserver", "EXAPORT=8563", "UID=user", "PWD=passwd"]);
expect(getParameters({ server: "myserver" })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=myserver", "EXAPORT=8563", "UID=user", "PWD=passwd", "FINGERPRINT=15F9CA9"]);
});

test('non-default port', () => {
expect(getParameters({ port: 1234 })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=1234", "UID=user", "PWD=passwd"]);
expect(getParameters({ port: 1234 })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=1234", "UID=user", "PWD=passwd", "FINGERPRINT=15F9CA9"]);
});

test('non-default user', () => {
expect(getParameters({ username: 'otheruser' })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=otheruser", "PWD=passwd"]);
expect(getParameters({ username: 'otheruser' })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=otheruser", "PWD=passwd", "FINGERPRINT=15F9CA9"]);
});

test('non-default password', () => {
expect(getParameters({ password: 'otherpassword' })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=otherpassword"]);
expect(getParameters({ password: 'otherpassword' })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=otherpassword", "FINGERPRINT=15F9CA9"]);
});

test('fingerprint undefined', () => {
expect(getParameters({ 'v-fingerprint': undefined })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd"]);
});


test('fingerprint null', () => {
expect(getParameters({ 'v-fingerprint': null })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd"]);
});

test('fingerprint empty string', () => {
expect(getParameters({ 'v-fingerprint': "" })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd"]);
});

test('fingerprint blank string', () => {
expect(getParameters({ 'v-fingerprint': " " })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd"]);
});

test('fingerprint non-default', () => {
expect(getParameters({ 'v-fingerprint': "other-fingerprint" })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd", "FINGERPRINT=other-fingerprint"]);
});

test('fingerprint trimmed', () => {
expect(getParameters({ 'v-fingerprint': " abc " })).toEqual(["DRIVER=LocateDriver result", "EXAHOST=exasoldb.example.com", "EXAPORT=8563", "UID=user", "PWD=passwd", "FINGERPRINT=abc"]);
});
1 change: 0 additions & 1 deletion src/exasol_jdbc/connectionResolver.tdr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<attribute-list>
<attr>server</attr>
<attr>port</attr>
<attr>dbname</attr>
<attr>schema</attr>
<attr>authentication</attr>
<attr>username</attr>
Expand Down
12 changes: 6 additions & 6 deletions src/exasol_jdbc/manifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8' ?>
<?xml version="1.0" encoding="utf-8" ?>

<connector-plugin class='exasol_jdbc' superclass='jdbc' plugin-version='1.0.0' name='Exasol JDBC' version='18.1' min-version-tableau='2021.3'>
<connector-plugin class="exasol_jdbc" superclass="jdbc" plugin-version="0.5.0" name="Exasol JDBC" version="18.1" min-version-tableau="2021.3">
<vendor-information>
<company name="Exasol AG"/>
<support-link url="https://www.exasol.com"/>
Expand Down Expand Up @@ -43,7 +43,7 @@
<!-- JDBC -->
<customization name="CAP_JDBC_EXPORT_DATA_BATCH" value="yes"/>

<!-- https://www.exasol.com/support/browse/SPOT-13158 -->
<!-- Not supported by JDBC driver -->
<customization name="CAP_JDBC_SET_CLIENT_INFO" value="no"/>

<!-- Requires JDBC driver option feedbackinterval=1 -->
Expand All @@ -53,8 +53,8 @@

</customizations>
</connection-customization>
<connection-fields file='connectionFields.xml'/>
<connection-metadata file='connectionMetadata.xml'/>
<connection-fields file="connectionFields.xml"/>
<connection-metadata file="connectionMetadata.xml"/>
<connection-resolver file="connectionResolver.tdr"/>
<dialect file='dialect.tdd'/>
<dialect file="dialect.tdd"/>
</connector-plugin>
22 changes: 16 additions & 6 deletions src/exasol_odbc/connectionBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@
logging.Log("[connectionBuilder.js] " + str)
}

const params = {};

if (odbcDriverDebugEnabled) {
log("ODBC Driver log enabled. Log file: " + odbcDriverLogFile);
params["EXALOGFILE"] = odbcDriverLogFile;
params["LOGMODE"] = "DEFAULT";
function isEmpty(str) {
return (!str || 0 === str.trim().length);
}

const params = {};

params["EXAHOST"] = attr[connectionHelper.attributeServer];
params["EXAPORT"] = attr[connectionHelper.attributePort];

Expand All @@ -27,6 +25,18 @@
params["UID"] = attr[connectionHelper.attributeUsername];
params["PWD"] = attr[connectionHelper.attributePassword];

const fingerprint = attr["v-fingerprint"];
if(!isEmpty(fingerprint)) {
params["FINGERPRINT"] = fingerprint.trim();
}


if (odbcDriverDebugEnabled) {
log("ODBC Driver log enabled. Log file: " + odbcDriverLogFile);
params["EXALOGFILE"] = odbcDriverLogFile;
params["LOGMODE"] = "DEFAULT";
}

const formattedParams = [];

const driverLocation = driverLocator.LocateDriver(attr);
Expand Down
8 changes: 0 additions & 8 deletions src/exasol_odbc/connectionDialog.tcd

This file was deleted.

15 changes: 15 additions & 0 deletions src/exasol_odbc/connectionFields.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>

<connection-fields>
<field name="server" label="Server" value-type="string" optional="false" category="endpoint" />

<field name="port" label="Port" value-type="string" optional="false" category="endpoint" default-value="8563" />

<field name="authentication" label="Authentication" category="authentication" value-type="string" editable="false" default-value="auth-user-pass" />

<field name="username" label="Username" value-type="string" category="authentication" />

<field name="password" label="Password" value-type="string" category="authentication" secure="true" />

<field name="v-fingerprint" label="Server Certificate Fingerprint" value-type="string" optional="true" category="general" />
</connection-fields>
7 changes: 7 additions & 0 deletions src/exasol_odbc/connectionMetadata.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>

<connection-metadata>
<database enabled='false' />
<schema enabled='true' label="Schema" />
<table enabled='true' label="Table" />
</connection-metadata>
2 changes: 2 additions & 0 deletions src/exasol_odbc/connectionResolver.tdr
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
<attr>server</attr>
<attr>port</attr>
<attr>schema</attr>
<attr>authentication</attr>
<attr>username</attr>
<attr>password</attr>
<attr>v-fingerprint</attr>
</attribute-list>
</required-attributes>
</connection-normalizer>
Expand Down
24 changes: 17 additions & 7 deletions src/exasol_odbc/manifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8' ?>
<?xml version="1.0" encoding="utf-8" ?>

<connector-plugin class='exasol_odbc' superclass='odbc' plugin-version='1.0.0' name='Exasol ODBC' version='18.1' min-version-tableau='2020.4'>
<connector-plugin class="exasol_odbc" superclass="odbc" plugin-version="0.5.0" name="Exasol ODBC" version="18.1" min-version-tableau="2020.4">
<vendor-information>
<company name="Exasol AG"/>
<support-link url="https://www.exasol.com/"/>
Expand All @@ -10,18 +10,30 @@
<vendor name="vendor"/>
<driver name="driver"/>
<customizations>
<!-- See https://tableau.github.io/connector-plugin-sdk/docs/capabilities -->

<!-- Temporary Tables -->
<customization name="CAP_CREATE_TEMP_TABLES" value="no"/>
<customization name="CAP_SELECT_INTO" value="no"/>
<customization name="CAP_SELECT_TOP_INTO" value="no"/>

<!-- Initial SQL -->
<customization name="CAP_SUPPORTS_INITIAL_SQL" value="yes"/>

<!-- Metadata -->
<customization name="CAP_FAST_METADATA" value="yes"/>
<customization name="CAP_QUERY_TOP_0_METADATA" value="no"/>
<customization name="CAP_QUERY_WHERE_FALSE_METADATA" value="yes"/>

<!-- ODBC -->
<customization name="CAP_ODBC_METADATA_SUPPRESS_EXECUTED_QUERY" value="yes"/>
<customization name="CAP_ODBC_METADATA_SUPPRESS_PREPARED_QUERY" value="yes"/>
<customization name="CAP_ODBC_METADATA_SUPPRESS_SQLCOLUMNS_API" value="no"/>
<customization name="CAP_ODBC_METADATA_SUPPRESS_SQLFOREIGNKEYS_API" value="no"/>
<customization name="CAP_ODBC_METADATA_STRING_TRUST_OCTET_LENGTH" value="yes"/>
<customization name="CAP_ODBC_UNBIND_EACH" value="no"/>

<!-- Query -->
<customization name="CAP_QUERY_BOOLEXPR_TO_INTEXPR" value="yes"/>
<customization name="CAP_QUERY_FULLJOIN_IND" value="yes"/>
<customization name="CAP_QUERY_GROUP_BY_ALIAS" value="yes"/>
Expand All @@ -33,12 +45,10 @@
<customization name="CAP_QUERY_SUBQUERIES" value="yes"/>
<customization name="CAP_QUERY_TOP_N" value="yes"/>

<customization name="CAP_ODBC_METADATA_STRING_TRUST_OCTET_LENGTH" value="yes"/>
<customization name="CAP_ODBC_UNBIND_EACH" value="no"/>

</customizations>
</connection-customization>
<connection-dialog file='connectionDialog.tcd'/>
<connection-fields file="connectionFields.xml"/>
<connection-metadata file="connectionMetadata.xml"/>
<connection-resolver file="connectionResolver.tdr"/>
<dialect file='dialect.tdd'/>
<dialect file="dialect.tdd"/>
</connector-plugin>
32 changes: 0 additions & 32 deletions tdvt_odbc/runtests

This file was deleted.

2 changes: 1 addition & 1 deletion tdvt_odbc/tds/Staples.exasol_odbc.tds
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<connection class='federated'>
<named-connections>
<named-connection name='leaf'>
<connection tdvtconnection='exasol_odbctestname' class='exasol_odbc' odbc-connect-string-extras='' port='8563' schema='TESTV1' server='exasol.test.lan/15F9CA9BC95E14F1F913FC449A26723841C118CFB644957866ABB73C1399A7FF' tablename='Staples' username='TABLEAU_TEST_USER' />
<connection tdvtconnection='exasol_odbctestname' class='exasol_odbc' odbc-connect-string-extras='' port='8563' schema='TESTV1' server='exasol.test.lan' tablename='Staples' username='TABLEAU_TEST_USER' v-fingerprint='15F9CA9BC95E14F1F913FC449A26723841C118CFB644957866ABB73C1399A7FF' />
</named-connection>
</named-connections>
<relation connection='leaf' name='Staples' table='[TESTV1].[Staples]' type='table' />
Expand Down
2 changes: 1 addition & 1 deletion tdvt_odbc/tds/cast_calcs.exasol_odbc.tds
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<connection class='federated'>
<named-connections>
<named-connection name='leaf'>
<connection tdvtconnection='exasol_odbctestname' class='exasol_odbc' odbc-connect-string-extras='' port='8563' schema='TESTV1' server='exasol.test.lan/15F9CA9BC95E14F1F913FC449A26723841C118CFB644957866ABB73C1399A7FF' tablename='Calcs' username='TABLEAU_TEST_USER' />
<connection tdvtconnection='exasol_odbctestname' class='exasol_odbc' odbc-connect-string-extras='' port='8563' schema='TESTV1' server='exasol.test.lan' tablename='Calcs' username='TABLEAU_TEST_USER' v-fingerprint='15F9CA9BC95E14F1F913FC449A26723841C118CFB644957866ABB73C1399A7FF' />
</named-connection>
</named-connections>
<relation connection='leaf' name='Calcs' table='[TESTV1].[Calcs]' type='table' />
Expand Down
Loading

0 comments on commit e7b9f2f

Please sign in to comment.