From e6cb4820989b59378cfdad14f30d0e6ceb37d78f Mon Sep 17 00:00:00 2001 From: Sachin Purohit Date: Wed, 11 Mar 2026 15:09:12 +0000 Subject: [PATCH 1/2] impl(bq_driver): creating a separate option for User account authentication --- .../odbc_authentication.cc | 33 ++++++++---- .../bq_client_interface/odbc_authentication.h | 3 +- .../odbc_authentication_test.cc | 5 +- .../odbc_bq_client_test.cc | 4 +- .../odbc/bq_driver/internal/driver_form.cc | 6 +-- .../internal/odbc_conn_handle_test.cc | 4 +- google/cloud/odbc/bq_driver/internal/utils.cc | 4 +- .../cloud/odbc/bq_driver/odbc_connection.cc | 4 +- google/cloud/odbc/bq_driver/odbc_windows.cc | 4 +- .../bq_apis/cancel_job_test.cc | 2 +- .../bq_apis/get_dataset_test.cc | 2 +- .../integration_tests/bq_apis/get_job_test.cc | 2 +- .../bq_apis/get_table_test.cc | 2 +- .../bq_apis/insert_job_test.cc | 2 +- .../bq_apis/list_dataset_test.cc | 2 +- .../bq_apis/list_job_test.cc | 6 +-- .../bq_apis/list_project_test.cc | 2 +- .../bq_apis/list_table_test.cc | 2 +- .../integration_tests/bq_apis/query_test.cc | 2 +- .../bq_apis/rm_project_test.cc | 54 +++++++++---------- .../internal/connection_test.cc | 2 +- .../client_library_utils/authentication.cc | 2 +- 22 files changed, 82 insertions(+), 67 deletions(-) diff --git a/google/cloud/odbc/bq_client_interface/odbc_authentication.cc b/google/cloud/odbc/bq_client_interface/odbc_authentication.cc index b8c1d7aa92..d3e7ffd181 100644 --- a/google/cloud/odbc/bq_client_interface/odbc_authentication.cc +++ b/google/cloud/odbc/bq_client_interface/odbc_authentication.cc @@ -42,14 +42,6 @@ StatusRecordOr> CreateServiceCredentials( return StatusRecord{SQLStates::k_HY000(), "The path to the file can't be empty"}; } - // Client libraries don't have a special function for user authentication. - // We use MakeGoogleDefaultCredentials() and override - // GOOGLE_APPLICATION_CREDENTIALS env var to point to the file with - // credentials. It works for both: user authentication and service - // authentication. - // https://github.com/googleapis/google-cloud-cpp/blob/main/google/cloud/credentials.h#L113 - // Read the contents of the key file directly instead of setting an - // environment variable. std::ifstream is(credentials_file_path); if (!is) { LOG(ERROR) << "CreateServiceCredentials:: Could not open Service Account " @@ -76,6 +68,27 @@ StatusRecordOr> CreateServiceCredentials( return ::google::cloud::MakeServiceAccountCredentials(contents, options); } +StatusRecordOr> CreateUserCredentials( + std::string const& credentials_file_path, Options const& options) { + if (credentials_file_path.empty()) { + LOG(ERROR) << "CreateUserCredentials:: The path to the file can't be empty"; + return StatusRecord{SQLStates::k_HY000(), + "The path to the file can't be empty"}; + } + + // 1. Set the environment to the provided path + // + // Client libraries don't have a special function for user authentication. + // We use MakeGoogleDefaultCredentials() and override + // GOOGLE_APPLICATION_CREDENTIALS env var to point to the file with + // credentials. + SetEnv("GOOGLE_APPLICATION_CREDENTIALS", credentials_file_path.c_str()); + + // 2. Create credentials. MakeGoogleDefaultCredentials will parse the file, + // detect the 'authorized_user' type, and initialize the refresh token flow. + return ::google::cloud::MakeGoogleDefaultCredentials(options); +} + StatusRecordOr> CreateApplicationDefaultCredentials(Options const& options) { // C++ client library in google-cloud-cpp first checks @@ -166,8 +179,10 @@ CreateExternalAccountAuthenticationBYOID(Oauth const& oauth, StatusRecordOr> CreateCredentials( Oauth const& oauth, Options const& options) { switch (oauth.auth_mechanism) { - case OauthMechanism::kServiceAndUserAccount: + case OauthMechanism::kServiceAccount: return CreateServiceCredentials(oauth.credentials_file_path, options); + case OauthMechanism::kUserAccount: + return CreateUserCredentials(oauth.credentials_file_path, options); case OauthMechanism::kApplicationDefault: return CreateApplicationDefaultCredentials(options); case OauthMechanism::kExternalUser: { diff --git a/google/cloud/odbc/bq_client_interface/odbc_authentication.h b/google/cloud/odbc/bq_client_interface/odbc_authentication.h index a6436e8abe..44803b1a36 100644 --- a/google/cloud/odbc/bq_client_interface/odbc_authentication.h +++ b/google/cloud/odbc/bq_client_interface/odbc_authentication.h @@ -39,7 +39,8 @@ std::string const kDefaultTokenUrl = "https://sts.googleapis.com/v1/token"; // NOTE: This should always specify the integral values with the type names // because the driver layer is tightly coupled to the integer values. enum class OauthMechanism { - kServiceAndUserAccount = 0, + kServiceAccount = 0, + kUserAccount = 1, kApplicationDefault = 3, kExternalUser = 4, }; diff --git a/google/cloud/odbc/bq_client_interface/odbc_authentication_test.cc b/google/cloud/odbc/bq_client_interface/odbc_authentication_test.cc index 8d3a66a4e9..87dfe9da13 100644 --- a/google/cloud/odbc/bq_client_interface/odbc_authentication_test.cc +++ b/google/cloud/odbc/bq_client_interface/odbc_authentication_test.cc @@ -41,7 +41,7 @@ TEST(ServiceAuthentication, InvalidPathFileDoesNotExist) { std::string invalid_path = "non_existing_key.json"; auto credentials = - CreateCredentials({OauthMechanism::kServiceAndUserAccount, invalid_path}); + CreateCredentials({OauthMechanism::kServiceAccount, invalid_path}); EXPECT_THAT(credentials, StatusRecordIs(odbc_internal::SQLStates::k_HY000(), @@ -57,8 +57,7 @@ TEST(DefaultApplicationAuthentication, DefaultApplicationAuthentication) { } TEST(ServiceAuthentication, EmptyPath) { - auto credentials = - CreateCredentials({OauthMechanism::kServiceAndUserAccount, ""}); + auto credentials = CreateCredentials({OauthMechanism::kServiceAccount, ""}); EXPECT_THAT(credentials, StatusRecordIs(odbc_internal::SQLStates::k_HY000(), diff --git a/google/cloud/odbc/bq_client_interface/odbc_bq_client_test.cc b/google/cloud/odbc/bq_client_interface/odbc_bq_client_test.cc index 4423be482d..fde25b7a02 100644 --- a/google/cloud/odbc/bq_client_interface/odbc_bq_client_test.cc +++ b/google/cloud/odbc/bq_client_interface/odbc_bq_client_test.cc @@ -25,8 +25,8 @@ using google::cloud::odbc_testing_utils::StatusRecordIs; using ::testing::HasSubstr; TEST(ODBCBQClient, CreateBQClientFailsWithInvalidCredentials) { - auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, ""}); + auto odbc_bq_client = + ODBCBQClient::CreateBQClient({OauthMechanism::kServiceAccount, ""}); auto const expected_sql_state = odbc_internal::SQLStates::k_HY000(); auto const expected_message = diff --git a/google/cloud/odbc/bq_driver/internal/driver_form.cc b/google/cloud/odbc/bq_driver/internal/driver_form.cc index 8d703dd48e..edbf43d213 100644 --- a/google/cloud/odbc/bq_driver/internal/driver_form.cc +++ b/google/cloud/odbc/bq_driver/internal/driver_form.cc @@ -242,7 +242,7 @@ StatusRecordOr DriverForm::GetCatalogAndDataset( std::string const& oauth_token, std::string const& catalog_name) { OauthMechanism oauth_value; if (oauth_token == "Service Authentication") { - oauth_value = OauthMechanism::kServiceAndUserAccount; + oauth_value = OauthMechanism::kServiceAccount; } else if (oauth_token == "Application Default Credentials") { oauth_value = OauthMechanism::kApplicationDefault; @@ -378,8 +378,8 @@ void DriverForm::SetValues(Section const& attributes_map) { use_trusted_store_ = GetValueOrDefault(attributes_map, kUseTrustedStore); std::string oauth_value = GetValueOrDefault(attributes_map, kOAuthMechanism); - if (oauth_value == std::to_string(static_cast( - OauthMechanism::kServiceAndUserAccount))) { + if (oauth_value == + std::to_string(static_cast(OauthMechanism::kServiceAccount))) { o_auth_mechanism_ = "Service Authentication"; } else if (oauth_value == std::to_string(static_cast( OauthMechanism::kApplicationDefault))) { diff --git a/google/cloud/odbc/bq_driver/internal/odbc_conn_handle_test.cc b/google/cloud/odbc/bq_driver/internal/odbc_conn_handle_test.cc index a38bdaccca..d58023ab2d 100644 --- a/google/cloud/odbc/bq_driver/internal/odbc_conn_handle_test.cc +++ b/google/cloud/odbc/bq_driver/internal/odbc_conn_handle_test.cc @@ -51,7 +51,7 @@ TEST(ConnectionHandle, ConnectWithInvalidFile) { std::string credentials_file_path = test_data_path + "random_file.json"; Authentication auth = { - {OauthMechanism::kServiceAndUserAccount, credentials_file_path}}; + {OauthMechanism::kServiceAccount, credentials_file_path}}; ConnectionHandle conn_handle; StatusRecord status = conn_handle.Connect(auth); EXPECT_EQ(status.ok(), false); @@ -700,7 +700,7 @@ TEST(ConnectionHandle, ValidateExternalUserSuccessJson) { TEST(ConnectionHandle, ValidateExternalUserSuccessNotExternalUser) { Authentication auth; - auth.oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + auth.oauth.auth_mechanism = OauthMechanism::kServiceAccount; StatusRecord status = ConnectionHandle::ValidateExternalUser(auth); EXPECT_TRUE(status.ok()); diff --git a/google/cloud/odbc/bq_driver/internal/utils.cc b/google/cloud/odbc/bq_driver/internal/utils.cc index 56aaaba952..30e0a5ecaf 100644 --- a/google/cloud/odbc/bq_driver/internal/utils.cc +++ b/google/cloud/odbc/bq_driver/internal/utils.cc @@ -1318,8 +1318,8 @@ StatusRecord NormalizeOAuthMechanism(Section& section) { if (section[kKeyFilePath].empty()) { return {SQLStates::k_HY000(), "KeyFilePath is missing or empty."}; } - oauth_value = std::to_string( - static_cast(OauthMechanism::kServiceAndUserAccount)); + oauth_value = + std::to_string(static_cast(OauthMechanism::kServiceAccount)); } else if (it->second == "Application Default Credentials") { oauth_value = std::to_string(static_cast(OauthMechanism::kApplicationDefault)); diff --git a/google/cloud/odbc/bq_driver/odbc_connection.cc b/google/cloud/odbc/bq_driver/odbc_connection.cc index 59ade298c0..4478e6140c 100644 --- a/google/cloud/odbc/bq_driver/odbc_connection.cc +++ b/google/cloud/odbc/bq_driver/odbc_connection.cc @@ -416,8 +416,8 @@ SQLRETURN SQLConnectInternal(SQLHDBC conn_handle, SQLCHAR* server_name, LOG(ERROR) << "SQLConnect:: " << status_record.message; return LogAndReturnCode(handle_ref, status_record); } - dsn_section["OAUTHMECHANISM"] = std::to_string( - static_cast(OauthMechanism::kServiceAndUserAccount)); + dsn_section["OAUTHMECHANISM"] = + std::to_string(static_cast(OauthMechanism::kServiceAccount)); dsn_section["KEYFILEPATH"] = auth_string_str; } // Populate the DSN info inside the handle. diff --git a/google/cloud/odbc/bq_driver/odbc_windows.cc b/google/cloud/odbc/bq_driver/odbc_windows.cc index b90bcd828b..ee3589dd48 100644 --- a/google/cloud/odbc/bq_driver/odbc_windows.cc +++ b/google/cloud/odbc/bq_driver/odbc_windows.cc @@ -51,8 +51,8 @@ using google::cloud::odbc_internal::StatusRecordOr; std::string ConvertOAuthMechanism(std::string o_auth_mechanism) { std::string o_auth_value; if (o_auth_mechanism == "Service Authentication") { - o_auth_value = std::to_string( - static_cast(OauthMechanism::kServiceAndUserAccount)); + o_auth_value = + std::to_string(static_cast(OauthMechanism::kServiceAccount)); } else if (o_auth_mechanism == "Application Default Credentials") { o_auth_value = std::to_string(static_cast(OauthMechanism::kApplicationDefault)); diff --git a/google/cloud/odbc/integration_tests/bq_apis/cancel_job_test.cc b/google/cloud/odbc/integration_tests/bq_apis/cancel_job_test.cc index 7531f71bb2..8b1b548251 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/cancel_job_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/cancel_job_test.cc @@ -174,7 +174,7 @@ TEST(ODBCBQClient_CancelJob, UserAccountAuth) { // Cancelling previous Job via ODBC BQ Client Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); diff --git a/google/cloud/odbc/integration_tests/bq_apis/get_dataset_test.cc b/google/cloud/odbc/integration_tests/bq_apis/get_dataset_test.cc index 6c1f7676fc..ec948ccfbf 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/get_dataset_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/get_dataset_test.cc @@ -164,7 +164,7 @@ TEST(ODBCBQClient_GetDataset, UserAccountAuth) { // Retrieving dataset via ODBC BQ Client Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); diff --git a/google/cloud/odbc/integration_tests/bq_apis/get_job_test.cc b/google/cloud/odbc/integration_tests/bq_apis/get_job_test.cc index 72ddbf6636..1e99d19059 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/get_job_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/get_job_test.cc @@ -165,7 +165,7 @@ TEST(ODBCBQClient_GetJob, UserAccountAuth) { std::string path_to_file_with_credentials = GetRequiredEnvVar("CPP_BIGQUERY_ODBC_TEST_USER_ACCOUNT_AUTH_KEY"); Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); diff --git a/google/cloud/odbc/integration_tests/bq_apis/get_table_test.cc b/google/cloud/odbc/integration_tests/bq_apis/get_table_test.cc index c9c310f8f6..5c51edbd72 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/get_table_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/get_table_test.cc @@ -189,7 +189,7 @@ TEST(ODBCBQClient_GetTable, UserAccountAuth) { // Retrieving table via ODBCBQClient. TableFilter filter{{}, TableMetadataView::Full()}; Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); diff --git a/google/cloud/odbc/integration_tests/bq_apis/insert_job_test.cc b/google/cloud/odbc/integration_tests/bq_apis/insert_job_test.cc index 0c1e735741..1fcadaecb8 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/insert_job_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/insert_job_test.cc @@ -314,7 +314,7 @@ TEST(ODBCBQClient_InsertJob, UserAccountAuth) { job.configuration = job_configuration; Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; // Insert Job using BQ Client auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); diff --git a/google/cloud/odbc/integration_tests/bq_apis/list_dataset_test.cc b/google/cloud/odbc/integration_tests/bq_apis/list_dataset_test.cc index 6c0a1a5ffb..00f746d320 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/list_dataset_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/list_dataset_test.cc @@ -193,7 +193,7 @@ TEST(ODBCBQClient_ListDatasets, UserAccountAuth) { // Retrieving datasets via ODBC BQ Client Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); diff --git a/google/cloud/odbc/integration_tests/bq_apis/list_job_test.cc b/google/cloud/odbc/integration_tests/bq_apis/list_job_test.cc index 799a84aa26..da9781b2f4 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/list_job_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/list_job_test.cc @@ -175,7 +175,7 @@ TEST(ODBCBQClient_ListJobs, DISABLED_UserAccountAuth) { std::string path_to_file_with_credentials = GetRequiredEnvVar("CPP_BIGQUERY_ODBC_TEST_USER_ACCOUNT_AUTH_KEY"); Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); @@ -202,7 +202,7 @@ TEST(ODBCBQClient_ListAllJobs, DISABLED_UserAccountAuth) { StatusOr parent_job_id = InsertJob(job_client); ASSERT_FALSE(parent_job_id->empty()) << parent_job_id.status().message(); Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); @@ -413,7 +413,7 @@ TEST(ODBCBQClient_ListAllJobs, DISABLED_ServiceAccountAuth) { StatusOr parent_job_id = InsertJob(job_client); ASSERT_FALSE(parent_job_id->empty()) << parent_job_id.status().message(); Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); diff --git a/google/cloud/odbc/integration_tests/bq_apis/list_project_test.cc b/google/cloud/odbc/integration_tests/bq_apis/list_project_test.cc index 81f4e94717..041086f44b 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/list_project_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/list_project_test.cc @@ -144,7 +144,7 @@ TEST(ODBCBQClient_ListAllProjects, UserAccountAuth) { // List projects via ODBC BQ Client Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); diff --git a/google/cloud/odbc/integration_tests/bq_apis/list_table_test.cc b/google/cloud/odbc/integration_tests/bq_apis/list_table_test.cc index 4bbc59dc88..076bd642a9 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/list_table_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/list_table_test.cc @@ -193,7 +193,7 @@ TEST(ODBCBQClient_ListAllTables, UserAccountAuth) { // Retrieving tables via ODBCBQClient. Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); diff --git a/google/cloud/odbc/integration_tests/bq_apis/query_test.cc b/google/cloud/odbc/integration_tests/bq_apis/query_test.cc index 90aa44b076..a737f14e56 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/query_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/query_test.cc @@ -291,7 +291,7 @@ TEST(ODBCBQClient_Query, UserAccountAuth) { // Query via ODBCBQClient Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; auto odbc_bq_client = ODBCBQClient::CreateBQClient(oauth); ASSERT_STATUS_RECORD_OK(odbc_bq_client); diff --git a/google/cloud/odbc/integration_tests/bq_apis/rm_project_test.cc b/google/cloud/odbc/integration_tests/bq_apis/rm_project_test.cc index 239db4128e..f55a530211 100644 --- a/google/cloud/odbc/integration_tests/bq_apis/rm_project_test.cc +++ b/google/cloud/odbc/integration_tests/bq_apis/rm_project_test.cc @@ -445,7 +445,7 @@ TEST(ODBCBQClient, UserAccountAuth_GetProjectRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); StatusRecordOr< ::google::cloud::bigquery_v2_minimal_internal::Project> @@ -464,7 +464,7 @@ TEST(ODBCBQClient, UserAccountAuth_GetProjectRM_Failure_ProjectNotFound) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); StatusRecordOr< ::google::cloud::bigquery_v2_minimal_internal::Project> @@ -483,7 +483,7 @@ TEST(ODBCBQClient, UserAccountAuth_ListProjectsRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -500,7 +500,7 @@ TEST(ODBCBQClient, UserAccountAuth_ListProjectsRM_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -518,7 +518,7 @@ TEST(ODBCBQClient, UserAccountAuth_SearchProjectsRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -535,7 +535,7 @@ TEST(ODBCBQClient, UserAccountAuth_SearchProjectsRM_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -553,7 +553,7 @@ TEST(ODBCBQClient, UserAccountAuth_FilterProjectsRMList_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {kRMProjectWithoutPrefix}; @@ -576,7 +576,7 @@ TEST(ODBCBQClient, UserAccountAuth_FilterProjectsRMSearch_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {"app1", kRMProjectWithoutPrefix}; @@ -601,7 +601,7 @@ TEST(ODBCBQClient, UserAccountAuth_FilterProjectsRMSearch_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {"app1", kRMProjectWithoutPrefix}; @@ -857,7 +857,7 @@ TEST(ODBCBQClient, GetProjectRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); StatusRecordOr< ::google::cloud::bigquery_v2_minimal_internal::Project> @@ -876,7 +876,7 @@ TEST(ODBCBQClient, ADC_GetProjectRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); StatusRecordOr< ::google::cloud::bigquery_v2_minimal_internal::Project> @@ -895,7 +895,7 @@ TEST(ODBCBQClient, GetProjectRM_Failure_ProjectNotFound) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); StatusRecordOr< ::google::cloud::bigquery_v2_minimal_internal::Project> @@ -914,7 +914,7 @@ TEST(ODBCBQClient, ADC_GetProjectRM_Failure_ProjectNotFound) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); StatusRecordOr< ::google::cloud::bigquery_v2_minimal_internal::Project> @@ -933,7 +933,7 @@ TEST(ODBCBQClient, ListProjectsRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -950,7 +950,7 @@ TEST(ODBCBQClient, ADC_ListProjectsRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -967,7 +967,7 @@ TEST(ODBCBQClient, ListProjectsRM_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -985,7 +985,7 @@ TEST(ODBCBQClient, ADC_ListProjectsRM_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -1003,7 +1003,7 @@ TEST(ODBCBQClient, SearchProjectsRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -1019,7 +1019,7 @@ TEST(ODBCBQClient, ADC_SearchProjectsRM_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -1036,7 +1036,7 @@ TEST(ODBCBQClient, SearchProjectsRM_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -1055,7 +1055,7 @@ TEST(ODBCBQClient, ADC_SearchProjectsRM_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); auto projects_status = @@ -1073,7 +1073,7 @@ TEST(ODBCBQClient, FilterProjectsRMList_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {kRMProjectWithoutPrefix}; @@ -1096,7 +1096,7 @@ TEST(ODBCBQClient, ADC_FilterProjectsRMList_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {kRMProjectWithoutPrefix}; @@ -1119,7 +1119,7 @@ TEST(ODBCBQClient, FilterProjectsRMSearch_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {"app1", kRMProjectWithoutPrefix}; @@ -1143,7 +1143,7 @@ TEST(ODBCBQClient, ADC_FilterProjectsRMSearch_Success) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {"app1", kRMProjectWithoutPrefix}; @@ -1168,7 +1168,7 @@ TEST(ODBCBQClient, FilterProjectsRMSearch_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {"app1", kRMProjectWithoutPrefix}; @@ -1190,7 +1190,7 @@ TEST(ODBCBQClient, ADC_FilterProjectsRMSearch_Failure) { ASSERT_FALSE(path_to_file_with_credentials.empty()); auto odbc_bq_client = ODBCBQClient::CreateBQClient( - {OauthMechanism::kServiceAndUserAccount, path_to_file_with_credentials}); + {OauthMechanism::kServiceAccount, path_to_file_with_credentials}); ASSERT_STATUS_RECORD_OK(odbc_bq_client); std::vector project_ids = {"app1", kRMProjectWithoutPrefix}; diff --git a/google/cloud/odbc/integration_tests/odbc_driver_tests/internal/connection_test.cc b/google/cloud/odbc/integration_tests/odbc_driver_tests/internal/connection_test.cc index ea7e274cf6..b64bf7656d 100644 --- a/google/cloud/odbc/integration_tests/odbc_driver_tests/internal/connection_test.cc +++ b/google/cloud/odbc/integration_tests/odbc_driver_tests/internal/connection_test.cc @@ -30,7 +30,7 @@ using google::cloud::odbc_internal::StatusRecord; TEST(BQDriverTest_Internal, ConnectionHandle_Connect) { std::string credentials_file_path = GetEnv("CPP_BIGQUERY_ODBC_TEST_SERVICE_ACCOUNT_AUTH_KEY").value_or(""); - Authentication auth = {OauthMechanism::kServiceAndUserAccount, + Authentication auth = {OauthMechanism::kServiceAccount, credentials_file_path}; auto* conn_handle = new ConnectionHandle(); StatusRecord status = conn_handle->Connect(auth); diff --git a/google/cloud/odbc/testing/client_library_utils/authentication.cc b/google/cloud/odbc/testing/client_library_utils/authentication.cc index 8c3167d09a..9969b18ed2 100644 --- a/google/cloud/odbc/testing/client_library_utils/authentication.cc +++ b/google/cloud/odbc/testing/client_library_utils/authentication.cc @@ -40,7 +40,7 @@ StatusOr CreateUserAccountAuthentication() { "variable is not set"); } Oauth oauth; - oauth.auth_mechanism = OauthMechanism::kServiceAndUserAccount; + oauth.auth_mechanism = OauthMechanism::kServiceAccount; oauth.credentials_file_path = path_to_file_with_credentials; StatusRecordOr> creds = CreateCredentials(oauth); if (!creds) { From c8777903f5977a8b2bc3229695387c7c11d752c4 Mon Sep 17 00:00:00 2001 From: Sachin Purohit Date: Tue, 2 Jun 2026 14:16:45 +0000 Subject: [PATCH 2/2] chore: using MakeUserAccountCredentials from google-cloud-cpp --- .../odbc_authentication.cc | 35 +++++++++++++------ .../odbc_authentication_test.cc | 20 +++++++++++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/google/cloud/odbc/bq_client_interface/odbc_authentication.cc b/google/cloud/odbc/bq_client_interface/odbc_authentication.cc index d3e7ffd181..55d129ef23 100644 --- a/google/cloud/odbc/bq_client_interface/odbc_authentication.cc +++ b/google/cloud/odbc/bq_client_interface/odbc_authentication.cc @@ -17,6 +17,7 @@ #include "google/cloud/odbc/internal/sql_state_constants.h" #include "google/cloud/odbc/internal/status_record_or.h" #include "google/cloud/credentials.h" +#include "google/cloud/internal/credentials_impl.h" #include "google/cloud/internal/getenv.h" #include "google/cloud/oauth2/access_token_generator.h" #include "google/cloud/status_or.h" @@ -76,17 +77,31 @@ StatusRecordOr> CreateUserCredentials( "The path to the file can't be empty"}; } - // 1. Set the environment to the provided path - // - // Client libraries don't have a special function for user authentication. - // We use MakeGoogleDefaultCredentials() and override - // GOOGLE_APPLICATION_CREDENTIALS env var to point to the file with - // credentials. - SetEnv("GOOGLE_APPLICATION_CREDENTIALS", credentials_file_path.c_str()); + std::ifstream is(credentials_file_path); + if (!is) { + LOG(ERROR) << "CreateUserCredentials:: Could not open User Account " + "key file: " + << credentials_file_path; + return StatusRecord{ + SQLStates::k_HY000(), + "Could not open User Account key file: " + credentials_file_path}; + } - // 2. Create credentials. MakeGoogleDefaultCredentials will parse the file, - // detect the 'authorized_user' type, and initialize the refresh token flow. - return ::google::cloud::MakeGoogleDefaultCredentials(options); + std::string contents((std::istreambuf_iterator(is)), + std::istreambuf_iterator()); + + if (contents.empty()) { + LOG(ERROR) << "CreateUserCredentials:: User Account key file is " + "empty or could not be read: " + << credentials_file_path; + return StatusRecord{ + SQLStates::k_HY000(), + "User Account key file is empty or could not be read: " + + credentials_file_path}; + } + + return ::google::cloud::internal::MakeUserAccountCredentials(contents, + options); } StatusRecordOr> diff --git a/google/cloud/odbc/bq_client_interface/odbc_authentication_test.cc b/google/cloud/odbc/bq_client_interface/odbc_authentication_test.cc index 87dfe9da13..3bb938c085 100644 --- a/google/cloud/odbc/bq_client_interface/odbc_authentication_test.cc +++ b/google/cloud/odbc/bq_client_interface/odbc_authentication_test.cc @@ -64,6 +64,26 @@ TEST(ServiceAuthentication, EmptyPath) { HasSubstr("The path to the file can't be empty"))); } +TEST(UserAuthentication, InvalidPathFileDoesNotExist) { + std::string invalid_path = "non_existing_key.json"; + + auto credentials = + CreateCredentials({OauthMechanism::kUserAccount, invalid_path}); + + EXPECT_THAT(credentials, + StatusRecordIs( + odbc_internal::SQLStates::k_HY000(), + testing::HasSubstr("Could not open User Account key file"))); +} + +TEST(UserAuthentication, EmptyPath) { + auto credentials = CreateCredentials({OauthMechanism::kUserAccount, ""}); + + EXPECT_THAT(credentials, + StatusRecordIs(odbc_internal::SQLStates::k_HY000(), + HasSubstr("The path to the file can't be empty"))); +} + TEST(GetOAuth2Token, GetToken) { auto const expiration = std::chrono::system_clock::now() + std::chrono::minutes(15);