diff --git a/test/src/unit-vfs.cc b/test/src/unit-vfs.cc index b1c9b8b1f54..5d41f612026 100644 --- a/test/src/unit-vfs.cc +++ b/test/src/unit-vfs.cc @@ -37,6 +37,11 @@ #include #include "tiledb/sm/filesystem/azure.h" #endif +#ifdef HAVE_GCS +#include +#include +#include "tiledb/sm/filesystem/gcs.h" +#endif #include "test/support/src/vfs_helpers.h" #include "tiledb/sm/filesystem/vfs.h" #include "tiledb/sm/global_state/unit_test_config.h" @@ -738,3 +743,44 @@ TEST_CASE("Validate vfs.s3.custom_headers.*", "[s3][custom-headers]") { REQUIRE_THROWS_WITH(s3.flush_object(uri), matcher); } #endif + +#ifdef HAVE_GCS +TEST_CASE( + "Validate GCS service account impersonation", "[gcs][impersonation]") { + ThreadPool thread_pool(2); + Config cfg = set_config_params(true); + GCS gcs; + std::string impersonate_service_account, target_service_account; + std::vector delegates; + + SECTION("Simple") { + impersonate_service_account = "account1"; + target_service_account = "account1"; + delegates = {}; + } + + SECTION("Nested") { + impersonate_service_account = "account1,account2,account3"; + target_service_account = "account3"; + delegates = {"account1", "account2"}; + } + + require_tiledb_ok(cfg.set( + "vfs.gcs.impersonate_service_account", impersonate_service_account)); + + require_tiledb_ok(gcs.init(cfg, &thread_pool)); + + auto credentials = gcs.make_credentials({}); + + // We are using an internal class only for inspection purposes. + auto impersonate_credentials = + dynamic_cast( + credentials.get()); + + REQUIRE(impersonate_credentials != nullptr); + REQUIRE( + impersonate_credentials->target_service_account() == + target_service_account); + REQUIRE(impersonate_credentials->delegates() == delegates); +} +#endif diff --git a/tiledb/sm/filesystem/gcs.cc b/tiledb/sm/filesystem/gcs.cc index 2a98faec4b0..48db39a0844 100644 --- a/tiledb/sm/filesystem/gcs.cc +++ b/tiledb/sm/filesystem/gcs.cc @@ -180,6 +180,17 @@ static shared_ptr apply_impersonation( std::move(credentials), std::move(service_accounts), std::move(options)); } +std::shared_ptr GCS::make_credentials( + const google::cloud::Options& options) const { + shared_ptr creds = nullptr; + if (!endpoint_.empty() || getenv("CLOUD_STORAGE_EMULATOR_ENDPOINT")) { + creds = google::cloud::MakeInsecureCredentials(); + } else { + creds = google::cloud::MakeGoogleDefaultCredentials(options); + } + return apply_impersonation(creds, impersonate_service_account_, options); +} + Status GCS::init_client() const { assert(state_ == State::INITIALIZED); @@ -201,7 +212,7 @@ Status GCS::init_client() const { } // Note that the order here is *extremely important* - // We must call MakeGoogleDefaultCredentials *with* a ca_options + // We must call make_credentials *with* a ca_options // argument, or else the Curl handle pool will be default-initialized // with no root dir (CURLOPT_CAINFO), defaulting to build host path. // Later initializations of ClientOptions/Client with the ca_options @@ -211,16 +222,9 @@ Status GCS::init_client() const { // Creates the client using the credentials file pointed to by the // env variable GOOGLE_APPLICATION_CREDENTIALS try { - shared_ptr creds = nullptr; - if (!endpoint_.empty() || getenv("CLOUD_STORAGE_EMULATOR_ENDPOINT")) { - creds = google::cloud::MakeInsecureCredentials(); - } else { - creds = google::cloud::MakeGoogleDefaultCredentials(ca_options); - } - creds = - apply_impersonation(creds, impersonate_service_account_, ca_options); auto client_options = ca_options; - client_options.set(creds); + client_options.set( + make_credentials(ca_options)); if (!endpoint_.empty()) { client_options.set(endpoint_); } diff --git a/tiledb/sm/filesystem/gcs.h b/tiledb/sm/filesystem/gcs.h index 6d83b8d0fcb..9a2b83bbaf4 100644 --- a/tiledb/sm/filesystem/gcs.h +++ b/tiledb/sm/filesystem/gcs.h @@ -49,11 +49,17 @@ using namespace tiledb::common; -namespace google::cloud::storage { +namespace google::cloud { +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN +class Credentials; +class Options; +GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END +namespace storage { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN class Client; GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace google::cloud::storage +} // namespace storage +} // namespace google::cloud namespace tiledb { @@ -309,6 +315,17 @@ class GCS { */ Status flush_object(const URI& uri); + /** + * Creates a GCS credentials object. + * + * This method is intended to be used by testing code only. + * + * @param options Options to configure the credentials. + * @return shared pointer to credentials + */ + std::shared_ptr make_credentials( + const google::cloud::Options& options) const; + private: /* ********************************* */ /* PRIVATE DATATYPES */