diff --git a/.gitignore b/.gitignore
index dd9b4ee..780389a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,7 @@ LICENSE.txt
/IntegrationTests/
/opensearchenlist/
/sqlodbc/
+/output/
/PerformanceTests/
/UnitTests/
*.filters
@@ -60,3 +61,6 @@ CTestTestfile.cmake
/src/PowerBIConnector/obj/
/src/PowerBIConnector/.vs/
src/vcpkg_installed/
+/extensionBiConnector/
+/comparisonBiConnector/
+/extensionTableauConnector/
diff --git a/CppProperties.json b/CppProperties.json
new file mode 100644
index 0000000..6017def
--- /dev/null
+++ b/CppProperties.json
@@ -0,0 +1,21 @@
+{
+ "configurations": [
+ {
+ "inheritEnvironments": [
+ "msvc_x64"
+ ],
+ "name": "x64-Release",
+ "includePath": [
+ "${env.INCLUDE}",
+ "${workspaceRoot}\\**"
+ ],
+ "defines": [
+ "WIN32",
+ "NDEBUG",
+ "UNICODE",
+ "_UNICODE"
+ ],
+ "intelliSenseMode": "windows-msvc-x64"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 8d58c9c..bb76f41 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# OpenSearch ODBC Driver
-OpenSearchODBC is a read-only ODBC driver for Windows and Mac for connecting to OpenSearch SQL support.
+OpenSearchODBC is a read-only ODBC driver for Windows and Mac for connecting to OpenSearch SQL support. This driver version allows to levereage on Oauth2 authentication/authorization.
### ODBC Driver
diff --git a/bi-connectors/PowerBIConnector/AmazonOpenSearchService.md b/bi-connectors/PowerBIConnector/AmazonOpenSearchService.md
index 8f665b9..a1c4ec1 100644
--- a/bi-connectors/PowerBIConnector/AmazonOpenSearchService.md
+++ b/bi-connectors/PowerBIConnector/AmazonOpenSearchService.md
@@ -52,7 +52,7 @@
## Troubleshooting
-* If you get the following error, please install the [OpenSearch SQL ODBC Driver](https://docs-beta.opensearch.org/search-plugins/sql/odbc/).
+* If you get the following error, please install the [OpenSearch SQL ODBC Driver](https://opensearch.org/docs/latest/search-plugins/sql/sql/odbc/).
diff --git a/bi-connectors/PowerBIConnector/OpenSearchProject.md b/bi-connectors/PowerBIConnector/OpenSearchProject.md
index 7153d56..f7c4fea 100644
--- a/bi-connectors/PowerBIConnector/OpenSearchProject.md
+++ b/bi-connectors/PowerBIConnector/OpenSearchProject.md
@@ -23,36 +23,53 @@
## Connect to OpenSearch Project
1. Open Power BI Desktop.
-2. Click on **Home** > **Get Data** > **More** > **Other**. Select **OpenSearch Project**. Click on **Connect**.
+2. If necessary clear the previous permissions.
+
+
+
+3. Click on **Home** > **Get Data** > **More** > **Other**. Select **OpenSearch Project**. Click on **Connect**.
-3. You will get a warning for using a third-party service. Click on **Continue**.
+4. You will get a warning for using a third-party service. Click on **Continue**.
-4. Enter host and port values and select your preferred SSL and Certificate validation options. Click on **OK**.
+5. Enter host and port values and select your preferred SSL and Certificate validation options. Click on **OK**.
-5. Select authentication option. Enter credentials if required and click on **Connect**.
+6. Select authentication option. Enter credentials if required and click on **Connect**.
-6. Select required table. Data preview will be loaded.
+7. Select required table. Data preview will be loaded.
-7. Click on **Load**.
+8. Click on **Load**.
-8. Select required columns for creating a graph.
+9. Select required columns for creating a graph.
+## Set up Oauth2
+
+* In the same folder of the source files to build put a JSON file filling the three fields required in the template provided.
+
+## Login with Oauth2
+
+1. Select the `OAUTH2` authentication and click on **Sign In**
+
+
+
+2. After provided the user-id and the user-secret, click on **Connect**
+
+
## Troubleshooting
-* If you get the following error, please install the [OpenSearch SQL ODBC Driver](https://docs-beta.opensearch.org/search-plugins/sql/odbc/).
+* If you get the following error, please install the [OpenSearch SQL ODBC Driver](https://opensearch.org/docs/latest/search-plugins/sql/sql/odbc/).
diff --git a/bi-connectors/PowerBIConnector/README.md b/bi-connectors/PowerBIConnector/README.md
index 9d90dc6..7eab3e2 100644
--- a/bi-connectors/PowerBIConnector/README.md
+++ b/bi-connectors/PowerBIConnector/README.md
@@ -3,6 +3,18 @@
The Power BI connector is available to download from the automated CI workflow: [link](https://github.com/opensearch-project/sql-odbc/actions/workflows/bi-connectors.yml).
The release snapshots are also available here: [OpenSearch Project](OpenSearchProject.mez) and [Amazon OpenSearch Service](AmazonOpenSearchService.mez).
+## Connector Development
+
+The development of the connector is related to the PoweQuery SDK for [VisualStudio](https://marketplace.visualstudio.com/items?itemName=Dakahn.PowerQuerySDK) or for Visual Studio Code from the `Extensions` left tab.
+
+Is possible to initialize a project for example in VSC with
+
+The main logic is in `.pq` file (not `.query.pq`) and is not possible to have multiple files of this extension. In connector development there are limitation to the functionalities that is possibile to use as the [data source functions](https://learn.microsoft.com/en-us/power-query/handling-data-access#data-source-functions). The reference and guides of [Power Query M](https://learn.microsoft.com/en-us/powerquery-m/) is available.
+
+After created the program it is required to run compiling and packaging phases as .
+
+The output `.mez` file will appear in `bin\AnyCPU\Debug` of the same folder.
+
## Connector Install
1. Put connector `mez` file into: `C:\Users\%USERNAME%\Documents\Power BI Desktop\Custom Connectors`.
diff --git a/bi-connectors/PowerBIConnector/img/Oauth2_init.png b/bi-connectors/PowerBIConnector/img/Oauth2_init.png
new file mode 100644
index 0000000..d5de4e1
Binary files /dev/null and b/bi-connectors/PowerBIConnector/img/Oauth2_init.png differ
diff --git a/bi-connectors/PowerBIConnector/img/Oauth2_signedIn.png b/bi-connectors/PowerBIConnector/img/Oauth2_signedIn.png
new file mode 100644
index 0000000..714d54b
Binary files /dev/null and b/bi-connectors/PowerBIConnector/img/Oauth2_signedIn.png differ
diff --git a/bi-connectors/PowerBIConnector/img/build_task.png b/bi-connectors/PowerBIConnector/img/build_task.png
new file mode 100644
index 0000000..58e5e91
Binary files /dev/null and b/bi-connectors/PowerBIConnector/img/build_task.png differ
diff --git a/bi-connectors/PowerBIConnector/img/powerbi_cached.png b/bi-connectors/PowerBIConnector/img/powerbi_cached.png
new file mode 100644
index 0000000..730a3ad
Binary files /dev/null and b/bi-connectors/PowerBIConnector/img/powerbi_cached.png differ
diff --git a/bi-connectors/PowerBIConnector/img/powerquery_SDK.png b/bi-connectors/PowerBIConnector/img/powerquery_SDK.png
new file mode 100644
index 0000000..c190784
Binary files /dev/null and b/bi-connectors/PowerBIConnector/img/powerquery_SDK.png differ
diff --git a/bi-connectors/PowerBIConnector/power_bi_support.md b/bi-connectors/PowerBIConnector/power_bi_support.md
index 2c5451a..ba20d0e 100644
--- a/bi-connectors/PowerBIConnector/power_bi_support.md
+++ b/bi-connectors/PowerBIConnector/power_bi_support.md
@@ -2,8 +2,8 @@
## Prerequisites
* Microsoft Power BI Desktop
-* [OpenSearch](https://docs-beta.opensearch.org/opensearch/install/index/)
-* [OpenSearch SQL ODBC driver](https://docs-beta.opensearch.org/search-plugins/sql/odbc/)
+* [OpenSearch](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/index/)
+* [OpenSearch SQL ODBC driver](https://opensearch.org/docs/latest/search-plugins/sql/sql/odbc/)
* [OpenSearchProject.mez](OpenSearchProject.mez) or [AmazonOpenSearchService.mez](AmazonOpenSearchService.mez)
* Optional: [sqlodbc_import.pbids](PBIDSExamples/sqlodbc_import.pbids) to help with repeated connections to the same server
@@ -86,7 +86,7 @@ It will take you straight to the **Navigator** window for selecting the tables f
## Troubleshooting
-* If you get an following error, please install [OpenSearch SQL ODBC Driver](https://docs-beta.opensearch.org/search-plugins/sql/odbc/).
+* If you get an following error, please install [OpenSearch SQL ODBC Driver](https://opensearch.org/docs/latest/search-plugins/sql/sql/odbc/).
diff --git a/bi-connectors/PowerBIConnector/src/OauthConf.json b/bi-connectors/PowerBIConnector/src/OauthConf.json
new file mode 100644
index 0000000..c727fdb
--- /dev/null
+++ b/bi-connectors/PowerBIConnector/src/OauthConf.json
@@ -0,0 +1,5 @@
+{
+ "ClientId": "",
+ "ClientSecret": "",
+ "IDPurl": ""
+}
\ No newline at end of file
diff --git a/bi-connectors/PowerBIConnector/src/OpenSearchProject.pq b/bi-connectors/PowerBIConnector/src/OpenSearchProject.pq
index 9a2d67d..b14be50 100644
--- a/bi-connectors/PowerBIConnector/src/OpenSearchProject.pq
+++ b/bi-connectors/PowerBIConnector/src/OpenSearchProject.pq
@@ -2,47 +2,57 @@
[Version = "1.0.1"]
section OpenSearchProject;
-// When set to true, additional trace information will be written out to the User log.
-// This should be set to false before release. Tracing is done through a call to
-// Diagnostics.LogValue(). When EnableTraceOutput is set to false, the call becomes a
+// When set to true, additional trace information will be written out to the User log.
+// This should be set to false before release. Tracing is done through a call to
+// Diagnostics.LogValue(). When EnableTraceOutput is set to false, the call becomes a
// no-op and simply returns the original value.
-EnableTraceOutput = false;
-[DataSource.Kind="OpenSearchProject", Publish="OpenSearchProject.Publish"]
+config = Text.FromBinary(Extension.Contents("OauthConf.json"));
+
+EnableTraceOutput = true;
+
+[DataSource.Kind = "OpenSearchProject", Publish = "OpenSearchProject.Publish"]
shared OpenSearchProject.Contents = Value.ReplaceType(OpenSearchProjectImpl, OpenSearchProjectType);
// Wrapper function to provide additional UI customization.
OpenSearchProjectType = type function (
- Server as (type text meta [
+ Server as (
+ type text meta [
Documentation.FieldCaption = "Server",
Documentation.FieldDescription = "The hostname of the OpenSearch server.",
- Documentation.SampleValues = { "localhost" }
- ]),
- Port as (type number meta [
+ Documentation.SampleValues = {"localhost"}
+ ]
+ ),
+ Port as (
+ type number meta [
Documentation.FieldCaption = "Port",
Documentation.FieldDescription = "Port which OpenSearch server listens on.",
- Documentation.SampleValues = { 9200 }
- ]),
- UseSSL as (type logical meta [
+ Documentation.SampleValues = {9200}
+ ]
+ ),
+ UseSSL as (
+ type logical meta [
Documentation.FieldCaption = "Use SSL",
Documentation.FieldDescription = "Use SSL",
- Documentation.AllowedValues = { true, false }
- ]),
- HostnameVerification as (type logical meta [
+ Documentation.AllowedValues = {true, false}
+ ]
+ ),
+ HostnameVerification as (
+ type logical meta [
Documentation.FieldCaption = "Certificate validation",
Documentation.FieldDescription = "Certificate validation",
- Documentation.AllowedValues = { true, false }
- ])
+ Documentation.AllowedValues = {true, false}
+ ]
)
- as table meta [
- Documentation.Name = "OpenSearch Project"
- ];
+) as table meta [
+ Documentation.Name = "OpenSearch Project"
+];
+
OpenSearchProjectImpl = (Server as text, Port as number, UseSSL as logical, HostnameVerification as logical) as table =>
let
Credential = Extension.CurrentCredential(),
AuthenticationMode = Credential[AuthenticationKind],
-
// Sets connection string properties for authentication.
CredentialConnectionString =
if AuthenticationMode = "UsernamePassword" then
@@ -56,11 +66,15 @@ OpenSearchProjectImpl = (Server as text, Port as number, UseSSL as logical, Host
Auth = "AWS_SIGV4",
Region = Credential[Key]
]
- else
+ else if AuthenticationMode = "OAuth" then
+ [
+ Auth = "OAUTH2",
+ JWT = Credential[access_token]
+ ]
+ else
[
Auth = "NONE"
],
-
// Sets connection string properties for encrypted connections.
EncryptedConnectionString =
if Credential[EncryptConnection] = null or Credential[EncryptConnection] = true then
@@ -71,40 +85,46 @@ OpenSearchProjectImpl = (Server as text, Port as number, UseSSL as logical, Host
[
UseSSL = 0
],
-
// Subtract the server from the user input in case it's entered like 'http://localhost' or 'https://srv.com:100500' or 'localhost:0'
// And build the proper string on our own
- FinalServerString = if UseSSL then
+ FinalServerString =
+ if UseSSL then
"https://" & Uri.Parts(Server)[Host] & ":" & Text.From(Port)
else
"http://" & Uri.Parts(Server)[Host] & ":" & Text.From(Port),
-
ConnectionString = [
Driver = "OpenSearch SQL ODBC Driver",
Host = FinalServerString,
HostnameVerification = if HostnameVerification then 1 else 0
],
-
- SQLGetInfo = Diagnostics.LogValue("SQLGetInfo_Options", [
- SQL_AGGREGATE_FUNCTIONS = ODBC[SQL_AF][All],
- SQL_SQL_CONFORMANCE = ODBC[SQL_SC][SQL_SC_SQL92_INTERMEDIATE]
- ]),
-
- SQLGetTypeInfo = (types) =>
- if (EnableTraceOutput <> true) then types else
- let
- // Outputting the entire table might be too large, and result in the value being truncated.
- // We can output a row at a time instead with Table.TransformRows()
- rows = Table.TransformRows(types, each Diagnostics.LogValue("SQLGetTypeInfo " & _[TYPE_NAME], _)),
- toTable = Table.FromRecords(rows)
- in
- Value.ReplaceType(toTable, Value.Type(types)),
-
+ SQLGetInfo = Diagnostics.LogValue(
+ "SQLGetInfo_Options",
+ [
+ SQL_AGGREGATE_FUNCTIONS = ODBC[SQL_AF][All],
+ SQL_SQL_CONFORMANCE = ODBC[SQL_SC][SQL_SC_SQL92_INTERMEDIATE]
+ ]
+ ),
+ SQLGetTypeInfo = (types) =>
+ if (EnableTraceOutput <> true) then
+ types
+ else
+ let
+ // Outputting the entire table might be too large, and result in the value being truncated.
+ // We can output a row at a time instead with Table.TransformRows()
+ rows = Table.TransformRows(types, each Diagnostics.LogValue("SQLGetTypeInfo " & _[TYPE_NAME], _)),
+ toTable = Table.FromRecords(rows)
+ in
+ Value.ReplaceType(toTable, Value.Type(types)),
// SQLColumns is a function handler that receives the results of an ODBC call to SQLColumns().
SQLColumns = (catalogName, schemaName, tableName, columnName, source) =>
- if (EnableTraceOutput <> true) then source else
+ if (EnableTraceOutput <> true) then
+ source
+ else
// the if statement conditions will force the values to evaluated/written to diagnostics
- if (Diagnostics.LogValue("SQLColumns.TableName", tableName) <> "***" and Diagnostics.LogValue("SQLColumns.ColumnName", columnName) <> "***") then
+ if (
+ Diagnostics.LogValue("SQLColumns.TableName", tableName) <> "***"
+ and Diagnostics.LogValue("SQLColumns.ColumnName", columnName) <> "***"
+ ) then
let
// Outputting the entire table might be too large, and result in the value being truncated.
// We can output a row at a time instead with Table.TransformRows()
@@ -114,24 +134,24 @@ OpenSearchProjectImpl = (Server as text, Port as number, UseSSL as logical, Host
Value.ReplaceType(toTable, Value.Type(source))
else
source,
-
SQLGetFunctions = Diagnostics.LogValue("SQLGetFunctions_Options", [
SQL_API_SQLBINDPARAMETER = false
]),
-
- SqlCapabilities = Diagnostics.LogValue("SqlCapabilities_Options", [
- SupportsTop = false,
- LimitClauseKind = LimitClauseKind.LimitOffset,
- Sql92Conformance = ODBC[SQL_SC][SQL_SC_SQL92_FULL],
- SupportsNumericLiterals = true,
- SupportsStringLiterals = true,
- SupportsOdbcDateLiterals = true,
- SupportsOdbcTimeLiterals = true,
- SupportsOdbcTimestampLiterals = true
- ]),
-
+ SqlCapabilities = Diagnostics.LogValue(
+ "SqlCapabilities_Options",
+ [
+ SupportsTop = false,
+ LimitClauseKind = LimitClauseKind.LimitOffset,
+ Sql92Conformance = ODBC[SQL_SC][SQL_SC_SQL92_FULL],
+ SupportsNumericLiterals = true,
+ SupportsStringLiterals = true,
+ SupportsOdbcDateLiterals = true,
+ SupportsOdbcTimeLiterals = true,
+ SupportsOdbcTimestampLiterals = true
+ ]
+ ),
OdbcOptions = [
- // Do not view the tables grouped by their schema names.
+ // Do not view the tables grouped by their schema names.
HierarchicalNavigation = false,
// Prevents execution of native SQL statements. Extensions should set this to true.
HideNativeQuery = true,
@@ -141,22 +161,17 @@ OpenSearchProjectImpl = (Server as text, Port as number, UseSSL as logical, Host
TolerateConcatOverflow = true,
// Enables connection pooling via the system ODBC manager
ClientConnectionPooling = true,
-
// These values should be set by previous steps
SQLColumns = SQLColumns,
SQLGetTypeInfo = SQLGetTypeInfo,
SQLGetInfo = SQLGetInfo,
SQLGetFunctions = SQLGetFunctions,
SqlCapabilities = SqlCapabilities,
-
OnError = OnOdbcError,
-
// Connection string properties used for encrypted connections.
CredentialConnectionString = EncryptedConnectionString
],
-
FullConnectionString = (ConnectionString & CredentialConnectionString & EncryptedConnectionString),
-
OdbcDatasource = Odbc.DataSource(FullConnectionString, OdbcOptions)
in
OdbcDatasource;
@@ -165,28 +180,31 @@ OpenSearchProjectImpl = (Server as text, Port as number, UseSSL as logical, Host
OnOdbcError = (errorRecord as record) =>
let
ErrorMessage = errorRecord[Message],
- ConnectionServer = errorRecord[Detail][DataSourcePath],
-
+ ConnectionServer = errorRecord[Detail][DataSourcePath],
IsDriverNotInstalled = Text.Contains(ErrorMessage, "doesn't correspond to an installed ODBC driver"),
-
OdbcError = errorRecord[Detail][OdbcErrors]{0},
OdbcErrorCode = OdbcError[NativeError],
-
// Failed to connect to given host
- IsHostUnreachable =
- OdbcErrorCode = 202
+ IsHostUnreachable = OdbcErrorCode = 202
in
if IsDriverNotInstalled then
- error Error.Record("DataSource.Error", "The OpenSearch SQL ODBC driver is not installed. Please install the driver")
- else if IsHostUnreachable then
- error Error.Record("DataSource.Error", "Couldn't reach server. Please double-check the server and auth. [" & ConnectionServer & "]")
- else
+ error
+ Error.Record(
+ "DataSource.Error", "The OpenSearch SQL ODBC driver is not installed. Please install the driver"
+ )
+ else if IsHostUnreachable then
+ error
+ Error.Record(
+ "DataSource.Error",
+ "Couldn't reach server. Please double-check the server and auth. [" & ConnectionServer & "]"
+ )
+ else
error errorRecord;
// Data Source Kind description
OpenSearchProject = [
- // Required for use with Power BI Service.
- TestConnection = (dataSourcePath) =>
+
+ TestConnection = (dataSourcePath) =>
let
json = Json.Document(dataSourcePath),
Server = json[Server],
@@ -194,7 +212,7 @@ OpenSearchProject = [
UseSSL = json[UseSSL],
HostnameVerification = json[HostnameVerification]
in
- { "OpenSearchProject.Contents", Server, Port, UseSSL, HostnameVerification },
+ {"OpenSearchProject.Contents", Server, Port, UseSSL, HostnameVerification},
// Authentication modes
Authentication = [
@@ -207,25 +225,129 @@ OpenSearchProject = [
Key = [
Label = "AWS_SIGV4",
KeyLabel = "Region"
+ ],
+ OAuth = [
+ Label = "OAUTH2",
+ StartLogin = StartLogin,
+ FinishLogin = FinishLogin,
+ Refresh = Refresh,
+ Logout = Logout
]
],
+ callbackUri = "https://oauth.powerbi.com/views/oauthredirect.html",
+ logout_uri = "https://login.microsoftonline.com/logout.srf",
+
+ oauthConfig = Json.Document(config),
+
+ AuthUrl = oauthConfig [IDPurl] & "auth?",
+ TokenUrl = oauthConfig [IDPurl] & "token",
+ LogoutUrl = oauthConfig [IDPurl] & "logout",
+ ClientId = oauthConfig [ClientId] ,
+ ClientSecret = oauthConfig[ClientSecret],
+
+ Scope = "opensearch",
+
+ // StartLogin function initiates OAuth2 authentication
+ StartLogin = (resourceUrl, state, display) =>
+ let
+ AuthorizeUrl = AuthUrl
+ & Uri.BuildQueryString(
+ [
+ client_id = ClientId,
+ scope = Scope,
+ state = state,
+ response_type = "code",
+ redirect_uri = callbackUri
+ ]
+ )
+ in
+ [
+ LoginUri = AuthorizeUrl,
+ CallbackUri = callbackUri,
+ WindowHeight = 720,
+ WindowWidth = 720,
+ Context = null
+ ],
+
+ FinishLogin = (context, callbackUri, state) => let Parts = Uri.Parts(callbackUri)[Query] in TokenMethod(Parts[code]),
+
+ TokenMethod = (code) =>
+ let
+ Response = Web.Contents(
+ TokenUrl,
+ [
+ Content = Text.ToBinary(
+ Uri.BuildQueryString(
+ [
+ client_id = ClientId,
+ client_secret = ClientSecret,
+ code = code,
+ grant_type = "authorization_code",
+ redirect_uri = callbackUri
+ ]
+ )
+ ),
+ Headers = [#"Content-type" = "application/x-www-form-urlencoded", #"Accept" = "application/json"]
+ ]
+ ),
+ Parts = Json.Document(Response)
+ in
+ Parts,
+
+ Refresh = (refreshToken) =>
+ let
+ tokenUrl = TokenUrl,
+ requestBody = "grant_type=refresh_token&refresh_token=" & refreshToken & "&client_id=" & ClientId & "&client_secret=" & ClientSecret,
+ refreshResponse = Json.Document(Web.Contents(tokenUrl, [Content = Text.ToBinary(requestBody), Headers = [#"Content-Type"="application/x-www-form-urlencoded"]])),
+ refreshedAccessToken = refreshResponse[access_token]
+ in
+ refreshedAccessToken,
+
+ Logout = (accessToken) =>
+ let
+ logoutUrl = LogoutUrl,
+ redirectUrl = callbackUri,
+ response = Web.Contents(logoutUrl, [Query = [redirect_uri = redirectUrl], Headers = [Authorization="Bearer " & accessToken]])
+ in
+ // Assuming successful logout
+ null,
+
// PBIDS Handler
DSRHandlers = [
- opensearchproject = [
- GetDSR = (Server, Port, UseSSL, HostnameVerification, optional Options) => [ protocol = "opensearchproject-odbc", address = [ server = Server, port = Port, useSSL = UseSSL, hostnameVerification = HostnameVerification ] ],
- GetFormula = (dsr, optional options) => () =>
- let
- db = OpenSearchProject.Contents(dsr[address][server], dsr[address][port], dsr[address][useSSL], dsr[address][hostnameVerification])
- in
- db,
- GetFriendlyName = (dsr) => "OpenSearch Project"
- ]
+ opensearchproject = [
+ GetDSR = (Server, Port, UseSSL, HostnameVerification, IDPurl, ClientId, ClientSecret, optional Options) =>
+ [
+ protocol = "opensearchproject-odbc",
+ address = [
+ server = Server,
+ port = Port,
+ useSSL = UseSSL,
+ idpURL = IDPurl,
+ username = ClientId,
+ secret = ClientSecret,
+ hostnameVerification = HostnameVerification
+ ]
+ ],
+ GetFormula = (dsr, optional options) =>
+ () =>
+ let
+ db = OpenSearchProject.Contents(
+ dsr[address][server],
+ dsr[address][port],
+ dsr[address][useSSL],
+ dsr[address][idpURL],
+ dsr[address][ClientId],
+ dsr[address][ClientSecret],
+ dsr[address][hostnameVerification]
+ )
+ in
+ db,
+ GetFriendlyName = (dsr) => "OpenSearch Project"
+ ]
],
-
// Enable Encryption
SupportsEncryption = true,
-
Label = Extension.LoadString("DataSourceLabel")
];
@@ -233,30 +355,38 @@ OpenSearchProject = [
OpenSearchProject.Publish = [
Beta = false,
Category = "Other",
- ButtonText = { Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp") },
+ ButtonText = {Extension.LoadString("ButtonTitle"), Extension.LoadString("ButtonHelp")},
LearnMoreUrl = "https://github.com/opensearch-project/sql-odbc/blob/main/bi-connectors/PowerBIConnector/OpenSearchProject.md",
-
SupportsDirectQuery = true,
-
SourceImage = OpenSearch.Icons,
SourceTypeImage = OpenSearch.Icons
];
OpenSearch.Icons = [
- Icon16 = { Extension.Contents("OpenSearch16.png"), Extension.Contents("OpenSearch20.png"), Extension.Contents("OpenSearch24.png"), Extension.Contents("OpenSearch32.png") },
- Icon32 = { Extension.Contents("OpenSearch32.png"), Extension.Contents("OpenSearch40.png"), Extension.Contents("OpenSearch48.png"), Extension.Contents("OpenSearch64.png") }
+ Icon16 = {
+ Extension.Contents("OpenSearch16.png"),
+ Extension.Contents("OpenSearch20.png"),
+ Extension.Contents("OpenSearch24.png"),
+ Extension.Contents("OpenSearch32.png")
+ },
+ Icon32 = {
+ Extension.Contents("OpenSearch32.png"),
+ Extension.Contents("OpenSearch40.png"),
+ Extension.Contents("OpenSearch48.png"),
+ Extension.Contents("OpenSearch64.png")
+ }
];
// Load common library functions
Extension.LoadFunction = (name as text) =>
let
- binary = Extension.Contents(name),
- asText = Text.FromBinary(binary)
+ binary = Extension.Contents(name), asText = Text.FromBinary(binary)
in
Expression.Evaluate(asText, #shared);
// Diagnostics module contains multiple functions. .
Diagnostics = Extension.LoadFunction("Diagnostics.pqm");
+
Diagnostics.LogValue = if (EnableTraceOutput) then Diagnostics[LogValue] else (prefix, value) => value;
// OdbcConstants contains numeric constants from the ODBC header files, and helper function to create bitfield values.
diff --git a/bi-connectors/PowerBIConnector/src/bin/AnyCPU/Debug/src.mez b/bi-connectors/PowerBIConnector/src/bin/AnyCPU/Debug/src.mez
new file mode 100644
index 0000000..ffc3b8e
Binary files /dev/null and b/bi-connectors/PowerBIConnector/src/bin/AnyCPU/Debug/src.mez differ
diff --git a/build_win_debug32.ps1 b/build_win_debug32.ps1
index 2717064..dd4edd9 100644
--- a/build_win_debug32.ps1
+++ b/build_win_debug32.ps1
@@ -3,4 +3,4 @@ $env:VCPKG_DEFAULT_TRIPLET = 'x86-windows'
cd src
vcpkg install
cd ..
-.\scripts\build_windows.ps1 $WORKING_DIR Debug 32
+.\scripts\build_windows.ps1 $WORKING_DIR Debug 32
\ No newline at end of file
diff --git a/build_win_debug64.ps1 b/build_win_debug64.ps1
index 98a9a24..ab65d2c 100644
--- a/build_win_debug64.ps1
+++ b/build_win_debug64.ps1
@@ -3,4 +3,4 @@ $env:VCPKG_DEFAULT_TRIPLET = 'x64-windows'
cd src
vcpkg install
cd ..
-.\scripts\build_windows.ps1 $WORKING_DIR Debug 64
+.\scripts\build_windows.ps1 $WORKING_DIR Debug 64
\ No newline at end of file
diff --git a/build_win_release32.ps1 b/build_win_release32.ps1
index c7e41da..dd7726c 100644
--- a/build_win_release32.ps1
+++ b/build_win_release32.ps1
@@ -3,4 +3,4 @@ $env:VCPKG_DEFAULT_TRIPLET = 'x86-windows'
cd src
vcpkg install
cd ..
-.\scripts\build_windows.ps1 $WORKING_DIR Release 32
+.\scripts\build_windows.ps1 $WORKING_DIR Release 32
\ No newline at end of file
diff --git a/build_win_release64.ps1 b/build_win_release64.ps1
index a17f4d6..8d1d32b 100644
--- a/build_win_release64.ps1
+++ b/build_win_release64.ps1
@@ -3,4 +3,4 @@ $env:VCPKG_DEFAULT_TRIPLET = 'x64-windows'
cd src
vcpkg install
cd ..
-.\scripts\build_windows.ps1 $WORKING_DIR Release 64
+.\scripts\build_windows.ps1 $WORKING_DIR Release 64
\ No newline at end of file
diff --git a/docs/dev/BUILD_INSTRUCTIONS.md b/docs/dev/BUILD_INSTRUCTIONS.md
index dfd3d77..6eef1c5 100644
--- a/docs/dev/BUILD_INSTRUCTIONS.md
+++ b/docs/dev/BUILD_INSTRUCTIONS.md
@@ -5,12 +5,11 @@
### Requirements
* Install [cmake](https://cmake.org/install/)
-* Install [Visual Studio 2019](https://visualstudio.microsoft.com/vs/)
- * Other versions may work, but only 2019 has been tested
+* Install [Visual Studio 2019](https://visualstudio.microsoft.com/vs/) (works also with Visual Studio 2022)
**NOTE:** All Windows/`.ps1` scripts must be run from a [Developer Powershell](https://devblogs.microsoft.com/visualstudio/the-powershell-you-know-and-love-now-with-a-side-of-visual-studio/).
-> A shortcut is installed on your system with Visual Studio (**"Developer Powershell for VS 2019"**)
+> A shortcut is installed on your system with Visual Studio (**"Developer Powershell for VS 2019"**) or (**"Developer Powershell for VS 2022"**)
> Programs launched with this prompt (ex: VS Code) also have access to the Developer shell.
diff --git a/docs/dev/Pagination.md b/docs/dev/Pagination.md
index ff2a862..b06b7c4 100644
--- a/docs/dev/Pagination.md
+++ b/docs/dev/Pagination.md
@@ -3,7 +3,7 @@
## Overview
OpenSearch ODBC Driver supports forward-only cursor. This document illustrates how the cursor(pagination) is handled in the driver.
-For information on how the pagination is supported on OpenSearch server, check [OpenSearch SQL Cursor (Pagination) Support](https://github.com/opensearch-project/sql/blob/main/docs/dev/Pagination.md).
+For information on how the pagination is supported on OpenSearch server, check [OpenSearch SQL Cursor (Pagination) Support](https://github.com/opensearch-project/sql-odbc/blob/cb5f4bd7ba4a15a2614ecbd3b53ddfcd9b4320fc/docs/dev/Pagination.md).
## Data Flow
@@ -14,7 +14,7 @@ For information on how the pagination is supported on OpenSearch server, check [
* Step 5 will send a request to close cursor whenever the connection is closed.
* ODBC Driver will provide an option to define fetch size as a connection parameter.
* If fetch size is zero, query will fallback to non-cursor behavior.
- * If fetch size is not given then the number of rows per request will be as per server-defined [default fetch size](https://github.com/opensearch-project/sql/blob/main/docs/dev/Pagination.md#42-salient-points).
+ * If fetch size is not given then the number of rows per request will be as per server-defined [default fetch size](https://github.com/opensearch-project/sql-odbc/blob/cb5f4bd7ba4a15a2614ecbd3b53ddfcd9b4320fc/docs/dev/Pagination.md).
* ODBC Driver will send the request to close cursor whenever the connection is closed.
## Detailed Design
diff --git a/docs/dev/run_tests.md b/docs/dev/run_tests.md
index 6d7dd14..93ad885 100644
--- a/docs/dev/run_tests.md
+++ b/docs/dev/run_tests.md
@@ -2,13 +2,13 @@
## Requirements
-* Latest version of [OpenSearch](https://docs-beta.opensearch.org/opensearch/install/index/)
+* Latest version of [OpenSearch](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/index/)
* [Required datasets loaded](#set-up-test-datasets)
* [DSN configured](#set-up-dsn)
### Set up test datasets
-Loading a dataset requires an [OpenSearch](https://docs-beta.opensearch.org/opensearch/install/index/) service running with [OpenSearch Dashboards](https://docs-beta.opensearch.org/dashboards/index/). If either of these are missing, please refer to the documentation on how to set them up.
+Loading a dataset requires an [OpenSearch](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/index/) service running with [OpenSearch Dashboards](https://opensearch.org/docs/latest/dashboards/). If either of these are missing, please refer to the documentation on how to set them up.
Note, if you wish to work with SSL/TLS, you need to configure OpenSearch and OpenSearch Dashboards to support it. See the [build instructions](./BUILD_INSTRUCTIONS.md) for more info.
diff --git a/docs/test/excel_connection.md b/docs/test/excel_connection.md
index 562ac1c..e80a1cc 100644
--- a/docs/test/excel_connection.md
+++ b/docs/test/excel_connection.md
@@ -2,7 +2,7 @@
## Prerequisites
* [Download and install](../../README.md) OpenSearch SQL ODBC Driver.
-* [Install and configure](https://docs-beta.opensearch.org/opensearch/install/index/) OpenSearch.
+* [Install and configure](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/index/) OpenSearch.
* Open ODBC Data Source Administrator. Click on **System DSN** > **OpenSearch SQL ODBC DSN** > **Configure**.
* Set all connection options & Click on **Test**. Connection test should return `Connection Successful`.
diff --git a/docs/user/configuration_options.md b/docs/user/configuration_options.md
index 0a8201c..d1faba4 100644
--- a/docs/user/configuration_options.md
+++ b/docs/user/configuration_options.md
@@ -12,13 +12,14 @@
#### Authentication Options
-| Option | Description | Type | Acceptable Values | Default |
-|--------------------|-------------------------------------------------------------------------------|--------|------------------------------------------------------|---------|
-| `Auth` | Authentication mechanism to use. | string | `BASIC` (basic HTTP), `AWS_SIGV4` (AWS auth), `NONE` | `NONE` |
-| `User` / `UID` | [`Auth=BASIC`] Username for the connection. | string | | |
-| `Password` / `PWD` | [`Auth=BASIC`] Password for the connection. | string | | |
-| `Region` | [`Auth=AWS_SIGV4`] Region used for signing requests | string | AWS region (eg. `us-west-1`) | |
-| `TunnelHost` | [`Auth=AWS_SIGV4`] VPC endpoint hostname if connected through tunnel or proxy | string | | |
+| Option | Description | Type | Acceptable Values | Default |
+|------------------------|-------------------------------------------------------------------------------|--------|----------------------------------------------------------------|---------|
+| `Auth` | Authentication mechanism to use. | string | `BASIC` (basic HTTP), `AWS_SIGV4` (AWS auth), `OAUTH2`, `NONE` | `NONE` |
+| `User` / `UID` | [`Auth=BASIC`] Username for the connection. | string | | |
+| `Password` / `PWD` | [`Auth=BASIC`] Password for the connection. | string | | |
+| `access_token` / `JWT` | [`Auth=OAUTH2`] Password for the connection. | string | | |
+| `Region` | [`Auth=AWS_SIGV4`] Region used for signing requests | string | AWS region (eg. `us-west-1`) | |
+| `TunnelHost` | [`Auth=AWS_SIGV4`] VPC endpoint hostname if connected through tunnel or proxy | string | | |
**NOTE:** To use `AWS_SIGV4` authentication you need to create `~/.aws/credentials` and add `opensearchodbc` profile with aws access key id, secret key and session token (if used).
@@ -33,9 +34,9 @@
#### Logging Options
-| Option | Description | Type | Acceptable Values | Default |
-|-------------|-----------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------|
-| `LogLevel` | Severity level for driver logs. | integer | `0` -- `OFF` `1` -- `FATAL` `2` -- `ERROR` `3` -- `WARNING` `4` -- `INFO` `5` -- `DEBUG` `6` -- `TRACE` `7` -- `ALL` | `3` |
+| Option | Description | Type | Acceptable Values | Default |
+|-------------|-----------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|
+| `LogLevel` | Severity level for driver logs. | integer | `0` -- `OFF` `1` -- `FATAL` `2` -- `ERROR` `3` -- `WARNING` `4` -- `INFO` `5` -- `DEBUG` `6` -- `TRACE` `7` -- `ALL` | `3` |
| `LogOutput` | Location for storing driver logs. | string | | WIN: `C:\` MAC: `/Library/ODBC/opensearch-sql-odbc` |
diff --git a/docs/user/img/odbc_setup.png b/docs/user/img/odbc_setup.png
new file mode 100644
index 0000000..04d72ec
Binary files /dev/null and b/docs/user/img/odbc_setup.png differ
diff --git a/docs/user/img/odbc_test.png b/docs/user/img/odbc_test.png
new file mode 100644
index 0000000..89e7e6b
Binary files /dev/null and b/docs/user/img/odbc_test.png differ
diff --git a/docs/user/microsoft_excel_support.md b/docs/user/microsoft_excel_support.md
index 1d53c11..9b5a732 100644
--- a/docs/user/microsoft_excel_support.md
+++ b/docs/user/microsoft_excel_support.md
@@ -3,8 +3,8 @@
## Prerequisites
* Microsoft Excel 2016 and higher
-* [OpenSearch](https://docs-beta.opensearch.org/opensearch/install/index/)
-* [OpenSearch SQL ODBC driver](https://docs-beta.opensearch.org/search-plugins/sql/odbc/)
+* [OpenSearch](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/index/)
+* [OpenSearch SQL ODBC driver](https://opensearch.org/docs/latest/search-plugins/sql/sql/odbc/)
* A preconfigured [User or System DSN](../../README.md)
## Test Successful Connection
@@ -70,7 +70,7 @@ Alternately, **Data** > **Refresh** option can also be used to refresh the data.
## Troubleshooting
-* If the table has large number of datarows, increase [the keepalive](https://github.com/opensearch-project/sql/blob/main/docs/dev/Pagination.md#opendistrosqlcursorkeep_alive) value accordlingly.
+* If the table has large number of datarows, increase [the keepalive](https://github.com/opensearch-project/sql-odbc/blob/cb5f4bd7ba4a15a2614ecbd3b53ddfcd9b4320fc/docs/dev/Pagination.md) value accordlingly.
* If the table has nested or object type column, you might get an error as below.
diff --git a/docs/user/microsoft_excel_support_mac.md b/docs/user/microsoft_excel_support_mac.md
index 7d0e852..6e5d463 100644
--- a/docs/user/microsoft_excel_support_mac.md
+++ b/docs/user/microsoft_excel_support_mac.md
@@ -3,8 +3,8 @@
## Prerequisites
* Microsoft Excel 2016 and higher
-* [OpenSearch](https://docs-beta.opensearch.org/opensearch/install/index/)
-* [OpenSearch SQL ODBC driver](https://docs-beta.opensearch.org/search-plugins/sql/odbc/)
+* [OpenSearch](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/index/)
+* [OpenSearch SQL ODBC driver](https://opensearch.org/docs/latest/search-plugins/sql/sql/odbc/)
* A preconfigured [User or System DSN](mac_configure_dsn.md)
## Test Successful Connection
@@ -84,7 +84,7 @@ Alternately, **Data** > **Refresh** option can also be used to refresh the data.
## Troubleshooting
-* If the table has large number of datarows, increase [the keepalive](https://github.com/opensearch-project/sql/blob/main/docs/dev/Pagination.md#opendistrosqlcursorkeep_alive) value accordlingly.
+* If the table has large number of datarows, increase [the keepalive](https://github.com/opensearch-project/sql-odbc/blob/cb5f4bd7ba4a15a2614ecbd3b53ddfcd9b4320fc/docs/dev/Pagination.md) value accordlingly.
* You might need to remove `;` from SQL statement to load data preview.
diff --git a/docs/user/windows_configure_dsn.md b/docs/user/windows_configure_dsn.md
index e356cf3..81ddf0c 100644
--- a/docs/user/windows_configure_dsn.md
+++ b/docs/user/windows_configure_dsn.md
@@ -60,4 +60,4 @@ For example, if you can connect to server using following curl command
In case if you use a tunnel or a proxy to connect to an OpenSearch cluster located into a VPC, `Host` doesn't represent a real OpenSearch hostname. To properly sign requests with AWS signature, real OpenSearch cluster hostname is required. It could be set in `Tunnel Host` field.
-
+
\ No newline at end of file
diff --git a/docs/user/windows_test_oauth.md b/docs/user/windows_test_oauth.md
new file mode 100644
index 0000000..fa2770b
--- /dev/null
+++ b/docs/user/windows_test_oauth.md
@@ -0,0 +1,22 @@
+# Test the Oauth2 connection on Windows
+
+## Test Default Connection
+
+1. Open ODBC Data Source Administrator.
+
+
+
+**NOTE**: Use 32 bit application to configure 32 bit driver and 64 bit app for 64 bit driver.
+
+2. Select the OpenSearch driver and click on the **Configure...** tab.
+
+
+
+3. Change values of configuration options accordingly, then select `OAUTH2` and insert the token.
+
+
+
+4. Click on `Test` to verify connectivity. You will get a message as `Connection successful`.
+
+
+
diff --git a/jenkins/release.jenkinsFile b/jenkins/release.jenkinsFile
index b8d8687..cd5a5a5 100644
--- a/jenkins/release.jenkinsFile
+++ b/jenkins/release.jenkinsFile
@@ -4,20 +4,21 @@ lib = library(identifier: 'jenkins@4.4.0', retriever: modernSCM([
]))
standardReleasePipelineWithGenericTrigger(
+ overrideDockerImage: 'opensearchstaging/ci-runner:release-centos7-clients-v4',
tokenIdCredential: 'jenkins-sql-odbc-generic-webhook-token',
causeString: 'A tag was cut on opensearch-project/sql-odbc repository causing this workflow to run',
downloadReleaseAsset: true,
publishRelease: true) {
publishToArtifactsProdBucket(
assumedRoleName: 'sql-odbc-upload-role',
- source: "windows32-installer/OpenSearch-SQL-ODBC-Driver-32-bit-${tag}-Windows.msi",
+ source: "${WORKSPACE}/windows32-installer/OpenSearch-SQL-ODBC-Driver-32-bit-${tag}-Windows.msi",
destination: "opensearch-clients/odbc/opensearch-sql-odbc-driver-32-bit-${tag}-Windows.msi",
signingPlatform: 'windows',
sigOverwrite: true
)
publishToArtifactsProdBucket(
assumedRoleName: 'sql-odbc-upload-role',
- source: "windows64-installer/OpenSearch-SQL-ODBC-Driver-64-bit-${tag}-Windows.msi",
+ source: "${WORKSPACE}/windows64-installer/OpenSearch-SQL-ODBC-Driver-64-bit-${tag}-Windows.msi",
destination: "opensearch-clients/odbc/opensearch-sql-odbc-driver-64-bit-${tag}-Windows.msi",
signingPlatform: 'windows',
sigOverwrite: true
diff --git a/odbc_result.txt b/odbc_result.txt
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/build_windows.ps1 b/scripts/build_windows.ps1
index f2090df..e8e7e69 100644
--- a/scripts/build_windows.ps1
+++ b/scripts/build_windows.ps1
@@ -36,4 +36,4 @@ if ($BITNESS -eq "32") {
$BITNESS = $null
$WIN_ARCH = "x86"
}
-Copy-Item .\libraries\VisualLeakDetector\bin$BITNESS\vld_$WIN_ARCH.dll $DRIVER_BIN_DIR
+Copy-Item .\libraries\VisualLeakDetector\bin$BITNESS\vld_$WIN_ARCH.dll $DRIVER_BIN_DIR
\ No newline at end of file
diff --git a/src/IntegrationTests/CMakeLists.txt b/src/IntegrationTests/CMakeLists.txt
index bc2dc38..96dd921 100644
--- a/src/IntegrationTests/CMakeLists.txt
+++ b/src/IntegrationTests/CMakeLists.txt
@@ -9,6 +9,7 @@ set(INFO_ITEST "${CMAKE_CURRENT_SOURCE_DIR}/ITODBCInfo")
set(RESULTS_ITEST "${CMAKE_CURRENT_SOURCE_DIR}/ITODBCResults")
set(TABLEAU_QUERIES_ITEST "${CMAKE_CURRENT_SOURCE_DIR}/ITODBCTableauQueries")
set(AWS_AUTH_ITEST "${CMAKE_CURRENT_SOURCE_DIR}/ITODBCAwsAuth")
+set(OAUTH_ITEST "${CMAKE_CURRENT_SOURCE_DIR}/ITODBCOauth")
set(PAGINATION_ITEST "${CMAKE_CURRENT_SOURCE_DIR}/ITODBCPagination")
# Projects to build
@@ -21,4 +22,5 @@ add_subdirectory(${INFO_ITEST})
add_subdirectory(${RESULTS_ITEST})
add_subdirectory(${TABLEAU_QUERIES_ITEST})
add_subdirectory(${AWS_AUTH_ITEST})
+add_subdirectory(${OAUTH_ITEST})
add_subdirectory(${PAGINATION_ITEST})
diff --git a/src/IntegrationTests/ITODBCHelper/it_odbc_helper.h b/src/IntegrationTests/ITODBCHelper/it_odbc_helper.h
index 8a911d5..578a4c3 100644
--- a/src/IntegrationTests/ITODBCHelper/it_odbc_helper.h
+++ b/src/IntegrationTests/ITODBCHelper/it_odbc_helper.h
@@ -26,6 +26,7 @@ std::vector< std::pair< std::wstring, std::wstring > > conn_str_pair = {
{L"port", L"9200"},
{L"user", L"admin"},
{L"password", L"admin"},
+ {L"access_token", L"token"},
{L"auth", L"BASIC"},
{L"useSSL", (use_ssl ? L"1" : L"0")},
{L"hostnameVerification", L"0"},
diff --git a/src/IntegrationTests/ITODBCOauth/CMakeLists.txt b/src/IntegrationTests/ITODBCOauth/CMakeLists.txt
new file mode 100644
index 0000000..75ce555
--- /dev/null
+++ b/src/IntegrationTests/ITODBCOauth/CMakeLists.txt
@@ -0,0 +1,20 @@
+project(itodbc_Oauth)
+
+# Source, headers, and include dirs
+set(SOURCE_FILES test_odbc_Oauth.cpp)
+include_directories( ${UT_HELPER}
+ ${OPENSEARCHODBC_SRC}
+ ${RAPIDJSON_SRC}
+ ${RABBIT_SRC}
+ ${LIBCURL_SRC}
+ ${VLD_SRC})
+
+# Generate executable
+add_executable(itodbc_Oauth ${SOURCE_FILES})
+
+# Find packages from vcpkg
+find_package(GTest CONFIG REQUIRED)
+
+# Library dependencies
+target_link_libraries(itodbc_Oauth ut_helper GTest::gtest_main aws-cpp-sdk-core ${VLD})
+target_compile_definitions(itodbc_Oauth PUBLIC _UNICODE UNICODE)
diff --git a/src/IntegrationTests/ITODBCOauth/packages.config b/src/IntegrationTests/ITODBCOauth/packages.config
new file mode 100644
index 0000000..3c6fe17
--- /dev/null
+++ b/src/IntegrationTests/ITODBCOauth/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/IntegrationTests/ITODBCOauth/pch.cpp b/src/IntegrationTests/ITODBCOauth/pch.cpp
new file mode 100644
index 0000000..97b544e
--- /dev/null
+++ b/src/IntegrationTests/ITODBCOauth/pch.cpp
@@ -0,0 +1,6 @@
+//
+// pch.cpp
+// Include the standard header and generate the precompiled header.
+//
+
+#include "pch.h"
diff --git a/src/IntegrationTests/ITODBCOauth/pch.h b/src/IntegrationTests/ITODBCOauth/pch.h
new file mode 100644
index 0000000..29c81ff
--- /dev/null
+++ b/src/IntegrationTests/ITODBCOauth/pch.h
@@ -0,0 +1,8 @@
+//
+// pch.h
+// Header for standard system include files.
+//
+
+#pragma once
+
+#include "gtest/gtest.h"
diff --git a/src/IntegrationTests/ITODBCOauth/test_odbc_Oauth.cpp b/src/IntegrationTests/ITODBCOauth/test_odbc_Oauth.cpp
new file mode 100644
index 0000000..d2339a6
--- /dev/null
+++ b/src/IntegrationTests/ITODBCOauth/test_odbc_Oauth.cpp
@@ -0,0 +1,142 @@
+// clang-format off
+#include "pch.h"
+#ifdef __APPLE__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#endif // __APPLE__
+#include
+#include
+#include
+#include
+#ifdef __APPLE__
+#pragma clang diagnostic pop
+#endif // __APPLE__
+#include "unit_test_helper.h"
+
+using namespace Aws;
+using namespace Aws::Client;
+using namespace Aws::Utils::Json;
+using namespace Aws::Http;
+using namespace std;
+
+class OAuth2TokenProvider {
+public:
+
+ static std::string GetToken() {
+ // Set up HTTP request with client credentials flow
+ Aws::String endpoint = "http://localhost:1852/realms/local-development/protocol/openid-connect/token";
+ Aws::String username = "opensearch-admin";
+ Aws::String password = "opensearch-admin-secret";
+ Aws::String scope = "opensearch";
+ Aws::String requestBody = "grant_type=client_credentials&client_id=" + username + "&client_secret=" + password + "&scope=" + scope;
+ auto request = CreateHttpRequest(endpoint, HttpMethod::HTTP_POST, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
+
+ // Set headers
+ request->SetHeaderValue(Aws::Http::CONTENT_TYPE_HEADER, "application/x-www-form-urlencoded");
+
+ // Set body
+ std::shared_ptr bodyStream = Aws::MakeShared("");
+ *bodyStream << requestBody;
+ request->AddContentBody(bodyStream);
+ request->SetContentLength(std::to_string(requestBody.length()));
+
+ // Create HTTP client
+ auto httpClient = Aws::Http::CreateHttpClient(Aws::Client::ClientConfiguration());
+
+ // Send request
+ auto httpResponse = httpClient->MakeRequest(request);
+
+ // Check if request was successful
+ if (!httpResponse || !httpResponse->GetResponseBody().good()) {
+ throw std::runtime_error("Failed to fetch token");
+ }
+
+ // Read token from response body
+ Aws::StringStream responseBody;
+ responseBody << httpResponse->GetResponseBody().rdbuf();
+ JsonValue jsonValue(responseBody.str());
+
+ if (!jsonValue.WasParseSuccessful()) {
+ throw std::runtime_error("Failed to parse JSON response");
+ }
+
+ JsonView jsonView = jsonValue.View();
+
+ if (!jsonView.ValueExists("access_token")) {
+ throw std::runtime_error("Access token not found in JSON response");
+ }
+
+ return jsonView.GetString("access_token").c_str();
+ }
+};
+
+TEST(OAuth2_Auth, TokenAuthentication) {
+ Aws::SDKOptions options;
+ Aws::String host = "http://localhost:9200";
+
+ EXPECT_NO_THROW(Aws::InitAPI(options));
+
+ auto request = CreateHttpRequest(host, HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
+
+ // Obtain the OAuth2 token
+ std::string token = OAuth2TokenProvider::GetToken();
+
+ ASSERT_FALSE(token.empty());
+
+ // Set the Authorization header with the Bearer token
+ request->SetAuthorization("Bearer " + token);
+
+ auto http_client = CreateHttpClient(Aws::Client::ClientConfiguration());
+
+ auto response = http_client->MakeRequest(request);
+ ASSERT_NE(response, nullptr);
+ EXPECT_EQ(Aws::Http::HttpResponseCode::OK, response->GetResponseCode());
+
+ EXPECT_NO_THROW(Aws::ShutdownAPI(options));
+}
+
+TEST(OAuth2_Auth, InvalidToken) {
+ Aws::SDKOptions options;
+ Aws::String host = "http://localhost:9200";
+
+ EXPECT_NO_THROW(Aws::InitAPI(options));
+
+ auto request = CreateHttpRequest(host, HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
+
+ // Obtain the OAuth2 token
+ std::string token = OAuth2TokenProvider::GetToken();
+
+ ASSERT_FALSE(token.empty());
+
+ // Corrupt the token
+ token.pop_back();
+
+ // Set the Authorization header with the Bearer token
+ request->SetAuthorization("Bearer " + token);
+
+ auto http_client = CreateHttpClient(Aws::Client::ClientConfiguration());
+
+ auto response = http_client->MakeRequest(request);
+ ASSERT_NE(response, nullptr);
+ EXPECT_EQ(Aws::Http::HttpResponseCode::FORBIDDEN, response->GetResponseCode());
+
+ EXPECT_NO_THROW(Aws::ShutdownAPI(options));
+}
+
+TEST(SettingSDKOptions, TurnLoggingOn) {
+ Aws::SDKOptions options;
+ options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Info;
+ EXPECT_NO_THROW(Aws::InitAPI(options));
+ EXPECT_NO_THROW(Aws::ShutdownAPI(options));
+}
+
+int main(int argc, char** argv) {
+ testing::internal::CaptureStdout();
+ ::testing::InitGoogleTest(&argc, argv);
+ int failures = RUN_ALL_TESTS();
+ std::string output = testing::internal::GetCapturedStdout();
+ std::cout << output << std::endl;
+ std::cout << (failures ? "Not all tests passed." : "All tests passed") << std::endl;
+ WriteFileIfSpecified(argv, argv + argc, "-fout", output);
+ return failures;
+}
\ No newline at end of file
diff --git a/src/PerformanceTests/PTODBCInfo/performance_odbc_info.cpp b/src/PerformanceTests/PTODBCInfo/performance_odbc_info.cpp
index f660d5d..0c8950b 100644
--- a/src/PerformanceTests/PTODBCInfo/performance_odbc_info.cpp
+++ b/src/PerformanceTests/PTODBCInfo/performance_odbc_info.cpp
@@ -48,6 +48,8 @@ runtime_options rt_opts = []() {
temp_opts.auth.username = wstring_to_string(it.second);
else if (tmp == L"password")
temp_opts.auth.password = wstring_to_string(it.second);
+ else if (tmp == L"access_token")
+ temp_opts.auth.access_token = wstring_to_string(it.second);
else if (tmp == L"region")
temp_opts.auth.region = wstring_to_string(it.second);
else if (tmp == L"tunnelhost")
diff --git a/src/TestRunner/test_runner.py b/src/TestRunner/test_runner.py
index ad2c4d9..9921bc4 100644
--- a/src/TestRunner/test_runner.py
+++ b/src/TestRunner/test_runner.py
@@ -18,7 +18,7 @@
".py", ".c", ".cmake", ".log",
".pdb", ".dll", ".sln", ".vcxproj", ".user",
".tlog", ".lastbuildstate", ".filters",
- ".obj", ".exp", ".lib", ".h", ".cpp", ".ilk")
+ ".obj", ".exp", ".lib", ".h", ".cpp", ".ilk", ".recipe",".pyc")
total_failures = 0
SYNC_START = "%%__PARSE__SYNC__START__%%"
SYNC_SEP = "%%__SEP__%%"
diff --git a/src/UnitTests/UTConn/test_conn.cpp b/src/UnitTests/UTConn/test_conn.cpp
index 0e56831..b2c385a 100644
--- a/src/UnitTests/UTConn/test_conn.cpp
+++ b/src/UnitTests/UTConn/test_conn.cpp
@@ -18,15 +18,19 @@ const std::string invalid_port = "920";
const std::string invalid_user = "amin";
const std::string invalid_pw = "amin";
const std::string invalid_region = "bad-region";
+const std::string no_token = "";
runtime_options valid_opt_val = {{valid_host, valid_port, "1", "0"},
- {"BASIC", valid_user, valid_pw, valid_region, valid_tunnel_host},
+ {"BASIC", valid_user, valid_pw, no_token, valid_region, valid_tunnel_host},
{use_ssl, false, "", "", "", ""}};
runtime_options invalid_opt_val = {
{invalid_host, invalid_port, "1", "0"},
- {"BASIC", invalid_user, invalid_pw, valid_region, valid_tunnel_host},
+ {"BASIC", invalid_user, invalid_pw, no_token, valid_region, valid_tunnel_host},
{use_ssl, false, "", "", "", ""}};
runtime_options missing_opt_val = {{"", "", "1", "0"},
- {"BASIC", "", invalid_pw, valid_region, valid_tunnel_host},
+ {"BASIC", "", invalid_pw, no_token, valid_region, valid_tunnel_host},
+ {use_ssl, false, "", "", "", ""}};
+runtime_options missing_opt_val_token = {{"", "", "1", "0"},
+ {"OAUTH2", "","", no_token, "", ""},
{use_ssl, false, "", "", "", ""}};
TEST(TestOpenSearchConnConnectionOptions, ValidParameters) {
@@ -81,6 +85,13 @@ TEST_F(TestOpenSearchConnConnectDBStart, MissingParameters) {
EXPECT_EQ(CONNECTION_BAD, m_conn.GetConnectionStatus());
}
+TEST_F(TestOpenSearchConnConnectDBStart, MissingParametersOauth) {
+ ASSERT_NE(true, m_conn.ConnectionOptions(missing_opt_val_token, 1, 1,
+ missing_option_count));
+ EXPECT_EQ(false, m_conn.ConnectDBStart());
+ EXPECT_EQ(CONNECTION_BAD, m_conn.GetConnectionStatus());
+}
+
TEST(TestOpenSearchConnDropDBConnection, InvalidParameters) {
OpenSearchCommunication conn;
ASSERT_EQ(CONNECTION_BAD, conn.GetConnectionStatus());
@@ -103,6 +114,17 @@ TEST(TestOpenSearchConnDropDBConnection, MissingParameters) {
EXPECT_EQ(CONNECTION_BAD, conn.GetConnectionStatus());
}
+TEST(TestOpenSearchConnDropDBConnection, MissingParametersOauth) {
+ OpenSearchCommunication conn;
+ ASSERT_EQ(CONNECTION_BAD, conn.GetConnectionStatus());
+ ASSERT_NE(true, conn.ConnectionOptions(missing_opt_val_token, 1, 1,
+ missing_option_count));
+ ASSERT_NE(true, conn.ConnectDBStart());
+ ASSERT_EQ(CONNECTION_BAD, conn.GetConnectionStatus());
+ conn.DropDBConnection();
+ EXPECT_EQ(CONNECTION_BAD, conn.GetConnectionStatus());
+}
+
TEST(TestOpenSearchConnDropDBConnection, ValidParameters) {
OpenSearchCommunication conn;
ASSERT_NE(false,
diff --git a/src/opensearchenlist/msdtc_enlist.cpp b/src/opensearchenlist/msdtc_enlist.cpp
index f427d83..cad9ab3 100644
--- a/src/opensearchenlist/msdtc_enlist.cpp
+++ b/src/opensearchenlist/msdtc_enlist.cpp
@@ -40,6 +40,11 @@
#define CSTR static const char *const
#endif /* CSTR */
+/* Makes CPPcheck happy */
+#ifndef KEYWORD_DTC_CHECK
+#define KEYWORD_DTC_CHECK
+#endif /*KEYWORD_DTC_CHECK*/
+
EXTERN_C {
HINSTANCE s_hModule; /* Saved module handle. */
}
diff --git a/src/sqlodbc/dlg_specific.c b/src/sqlodbc/dlg_specific.c
index 3d2f8f8..056cf29 100644
--- a/src/sqlodbc/dlg_specific.c
+++ b/src/sqlodbc/dlg_specific.c
@@ -26,34 +26,60 @@ static opensearchNAME decode_or_remove_braces(const char *in);
void makeConnectString(char *connect_string, const ConnInfo *ci, UWORD len) {
UNUSED(len);
char got_dsn = (ci->dsn[0] != '\0');
- char encoded_item[LARGE_REGISTRY_LEN];
+ char encoded_item[XLARGE_REGISTRY_LEN]; // This value can manage a JWT token size
char *connsetStr = NULL;
char *esoptStr = NULL;
+
#ifdef _HANDLE_ENLIST_IN_DTC_
char xaOptStr[16];
#endif
ssize_t hlen, nlen, olen;
-
- encode(ci->password, encoded_item, sizeof(encoded_item));
- /* fundamental info */
- nlen = MAX_CONNECT_STRING;
- olen = snprintf(
- connect_string, nlen,
- "%s=%s;" INI_SERVER
- "=%s;"
- "database=OpenSearch;" INI_PORT "=%s;" INI_USERNAME_ABBR
- "=%s;" INI_PASSWORD_ABBR "=%s;" INI_AUTH_MODE "=%s;" INI_REGION
- "=%s;" INI_TUNNEL_HOST "=%s;" INI_SSL_USE "=%d;" INI_SSL_HOST_VERIFY
- "=%d;" INI_LOG_LEVEL "=%d;" INI_LOG_OUTPUT "=%s;" INI_TIMEOUT "=%s;"
- INI_FETCH_SIZE "=%s;",
- got_dsn ? "DSN" : "DRIVER", got_dsn ? ci->dsn : ci->drivername,
- ci->server, ci->port, ci->username, encoded_item, ci->authtype,
- ci->region, ci->tunnel_host, (int)ci->use_ssl, (int)ci->verify_server,
- (int)ci->drivers.loglevel, ci->drivers.output_dir,
- ci->response_timeout, ci->fetch_size);
- if (olen < 0 || olen >= nlen) {
- connect_string[0] = '\0';
- return;
+ // Build the connect string with the JWT token and so encoded_item as ci->access_token
+ if (ci->username[0] == '\0') {
+ encode(ci->access_token, encoded_item, sizeof(encoded_item));
+ /* fundamental info */
+ nlen = MAX_CONNECT_STRING;
+ olen = snprintf(
+ connect_string, nlen,
+ "%s=%s;" INI_SERVER
+ "=%s;"
+ "database=OpenSearch;" INI_PORT "=%s;" INI_USERNAME_ABBR
+ "=%s;" INI_TOKEN_ABBR "=%s;" INI_AUTH_MODE "=%s;" INI_REGION
+ "=%s;" INI_TUNNEL_HOST "=%s;" INI_SSL_USE "=%d;" INI_SSL_HOST_VERIFY
+ "=%d;" INI_LOG_LEVEL "=%d;" INI_LOG_OUTPUT "=%s;" INI_TIMEOUT "=%s;"
+ INI_FETCH_SIZE "=%s;",
+ got_dsn ? "DSN" : "DRIVER", got_dsn ? ci->dsn : ci->drivername,
+ ci->server, ci->port, ci->username, encoded_item, ci->authtype,
+ ci->region, ci->tunnel_host, (int)ci->use_ssl, (int)ci->verify_server,
+ (int)ci->drivers.loglevel, ci->drivers.output_dir,
+ ci->response_timeout, ci->fetch_size);
+ if (olen < 0 || olen >= nlen) {
+ connect_string[0] = '\0';
+ return;
+ }
+ // Build the connect string with password as encoded_item as ci->password
+ } else {
+ encode(ci->password, encoded_item, sizeof(encoded_item));
+ /* fundamental info */
+ nlen = MAX_CONNECT_STRING;
+ olen = snprintf(
+ connect_string, nlen,
+ "%s=%s;" INI_SERVER
+ "=%s;"
+ "database=OpenSearch;" INI_PORT "=%s;" INI_USERNAME_ABBR
+ "=%s;" INI_PASSWORD_ABBR "=%s;" INI_AUTH_MODE "=%s;" INI_REGION
+ "=%s;" INI_TUNNEL_HOST "=%s;" INI_SSL_USE "=%d;" INI_SSL_HOST_VERIFY
+ "=%d;" INI_LOG_LEVEL "=%d;" INI_LOG_OUTPUT "=%s;" INI_TIMEOUT "=%s;"
+ INI_FETCH_SIZE "=%s;",
+ got_dsn ? "DSN" : "DRIVER", got_dsn ? ci->dsn : ci->drivername,
+ ci->server, ci->port, ci->username, encoded_item, ci->authtype,
+ ci->region, ci->tunnel_host, (int)ci->use_ssl, (int)ci->verify_server,
+ (int)ci->drivers.loglevel, ci->drivers.output_dir,
+ ci->response_timeout, ci->fetch_size);
+ if (olen < 0 || olen >= nlen) {
+ connect_string[0] = '\0';
+ return;
+ }
}
/* extra info */
@@ -106,7 +132,10 @@ BOOL copyConnAttributes(ConnInfo *ci, const char *attribute,
MYLOG(OPENSEARCH_DEBUG, "key='%s' value='xxxxxxxx'\n", attribute);
printed = TRUE;
#endif
- } else if (stricmp(attribute, INI_AUTH_MODE) == 0)
+ } else if ((stricmp(attribute, INI_TOKEN) == 0)
+ || (stricmp(attribute, INI_TOKEN_ABBR) == 0))
+ ci->access_token = decode_or_remove_braces(value);
+ else if (stricmp(attribute, INI_AUTH_MODE) == 0)
STRCPY_FIXED(ci->authtype, value);
else if (stricmp(attribute, INI_REGION) == 0)
STRCPY_FIXED(ci->region, value);
@@ -147,6 +176,9 @@ static void getCiDefaults(ConnInfo *ci) {
if (ci->password.name != NULL)
free(ci->password.name);
ci->password.name = NULL;
+ if (ci->access_token.name != NULL)
+ free(ci->access_token.name);
+ ci->access_token.name = NULL;
strncpy(ci->username, DEFAULT_USERNAME, MEDIUM_REGISTRY_LEN);
strncpy(ci->region, DEFAULT_REGION, MEDIUM_REGISTRY_LEN);
strncpy(ci->tunnel_host, DEFAULT_TUNNEL_HOST, MEDIUM_REGISTRY_LEN);
@@ -246,6 +278,14 @@ void getDSNinfo(ConnInfo *ci, const char *configDrvrname) {
sizeof(temp), ODBC_INI)
> 0)
ci->password = decode(temp);
+ if (SQLGetPrivateProfileString(DSN, INI_TOKEN, NULL_STRING, temp,
+ sizeof(temp), ODBC_INI)
+ > 0)
+ ci->access_token = decode(temp);
+ if (SQLGetPrivateProfileString(DSN, INI_TOKEN_ABBR, NULL_STRING, temp,
+ sizeof(temp), ODBC_INI)
+ > 0)
+ ci->access_token = decode(temp);
if (SQLGetPrivateProfileString(DSN, INI_AUTH_MODE, NULL_STRING, temp,
sizeof(temp), ODBC_INI)
> 0)
@@ -310,6 +350,8 @@ void writeDSNinfo(const ConnInfo *ci) {
SQLWritePrivateProfileString(DSN, INI_USERNAME, ci->username, ODBC_INI);
encode(ci->password, encoded_item, sizeof(encoded_item));
SQLWritePrivateProfileString(DSN, INI_PASSWORD, encoded_item, ODBC_INI);
+ encode(ci->access_token, encoded_item, sizeof(encoded_item));
+ SQLWritePrivateProfileString(DSN, INI_TOKEN, encoded_item, ODBC_INI);
SQLWritePrivateProfileString(DSN, INI_AUTH_MODE, ci->authtype, ODBC_INI);
SQLWritePrivateProfileString(DSN, INI_REGION, ci->region, ODBC_INI);
SQLWritePrivateProfileString(DSN, INI_TUNNEL_HOST, ci->tunnel_host, ODBC_INI);
@@ -438,6 +480,7 @@ static opensearchNAME decode_or_remove_braces(const char *in) {
void CC_conninfo_release(ConnInfo *conninfo) {
NULL_THE_NAME(conninfo->password);
+ NULL_THE_NAME(conninfo->access_token);
finalize_globals(&conninfo->drivers);
}
@@ -461,6 +504,9 @@ void CC_conninfo_init(ConnInfo *conninfo, UInt4 option) {
if (conninfo->password.name != NULL)
free(conninfo->password.name);
conninfo->password.name = NULL;
+ if (conninfo->access_token.name != NULL)
+ free(conninfo->access_token.name);
+ conninfo->access_token.name = NULL;
strncpy(conninfo->username, DEFAULT_USERNAME, MEDIUM_REGISTRY_LEN);
strncpy(conninfo->region, DEFAULT_REGION, MEDIUM_REGISTRY_LEN);
strncpy(conninfo->tunnel_host, DEFAULT_TUNNEL_HOST, MEDIUM_REGISTRY_LEN);
@@ -506,6 +552,7 @@ void CC_copy_conninfo(ConnInfo *ci, const ConnInfo *sci) {
CORR_STRCPY(region);
CORR_STRCPY(tunnel_host);
NAME_TO_NAME(ci->password, sci->password);
+ NAME_TO_NAME(ci->access_token, sci->access_token);
CORR_VALCPY(use_ssl);
CORR_VALCPY(verify_server);
CORR_STRCPY(port);
diff --git a/src/sqlodbc/dlg_specific.h b/src/sqlodbc/dlg_specific.h
index c93fcad..4aec28b 100644
--- a/src/sqlodbc/dlg_specific.h
+++ b/src/sqlodbc/dlg_specific.h
@@ -43,6 +43,10 @@ extern "C" {
#define INI_PASSWORD "password"
#define INI_PASSWORD_ABBR "PWD"
#define INI_AUTH_MODE "auth"
+
+#define INI_TOKEN "access_token"
+#define INI_TOKEN_ABBR "JWT"
+
#define INI_REGION "region"
#define INI_TUNNEL_HOST "TunnelHost"
#define INI_SSL_USE "useSSL"
@@ -54,8 +58,8 @@ extern "C" {
#define DEFAULT_FETCH_SIZE -1
#define DEFAULT_FETCH_SIZE_STR "-1"
-#define DEFAULT_RESPONSE_TIMEOUT 10 // Seconds
-#define DEFAULT_RESPONSE_TIMEOUT_STR "10"
+#define DEFAULT_RESPONSE_TIMEOUT 20 // Seconds
+#define DEFAULT_RESPONSE_TIMEOUT_STR "20"
#define DEFAULT_AUTHTYPE "NONE"
#define DEFAULT_HOST ""
#define DEFAULT_PORT ""
@@ -68,7 +72,8 @@ extern "C" {
#define AUTHTYPE_NONE "NONE"
#define AUTHTYPE_BASIC "BASIC"
-#define AUTHTYPE_IAM "AWS_SIGV4"
+#define AUTHTYPE_IAM "AWS_SIGV4"
+#define AUTHTYPE_OAUTH2 "OAUTH2"
#ifdef _HANDLE_ENLIST_IN_DTC_
#define INI_XAOPT "XaOpt"
diff --git a/src/sqlodbc/dlg_wingui.c b/src/sqlodbc/dlg_wingui.c
index 06bed13..f29ffb7 100644
--- a/src/sqlodbc/dlg_wingui.c
+++ b/src/sqlodbc/dlg_wingui.c
@@ -13,7 +13,7 @@
#define HTTP_PREFIX "http://"
#define HTTPS_PREFIX "https://"
-#define AUTHMODE_CNT 3
+#define AUTHMODE_CNT 4
#define LOGLEVEL_CNT 8
extern HINSTANCE s_hModule;
@@ -30,7 +30,8 @@ int loglevels[LOGLEVEL_CNT] = {
static const struct authmode authmodes[AUTHMODE_CNT] = {
{IDS_AUTHTYPE_NONE, AUTHTYPE_IAM},
{IDS_AUTHTYPE_BASIC, AUTHTYPE_BASIC},
- {IDS_AUTHTYPE_IAM, AUTHTYPE_NONE}};
+ {IDS_AUTHTYPE_IAM, AUTHTYPE_NONE},
+ {IDS_AUTHTYPE_OAUTH2, AUTHTYPE_OAUTH2}};
const struct authmode *GetCurrentAuthMode(HWND hdlg) {
unsigned int ams_cnt = 0;
@@ -62,16 +63,26 @@ void SetAuthenticationVisibility(HWND hdlg, const struct authmode *am) {
if (strcmp(am->authtype_str, AUTHTYPE_BASIC) == 0) {
EnableWindow(GetDlgItem(hdlg, IDC_USER), TRUE);
EnableWindow(GetDlgItem(hdlg, IDC_PASSWORD), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDC_TOKEN), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_REGION), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_TUNNEL_HOST), FALSE);
} else if (strcmp(am->authtype_str, AUTHTYPE_IAM) == 0) {
EnableWindow(GetDlgItem(hdlg, IDC_USER), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_PASSWORD), FALSE);
+ EnableWindow(GetDlgItem(hdlg, IDC_TOKEN), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_REGION), TRUE);
EnableWindow(GetDlgItem(hdlg, IDC_TUNNEL_HOST), TRUE);
- } else {
+ } else if (strcmp(am->authtype_str, AUTHTYPE_OAUTH2) == 0) {
EnableWindow(GetDlgItem(hdlg, IDC_USER), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_PASSWORD), FALSE);
+ EnableWindow(GetDlgItem(hdlg, IDC_TOKEN), TRUE);
+ EnableWindow(GetDlgItem(hdlg, IDC_REGION), FALSE);
+ EnableWindow(GetDlgItem(hdlg, IDC_TUNNEL_HOST), FALSE);
+ }
+ else {
+ EnableWindow(GetDlgItem(hdlg, IDC_USER), FALSE);
+ EnableWindow(GetDlgItem(hdlg, IDC_PASSWORD), FALSE);
+ EnableWindow(GetDlgItem(hdlg, IDC_TOKEN), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_REGION), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_TUNNEL_HOST), FALSE);
}
@@ -88,10 +99,10 @@ void SetDlgStuff(HWND hdlg, const ConnInfo *ci) {
int authtype_selection_idx = 0;
unsigned int ams_cnt = 0;
const struct authmode *ams = GetAuthModes(&ams_cnt);
- char buff[MEDIUM_REGISTRY_LEN + 1];
+ char buff[MEDIUM_LARGE_REGISTRY_LEN + 1];
for (unsigned int i = 0; i < ams_cnt; i++) {
LoadString(GetWindowInstance(hdlg), ams[i].authtype_id, buff,
- MEDIUM_REGISTRY_LEN);
+ MEDIUM_LARGE_REGISTRY_LEN);
SendDlgItemMessage(hdlg, IDC_AUTHTYPE, CB_ADDSTRING, 0, (WPARAM)buff);
if (!stricmp(ci->authtype, ams[i].authtype_str)) {
authtype_selection_idx = i;
@@ -101,12 +112,13 @@ void SetDlgStuff(HWND hdlg, const ConnInfo *ci) {
ams[authtype_selection_idx].authtype_id, (WPARAM)0);
SetDlgItemText(hdlg, IDC_USER, ci->username);
SetDlgItemText(hdlg, IDC_PASSWORD, SAFE_NAME(ci->password));
+ SetDlgItemText(hdlg, IDC_TOKEN, SAFE_NAME(ci->access_token));
SetDlgItemText(hdlg, IDC_REGION, ci->region);
SetDlgItemText(hdlg, IDC_TUNNEL_HOST, ci->tunnel_host);
}
static void GetNameField(HWND hdlg, int item, opensearchNAME *name) {
- char medium_buf[MEDIUM_REGISTRY_LEN + 1];
+ char medium_buf[MEDIUM_LARGE_REGISTRY_LEN + 1];
GetDlgItemText(hdlg, item, medium_buf, sizeof(medium_buf));
STR_TO_NAME((*name), medium_buf);
}
@@ -120,6 +132,7 @@ void GetDlgStuff(HWND hdlg, ConnInfo *ci) {
// Authentication
GetDlgItemText(hdlg, IDC_USER, ci->username, sizeof(ci->username));
GetNameField(hdlg, IDC_PASSWORD, &ci->password);
+ GetNameField(hdlg, IDC_TOKEN, &ci->access_token);
GetDlgItemText(hdlg, IDC_REGION, ci->region, sizeof(ci->region));
GetDlgItemText(hdlg, IDC_TUNNEL_HOST, ci->tunnel_host,
sizeof(ci->tunnel_host));
diff --git a/src/sqlodbc/drvconn.c b/src/sqlodbc/drvconn.c
index 71b166a..44ed49c 100644
--- a/src/sqlodbc/drvconn.c
+++ b/src/sqlodbc/drvconn.c
@@ -50,6 +50,25 @@ char *hide_password(const char *str) {
return outstr;
}
+char *hide_token(const char *str) {
+ char *outstr, *jwtp;
+
+ if (!str)
+ return NULL;
+ outstr = strdup(str);
+ if (!outstr)
+ return NULL;
+ if (jwtp = strstr(outstr, "JWT="), !jwtp)
+ jwtp = strstr(outstr, "jwt=");
+ if (jwtp) {
+ char *p;
+
+ for (p = jwtp + 4; *p && *p != ';'; p++)
+ *p = 'x';
+ }
+ return outstr;
+}
+
int paramRequired(const ConnInfo *ci, int reqs) {
int required = 0;
const char *pw = SAFE_NAME(ci->password);
@@ -204,7 +223,8 @@ BOOL dconn_get_attributes(copyfunc func, const char *connect_string,
MYLOG(OPENSEARCH_DEBUG, "our_connect_string = '%s'\n", our_connect_string);
#else
if (get_mylog()) {
- char *hide_str = hide_password(our_connect_string);
+ char *hide_str = hide_password(our_connect_string);
+ hide_str = hide_token(our_connect_string);
MYLOG(OPENSEARCH_DEBUG, "our_connect_string = '%s'\n", hide_str);
free(hide_str);
diff --git a/src/sqlodbc/opensearch_communication.cpp b/src/sqlodbc/opensearch_communication.cpp
index 571357f..36a59f6 100644
--- a/src/sqlodbc/opensearch_communication.cpp
+++ b/src/sqlodbc/opensearch_communication.cpp
@@ -340,6 +340,13 @@ bool OpenSearchCommunication::CheckConnectionOptions() {
SetErrorDetails("Auth error", m_error_message,
ConnErrorType::CONN_ERROR_INVALID_AUTH);
}
+ } else if (m_rt_opts.auth.auth_type == AUTHTYPE_OAUTH2){
+ if (m_rt_opts.auth.access_token.empty()) {
+ m_error_message = AUTHTYPE_OAUTH2
+ " no token sended. ";
+ SetErrorDetails("Auth error", m_error_message,
+ ConnErrorType::CONN_ERROR_INVALID_AUTH);
+ }
} else {
m_error_message = "Unknown authentication type: '"
+ m_rt_opts.auth.auth_type + "'";
@@ -459,8 +466,11 @@ OpenSearchCommunication::IssueRequest(
.c_str());
}
signer.SignRequest(*request);
+ } else if (m_rt_opts.auth.auth_type == AUTHTYPE_OAUTH2) {
+ request->SetAuthorization("Bearer " + m_rt_opts.auth.access_token);
}
+
// Issue request and return response
return m_http_client->MakeRequest(request);
}
diff --git a/src/sqlodbc/opensearch_connection.cpp b/src/sqlodbc/opensearch_connection.cpp
index 1262a5d..822ab35 100644
--- a/src/sqlodbc/opensearch_connection.cpp
+++ b/src/sqlodbc/opensearch_connection.cpp
@@ -122,6 +122,7 @@ int LIBOPENSEARCH_connect(ConnectionClass *self) {
rt_opts.auth.auth_type.assign(self->connInfo.authtype);
rt_opts.auth.username.assign(self->connInfo.username);
rt_opts.auth.password.assign(SAFE_NAME(self->connInfo.password));
+ rt_opts.auth.access_token.assign(SAFE_NAME(self->connInfo.access_token));
rt_opts.auth.region.assign(self->connInfo.region);
rt_opts.auth.tunnel_host.assign(self->connInfo.tunnel_host);
diff --git a/src/sqlodbc/opensearch_odbc.h b/src/sqlodbc/opensearch_odbc.h
index a20ce48..0f8dec7 100644
--- a/src/sqlodbc/opensearch_odbc.h
+++ b/src/sqlodbc/opensearch_odbc.h
@@ -381,8 +381,8 @@ BOOL isSqlServr(void);
/* Limits */
#define MAXESPATH 1024
-/* see an easy way round this - DJP 24-1-2001 */
-#define MAX_CONNECT_STRING 4096
+/* see an easy way round this - DJP 24-1-2001 predefined as 4096*/
+#define MAX_CONNECT_STRING 20480
#define FETCH_MAX \
100 /* default number of rows to cache \ \
* for declare/fetch */
@@ -409,7 +409,9 @@ BOOL isSqlServr(void);
#define INDEX_KEYS_STORAGE_COUNT 32
/* Registry length limits */
+#define XLARGE_REGISTRY_LEN 8192 /* used for adapt to the lenght of the JWT token in OAUTH2*/
#define LARGE_REGISTRY_LEN 4096 /* used for special cases */
+#define MEDIUM_LARGE_REGISTRY_LEN 1536 /* used for adapt to the lenght of the JWT token in OAUTH2 */
#define MEDIUM_REGISTRY_LEN \
256 /* normal size for \ \
* user,database,etc. */
@@ -611,6 +613,7 @@ typedef struct {
char authtype[MEDIUM_REGISTRY_LEN];
char username[MEDIUM_REGISTRY_LEN];
opensearchNAME password;
+ opensearchNAME access_token;
char region[MEDIUM_REGISTRY_LEN];
char tunnel_host[MEDIUM_REGISTRY_LEN];
diff --git a/src/sqlodbc/opensearch_odbc.rc b/src/sqlodbc/opensearch_odbc.rc
index ceb19fa..2bb634f 100644
--- a/src/sqlodbc/opensearch_odbc.rc
+++ b/src/sqlodbc/opensearch_odbc.rc
@@ -7,7 +7,11 @@
//
// Generated from the TEXTINCLUDE 2 resource.
//
-#include "afxres.h"
+
+// The file "afxres.h" is not available in the Visual Studio 2019 Build Tools "MFC C++ v14.29 (16.10) for Build Tools v142 (x86 and x64)" and "winres.h" will substitute that.
+// #include "afxres.h"
+
+#include "winres.h"
#include "version.h"
/////////////////////////////////////////////////////////////////////////////
@@ -33,7 +37,10 @@ END
2 TEXTINCLUDE
BEGIN
- "#include ""afxres.h""\r\n"
+ // The file "afxres.h" is not available in the Visual Studio 2019 Build Tools "MFC C++ v14.29 (16.10) for Build Tools v142 (x86 and x64)" and "winres.h" will substitute that.
+ // "#include ""afxres.h""\r\n"
+
+ "#include ""winres.h""\r\n"
"#include ""version.h""\r\n"
"\0"
END
@@ -62,7 +69,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// Dialog
//
-DLG_CONFIG DIALOGEX 65, 43, 275, 270
+DLG_CONFIG DIALOGEX 65, 63, 275, 290
STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "OpenSearch ODBC Driver DSN Setup"
FONT 8, "MS Sans Serif", 0, 0, 0x0
@@ -74,23 +81,25 @@ BEGIN
EDITTEXT IDC_SERVER,60,44,192,12,ES_AUTOHSCROLL
LTEXT "Port",IDC_STATIC,20,66,19,8
EDITTEXT IDC_PORT,60,64,192,13,ES_AUTOHSCROLL
- GROUPBOX "Authentication Settings",IDC_AUTH_SETTINGS,7,93,260,115,BS_FLAT
+ GROUPBOX "Authentication Settings",IDC_AUTH_SETTINGS,7,93,260,135,BS_FLAT
LTEXT "Auth",IDC_AUTH_STATIC,21,110,19,8
COMBOBOX IDC_AUTHTYPE,65,108,192,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
LTEXT "User",IDC_USERNAME_STATIC,20,129,19,8
EDITTEXT IDC_USER,65,127,191,12,ES_AUTOHSCROLL | WS_DISABLED
LTEXT "Password",IDC_PASSWORD_STATIC,20,149,41,12
EDITTEXT IDC_PASSWORD,65,147,191,12,ES_PASSWORD | ES_AUTOHSCROLL | WS_DISABLED
- LTEXT "Region",IDC_REGION_STATIC,20,170,28,8
- EDITTEXT IDC_REGION,65,168,191,12,ES_AUTOHSCROLL | WS_DISABLED
- LTEXT "Tunnel host",IDC_TUNNEL_HOST_STATIC,20,190,56,8
- EDITTEXT IDC_TUNNEL_HOST,65,188,191,12,ES_AUTOHSCROLL | WS_DISABLED
- PUSHBUTTON "Advanced Options",ID_ADVANCED_OPTIONS,21,215,111,15,WS_GROUP
- PUSHBUTTON "Logging Options",ID_LOG_OPTIONS,144,215,108,15,WS_GROUP
- LTEXT "V.N.N.N",IDC_DRIVER_VERSION,10,248,108,8
- DEFPUSHBUTTON "OK",IDOK,119,244,44,15,WS_GROUP
- DEFPUSHBUTTON "Test",IDOK2,167,244,44,15,WS_GROUP
- PUSHBUTTON "Cancel",IDCANCEL,215,244,44,15
+ LTEXT "Token",IDC_TOKEN_STATIC,20,169,41,12
+ EDITTEXT IDC_TOKEN,65,167,191,12,ES_AUTOHSCROLL | WS_DISABLED
+ LTEXT "Region",IDC_REGION_STATIC,20,189,28,8
+ EDITTEXT IDC_REGION,65,187,191,12,ES_AUTOHSCROLL | WS_DISABLED
+ LTEXT "Tunnel host",IDC_TUNNEL_HOST_STATIC,20,209,56,8
+ EDITTEXT IDC_TUNNEL_HOST,65,207,191,12,ES_AUTOHSCROLL | WS_DISABLED
+ PUSHBUTTON "Advanced Options",ID_ADVANCED_OPTIONS,21,235,111,15,WS_GROUP
+ PUSHBUTTON "Logging Options",ID_LOG_OPTIONS,144,235,108,15,WS_GROUP
+ LTEXT "V.N.N.N",IDC_DRIVER_VERSION,10,268,108,8
+ DEFPUSHBUTTON "OK",IDOK,119,264,44,15,WS_GROUP
+ DEFPUSHBUTTON "Test",IDOK2,167,264,44,15,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,215,264,44,15
END
DLG_ADVANCED_OPTIONS DIALOGEX 0, 0, 157, 113
@@ -232,6 +241,7 @@ BEGIN
IDS_AUTHTYPE_NONE "NONE"
IDS_AUTHTYPE_BASIC "BASIC"
IDS_AUTHTYPE_IAM "AWS_SIGV4"
+ IDS_AUTHTYPE_OAUTH2 "OAUTH2"
IDS_LOGTYPE_OFF "LOG_OFF"
IDS_LOGTYPE_FATAL "LOG_FATAL"
IDS_LOGTYPE_ERROR "LOG_ERROR"
diff --git a/src/sqlodbc/opensearch_types.h b/src/sqlodbc/opensearch_types.h
index 3c61b4a..27568e1 100644
--- a/src/sqlodbc/opensearch_types.h
+++ b/src/sqlodbc/opensearch_types.h
@@ -254,6 +254,7 @@ typedef struct authentication_options {
std::string auth_type;
std::string username;
std::string password;
+ std::string access_token;
std::string region;
std::string tunnel_host;
} authentication_options;
diff --git a/src/sqlodbc/resource.h b/src/sqlodbc/resource.h
index 45aa0eb..9dec11b 100644
--- a/src/sqlodbc/resource.h
+++ b/src/sqlodbc/resource.h
@@ -7,6 +7,7 @@
#define IDOK2 3
#define IDC_TEST 4
#define IDC_PASSWORD_STATIC 4
+#define IDC_TOKEN_STATIC 6
#define IDC_SSL_STATIC 4
#define IDC_HOST_VER_STATIC 5
#define IDC_DSNAME 400
@@ -26,10 +27,12 @@
#define IDS_LOGTYPE_DEBUG 425
#define IDS_LOGTYPE_TRACE 426
#define IDS_LOGTYPE_ALL 427
+#define IDS_AUTHTYPE_OAUTH2 428
#define DLG_CONFIG 1001
#define IDC_PORT 1002
#define IDC_USER 1006
#define IDC_PASSWORD 1009
+#define IDC_TOKEN 1010
#define IDC_MANAGEDSN 1077
#define IDC_EDIT1 1112
#define IDC_CONNTIMEOUT_STATIC 1112
diff --git a/src/sqlodbc/setup.c b/src/sqlodbc/setup.c
index 89cc0dc..66e1fb7 100644
--- a/src/sqlodbc/setup.c
+++ b/src/sqlodbc/setup.c
@@ -272,9 +272,12 @@ INT_PTR CALLBACK ConfigDlgProc(HWND hdlg, UINT wMsg, WPARAM wParam,
} else if (!stricmp(ci->authtype, AUTHTYPE_BASIC)) {
SendDlgItemMessage(hdlg, IDC_AUTHTYPE, CB_SETCURSEL, 1,
(WPARAM)0);
- } else { // AUTHTYPE_NONE
+ } else if (!stricmp(ci->authtype, AUTHTYPE_OAUTH2)) {
SendDlgItemMessage(hdlg, IDC_AUTHTYPE, CB_SETCURSEL, 2,
(WPARAM)0);
+ } else { // AUTHTYPE_NONE
+ SendDlgItemMessage(hdlg, IDC_AUTHTYPE, CB_SETCURSEL, 3,
+ (WPARAM)0);
}
return TRUE; /* Focus was not set */
diff --git a/src/vcpkg.json b/src/vcpkg.json
index 7e34853..925d035 100644
--- a/src/vcpkg.json
+++ b/src/vcpkg.json
@@ -2,7 +2,7 @@
"name": "sql-odbc",
"version-string": "1.1.0.1",
"dependencies": [
- "aws-sdk-cpp",
+ {"name": "aws-sdk-cpp", "version>=": "1.11.285"},
"rapidjson",
"zlib",
"gtest",
diff --git a/test_output.html b/test_output.html
new file mode 100644
index 0000000..b004b39
--- /dev/null
+++ b/test_output.html
@@ -0,0 +1,3476 @@
+
+
+
+
+
+ Elastic Search Test Results
+
+
+
+
+
+