diff --git a/ci/cloudbuild/builds/cmake-install.sh b/ci/cloudbuild/builds/cmake-install.sh index 7de7f89fcb4c3..4425c8a55a13c 100755 --- a/ci/cloudbuild/builds/cmake-install.sh +++ b/ci/cloudbuild/builds/cmake-install.sh @@ -133,7 +133,6 @@ expected_dirs+=( ./include/google/cloud/storage/internal/grpc ./include/google/cloud/storage/internal/rest ./include/google/cloud/storage/mocks - ./include/google/cloud/storage/oauth2 ./include/google/cloud/storage/testing # no gRPC services in google/cloud/workflows/type. ./include/google/cloud/workflows/type diff --git a/google/cloud/credentials.cc b/google/cloud/credentials.cc index bd7d2c2be7c8b..69a757c62b2ea 100644 --- a/google/cloud/credentials.cc +++ b/google/cloud/credentials.cc @@ -48,7 +48,13 @@ std::shared_ptr MakeImpersonateServiceAccountCredentials( std::shared_ptr MakeServiceAccountCredentials( std::string json_object, Options opts) { return std::make_shared( - std::move(json_object), std::move(opts)); + std::move(json_object), absl::nullopt, std::move(opts)); +} + +std::shared_ptr MakeServiceAccountCredentialsFromFile( + std::string const& file_path, Options opts) { + return std::make_shared( + absl::nullopt, file_path, std::move(opts)); } std::shared_ptr MakeExternalAccountCredentials( diff --git a/google/cloud/credentials.h b/google/cloud/credentials.h index 93166067d1974..2e37dc0d23d5c 100644 --- a/google/cloud/credentials.h +++ b/google/cloud/credentials.h @@ -301,6 +301,58 @@ std::shared_ptr MakeImpersonateServiceAccountCredentials( std::shared_ptr MakeServiceAccountCredentials( std::string json_object, Options opts = {}); +/** + * Creates service account credentials from a service account key contained in + * a file. + * + * A [service account] is an account for an application or compute workload + * instead of an individual end user. The recommended practice is to use + * Google Default Credentials, which relies on the configuration of the Google + * Cloud system hosting your application (GCE, GKE, Cloud Run) to authenticate + * your workload or application. But sometimes you may need to create and + * download a [service account key], for example, to use a service account + * when running your application on a system that is not part of Google Cloud. + * + * Service account credentials are used in this latter case. + * + * You can create multiple service account keys for a single service account. + * When you create a service account key, the key is returned as string, in the + * format described by [aip/4112]. This string contains an id for the service + * account, as well as the cryptographical materials (a RSA private key) + * required to authenticate the caller. + * + * Therefore, services account keys should be treated as any other secret + * with security implications. Think of them as unencrypted passwords. Do not + * store them where unauthorized persons or programs may read them. + * + * As stated above, most applications should probably use default credentials, + * maybe pointing them to a file with these contents. Using this function may be + * useful when the service account key is obtained from Cloud Secret Manager or + * a similar service. + * + * [aip/4112]: https://google.aip.dev/auth/4112 + * [service account]: https://cloud.google.com/iam/docs/overview#service_account + * [service account key]: + * https://cloud.google.com/iam/docs/creating-managing-service-account-keys#iam-service-account-keys-create-cpp + * + * Use `ScopesOption` to restrict the authentication scope for the obtained + * credentials. + * + * @ingroup guac + * + * @note While JSON file formats are supported for both REST and gRPC transport, + * PKCS#12 is only supported for REST transport. + * + * @param file_path path to file containing the service account key + * Typically applications read this from a file, or download the contents from + * something like Google's secret manager service. + * @param opts optional configuration values. Note that the effect of these + * parameters depends on the underlying transport. For example, + * `LoggingComponentsOption` is ignored by gRPC-based services. + */ +std::shared_ptr MakeServiceAccountCredentialsFromFile( + std::string const& file_path, Options opts = {}); + /** * Creates credentials based on external accounts. * diff --git a/google/cloud/internal/credentials_impl.cc b/google/cloud/internal/credentials_impl.cc index e7c1fd2f82a90..f3a4b8ee9e515 100644 --- a/google/cloud/internal/credentials_impl.cc +++ b/google/cloud/internal/credentials_impl.cc @@ -87,9 +87,11 @@ std::vector const& ImpersonateServiceAccountConfig::delegates() return options_.get(); } -ServiceAccountConfig::ServiceAccountConfig(std::string json_object, - Options opts) +ServiceAccountConfig::ServiceAccountConfig( + absl::optional json_object, + absl::optional file_path, Options opts) : json_object_(std::move(json_object)), + file_path_(std::move(file_path)), options_(PopulateAuthOptions(std::move(opts))) {} ExternalAccountConfig::ExternalAccountConfig(std::string json_object, diff --git a/google/cloud/internal/credentials_impl.h b/google/cloud/internal/credentials_impl.h index 94af7f2e40b89..4b7eea36ab21e 100644 --- a/google/cloud/internal/credentials_impl.h +++ b/google/cloud/internal/credentials_impl.h @@ -144,15 +144,23 @@ class ImpersonateServiceAccountConfig : public Credentials { class ServiceAccountConfig : public Credentials { public: - ServiceAccountConfig(std::string json_object, Options opts); - - std::string const& json_object() const { return json_object_; } + // Only one of json_object or file_path should have a value. + // TODO(#15886): Use the C++ type system to make better constructors that + // enforces this comment. + ServiceAccountConfig(absl::optional json_object, + absl::optional file_path, Options opts); + + absl::optional const& json_object() const { + return json_object_; + } + absl::optional const& file_path() const { return file_path_; } Options const& options() const { return options_; } private: void dispatch(CredentialsVisitor& v) const override { v.visit(*this); } - std::string json_object_; + absl::optional json_object_; + absl::optional file_path_; Options options_; }; diff --git a/google/cloud/internal/oauth2_service_account_credentials.cc b/google/cloud/internal/oauth2_service_account_credentials.cc index d72e803e1d4d4..6081a3d54ba47 100644 --- a/google/cloud/internal/oauth2_service_account_credentials.cc +++ b/google/cloud/internal/oauth2_service_account_credentials.cc @@ -13,15 +13,18 @@ // limitations under the License. #include "google/cloud/internal/oauth2_service_account_credentials.h" +#include "google/cloud/credentials.h" #include "google/cloud/internal/absl_str_join_quiet.h" #include "google/cloud/internal/getenv.h" #include "google/cloud/internal/make_jwt_assertion.h" #include "google/cloud/internal/make_status.h" #include "google/cloud/internal/oauth2_google_credentials.h" #include "google/cloud/internal/oauth2_universe_domain.h" +#include "google/cloud/internal/parse_service_account_p12_file.h" #include "google/cloud/internal/rest_response.h" #include "google/cloud/internal/sign_using_sha256.h" #include +#include #include namespace google { @@ -240,6 +243,74 @@ StatusOr MakeSelfSignedJWT( info.private_key); } +StatusOr> +CreateServiceAccountCredentialsFromJsonContents( + std::string const& contents, Options const& options, + HttpClientFactory client_factory) { + auto info = ParseServiceAccountCredentials(contents, "memory"); + if (!info) return info.status(); + + if (options.has()) { + auto const& s = options.get(); + std::set scopes{s.begin(), s.end()}; + info->scopes = std::move(scopes); + } + + // Verify this is usable before returning it. + auto const tp = std::chrono::system_clock::time_point{}; + auto const components = AssertionComponentsFromInfo(*info, tp); + auto jwt = MakeJWTAssertionNoThrow(components.first, components.second, + info->private_key); + if (!jwt) return jwt.status(); + return StatusOr>( + std::make_shared(*info, options, + std::move(client_factory))); +} + +StatusOr> +CreateServiceAccountCredentialsFromJsonFilePath( + std::string const& path, Options const& options, + HttpClientFactory client_factory) { + std::ifstream is(path); + if (!is.is_open()) { + // We use kUnknown here because we don't know if the file does not exist, or + // if we were unable to open it for some other reason. + return internal::UnknownError("Cannot open credentials file " + path, + GCP_ERROR_INFO()); + } + std::string contents(std::istreambuf_iterator{is}, {}); + return CreateServiceAccountCredentialsFromJsonContents( + std::move(contents), options, std::move(client_factory)); +} + +StatusOr> +CreateServiceAccountCredentialsFromP12FilePath( + std::string const& path, Options const& options, + HttpClientFactory client_factory) { + auto info = ParseServiceAccountP12File(path); + if (!info) return std::move(info).status(); + + if (options.has()) { + auto const& s = options.get(); + std::set scopes{s.begin(), s.end()}; + info->scopes = std::move(scopes); + } + return StatusOr>( + std::make_shared(*info, options, + std::move(client_factory))); +} + +StatusOr> +CreateServiceAccountCredentialsFromFilePath(std::string const& path, + Options const& options, + HttpClientFactory client_factory) { + auto credentials = CreateServiceAccountCredentialsFromJsonFilePath( + path, options, client_factory); + if (credentials) return *credentials; + return CreateServiceAccountCredentialsFromP12FilePath( + path, options, std::move(client_factory)); +} + ServiceAccountCredentials::ServiceAccountCredentials( ServiceAccountCredentialsInfo info, Options options, HttpClientFactory client_factory) @@ -313,7 +384,8 @@ bool ServiceAccountUseOAuth(ServiceAccountCredentialsInfo const& info) { } bool ServiceAccountCredentials::UseOAuth() { - return ServiceAccountUseOAuth(info_); + return options_.has() || + ServiceAccountUseOAuth(info_); } StatusOr ServiceAccountCredentials::GetTokenOAuth( diff --git a/google/cloud/internal/oauth2_service_account_credentials.h b/google/cloud/internal/oauth2_service_account_credentials.h index 20957c579337b..608d97b89d96e 100644 --- a/google/cloud/internal/oauth2_service_account_credentials.h +++ b/google/cloud/internal/oauth2_service_account_credentials.h @@ -24,6 +24,7 @@ #include "absl/types/optional.h" #include #include +#include #include namespace google { @@ -127,6 +128,30 @@ StatusOr MakeSelfSignedJWT( ServiceAccountCredentialsInfo const& info, std::chrono::system_clock::time_point tp); +StatusOr> +CreateServiceAccountCredentialsFromJsonContents( + std::string const& contents, Options const& options, + HttpClientFactory client_factory); + +StatusOr> +CreateServiceAccountCredentialsFromJsonFilePath( + std::string const& path, Options const& options, + HttpClientFactory client_factory); + +StatusOr> +CreateServiceAccountCredentialsFromP12FilePath( + std::string const& path, Options const& options, + HttpClientFactory client_factory); + +StatusOr> +CreateServiceAccountCredentialsFromFilePath(std::string const& path, + Options const& options, + HttpClientFactory client_factory); + +struct DisableSelfSignedJWTOption { + using Type = std::monostate; +}; + /** * Implements service account credentials for REST clients. * diff --git a/google/cloud/internal/unified_grpc_credentials.cc b/google/cloud/internal/unified_grpc_credentials.cc index 36b42821c6fc0..45851cce620a7 100644 --- a/google/cloud/internal/unified_grpc_credentials.cc +++ b/google/cloud/internal/unified_grpc_credentials.cc @@ -115,8 +115,28 @@ std::shared_ptr CreateAuthenticationStrategy( std::move(options)); } void visit(ServiceAccountConfig const& cfg) override { - result = std::make_unique( - cfg.json_object(), std::move(options)); + if (cfg.file_path().has_value()) { + std::ifstream is(*cfg.file_path()); + if (!is.is_open()) { + // We use kUnknown here because we don't know if the file does not + // exist, or if we were unable to open it for some other reason. + result = std::make_unique( + ErrorCredentialsConfig{UnknownError( + "Cannot open credentials file " + *cfg.file_path(), + GCP_ERROR_INFO())}); + } + std::string contents(std::istreambuf_iterator{is}, {}); + result = std::make_unique( + std::move(contents), std::move(options)); + } else if (cfg.json_object().has_value()) { + result = std::make_unique( + *cfg.json_object(), std::move(options)); + } else { + result = std::make_unique( + ErrorCredentialsConfig{InternalError( + "ServiceAccountConfig has neither json_object nor file_path", + GCP_ERROR_INFO())}); + } } void visit(ExternalAccountConfig const& cfg) override { grpc::SslCredentialsOptions ssl_options; diff --git a/google/cloud/internal/unified_rest_credentials.cc b/google/cloud/internal/unified_rest_credentials.cc index 65f1d873e369c..49a1eb4443ce4 100644 --- a/google/cloud/internal/unified_rest_credentials.cc +++ b/google/cloud/internal/unified_rest_credentials.cc @@ -15,6 +15,7 @@ #include "google/cloud/internal/unified_rest_credentials.h" #include "google/cloud/common_options.h" #include "google/cloud/internal/make_jwt_assertion.h" +#include "google/cloud/internal/make_status.h" #include "google/cloud/internal/oauth2_access_token_credentials.h" #include "google/cloud/internal/oauth2_anonymous_credentials.h" #include "google/cloud/internal/oauth2_api_key_credentials.h" @@ -49,25 +50,6 @@ std::shared_ptr MakeErrorCredentials( return std::make_shared(std::move(status)); } -std::shared_ptr -CreateServiceAccountCredentialsFromJsonContents( - std::string const& contents, Options const& options, - oauth2_internal::HttpClientFactory client_factory) { - auto info = - oauth2_internal::ParseServiceAccountCredentials(contents, "memory"); - if (!info) return MakeErrorCredentials(std::move(info).status()); - - // Verify this is usable before returning it. - auto const tp = std::chrono::system_clock::time_point{}; - auto const components = AssertionComponentsFromInfo(*info, tp); - auto jwt = internal::MakeJWTAssertionNoThrow( - components.first, components.second, info->private_key); - if (!jwt) return MakeErrorCredentials(std::move(jwt).status()); - - return std::make_shared( - *info, options, std::move(client_factory)); -} - } // namespace std::shared_ptr MapCredentials( @@ -120,10 +102,29 @@ std::shared_ptr MapCredentials( } void visit(ServiceAccountConfig const& cfg) override { - result = Decorate( - CreateServiceAccountCredentialsFromJsonContents( - cfg.json_object(), cfg.options(), std::move(client_factory_)), - cfg.options()); + if (cfg.file_path().has_value()) { + auto creds = + oauth2_internal::CreateServiceAccountCredentialsFromFilePath( + *cfg.file_path(), cfg.options(), std::move(client_factory_)); + if (creds) { + result = Decorate(*creds, cfg.options()); + } else { + result = MakeErrorCredentials(std::move(creds).status()); + } + } else if (cfg.json_object().has_value()) { + auto creds = + oauth2_internal::CreateServiceAccountCredentialsFromJsonContents( + *cfg.json_object(), cfg.options(), std::move(client_factory_)); + if (creds) { + result = Decorate(std::move(*creds), cfg.options()); + return; + } + result = MakeErrorCredentials(std::move(creds).status()); + } else { + result = MakeErrorCredentials(internal::InternalError( + "ServiceAccountConfig has neither json_object nor file_path", + GCP_ERROR_INFO())); + } } void visit(ExternalAccountConfig const& cfg) override { diff --git a/google/cloud/internal/unified_rest_credentials_test.cc b/google/cloud/internal/unified_rest_credentials_test.cc index 77eaa7ab7c3a8..3237467b16f0b 100644 --- a/google/cloud/internal/unified_rest_credentials_test.cc +++ b/google/cloud/internal/unified_rest_credentials_test.cc @@ -405,7 +405,7 @@ TEST(UnifiedRestCredentialsTest, ServiceAccount) { EXPECT_CALL(client_factory, Call).Times(0); auto const config = - internal::ServiceAccountConfig(contents.dump(), Options{}); + internal::ServiceAccountConfig(contents.dump(), absl::nullopt, Options{}); auto credentials = MapCredentials(config, client_factory.AsStdFunction()); auto access_token = credentials->GetToken(now); ASSERT_STATUS_OK(access_token); diff --git a/google/cloud/storage/benchmarks/throughput_experiment.cc b/google/cloud/storage/benchmarks/throughput_experiment.cc index 7599fc2a6c2d9..2611a280b43eb 100644 --- a/google/cloud/storage/benchmarks/throughput_experiment.cc +++ b/google/cloud/storage/benchmarks/throughput_experiment.cc @@ -16,6 +16,7 @@ #include "google/cloud/storage/benchmarks/benchmark_utils.h" #include "google/cloud/storage/client.h" #include "google/cloud/internal/make_status.h" +#include "google/cloud/internal/unified_rest_credentials.h" #if GOOGLE_CLOUD_CPP_STORAGE_HAVE_GRPC #include "google/cloud/storage/internal/grpc/ctype_cord_workaround.h" #include "google/cloud/grpc_error_delegate.h" @@ -260,8 +261,7 @@ class DownloadObjectLibcurl : public ThroughputExperiment { : endpoint_(options.rest_options.get()), target_api_version_path_( options.rest_options.get()), - creds_(google::cloud::storage::oauth2::GoogleDefaultCredentials() - .value()) { + creds_(rest_internal::MapCredentials(*MakeGoogleDefaultCredentials())) { if (target_api_version_path_.empty()) { target_api_version_path_ = "v1"; } @@ -271,13 +271,14 @@ class DownloadObjectLibcurl : public ThroughputExperiment { ThroughputResult Run(std::string const& bucket_name, std::string const& object_name, ThroughputExperimentConfig const& config) override { - auto header = creds_->AuthorizationHeader(); - if (!header) return {}; + auto token = creds_->GetToken(std::chrono::system_clock::now()); + if (!token) return {}; + auto header = std::string{"Authorization: "} + token->token; auto const start = std::chrono::system_clock::now(); auto timer = Timer::PerThread(); struct curl_slist* slist1 = nullptr; - slist1 = curl_slist_append(slist1, header->c_str()); + slist1 = curl_slist_append(slist1, header.c_str()); auto* hnd = curl_easy_init(); curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L); @@ -339,7 +340,7 @@ class DownloadObjectLibcurl : public ThroughputExperiment { private: std::string endpoint_; std::string target_api_version_path_; - std::shared_ptr creds_; + std::shared_ptr creds_; }; #if GOOGLE_CLOUD_CPP_STORAGE_HAVE_GRPC diff --git a/google/cloud/storage/ci/run_integration_tests_emulator_bazel.sh b/google/cloud/storage/ci/run_integration_tests_emulator_bazel.sh index 81b38c714b729..5963a918cd808 100755 --- a/google/cloud/storage/ci/run_integration_tests_emulator_bazel.sh +++ b/google/cloud/storage/ci/run_integration_tests_emulator_bazel.sh @@ -59,7 +59,6 @@ readonly PRODUCTION_ONLY_BASE=( "//google/cloud/storage/tests:alternative_endpoint_integration_test" "//google/cloud/storage/tests:key_file_integration_test" "//google/cloud/storage/tests:signed_url_integration_test" - "//google/cloud/storage/tests:unified_credentials_integration_test" ) for base in "${PRODUCTION_ONLY_BASE[@]}"; do production_only_targets+=( diff --git a/google/cloud/storage/client.cc b/google/cloud/storage/client.cc index 1758c361e5b70..0cbc613e0924d 100644 --- a/google/cloud/storage/client.cc +++ b/google/cloud/storage/client.cc @@ -16,10 +16,6 @@ #include "google/cloud/storage/idempotency_policy.h" #include "google/cloud/storage/internal/base64.h" #include "google/cloud/storage/internal/connection_factory.h" -#include "google/cloud/storage/internal/unified_rest_credentials.h" -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/oauth2/google_credentials.h" -#include "google/cloud/storage/oauth2/service_account_credentials.h" #include "google/cloud/storage/options.h" #include "google/cloud/internal/absl_str_cat_quiet.h" #include "google/cloud/internal/absl_str_join_quiet.h" @@ -28,11 +24,14 @@ #include "google/cloud/internal/filesystem.h" #include "google/cloud/internal/getenv.h" #include "google/cloud/internal/make_status.h" +#include "google/cloud/internal/oauth2_service_account_credentials.h" #include "google/cloud/internal/opentelemetry.h" #include "google/cloud/internal/populate_common_options.h" #include "google/cloud/internal/rest_options.h" #include "google/cloud/internal/rest_response.h" #include "google/cloud/internal/service_endpoint.h" +#include "google/cloud/internal/sha256_hash.h" +#include "google/cloud/internal/unified_rest_credentials.h" #include "google/cloud/log.h" #include "google/cloud/opentelemetry_options.h" #include "google/cloud/universe_domain_options.h" @@ -52,6 +51,32 @@ namespace cloud { namespace storage { GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN +namespace { +class WrapRestCredentials { + public: + explicit WrapRestCredentials( + std::shared_ptr impl) + : impl_(std::move(impl)) {} + + StatusOr AuthorizationHeader() { + return oauth2_internal::AuthenticationHeaderJoined(*impl_); + } + + StatusOr> SignBlob( + SigningAccount const& signing_account, std::string const& blob) const { + return impl_->SignBlob(signing_account.value_or(impl_->AccountEmail()), + blob); + } + + std::string AccountEmail() const { return impl_->AccountEmail(); } + std::string KeyId() const { return impl_->KeyId(); } + + private: + std::shared_ptr impl_; +}; + +} // namespace + using ::google::cloud::rest_internal::CurlHandle; static_assert(std::is_copy_constructible::value, @@ -249,17 +274,21 @@ std::string Client::SigningEmail(SigningAccount const& signing_account) const { if (signing_account.has_value()) { return signing_account.value(); } - return connection_->options().get()->AccountEmail(); + + auto credentials = WrapRestCredentials(rest_internal::MapCredentials( + *connection_->options().get())); + return credentials.AccountEmail(); } StatusOr Client::SignBlobImpl( SigningAccount const& signing_account, std::string const& string_to_sign) { - auto credentials = connection_->options().get(); + auto credentials = WrapRestCredentials(rest_internal::MapCredentials( + *connection_->options().get())); // First try to sign locally. - auto signed_blob = credentials->SignBlob(signing_account, string_to_sign); + auto signed_blob = credentials.SignBlob(signing_account, string_to_sign); if (signed_blob) { - return SignBlobResponseRaw{credentials->KeyId(), *std::move(signed_blob)}; + return SignBlobResponseRaw{credentials.KeyId(), *std::move(signed_blob)}; } // If signing locally fails that may be because the credentials do not @@ -480,8 +509,12 @@ Options ApplyPolicy(Options opts, IdempotencyPolicy const& p) { return opts; } -Options DefaultOptions(std::shared_ptr credentials, +Options DefaultOptions(std::shared_ptr const&, Options opts) { + return DefaultOptions(std::move(opts)); +} + +Options DefaultOptions(Options opts) { auto ud = GetEnv("GOOGLE_CLOUD_UNIVERSE_DOMAIN"); if (ud && !ud->empty()) { opts.set(*std::move(ud)); @@ -491,38 +524,42 @@ Options DefaultOptions(std::shared_ptr credentials, auto iam_ep = absl::StrCat(google::cloud::internal::UniverseDomainEndpoint( "https://iamcredentials.googleapis.com", opts), "/v1"); - auto o = - Options{} - .set(std::move(credentials)) - .set(std::move(gcs_ep)) - .set(std::move(iam_ep)) - .set("v1") - .set(DefaultConnectionPoolSize()) - .set( - GOOGLE_CLOUD_CPP_STORAGE_DEFAULT_DOWNLOAD_BUFFER_SIZE) - .set( - GOOGLE_CLOUD_CPP_STORAGE_DEFAULT_UPLOAD_BUFFER_SIZE) - .set( - GOOGLE_CLOUD_CPP_STORAGE_DEFAULT_MAXIMUM_SIMPLE_UPLOAD_SIZE) - .set(true) - .set(true) - .set(0) - .set(0) - .set(std::chrono::seconds( - GOOGLE_CLOUD_CPP_STORAGE_DEFAULT_DOWNLOAD_STALL_TIMEOUT)) - .set(1) - .set(1) - .set( - LimitedTimeRetryPolicy( - STORAGE_CLIENT_DEFAULT_MAXIMUM_RETRY_PERIOD) - .clone()) - .set( - ExponentialBackoffPolicy( - STORAGE_CLIENT_DEFAULT_INITIAL_BACKOFF_DELAY, - STORAGE_CLIENT_DEFAULT_MAXIMUM_BACKOFF_DELAY, - STORAGE_CLIENT_DEFAULT_BACKOFF_SCALING) - .clone()) - .set(AlwaysRetryIdempotencyPolicy().clone()); + Options o; + if (!opts.has()) { + o.set(MakeGoogleDefaultCredentials()); + } + // Storage has more stringent requirements w.r.t. self-signed JWTs + // than most services. Any scope makes the self-signed JWTs unusable with + // storage, but they remain usable with other services. We need to disable + // self-signed JWTs. + o.set({}) + .set(std::move(gcs_ep)) + .set(std::move(iam_ep)) + .set("v1") + .set(DefaultConnectionPoolSize()) + .set( + GOOGLE_CLOUD_CPP_STORAGE_DEFAULT_DOWNLOAD_BUFFER_SIZE) + .set( + GOOGLE_CLOUD_CPP_STORAGE_DEFAULT_UPLOAD_BUFFER_SIZE) + .set( + GOOGLE_CLOUD_CPP_STORAGE_DEFAULT_MAXIMUM_SIMPLE_UPLOAD_SIZE) + .set(true) + .set(true) + .set(0) + .set(0) + .set(std::chrono::seconds( + GOOGLE_CLOUD_CPP_STORAGE_DEFAULT_DOWNLOAD_STALL_TIMEOUT)) + .set(1) + .set(1) + .set( + LimitedTimeRetryPolicy(STORAGE_CLIENT_DEFAULT_MAXIMUM_RETRY_PERIOD) + .clone()) + .set( + ExponentialBackoffPolicy(STORAGE_CLIENT_DEFAULT_INITIAL_BACKOFF_DELAY, + STORAGE_CLIENT_DEFAULT_MAXIMUM_BACKOFF_DELAY, + STORAGE_CLIENT_DEFAULT_BACKOFF_SCALING) + .clone()) + .set(AlwaysRetryIdempotencyPolicy().clone()); o = google::cloud::internal::MergeOptions(std::move(opts), std::move(o)); // If the application did not set `DownloadStallTimeoutOption` then use the @@ -596,22 +633,19 @@ Options DefaultOptions(std::shared_ptr credentials, } Options DefaultOptionsWithCredentials(Options opts) { - if (opts.has()) { - auto credentials = opts.get(); - return internal::DefaultOptions(std::move(credentials), std::move(opts)); - } if (opts.has()) { auto credentials = - internal::MapCredentials(*opts.get()); + rest_internal::MapCredentials(*opts.get()); return internal::DefaultOptions(std::move(credentials), std::move(opts)); } if (GetEmulator().has_value()) { return internal::DefaultOptions( - internal::MapCredentials(*google::cloud::MakeInsecureCredentials()), + rest_internal::MapCredentials( + *google::cloud::MakeInsecureCredentials()), std::move(opts)); } - auto credentials = - internal::MapCredentials(*google::cloud::MakeGoogleDefaultCredentials( + auto credentials = rest_internal::MapCredentials( + *google::cloud::MakeGoogleDefaultCredentials( google::cloud::internal::MakeAuthOptions(opts))); return internal::DefaultOptions(std::move(credentials), std::move(opts)); } diff --git a/google/cloud/storage/client.h b/google/cloud/storage/client.h index 6c8b8ec463d6f..9b62a8be5d088 100644 --- a/google/cloud/storage/client.h +++ b/google/cloud/storage/client.h @@ -28,13 +28,13 @@ #include "google/cloud/storage/list_objects_reader.h" #include "google/cloud/storage/notification_event_type.h" #include "google/cloud/storage/notification_payload_format.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/object_rewriter.h" #include "google/cloud/storage/object_stream.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/upload_options.h" #include "google/cloud/storage/version.h" #include "google/cloud/internal/group_options.h" +#include "google/cloud/internal/oauth2_credentials.h" #include "google/cloud/internal/throw_delegate.h" #include "google/cloud/options.h" #include "google/cloud/status.h" @@ -67,8 +67,10 @@ Options ApplyPolicies(Options opts, P&& head, Policies&&... tail) { return ApplyPolicies(std::move(opts), std::forward(tail)...); } -Options DefaultOptions(std::shared_ptr credentials, - Options opts); +Options DefaultOptions( + std::shared_ptr const& credentials, + Options opts); +Options DefaultOptions(Options opts = {}); Options DefaultOptionsWithCredentials(Options opts); } // namespace internal diff --git a/google/cloud/storage/client_bucket_acl_test.cc b/google/cloud/storage/client_bucket_acl_test.cc index 09828ed385151..0c481bd50622b 100644 --- a/google/cloud/storage/client_bucket_acl_test.cc +++ b/google/cloud/storage/client_bucket_acl_test.cc @@ -14,7 +14,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/bucket_access_control_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/client_bucket_test.cc b/google/cloud/storage/client_bucket_test.cc index d7e426f79c6d6..9bc25ec988781 100644 --- a/google/cloud/storage/client_bucket_test.cc +++ b/google/cloud/storage/client_bucket_test.cc @@ -14,7 +14,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/bucket_metadata_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/client_default_object_acl_test.cc b/google/cloud/storage/client_default_object_acl_test.cc index 7505e6a0ba26a..fb9ff8400a0c9 100644 --- a/google/cloud/storage/client_default_object_acl_test.cc +++ b/google/cloud/storage/client_default_object_acl_test.cc @@ -14,7 +14,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/object_access_control_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/client_notifications_test.cc b/google/cloud/storage/client_notifications_test.cc index dae426e0f7a38..e1d827bbdc990 100644 --- a/google/cloud/storage/client_notifications_test.cc +++ b/google/cloud/storage/client_notifications_test.cc @@ -16,7 +16,6 @@ #include "google/cloud/storage/internal/notification_metadata_parser.h" #include "google/cloud/storage/notification_event_type.h" #include "google/cloud/storage/notification_payload_format.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/client_object_acl_test.cc b/google/cloud/storage/client_object_acl_test.cc index fcec747c12599..3ef51a51ec21f 100644 --- a/google/cloud/storage/client_object_acl_test.cc +++ b/google/cloud/storage/client_object_acl_test.cc @@ -14,7 +14,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/object_access_control_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/client_object_copy_test.cc b/google/cloud/storage/client_object_copy_test.cc index e3ccfe6060877..873721aaa5b60 100644 --- a/google/cloud/storage/client_object_copy_test.cc +++ b/google/cloud/storage/client_object_copy_test.cc @@ -14,7 +14,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/object_metadata_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/client_object_test.cc b/google/cloud/storage/client_object_test.cc index ab743c2aafda4..06b0cb060a6bd 100644 --- a/google/cloud/storage/client_object_test.cc +++ b/google/cloud/storage/client_object_test.cc @@ -14,7 +14,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/object_metadata_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/client_service_account_test.cc b/google/cloud/storage/client_service_account_test.cc index 38301786909c9..3be52afdefc07 100644 --- a/google/cloud/storage/client_service_account_test.cc +++ b/google/cloud/storage/client_service_account_test.cc @@ -15,7 +15,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/hmac_key_metadata_parser.h" #include "google/cloud/storage/internal/service_account_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/client_sign_policy_document_test.cc b/google/cloud/storage/client_sign_policy_document_test.cc index 860b5792cebb8..0763425b3ac96 100644 --- a/google/cloud/storage/client_sign_policy_document_test.cc +++ b/google/cloud/storage/client_sign_policy_document_test.cc @@ -14,8 +14,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/base64.h" -#include "google/cloud/storage/oauth2/google_application_default_credentials_file.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" #include "google/cloud/storage/testing/mock_client.h" diff --git a/google/cloud/storage/client_sign_url_test.cc b/google/cloud/storage/client_sign_url_test.cc index cd0adb12f5190..a136f226a2323 100644 --- a/google/cloud/storage/client_sign_url_test.cc +++ b/google/cloud/storage/client_sign_url_test.cc @@ -13,8 +13,6 @@ // limitations under the License. #include "google/cloud/storage/client.h" -#include "google/cloud/storage/oauth2/google_application_default_credentials_file.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" #include "google/cloud/testing_util/status_matchers.h" diff --git a/google/cloud/storage/client_test.cc b/google/cloud/storage/client_test.cc index f3dd8ae3a7310..8d49ac1d7ea72 100644 --- a/google/cloud/storage/client_test.cc +++ b/google/cloud/storage/client_test.cc @@ -13,7 +13,6 @@ // limitations under the License. #include "google/cloud/storage/client.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/mock_client.h" @@ -261,8 +260,7 @@ TEST_F(ClientTest, OTelDisableTracing) { TEST_F(ClientTest, EndpointsDefault) { testing_util::ScopedEnvironment endpoint("CLOUD_STORAGE_EMULATOR_ENDPOINT", {}); - auto options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto options = internal::DefaultOptions(); EXPECT_EQ("https://storage.googleapis.com", options.get()); EXPECT_EQ("https://iamcredentials.googleapis.com/v1", @@ -273,7 +271,6 @@ TEST_F(ClientTest, EndpointsOverride) { testing_util::ScopedEnvironment endpoint("CLOUD_STORAGE_EMULATOR_ENDPOINT", {}); auto options = internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{}.set("http://127.0.0.1.nip.io:1234")); EXPECT_EQ("http://127.0.0.1.nip.io:1234", options.get()); EXPECT_EQ("https://iamcredentials.googleapis.com/v1", @@ -283,8 +280,7 @@ TEST_F(ClientTest, EndpointsOverride) { TEST_F(ClientTest, EndpointsEmulator) { testing_util::ScopedEnvironment endpoint("CLOUD_STORAGE_EMULATOR_ENDPOINT", "http://localhost:1234"); - auto options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto options = internal::DefaultOptions(); EXPECT_EQ("http://localhost:1234", options.get()); EXPECT_EQ("http://localhost:1234/iamapi", options.get()); } @@ -293,23 +289,21 @@ TEST_F(ClientTest, OldEndpointsEmulator) { google::cloud::testing_util::UnsetEnv("CLOUD_STORAGE_EMULATOR_ENDPOINT"); testing_util::ScopedEnvironment endpoint("CLOUD_STORAGE_TESTBENCH_ENDPOINT", "http://localhost:1234"); - auto options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto options = internal::DefaultOptions(); EXPECT_EQ("http://localhost:1234", options.get()); EXPECT_EQ("http://localhost:1234/iamapi", options.get()); } TEST_F(ClientTest, DefaultOptions) { - auto o = internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto o = internal::DefaultOptions(); EXPECT_EQ("https://storage.googleapis.com", o.get()); // Verify any set values are respected overridden. o = internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{}.set("https://private.googleapis.com")); EXPECT_EQ("https://private.googleapis.com", o.get()); - o = internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + o = internal::DefaultOptions(); EXPECT_EQ("https://storage.googleapis.com", o.get()); EXPECT_EQ("https://iamcredentials.googleapis.com/v1", o.get()); @@ -354,7 +348,6 @@ TEST_F(ClientTest, DefaultOptions) { TEST_F(ClientTest, IncorporatesUniverseDomain) { auto o = internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{}.set( "my-ud.net")); EXPECT_EQ(o.get(), "https://storage.my-ud.net"); @@ -365,7 +358,6 @@ TEST_F(ClientTest, IncorporatesUniverseDomainEnvVar) { ScopedEnvironment ud("GOOGLE_CLOUD_UNIVERSE_DOMAIN", "ud-env-var.net"); auto o = internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{}.set( "ud-option.net")); EXPECT_EQ(o.get(), "https://storage.ud-env-var.net"); @@ -377,7 +369,6 @@ TEST_F(ClientTest, CustomEndpointOverridesUniverseDomain) { ScopedEnvironment ud("GOOGLE_CLOUD_UNIVERSE_DOMAIN", "ud-env-var.net"); auto o = internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{} .set("https://custom-storage.googleapis.com") .set( @@ -392,7 +383,6 @@ TEST_F(ClientTest, CustomEndpointOverridesUniverseDomain) { TEST_F(ClientTest, HttpVersion) { namespace rest = ::google::cloud::rest_internal; auto const options = internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{}.set("2.0")); EXPECT_EQ("2.0", options.get()); } @@ -400,7 +390,6 @@ TEST_F(ClientTest, HttpVersion) { TEST_F(ClientTest, CAPathOption) { namespace rest = ::google::cloud::rest_internal; auto const options = internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{}.set("test-only")); EXPECT_EQ("test-only", options.get()); } @@ -409,8 +398,7 @@ TEST_F(ClientTest, LoggingWithoutEnv) { ScopedEnvironment env_common("GOOGLE_CLOUD_CPP_ENABLE_TRACING", absl::nullopt); ScopedEnvironment env("CLOUD_STORAGE_ENABLE_TRACING", absl::nullopt); - auto const options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto const options = internal::DefaultOptions(); EXPECT_FALSE(options.has()); } @@ -418,8 +406,7 @@ TEST_F(ClientTest, LoggingWithEnv) { ScopedEnvironment env_common("GOOGLE_CLOUD_CPP_ENABLE_TRACING", absl::nullopt); ScopedEnvironment env("CLOUD_STORAGE_ENABLE_TRACING", "rpc,http"); - auto const options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto const options = internal::DefaultOptions(); EXPECT_THAT(options.get(), UnorderedElementsAre("rpc", "http")); } @@ -427,43 +414,37 @@ TEST_F(ClientTest, LoggingWithEnv) { TEST_F(ClientTest, TracingWithoutEnv) { ScopedEnvironment env("GOOGLE_CLOUD_CPP_OPENTELEMETRY_TRACING", absl::nullopt); - auto options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto options = internal::DefaultOptions(); EXPECT_FALSE(options.get()); options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), - Options{}.set(true)); + internal::DefaultOptions(Options{}.set(true)); EXPECT_TRUE(options.get()); } TEST_F(ClientTest, TracingWithEnv) { ScopedEnvironment env("GOOGLE_CLOUD_CPP_OPENTELEMETRY_TRACING", "ON"); auto const options = internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{}.set(false)); EXPECT_TRUE(options.get()); } TEST_F(ClientTest, ProjectIdWithoutEnv) { ScopedEnvironment env("GOOGLE_CLOUD_PROJECT", absl::nullopt); - auto const options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto const options = internal::DefaultOptions(); EXPECT_FALSE(options.has()); } TEST_F(ClientTest, ProjectIdtWithEnv) { ScopedEnvironment env("GOOGLE_CLOUD_PROJECT", "my-project"); - auto const options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), {}); + auto const options = internal::DefaultOptions(); EXPECT_EQ("my-project", options.get()); } TEST_F(ClientTest, OverrideWithRestInternal) { namespace rest = ::google::cloud::rest_internal; auto const options = - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), - Options{} + internal::DefaultOptions(Options{} .set(1234) .set(2345)); EXPECT_EQ(1234, options.get()); @@ -472,29 +453,24 @@ TEST_F(ClientTest, OverrideWithRestInternal) { TEST_F(ClientTest, Timeouts) { EXPECT_EQ(std::chrono::seconds(42), - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), - Options{}.set( + internal::DefaultOptions(Options{}.set( std::chrono::seconds(42))) .get()); EXPECT_EQ(std::chrono::seconds(7), internal::DefaultOptions( - oauth2::CreateAnonymousCredentials(), Options{} .set(std::chrono::seconds(42)) .set(std::chrono::seconds(7))) .get()); EXPECT_EQ(std::chrono::seconds(7), - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), - Options{}.set( + internal::DefaultOptions(Options{}.set( std::chrono::seconds(7))) .get()); - EXPECT_NE( - std::chrono::seconds(0), - internal::DefaultOptions(oauth2::CreateAnonymousCredentials(), Options{}) - .get()); + EXPECT_NE(std::chrono::seconds(0), + internal::DefaultOptions().get()); } } // namespace diff --git a/google/cloud/storage/client_write_object_test.cc b/google/cloud/storage/client_write_object_test.cc index 49e91651bb229..65a04e2f04ae0 100644 --- a/google/cloud/storage/client_write_object_test.cc +++ b/google/cloud/storage/client_write_object_test.cc @@ -14,7 +14,6 @@ #include "google/cloud/storage/client.h" #include "google/cloud/storage/internal/object_metadata_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" #include "google/cloud/storage/testing/client_unit_test.h" diff --git a/google/cloud/storage/google_cloud_cpp_storage.bzl b/google/cloud/storage/google_cloud_cpp_storage.bzl index 7b1dda7134f8e..7f244903705ab 100644 --- a/google/cloud/storage/google_cloud_cpp_storage.bzl +++ b/google/cloud/storage/google_cloud_cpp_storage.bzl @@ -45,7 +45,6 @@ google_cloud_cpp_storage_hdrs = [ "iam_policy.h", "idempotency_policy.h", "include_folders_as_prefixes.h", - "internal/access_token_credentials.h", "internal/base64.h", "internal/binary_data_as_debug_string.h", "internal/bucket_access_control_parser.h", @@ -62,7 +61,6 @@ google_cloud_cpp_storage_hdrs = [ "internal/curl/request_builder.h", "internal/default_object_acl_requests.h", "internal/empty_response.h", - "internal/error_credentials.h", "internal/generate_message_boundary.h", "internal/generic_object_request.h", "internal/generic_request.h", @@ -77,7 +75,6 @@ google_cloud_cpp_storage_hdrs = [ "internal/hmac_key_metadata_parser.h", "internal/hmac_key_requests.h", "internal/http_response.h", - "internal/impersonate_service_account_credentials.h", "internal/lifecycle_rule_parser.h", "internal/logging_stub.h", "internal/make_jwt_assertion.h", @@ -108,7 +105,6 @@ google_cloud_cpp_storage_hdrs = [ "internal/tracing_connection.h", "internal/tracing_object_read_source.h", "internal/tuple_filter.h", - "internal/unified_rest_credentials.h", "internal/well_known_parameters_impl.h", "lifecycle_rule.h", "list_buckets_extended_reader.h", @@ -119,15 +115,6 @@ google_cloud_cpp_storage_hdrs = [ "notification_event_type.h", "notification_metadata.h", "notification_payload_format.h", - "oauth2/anonymous_credentials.h", - "oauth2/authorized_user_credentials.h", - "oauth2/compute_engine_credentials.h", - "oauth2/credential_constants.h", - "oauth2/credentials.h", - "oauth2/google_application_default_credentials_file.h", - "oauth2/google_credentials.h", - "oauth2/refreshing_credentials_wrapper.h", - "oauth2/service_account_credentials.h", "object_access_control.h", "object_metadata.h", "object_read_stream.h", @@ -173,7 +160,6 @@ google_cloud_cpp_storage_srcs = [ "hmac_key_metadata.cc", "iam_policy.cc", "idempotency_policy.cc", - "internal/access_token_credentials.cc", "internal/base64.cc", "internal/bucket_access_control_parser.cc", "internal/bucket_acl_requests.cc", @@ -186,7 +172,6 @@ google_cloud_cpp_storage_srcs = [ "internal/crc32c.cc", "internal/default_object_acl_requests.cc", "internal/empty_response.cc", - "internal/error_credentials.cc", "internal/generate_message_boundary.cc", "internal/generic_stub_adapter.cc", "internal/generic_stub_factory.cc", @@ -198,7 +183,6 @@ google_cloud_cpp_storage_srcs = [ "internal/hmac_key_metadata_parser.cc", "internal/hmac_key_requests.cc", "internal/http_response.cc", - "internal/impersonate_service_account_credentials.cc", "internal/lifecycle_rule_parser.cc", "internal/logging_stub.cc", "internal/make_jwt_assertion.cc", @@ -228,21 +212,12 @@ google_cloud_cpp_storage_srcs = [ "internal/storage_connection.cc", "internal/tracing_connection.cc", "internal/tracing_object_read_source.cc", - "internal/unified_rest_credentials.cc", "internal/win32/hash_function_impl.cc", "lifecycle_rule.cc", "list_buckets_reader.cc", "list_hmac_keys_reader.cc", "list_objects_reader.cc", "notification_metadata.cc", - "oauth2/anonymous_credentials.cc", - "oauth2/authorized_user_credentials.cc", - "oauth2/compute_engine_credentials.cc", - "oauth2/credentials.cc", - "oauth2/google_application_default_credentials_file.cc", - "oauth2/google_credentials.cc", - "oauth2/refreshing_credentials_wrapper.cc", - "oauth2/service_account_credentials.cc", "object_access_control.cc", "object_metadata.cc", "object_read_stream.cc", diff --git a/google/cloud/storage/google_cloud_cpp_storage.cmake b/google/cloud/storage/google_cloud_cpp_storage.cmake index c374c34ce4a34..f24107bca2cd9 100644 --- a/google/cloud/storage/google_cloud_cpp_storage.cmake +++ b/google/cloud/storage/google_cloud_cpp_storage.cmake @@ -67,8 +67,6 @@ add_library( idempotency_policy.cc idempotency_policy.h include_folders_as_prefixes.h - internal/access_token_credentials.cc - internal/access_token_credentials.h internal/base64.cc internal/base64.h internal/binary_data_as_debug_string.h @@ -97,8 +95,6 @@ add_library( internal/default_object_acl_requests.h internal/empty_response.cc internal/empty_response.h - internal/error_credentials.cc - internal/error_credentials.h internal/generate_message_boundary.cc internal/generate_message_boundary.h internal/generic_object_request.h @@ -124,8 +120,6 @@ add_library( internal/hmac_key_requests.h internal/http_response.cc internal/http_response.h - internal/impersonate_service_account_credentials.cc - internal/impersonate_service_account_credentials.h internal/lifecycle_rule_parser.cc internal/lifecycle_rule_parser.h internal/logging_stub.cc @@ -185,8 +179,6 @@ add_library( internal/tracing_object_read_source.cc internal/tracing_object_read_source.h internal/tuple_filter.h - internal/unified_rest_credentials.cc - internal/unified_rest_credentials.h internal/well_known_parameters_impl.h internal/win32/hash_function_impl.cc lifecycle_rule.cc @@ -203,23 +195,6 @@ add_library( notification_metadata.cc notification_metadata.h notification_payload_format.h - oauth2/anonymous_credentials.cc - oauth2/anonymous_credentials.h - oauth2/authorized_user_credentials.cc - oauth2/authorized_user_credentials.h - oauth2/compute_engine_credentials.cc - oauth2/compute_engine_credentials.h - oauth2/credential_constants.h - oauth2/credentials.cc - oauth2/credentials.h - oauth2/google_application_default_credentials_file.cc - oauth2/google_application_default_credentials_file.h - oauth2/google_credentials.cc - oauth2/google_credentials.h - oauth2/refreshing_credentials_wrapper.cc - oauth2/refreshing_credentials_wrapper.h - oauth2/service_account_credentials.cc - oauth2/service_account_credentials.h object_access_control.cc object_access_control.h object_metadata.cc @@ -441,7 +416,6 @@ if (BUILD_TESTING) hashing_options_test.cc hmac_key_metadata_test.cc idempotency_policy_test.cc - internal/access_token_credentials_test.cc internal/base64_test.cc internal/bucket_acl_requests_test.cc internal/bucket_requests_test.cc @@ -468,7 +442,6 @@ if (BUILD_TESTING) internal/hash_values_test.cc internal/hmac_key_requests_test.cc internal/http_response_test.cc - internal/impersonate_service_account_credentials_test.cc internal/logging_stub_test.cc internal/make_jwt_assertion_test.cc internal/md5hash_test.cc @@ -492,7 +465,6 @@ if (BUILD_TESTING) internal/tracing_connection_test.cc internal/tracing_object_read_source_test.cc internal/tuple_filter_test.cc - internal/unified_rest_credentials_test.cc lifecycle_rule_test.cc list_buckets_extended_reader_test.cc list_buckets_reader_test.cc @@ -500,12 +472,6 @@ if (BUILD_TESTING) list_objects_and_prefixes_reader_test.cc list_objects_reader_test.cc notification_metadata_test.cc - oauth2/anonymous_credentials_test.cc - oauth2/authorized_user_credentials_test.cc - oauth2/compute_engine_credentials_test.cc - oauth2/google_application_default_credentials_file_test.cc - oauth2/google_credentials_test.cc - oauth2/service_account_credentials_test.cc object_access_control_test.cc object_metadata_test.cc object_retention_test.cc diff --git a/google/cloud/storage/internal/access_token_credentials.cc b/google/cloud/storage/internal/access_token_credentials.cc deleted file mode 100644 index f0315c76adb1a..0000000000000 --- a/google/cloud/storage/internal/access_token_credentials.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/internal/access_token_credentials.h" - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { - -AccessTokenCredentials::AccessTokenCredentials( - google::cloud::AccessToken const& access_token) - : header_("Authorization: Bearer " + access_token.token) {} - -StatusOr AccessTokenCredentials::AuthorizationHeader() { - return header_; -} - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/internal/access_token_credentials.h b/google/cloud/storage/internal/access_token_credentials.h deleted file mode 100644 index 5e8920d3b93a0..0000000000000 --- a/google/cloud/storage/internal/access_token_credentials.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_ACCESS_TOKEN_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_ACCESS_TOKEN_CREDENTIALS_H - -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/version.h" -#include "google/cloud/internal/credentials_impl.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { - -class AccessTokenCredentials : public oauth2::Credentials { - public: - explicit AccessTokenCredentials( - google::cloud::AccessToken const& access_token); - - StatusOr AuthorizationHeader() override; - - private: - std::string header_; -}; - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_ACCESS_TOKEN_CREDENTIALS_H diff --git a/google/cloud/storage/internal/access_token_credentials_test.cc b/google/cloud/storage/internal/access_token_credentials_test.cc deleted file mode 100644 index b668d628d758f..0000000000000 --- a/google/cloud/storage/internal/access_token_credentials_test.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/internal/access_token_credentials.h" -#include "google/cloud/testing_util/status_matchers.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { -namespace { - -using ::google::cloud::AccessToken; - -TEST(AccessTokenCredentials, Simple) { - auto const expiration = - std::chrono::system_clock::now() - std::chrono::minutes(10); - - AccessTokenCredentials tested(AccessToken{"token1", expiration}); - EXPECT_EQ("Authorization: Bearer token1", - tested.AuthorizationHeader().value()); - EXPECT_EQ("Authorization: Bearer token1", - tested.AuthorizationHeader().value()); -} - -} // namespace -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/internal/error_credentials.cc b/google/cloud/storage/internal/error_credentials.cc deleted file mode 100644 index bea0279b63952..0000000000000 --- a/google/cloud/storage/internal/error_credentials.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/internal/error_credentials.h" - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { - -StatusOr ErrorCredentials::AuthorizationHeader() { - return status_; -} - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/internal/error_credentials.h b/google/cloud/storage/internal/error_credentials.h deleted file mode 100644 index 56a394d0be9a2..0000000000000 --- a/google/cloud/storage/internal/error_credentials.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_ERROR_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_ERROR_CREDENTIALS_H - -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/version.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { - -/** - * Report errors loading credentials when the RPC is called. - * - * With the "unified authentication client" approach the application just - * declares its *intent*, e.g., "load the default credentials", the actual work - * is delayed and depends on how the client library is implemented. We also want - * the behavior with gRPC and REST to be as similar as possible. - * - * For some credential types (e.g. service account impersonation) there may be - * problems with the credentials that are not manifest until after several RPCs - * succeed. - * - * For gRPC, creating the credentials always succeeds, but using them may fail. - * - * With REST we typically validate the credentials when loaded, and then again - * when we try to use them. - * - * This last approach was problematic, because it made some credentials fail - * early. This class allows us to treat all credentials, including REST - * credentials that failed to load as "evaluated at RPC time". - */ -class ErrorCredentials : public oauth2::Credentials { - public: - explicit ErrorCredentials(Status status) : status_(std::move(status)) {} - - StatusOr AuthorizationHeader() override; - - private: - Status status_; -}; - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_ERROR_CREDENTIALS_H diff --git a/google/cloud/storage/internal/generic_stub.h b/google/cloud/storage/internal/generic_stub.h index 1e03abe2f5a4c..7c48c83751548 100644 --- a/google/cloud/storage/internal/generic_stub.h +++ b/google/cloud/storage/internal/generic_stub.h @@ -28,7 +28,6 @@ #include "google/cloud/storage/internal/object_requests.h" #include "google/cloud/storage/internal/service_account_requests.h" #include "google/cloud/storage/internal/sign_blob_requests.h" -#include "google/cloud/storage/oauth2/credentials.h" #include "google/cloud/storage/object_metadata.h" #include "google/cloud/storage/service_account.h" #include "google/cloud/internal/rest_context.h" diff --git a/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc b/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc index 4a1769efad220..e0ca8a101528c 100644 --- a/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc +++ b/google/cloud/storage/internal/grpc/bucket_request_parser_test.cc @@ -13,7 +13,6 @@ // limitations under the License. #include "google/cloud/storage/internal/grpc/bucket_request_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/testing_util/is_proto_equal.h" #include "google/cloud/testing_util/status_matchers.h" #include diff --git a/google/cloud/storage/internal/grpc/default_options.cc b/google/cloud/storage/internal/grpc/default_options.cc index 77cfc1803f893..27ac0996a369b 100644 --- a/google/cloud/storage/internal/grpc/default_options.cc +++ b/google/cloud/storage/internal/grpc/default_options.cc @@ -77,13 +77,18 @@ Options DefaultOptionsGrpc( google::cloud::internal::MakeAuthOptions(options))); } + auto const preserve_creds = + GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TESTING_PRESERVE_CREDENTIALS"); auto const testbench = GetEnv("CLOUD_STORAGE_EXPERIMENTAL_GRPC_TESTBENCH_ENDPOINT"); if (testbench.has_value() && !testbench->empty()) { options.set(*testbench); - // The emulator does not support HTTPS or authentication, use insecure - // (sometimes called "anonymous") credentials, which disable SSL. - options.set(MakeInsecureCredentials()); + + if (!preserve_creds.has_value()) { + // The emulator does not support HTTPS or authentication, use insecure + // (sometimes called "anonymous") credentials, which disable SSL. + options.set(MakeInsecureCredentials()); + } } // gRPC <= 1.64 may crash when metrics are enabled, so we don't enable them by diff --git a/google/cloud/storage/internal/grpc/object_request_parser_test.cc b/google/cloud/storage/internal/grpc/object_request_parser_test.cc index 8ee4db454a5e1..ad9c521c18759 100644 --- a/google/cloud/storage/internal/grpc/object_request_parser_test.cc +++ b/google/cloud/storage/internal/grpc/object_request_parser_test.cc @@ -15,7 +15,6 @@ #include "google/cloud/storage/internal/grpc/object_request_parser.h" #include "google/cloud/storage/internal/grpc/stub.h" #include "google/cloud/storage/internal/hash_function_impl.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/grpc_options.h" #include "google/cloud/testing_util/is_proto_equal.h" #include "google/cloud/testing_util/scoped_environment.h" diff --git a/google/cloud/storage/internal/grpc/stub_insert_object_media_test.cc b/google/cloud/storage/internal/grpc/stub_insert_object_media_test.cc index 3318c9fbba411..4481b0089a9c9 100644 --- a/google/cloud/storage/internal/grpc/stub_insert_object_media_test.cc +++ b/google/cloud/storage/internal/grpc/stub_insert_object_media_test.cc @@ -13,7 +13,6 @@ // limitations under the License. #include "google/cloud/storage/internal/grpc/stub.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/options.h" #include "google/cloud/storage/testing/mock_storage_stub.h" #include "google/cloud/grpc_options.h" diff --git a/google/cloud/storage/internal/impersonate_service_account_credentials.cc b/google/cloud/storage/internal/impersonate_service_account_credentials.cc deleted file mode 100644 index f63f325eedea9..0000000000000 --- a/google/cloud/storage/internal/impersonate_service_account_credentials.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/internal/impersonate_service_account_credentials.h" -#include "google/cloud/internal/rest_client.h" -#include "google/cloud/internal/unified_rest_credentials.h" -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { -namespace { - -oauth2_internal::GenerateAccessTokenRequest MakeRequest( - google::cloud::internal::ImpersonateServiceAccountConfig const& config) { - return oauth2_internal::GenerateAccessTokenRequest{ - /*.service_account=*/config.target_service_account(), - /*.lifetime=*/config.lifetime(), - /*.scopes=*/config.scopes(), - /*.delegates=*/config.delegates(), - }; -} - -auto constexpr kUseSlack = std::chrono::seconds(30); - -} // namespace - -ImpersonateServiceAccountCredentials::ImpersonateServiceAccountCredentials( - google::cloud::internal::ImpersonateServiceAccountConfig const& config) - : ImpersonateServiceAccountCredentials( - config, oauth2_internal::MakeMinimalIamCredentialsRestStub( - rest_internal::MapCredentials(*config.base_credentials()), - config.options(), [](Options const& o) { - return rest_internal::MakeDefaultRestClient("", o); - })) {} - -ImpersonateServiceAccountCredentials::ImpersonateServiceAccountCredentials( - google::cloud::internal::ImpersonateServiceAccountConfig const& config, - std::shared_ptr stub) - : stub_(std::move(stub)), request_(MakeRequest(config)) {} - -StatusOr -ImpersonateServiceAccountCredentials::AuthorizationHeader() { - return AuthorizationHeader(std::chrono::system_clock::now()); -} - -StatusOr ImpersonateServiceAccountCredentials::AuthorizationHeader( - std::chrono::system_clock::time_point now) { - std::unique_lock lk(mu_); - if (now + kUseSlack <= expiration_) return header_; - auto response = stub_->GenerateAccessToken(request_); - if (!response) return std::move(response).status(); - expiration_ = response->expiration; - header_ = "Authorization: Bearer " + response->token; - return header_; -} - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/internal/impersonate_service_account_credentials.h b/google/cloud/storage/internal/impersonate_service_account_credentials.h deleted file mode 100644 index 463868eab93f5..0000000000000 --- a/google/cloud/storage/internal/impersonate_service_account_credentials.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_IMPERSONATE_SERVICE_ACCOUNT_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_IMPERSONATE_SERVICE_ACCOUNT_CREDENTIALS_H - -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/version.h" -#include "google/cloud/credentials.h" -#include "google/cloud/internal/oauth2_minimal_iam_credentials_rest.h" -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { - -class ImpersonateServiceAccountCredentials : public oauth2::Credentials { - public: - explicit ImpersonateServiceAccountCredentials( - google::cloud::internal::ImpersonateServiceAccountConfig const& config); - explicit ImpersonateServiceAccountCredentials( - google::cloud::internal::ImpersonateServiceAccountConfig const& config, - std::shared_ptr stub); - - StatusOr AuthorizationHeader() override; - StatusOr AuthorizationHeader( - std::chrono::system_clock::time_point now); - - private: - std::shared_ptr stub_; - oauth2_internal::GenerateAccessTokenRequest request_; - std::mutex mu_; - std::string header_; - std::chrono::system_clock::time_point expiration_; -}; - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_IMPERSONATE_SERVICE_ACCOUNT_CREDENTIALS_H diff --git a/google/cloud/storage/internal/impersonate_service_account_credentials_test.cc b/google/cloud/storage/internal/impersonate_service_account_credentials_test.cc deleted file mode 100644 index e4009eafeea03..0000000000000 --- a/google/cloud/storage/internal/impersonate_service_account_credentials_test.cc +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/internal/impersonate_service_account_credentials.h" -#include "google/cloud/testing_util/status_matchers.h" -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { -namespace { - -using ::google::cloud::AccessToken; -using ::google::cloud::AccessTokenLifetimeOption; -using ::google::cloud::testing_util::IsOk; -using ::std::chrono::minutes; -using ::testing::EndsWith; -using ::testing::Return; -using ::testing::StartsWith; - -class MockMinimalIamCredentialsRest - : public oauth2_internal::MinimalIamCredentialsRest { - public: - MOCK_METHOD(StatusOr, GenerateAccessToken, - (oauth2_internal::GenerateAccessTokenRequest const&), (override)); - MOCK_METHOD(StatusOr, universe_domain, (Options const& options), - (override, const)); -}; - -TEST(ImpersonateServiceAccountCredentialsTest, Basic) { - auto const now = std::chrono::system_clock::now(); - - auto mock = std::make_shared(); - EXPECT_CALL(*mock, GenerateAccessToken) - .WillOnce( - Return(make_status_or(AccessToken{"token1", now + minutes(15)}))) - .WillOnce( - Return(make_status_or(AccessToken{"token2", now + minutes(30)}))); - - auto config = google::cloud::internal::ImpersonateServiceAccountConfig( - google::cloud::MakeGoogleDefaultCredentials(), - "test-only-invalid@test.invalid", - Options{}.set(std::chrono::minutes(15))); - ImpersonateServiceAccountCredentials under_test(config, mock); - - for (auto const i : {1, 5, 9}) { - SCOPED_TRACE("Testing with i = " + std::to_string(i)); - auto header = under_test.AuthorizationHeader(now + minutes(i)); - ASSERT_THAT(header, IsOk()); - EXPECT_THAT(*header, StartsWith("Authorization: Bearer")); - EXPECT_THAT(*header, EndsWith("token1")); - } - - auto header = under_test.AuthorizationHeader(now + minutes(20)); - ASSERT_THAT(header, IsOk()); - EXPECT_THAT(*header, StartsWith("Authorization: Bearer")); - EXPECT_THAT(*header, EndsWith("token2")); -} - -} // namespace -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/internal/object_write_streambuf_test.cc b/google/cloud/storage/internal/object_write_streambuf_test.cc index 9b401ab4cd750..c25052961c9da 100644 --- a/google/cloud/storage/internal/object_write_streambuf_test.cc +++ b/google/cloud/storage/internal/object_write_streambuf_test.cc @@ -444,7 +444,7 @@ TEST(ObjectWriteStreambufTest, Regression8868) { auto retry = StorageConnectionImpl::Create(std::move(mock)); google::cloud::internal::OptionsSpan const span( Options{} - .set(oauth2::CreateAnonymousCredentials()) + .set(MakeInsecureCredentials()) .set(LimitedErrorCountRetryPolicy(3).clone()) .set( ExponentialBackoffPolicy(us(1), us(2), 2).clone()) diff --git a/google/cloud/storage/internal/rest/stub.cc b/google/cloud/storage/internal/rest/stub.cc index 62acc29fc847e..b8f135cb59671 100644 --- a/google/cloud/storage/internal/rest/stub.cc +++ b/google/cloud/storage/internal/rest/stub.cc @@ -44,7 +44,6 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN namespace internal { namespace rest = google::cloud::rest_internal; -using ::google::cloud::internal::AuthHeaderError; using ::google::cloud::internal::GetEnv; using ::google::cloud::internal::UrlEncode; @@ -108,18 +107,6 @@ StatusOr CreateFromJson( return ReturnType::CreateFromJson(*payload); } -Status AddAuthorizationHeader(Options const& options, - RestRequestBuilder& builder) { - // In tests this option may not be set. And over time we want to retire it. - if (!options.has()) return {}; - auto auth_header = - options.get()->AuthorizationHeader(); - if (!auth_header) return AuthHeaderError(std::move(auth_header).status()); - builder.AddHeader("Authorization", std::string(absl::StripPrefix( - *auth_header, "Authorization: "))); - return {}; -} - void AddCustomHeaders(Options const& options, RestRequestBuilder& builder) { if (!options.has()) return; for (auto const& h : options.get()) { @@ -128,8 +115,6 @@ void AddCustomHeaders(Options const& options, RestRequestBuilder& builder) { } Status AddHeaders(Options const& options, RestRequestBuilder& builder) { - auto ah = AddAuthorizationHeader(options, builder); - if (!ah.ok()) return ah; AddCustomHeaders(options, builder); return {}; } diff --git a/google/cloud/storage/internal/storage_connection.h b/google/cloud/storage/internal/storage_connection.h index 28cfe3af468cb..a002570e6283f 100644 --- a/google/cloud/storage/internal/storage_connection.h +++ b/google/cloud/storage/internal/storage_connection.h @@ -28,7 +28,6 @@ #include "google/cloud/storage/internal/object_requests.h" #include "google/cloud/storage/internal/service_account_requests.h" #include "google/cloud/storage/internal/sign_blob_requests.h" -#include "google/cloud/storage/oauth2/credentials.h" #include "google/cloud/storage/object_metadata.h" #include "google/cloud/storage/object_read_stream.h" #include "google/cloud/storage/service_account.h" diff --git a/google/cloud/storage/internal/unified_rest_credentials.cc b/google/cloud/storage/internal/unified_rest_credentials.cc deleted file mode 100644 index 88045788f734b..0000000000000 --- a/google/cloud/storage/internal/unified_rest_credentials.cc +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/internal/unified_rest_credentials.h" -#include "google/cloud/storage/internal/access_token_credentials.h" -#include "google/cloud/storage/internal/error_credentials.h" -#include "google/cloud/storage/internal/impersonate_service_account_credentials.h" -#include "google/cloud/storage/oauth2/google_credentials.h" -#include "google/cloud/storage/oauth2/service_account_credentials.h" -#include "google/cloud/internal/getenv.h" -#include "google/cloud/internal/oauth2_access_token_credentials.h" -#include "google/cloud/internal/oauth2_compute_engine_credentials.h" -#include "google/cloud/internal/oauth2_credentials.h" -#include "google/cloud/internal/oauth2_decorate_credentials.h" -#include "google/cloud/internal/oauth2_external_account_credentials.h" -#include "google/cloud/internal/oauth2_google_credentials.h" -#include "google/cloud/internal/oauth2_service_account_credentials.h" -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { -namespace { - -using ::google::cloud::internal::AccessTokenConfig; -using ::google::cloud::internal::ApiKeyConfig; -using ::google::cloud::internal::ComputeEngineCredentialsConfig; -using ::google::cloud::internal::CredentialsVisitor; -using ::google::cloud::internal::ErrorCredentialsConfig; -using ::google::cloud::internal::ExternalAccountConfig; -using ::google::cloud::internal::GoogleDefaultCredentialsConfig; -using ::google::cloud::internal::ImpersonateServiceAccountConfig; -using ::google::cloud::internal::InsecureCredentialsConfig; -using ::google::cloud::internal::ServiceAccountConfig; -using ::google::cloud::oauth2_internal::Decorate; - -std::shared_ptr MakeErrorCredentials(Status status) { - return std::make_shared(std::move(status)); -} - -class WrapRestCredentials : public oauth2::Credentials { - public: - explicit WrapRestCredentials( - std::shared_ptr impl) - : impl_(std::move(impl)) {} - - StatusOr AuthorizationHeader() override { - return oauth2_internal::AuthenticationHeaderJoined(*impl_); - } - - StatusOr> SignBlob( - SigningAccount const& signing_account, - std::string const& blob) const override { - return impl_->SignBlob(signing_account.value_or(impl_->AccountEmail()), - blob); - } - - std::string AccountEmail() const override { return impl_->AccountEmail(); } - std::string KeyId() const override { return impl_->KeyId(); } - - private: - std::shared_ptr impl_; -}; - -} // namespace - -std::shared_ptr MapCredentials( - google::cloud::Credentials const& credentials) { - class RestVisitor : public CredentialsVisitor { - public: - explicit RestVisitor(oauth2_internal::HttpClientFactory client_factory) - : client_factory_(std::move(client_factory)) {} - - std::shared_ptr result; - - void visit(ErrorCredentialsConfig const& cfg) override { - result = MakeErrorCredentials(cfg.status()); - } - - void visit(InsecureCredentialsConfig const&) override { - result = google::cloud::storage::oauth2::CreateAnonymousCredentials(); - } - void visit(GoogleDefaultCredentialsConfig const& cfg) override { - auto credentials = oauth2_internal::GoogleDefaultCredentials( - cfg.options(), std::move(client_factory_)); - if (credentials) { - result = std::make_shared( - Decorate(*std::move(credentials), cfg.options())); - return; - } - result = MakeErrorCredentials(std::move(credentials).status()); - } - void visit(AccessTokenConfig const& cfg) override { - result = std::make_shared(cfg.access_token()); - } - void visit(ImpersonateServiceAccountConfig const& config) override { - result = std::make_shared(config); - } - void visit(ServiceAccountConfig const& cfg) override { - auto info = oauth2::ParseServiceAccountCredentials(cfg.json_object(), {}); - if (!info) { - result = MakeErrorCredentials(std::move(info).status()); - return; - } - auto impl = std::make_shared( - internal::MapServiceAccountCredentialsInfo(*std::move(info)), - cfg.options(), std::move(client_factory_)); - result = - std::make_shared(WithCaching(std::move(impl))); - } - - void visit(ExternalAccountConfig const& cfg) override { - auto const ec = google::cloud::internal::ErrorContext(); - auto info = oauth2_internal::ParseExternalAccountConfiguration( - cfg.json_object(), ec); - if (!info) { - result = std::make_shared(std::move(info).status()); - return; - } - auto impl = std::make_shared( - *info, std::move(client_factory_), cfg.options()); - result = std::make_shared( - Decorate(std::move(impl), cfg.options())); - } - void visit(internal::ApiKeyConfig const&) override { - // Circa 2024, GCS does not support API key authentication. Moreover, we - // would have to grow the deprecated `storage::oauth2::Credentials` class - // to support setting the `x-goog-api-key` header. For these reasons, we - // just return anonymous (no-op) credentials. - result = google::cloud::storage::oauth2::CreateAnonymousCredentials(); - } - void visit(internal::ComputeEngineCredentialsConfig const& cfg) override { - result = std::make_shared( - Decorate(std::make_shared( - cfg.options(), std::move(client_factory_)), - cfg.options())); - } - - private: - oauth2_internal::HttpClientFactory client_factory_; - }; - - RestVisitor visitor([](Options const& o) { - return rest_internal::MakeDefaultRestClient(std::string{}, o); - }); - CredentialsVisitor::dispatch(credentials, visitor); - return std::move(visitor.result); -} - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/internal/unified_rest_credentials.h b/google/cloud/storage/internal/unified_rest_credentials.h deleted file mode 100644 index 480cfaddea161..0000000000000 --- a/google/cloud/storage/internal/unified_rest_credentials.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_UNIFIED_REST_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_UNIFIED_REST_CREDENTIALS_H - -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/version.h" -#include "google/cloud/credentials.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { - -std::shared_ptr MapCredentials( - google::cloud::Credentials const& credentials); - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_INTERNAL_UNIFIED_REST_CREDENTIALS_H diff --git a/google/cloud/storage/internal/unified_rest_credentials_test.cc b/google/cloud/storage/internal/unified_rest_credentials_test.cc deleted file mode 100644 index 237d7cacdb7d3..0000000000000 --- a/google/cloud/storage/internal/unified_rest_credentials_test.cc +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/internal/unified_rest_credentials.h" -#include "google/cloud/storage/testing/constants.h" -#include "google/cloud/internal/credentials_impl.h" -#include "google/cloud/internal/filesystem.h" -#include "google/cloud/internal/random.h" -#include "google/cloud/testing_util/scoped_environment.h" -#include "google/cloud/testing_util/status_matchers.h" -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace internal { -namespace { - -using ::google::cloud::MakeAccessTokenCredentials; -using ::google::cloud::MakeGoogleDefaultCredentials; -using ::google::cloud::MakeInsecureCredentials; -using ::google::cloud::testing_util::IsOk; -using ::google::cloud::testing_util::ScopedEnvironment; -using ::google::cloud::testing_util::StatusIs; -using ::testing::IsEmpty; - -class UnifiedRestCredentialsTest : public ::testing::Test { - public: - UnifiedRestCredentialsTest() : generator_(std::random_device{}()) {} - - std::string TempKeyFileName() { - return google::cloud::internal::PathAppend( - ::testing::TempDir(), - ::google::cloud::internal::Sample( - generator_, 16, "abcdefghijlkmnopqrstuvwxyz0123456789") + - ".json"); - } - - private: - google::cloud::internal::DefaultPRNG generator_; -}; - -TEST_F(UnifiedRestCredentialsTest, Insecure) { - auto credentials = MapCredentials(*MakeInsecureCredentials()); - auto header = credentials->AuthorizationHeader(); - ASSERT_THAT(header, IsOk()); - EXPECT_THAT(*header, IsEmpty()); -} - -TEST_F(UnifiedRestCredentialsTest, Error) { - Status const error_status{StatusCode::kFailedPrecondition, - "Precondition failed."}; - auto credentials = MapCredentials( - *google::cloud::internal::MakeErrorCredentials(error_status)); - auto header = credentials->AuthorizationHeader(); - EXPECT_THAT(header, StatusIs(error_status.code())); -} - -TEST_F(UnifiedRestCredentialsTest, AccessToken) { - auto credentials = MapCredentials( - *MakeAccessTokenCredentials("token1", std::chrono::system_clock::now())); - for (std::string expected : {"token1", "token1", "token1"}) { - auto header = credentials->AuthorizationHeader(); - ASSERT_THAT(header, IsOk()); - EXPECT_EQ("Authorization: Bearer " + expected, *header); - } -} - -TEST_F(UnifiedRestCredentialsTest, LoadError) { - // Create a name for a non-existing file, try to load it, and verify it - // returns errors. - auto const filename = TempKeyFileName(); - ScopedEnvironment env("GOOGLE_APPLICATION_CREDENTIALS", filename); - - auto credentials = MapCredentials(*MakeGoogleDefaultCredentials()); - EXPECT_THAT(credentials->AuthorizationHeader(), Not(IsOk())); -} - -TEST_F(UnifiedRestCredentialsTest, LoadSuccess) { - // Create a loadable, i.e., syntactically valid, key file, load it, and it - // has the right contents. - auto constexpr kKeyId = "test-only-key-id"; - auto constexpr kClientEmail = - "sa@invalid-test-only-project.iam.gserviceaccount.com"; - auto contents = nlohmann::json{ - {"type", "service_account"}, - {"project_id", "invalid-test-only-project"}, - {"private_key_id", kKeyId}, - {"private_key", google::cloud::storage::testing::kWellFormattedKey}, - {"client_email", kClientEmail}, - {"client_id", "invalid-test-only-client-id"}, - {"auth_uri", "https://accounts.google.com/o/oauth2/auth"}, - {"token_uri", "https://accounts.google.com/o/oauth2/token"}, - {"auth_provider_x509_cert_url", - "https://www.googleapis.com/oauth2/v1/certs"}, - {"client_x509_cert_url", - "https://www.googleapis.com/robot/v1/metadata/x509/" - "foo-email%40foo-project.iam.gserviceaccount.com"}, - }; - auto const filename = TempKeyFileName(); - std::ofstream(filename) << contents.dump(4) << "\n"; - - ScopedEnvironment env("GOOGLE_APPLICATION_CREDENTIALS", filename); - - auto credentials = MapCredentials(*MakeGoogleDefaultCredentials()); - // Calling AuthenticationHeader() makes RPCs which would turn this into an - // integration test, fortunately there are easier ways to verify the file was - // loaded correctly: - EXPECT_EQ(kClientEmail, credentials->AccountEmail()); - EXPECT_EQ(kKeyId, credentials->KeyId()); - - std::remove(filename.c_str()); -} - -} // namespace -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/anonymous_credentials.cc b/google/cloud/storage/oauth2/anonymous_credentials.cc deleted file mode 100644 index faaf664a95e18..0000000000000 --- a/google/cloud/storage/oauth2/anonymous_credentials.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/anonymous_credentials.h" - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -StatusOr AnonymousCredentials::AuthorizationHeader() { - return std::string{}; -} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/anonymous_credentials.h b/google/cloud/storage/oauth2/anonymous_credentials.h deleted file mode 100644 index 21b54463f5b7b..0000000000000 --- a/google/cloud/storage/oauth2/anonymous_credentials.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_ANONYMOUS_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_ANONYMOUS_CREDENTIALS_H - -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/version.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -/** - * A `Credentials` type representing "anonymous" Google OAuth2.0 credentials. - * - * This is only useful in two cases: (a) in testing, where you want to access - * a test bench without having to worry about authentication or SSL setup, and - * (b) when accessing publicly readable resources (e.g. a Google Cloud Storage - * object that is readable by the "allUsers" entity), which requires no - * authentication or authorization. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. " - "Prefer using the unified credentials " - "documented in @ref guac.") AnonymousCredentials : public Credentials { - public: - AnonymousCredentials() = default; - - /** - * While other Credentials subclasses return a string containing an - * Authorization HTTP header from this method, this class always returns an - * empty string as its value. - */ - StatusOr AuthorizationHeader() override; -}; - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_ANONYMOUS_CREDENTIALS_H diff --git a/google/cloud/storage/oauth2/anonymous_credentials_test.cc b/google/cloud/storage/oauth2/anonymous_credentials_test.cc deleted file mode 100644 index 3404352fb6075..0000000000000 --- a/google/cloud/storage/oauth2/anonymous_credentials_test.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/anonymous_credentials.h" -#include "google/cloud/testing_util/status_matchers.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { -namespace { - -class AnonymousCredentialsTest : public ::testing::Test {}; - -/// @test Verify `AnonymousCredentials` works as expected. -TEST_F(AnonymousCredentialsTest, AuthorizationHeaderReturnsEmptyString) { - AnonymousCredentials credentials; - auto header = credentials.AuthorizationHeader(); - ASSERT_STATUS_OK(header); - EXPECT_EQ("", header.value()); -} - -} // namespace -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/authorized_user_credentials.cc b/google/cloud/storage/oauth2/authorized_user_credentials.cc deleted file mode 100644 index 2aa7c4ba50bd7..0000000000000 --- a/google/cloud/storage/oauth2/authorized_user_credentials.cc +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/authorized_user_credentials.h" -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -StatusOr ParseAuthorizedUserCredentials( - std::string const& content, std::string const& source, - std::string const& default_token_uri) { - auto info = google::cloud::oauth2_internal::ParseAuthorizedUserCredentials( - content, source, default_token_uri); - if (!info.ok()) return info.status(); - AuthorizedUserCredentialsInfo i; - i.token_uri = std::move(info->token_uri); - i.refresh_token = std::move(info->refresh_token); - i.client_secret = std::move(info->client_secret); - i.client_id = std::move(info->client_id); - return i; -} - -StatusOr -ParseAuthorizedUserRefreshResponse( - storage::internal::HttpResponse const& response, - std::chrono::system_clock::time_point now) { - auto access_token = nlohmann::json::parse(response.payload, nullptr, false); - if (!access_token.is_object() || access_token.count("access_token") == 0 || - access_token.count("expires_in") == 0 || - access_token.count("id_token") == 0 || - access_token.count("token_type") == 0) { - auto payload = - response.payload + - "Could not find all required fields in response (access_token," - " id_token, expires_in, token_type) while trying to obtain an access" - " token for authorized user credentials."; - return AsStatus(storage::internal::HttpResponse{response.status_code, - payload, response.headers}); - } - std::string header = "Authorization: "; - header += access_token.value("token_type", ""); - header += ' '; - header += access_token.value("access_token", ""); - std::string new_id = access_token.value("id_token", ""); - auto expires_in = std::chrono::seconds(access_token.value("expires_in", 0)); - auto new_expiration = now + expires_in; - return RefreshingCredentialsWrapper::TemporaryToken{std::move(header), - new_expiration}; -} - -AuthorizedUserCredentials:: - AuthorizedUserCredentials(AuthorizedUserCredentialsInfo const& info, - Options options) - : AuthorizedUserCredentials( - google::cloud::oauth2_internal::AuthorizedUserCredentialsInfo{ - info.client_id, info.client_secret, info.refresh_token, - info.token_uri, info.universe_domain}, - std::move(options)) {} - -AuthorizedUserCredentials:: - AuthorizedUserCredentials( - google::cloud::oauth2_internal::AuthorizedUserCredentialsInfo info, - Options options) - : AuthorizedUserCredentials( - std::move(info), std::move(options), [](Options const& o) { - return rest_internal::MakeDefaultRestClient(std::string{}, o); - }) {} - -AuthorizedUserCredentials:: - AuthorizedUserCredentials( - google::cloud::oauth2_internal::AuthorizedUserCredentialsInfo info, - Options options, oauth2_internal::HttpClientFactory client_factory) - : impl_(std::make_shared( - std::make_shared( - std::move(info), std::move(options), - std::move(client_factory)))) {} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/authorized_user_credentials.h b/google/cloud/storage/oauth2/authorized_user_credentials.h deleted file mode 100644 index 8393e73a5d4ce..0000000000000 --- a/google/cloud/storage/oauth2/authorized_user_credentials.h +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_AUTHORIZED_USER_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_AUTHORIZED_USER_CREDENTIALS_H - -#include "google/cloud/storage/internal/curl/request_builder.h" -#include "google/cloud/storage/internal/http_response.h" -#include "google/cloud/storage/oauth2/credential_constants.h" -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/oauth2/refreshing_credentials_wrapper.h" -#include "google/cloud/storage/version.h" -#include "google/cloud/internal/curl_handle_factory.h" -#include "google/cloud/internal/oauth2_authorized_user_credentials.h" -#include "google/cloud/internal/oauth2_cached_credentials.h" -#include "google/cloud/internal/oauth2_credential_constants.h" -#include "google/cloud/options.h" -#include "google/cloud/status.h" -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -/** - * Object to hold information used to instantiate an AuthorizedUserCredentials. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -struct GOOGLE_CLOUD_CPP_DEPRECATED( - "This struct will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") - AuthorizedUserCredentialsInfo { - std::string client_id; - std::string client_secret; - std::string refresh_token; - std::string token_uri; - std::string universe_domain; -}; - -/** - * Parses a refresh response JSON string into an authorization header. - * - * The header and the current time (for the expiration) form a TemporaryToken. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr -ParseAuthorizedUserRefreshResponse( - storage::internal::HttpResponse const& response, - std::chrono::system_clock::time_point now); - -/** - * Parses a user credentials JSON string into an AuthorizedUserCredentialsInfo. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr ParseAuthorizedUserCredentials( - std::string const& content, std::string const& source, - std::string const& default_token_uri = - google::cloud::oauth2_internal::GoogleOAuthRefreshEndpoint()); - -/** - * Wrapper class for Google OAuth 2.0 user account credentials. - * - * Takes a AuthorizedUserCredentialsInfo and obtains access tokens from the - * Google Authorization Service as needed. Instances of this class should - * usually be created via the convenience methods declared in - * google_credentials.h. - * - * An HTTP Authorization header, with an access token as its value, - * can be obtained by calling the AuthorizationHeader() method; if the current - * access token is invalid or nearing expiration, this will class will first - * obtain a new access token before returning the Authorization header string. - * - * @see https://developers.google.com/identity/protocols/OAuth2 for an overview - * of using user credentials with Google's OAuth 2.0 system. - * - * @tparam HttpRequestBuilderType a dependency injection point. It makes it - * possible to mock internal libcurl wrappers. This should generally not be - * overridden except for testing. - * @tparam ClockType a dependency injection point to fetch the current time. - * This should generally not be overridden except for testing. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -template -class AuthorizedUserCredentials; - -/// @copydoc AuthorizedUserCredentials -template <> -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") - AuthorizedUserCredentials : public Credentials { - public: - explicit AuthorizedUserCredentials(AuthorizedUserCredentialsInfo const& info, - Options options = {}); - - explicit AuthorizedUserCredentials( - google::cloud::oauth2_internal::AuthorizedUserCredentialsInfo info, - Options options = {}); - - StatusOr AuthorizationHeader() override { - return oauth2_internal::AuthenticationHeaderJoined(*impl_); - } - - private: - friend struct AuthorizedUserCredentialsTester; - AuthorizedUserCredentials( - google::cloud::oauth2_internal::AuthorizedUserCredentialsInfo, - Options options, oauth2_internal::HttpClientFactory client_factory); - - StatusOr AuthorizationHeaderForTesting( - std::chrono::system_clock::time_point tp) { - return oauth2_internal::AuthenticationHeaderJoined(*impl_, tp); - } - - std::shared_ptr impl_; -}; - -/// @copydoc AuthorizedUserCredentials -template -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") AuthorizedUserCredentials - : public Credentials { - public: - explicit AuthorizedUserCredentials(AuthorizedUserCredentialsInfo info, - Options options = {}) - : info_(std::move(info)), options_(std::move(options)), clock_() {} - - StatusOr AuthorizationHeader() override { - std::unique_lock lock(mu_); - return refreshing_creds_.AuthorizationHeader(clock_.now(), - [this] { return Refresh(); }); - } - - private: - StatusOr Refresh() { - HttpRequestBuilderType builder( - info_.token_uri, rest_internal::GetDefaultCurlHandleFactory(options_)); - std::string payload("grant_type=refresh_token"); - payload += "&client_id="; - payload += builder.MakeEscapedString(info_.client_id).get(); - payload += "&client_secret="; - payload += builder.MakeEscapedString(info_.client_secret).get(); - payload += "&refresh_token="; - payload += builder.MakeEscapedString(info_.refresh_token).get(); - auto response = std::move(builder).BuildRequest().MakeRequest(payload); - if (!response) return std::move(response).status(); - if (response->status_code >= 300) return AsStatus(*response); - return ParseAuthorizedUserRefreshResponse(*response, clock_.now()); - } - - AuthorizedUserCredentialsInfo info_; - Options options_; - ClockType clock_; - mutable std::mutex mu_; - RefreshingCredentialsWrapper refreshing_creds_; -}; - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_AUTHORIZED_USER_CREDENTIALS_H diff --git a/google/cloud/storage/oauth2/authorized_user_credentials_test.cc b/google/cloud/storage/oauth2/authorized_user_credentials_test.cc deleted file mode 100644 index 785f6e9ee7b92..0000000000000 --- a/google/cloud/storage/oauth2/authorized_user_credentials_test.cc +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/authorized_user_credentials.h" -#include "google/cloud/storage/oauth2/credential_constants.h" -#include "google/cloud/storage/testing/mock_http_request.h" -#include "google/cloud/common_options.h" -#include "google/cloud/testing_util/mock_fake_clock.h" -#include "google/cloud/testing_util/mock_http_payload.h" -#include "google/cloud/testing_util/mock_rest_client.h" -#include "google/cloud/testing_util/mock_rest_response.h" -#include "google/cloud/testing_util/status_matchers.h" -#include -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -// Define a helper to test the specialization. -struct AuthorizedUserCredentialsTester { - static StatusOr Header( - AuthorizedUserCredentials<>& tested, - std::chrono::system_clock::time_point tp) { - return tested.AuthorizationHeaderForTesting(tp); - } - - static AuthorizedUserCredentials<> MakeAuthorizedUserCredentials( - google::cloud::oauth2_internal::AuthorizedUserCredentialsInfo info, - oauth2_internal::HttpClientFactory factory) { - return AuthorizedUserCredentials<>(std::move(info), Options{}, - std::move(factory)); - } -}; - -namespace { - -using ::google::cloud::storage::internal::HttpResponse; -using ::google::cloud::storage::testing::MockHttpRequest; -using ::google::cloud::storage::testing::MockHttpRequestBuilder; -using ::google::cloud::testing_util::FakeClock; -using ::google::cloud::testing_util::IsOk; -using ::google::cloud::testing_util::IsOkAndHolds; -using ::google::cloud::testing_util::MakeMockHttpPayloadSuccess; -using ::google::cloud::testing_util::MockRestClient; -using ::google::cloud::testing_util::MockRestResponse; -using ::google::cloud::testing_util::StatusIs; -using ::testing::_; -using ::testing::AllOf; -using ::testing::An; -using ::testing::AtLeast; -using ::testing::ByMove; -using ::testing::HasSubstr; -using ::testing::Not; -using ::testing::Return; -using ::testing::StrEq; - -class AuthorizedUserCredentialsTest : public ::testing::Test { - protected: - void SetUp() override { - MockHttpRequestBuilder::mock_ = - std::make_shared(); - } - void TearDown() override { MockHttpRequestBuilder::mock_.reset(); } -}; - -/// @test Verify that we can create credentials from a JWT string. -TEST_F(AuthorizedUserCredentialsTest, Simple) { - std::string response = R"""({ - "token_type": "Type", - "access_token": "access-token-value", - "id_token": "id-token-value", - "expires_in": 1234 -})"""; - auto mock_request = std::make_shared(); - EXPECT_CALL(*mock_request, MakeRequest) - .WillOnce([response](std::string const& payload) { - EXPECT_THAT(payload, HasSubstr("grant_type=refresh_token")); - EXPECT_THAT(payload, HasSubstr("client_id=a-client-id.example.com")); - EXPECT_THAT(payload, HasSubstr("client_secret=a-123456ABCDEF")); - EXPECT_THAT(payload, HasSubstr("refresh_token=1/THETOKEN")); - return HttpResponse{200, response, {}}; - }); - - auto mock_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_builder, - Constructor(StrEq("https://oauth2.googleapis.com/token"), _, _)) - .Times(1); - EXPECT_CALL(*mock_builder, BuildRequest()).WillOnce([mock_request]() { - MockHttpRequest result; - result.mock = mock_request; - return result; - }); - EXPECT_CALL(*mock_builder, MakeEscapedString) - .WillRepeatedly([](std::string const& s) { - auto t = std::unique_ptr(new char[s.size() + 1]); - std::copy(s.begin(), s.end(), t.get()); - t[s.size()] = '\0'; - return t; - }); - - std::string config = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "type": "magic_type" -})"""; - - auto info = ParseAuthorizedUserCredentials(config, "test"); - ASSERT_STATUS_OK(info); - AuthorizedUserCredentials credentials(*info); - EXPECT_EQ("Authorization: Type access-token-value", - credentials.AuthorizationHeader().value()); -} - -/// @test Verify that we can refresh service account credentials. -TEST_F(AuthorizedUserCredentialsTest, Refresh) { - // Prepare two responses, the first one is used but becomes immediately - // expired, resulting in another refresh next time the caller tries to get - // an authorization header. - std::string r1 = R"""({ - "token_type": "Type", - "access_token": "access-token-r1", - "id_token": "id-token-value", - "expires_in": 0 -})"""; - std::string r2 = R"""({ - "token_type": "Type", - "access_token": "access-token-r2", - "id_token": "id-token-value", - "expires_in": 1000 -})"""; - - // Now setup the builder to return those responses. - auto mock_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_builder, BuildRequest()) - .WillOnce([&] { - MockHttpRequest request; - EXPECT_CALL(*request.mock, MakeRequest) - .WillOnce(Return(HttpResponse{200, r1, {}})); - return request; - }) - .WillOnce([&] { - MockHttpRequest request; - EXPECT_CALL(*request.mock, MakeRequest) - .WillOnce(Return(HttpResponse{200, r2, {}})); - return request; - }); - EXPECT_CALL(*mock_builder, Constructor(GoogleOAuthRefreshEndpoint(), _, _)) - .Times(AtLeast(1)); - EXPECT_CALL(*mock_builder, MakeEscapedString) - .WillRepeatedly([](std::string const& s) { - auto t = std::unique_ptr(new char[s.size() + 1]); - std::copy(s.begin(), s.end(), t.get()); - t[s.size()] = '\0'; - return t; - }); - - std::string config = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "type": "magic_type" -})"""; - auto info = ParseAuthorizedUserCredentials(config, "test"); - ASSERT_STATUS_OK(info); - AuthorizedUserCredentials credentials(*info); - EXPECT_EQ("Authorization: Type access-token-r1", - credentials.AuthorizationHeader().value()); - EXPECT_EQ("Authorization: Type access-token-r2", - credentials.AuthorizationHeader().value()); - EXPECT_EQ("Authorization: Type access-token-r2", - credentials.AuthorizationHeader().value()); -} - -/// @test Mock a failed refresh response. -TEST_F(AuthorizedUserCredentialsTest, FailedRefresh) { - // Now setup the builder to return those responses. - auto mock_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_builder, BuildRequest()) - .WillOnce([] { - MockHttpRequest request; - EXPECT_CALL(*request.mock, MakeRequest) - .WillOnce(Return(Status(StatusCode::kAborted, "Fake Curl error"))); - return request; - }) - .WillOnce([] { - MockHttpRequest request; - EXPECT_CALL(*request.mock, MakeRequest) - .WillOnce(Return(HttpResponse{400, "", {}})); - return request; - }); - EXPECT_CALL(*mock_builder, Constructor(GoogleOAuthRefreshEndpoint(), _, _)) - .Times(AtLeast(1)); - EXPECT_CALL(*mock_builder, MakeEscapedString(An())) - .WillRepeatedly([](std::string const& s) { - auto t = std::unique_ptr(new char[s.size() + 1]); - std::copy(s.begin(), s.end(), t.get()); - t[s.size()] = '\0'; - return t; - }); - - std::string config = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "type": "magic_type" -})"""; - auto info = ParseAuthorizedUserCredentials(config, "test"); - ASSERT_STATUS_OK(info); - AuthorizedUserCredentials credentials(*info); - // Response 1 - auto status = credentials.AuthorizationHeader(); - EXPECT_THAT(status, StatusIs(StatusCode::kAborted)); - // Response 2 - status = credentials.AuthorizationHeader(); - EXPECT_THAT(status, Not(IsOk())); -} - -/// @test Verify that the options are used in the constructor. -TEST_F(AuthorizedUserCredentialsTest, UsesCARootsInfo) { - // Now setup the builder to return a valid response. - auto mock_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_builder, BuildRequest()).WillOnce([&] { - MockHttpRequest request; - nlohmann::json response{{"token_type", "Mock-Type"}, - {"access_token", "fake-token"}, - {"id_token", "fake-id-token-value"}, - {"expires_in", 3600}}; - EXPECT_CALL(*request.mock, MakeRequest) - .WillOnce(Return(HttpResponse{200, response.dump(), {}})); - return request; - }); - - // This is the key check in this test, verify the constructor is called with - // the right parameters. - auto const cainfo = std::string{"fake-cainfo-path-aka-roots-pem"}; - EXPECT_CALL(*mock_builder, Constructor(GoogleOAuthRefreshEndpoint(), - absl::make_optional(cainfo), _)) - .Times(AtLeast(1)); - EXPECT_CALL(*mock_builder, MakeEscapedString(An())) - .WillRepeatedly([](std::string const& s) { - auto t = std::unique_ptr(new char[s.size() + 1]); - std::copy(s.begin(), s.end(), t.get()); - t[s.size()] = '\0'; - return t; - }); - - std::string config = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "type": "magic_type" -})"""; - auto info = ParseAuthorizedUserCredentials(config, "test"); - ASSERT_STATUS_OK(info); - AuthorizedUserCredentials credentials( - *info, Options{}.set(cainfo)); - EXPECT_EQ("Authorization: Mock-Type fake-token", - credentials.AuthorizationHeader().value()); -} - -/// @test Verify that parsing an authorized user account JSON string works. -TEST_F(AuthorizedUserCredentialsTest, ParseSimple) { - std::string config = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "token_uri": "https://oauth2.googleapis.com/test_endpoint", - "type": "magic_type" -})"""; - - auto actual = - ParseAuthorizedUserCredentials(config, "test-data", "unused-uri"); - ASSERT_STATUS_OK(actual); - EXPECT_EQ("a-client-id.example.com", actual->client_id); - EXPECT_EQ("a-123456ABCDEF", actual->client_secret); - EXPECT_EQ("1/THETOKEN", actual->refresh_token); - EXPECT_EQ("https://oauth2.googleapis.com/test_endpoint", actual->token_uri); -} - -/// @test Verify that parsing an authorized user account JSON string works. -TEST_F(AuthorizedUserCredentialsTest, ParseUsesExplicitDefaultTokenUri) { - // No token_uri attribute here, so the default passed below should be used. - std::string config = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "type": "magic_type" -})"""; - - auto actual = ParseAuthorizedUserCredentials( - config, "test-data", "https://oauth2.googleapis.com/test_endpoint"); - ASSERT_STATUS_OK(actual); - EXPECT_EQ("a-client-id.example.com", actual->client_id); - EXPECT_EQ("a-123456ABCDEF", actual->client_secret); - EXPECT_EQ("1/THETOKEN", actual->refresh_token); - EXPECT_EQ("https://oauth2.googleapis.com/test_endpoint", actual->token_uri); -} - -/// @test Verify that parsing an authorized user account JSON string works. -TEST_F(AuthorizedUserCredentialsTest, ParseUsesImplicitDefaultTokenUri) { - // No token_uri attribute here. - std::string config = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "type": "magic_type" -})"""; - - // No token_uri passed in here, either. - auto actual = ParseAuthorizedUserCredentials(config, "test-data"); - ASSERT_STATUS_OK(actual); - EXPECT_EQ("a-client-id.example.com", actual->client_id); - EXPECT_EQ("a-123456ABCDEF", actual->client_secret); - EXPECT_EQ("1/THETOKEN", actual->refresh_token); - EXPECT_EQ(std::string(GoogleOAuthRefreshEndpoint()), actual->token_uri); -} - -/// @test Verify that invalid contents result in a readable error. -TEST_F(AuthorizedUserCredentialsTest, ParseInvalidContentsFails) { - EXPECT_THAT(ParseAuthorizedUserCredentials( - R"""( not-a-valid-json-string })""", "test-as-a-source"), - StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr("Invalid AuthorizedUserCredentials"), - HasSubstr("test-as-a-source")))); - - EXPECT_THAT(ParseAuthorizedUserCredentials( - R"""("valid-json-but-not-an-object")""", "test-as-a-source"), - StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr("Invalid AuthorizedUserCredentials"), - HasSubstr("test-as-a-source")))); -} - -/// @test Parsing a service account JSON string should detect empty fields. -TEST_F(AuthorizedUserCredentialsTest, ParseEmptyFieldFails) { - std::string contents = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "type": "magic_type" -})"""; - - for (auto const& field : {"client_id", "client_secret", "refresh_token"}) { - auto json = nlohmann::json::parse(contents); - json[field] = ""; - auto info = ParseAuthorizedUserCredentials(json.dump(), "test-data"); - EXPECT_THAT(info, - StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr(field), HasSubstr(" field is empty"), - HasSubstr("test-data")))); - } -} - -/// @test Parsing a service account JSON string should detect missing fields. -TEST_F(AuthorizedUserCredentialsTest, ParseMissingFieldFails) { - std::string contents = R"""({ - "client_id": "a-client-id.example.com", - "client_secret": "a-123456ABCDEF", - "refresh_token": "1/THETOKEN", - "type": "magic_type" -})"""; - - for (auto const& field : {"client_id", "client_secret", "refresh_token"}) { - auto json = nlohmann::json::parse(contents); - json.erase(field); - auto info = ParseAuthorizedUserCredentials(json.dump(), "test-data"); - EXPECT_THAT(info, - StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr(field), HasSubstr(" field is missing"), - HasSubstr("test-data")))); - } -} - -/// @test Parsing an invalid refresh response results in failure. -TEST_F(AuthorizedUserCredentialsTest, ParseAuthorizedUserRefreshResponseError) { - std::string r1 = R"""({})"""; - // Does not have access_token. - std::string r2 = R"""({ - "token_type": "Type", - "id_token": "id-token-value", - "expires_in": 1000 -})"""; - - FakeClock::reset_clock(1000); - auto status = ParseAuthorizedUserRefreshResponse(HttpResponse{400, r1, {}}, - FakeClock::now()); - EXPECT_THAT(status, - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); - - status = ParseAuthorizedUserRefreshResponse(HttpResponse{400, r2, {}}, - FakeClock::now()); - EXPECT_THAT(status, - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); - - EXPECT_THAT(ParseAuthorizedUserRefreshResponse( - HttpResponse{400, R"js("valid-json-but-not-an-object)js", {}}, - FakeClock::now()), - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); -} - -/// @test Parsing a refresh response yields a TemporaryToken. -TEST_F(AuthorizedUserCredentialsTest, ParseAuthorizedUserRefreshResponse) { - std::string r1 = R"""({ - "token_type": "Type", - "access_token": "access-token-r1", - "id_token": "id-token-value", - "expires_in": 1000 -})"""; - - auto expires_in = 1000; - FakeClock::reset_clock(2000); - auto status = ParseAuthorizedUserRefreshResponse(HttpResponse{200, r1, {}}, - FakeClock::now()); - EXPECT_STATUS_OK(status); - auto token = *status; - EXPECT_EQ( - std::chrono::time_point_cast(token.expiration_time) - .time_since_epoch() - .count(), - FakeClock::now_value_ + expires_in); - EXPECT_EQ(token.token, "Authorization: Type access-token-r1"); -} - -TEST_F(AuthorizedUserCredentialsTest, Caching) { - // We need to mock the Security Token Service or this would be an - // integration test that requires a valid user account. - auto make_mock_client = [](std::string const& payload) { - auto response = std::make_unique(); - EXPECT_CALL(*response, StatusCode) - .WillRepeatedly( - Return(google::cloud::rest_internal::HttpStatusCode::kOk)); - EXPECT_CALL(std::move(*response), ExtractPayload) - .WillOnce(Return(ByMove(MakeMockHttpPayloadSuccess(payload)))); - auto mock = std::make_unique(); - using PostPayloadType = std::vector>; - EXPECT_CALL(*mock, Post(_, _, An())) - .WillOnce(Return(ByMove(std::unique_ptr( - std::move(response))))); - return std::unique_ptr(std::move(mock)); - }; - - auto constexpr kPayload1 = R"js({ - "access_token": "access-token-1", "id_token": "id-token-1", - "token_type": "Bearer", "expires_in": 3600})js"; - auto constexpr kPayload2 = R"js({ - "access_token": "access-token-2", "id_token": "id-token-2", - "token_type": "Bearer", "expires_in": 3600})js"; - - using MockHttpClientFactory = - ::testing::MockFunction( - Options const&)>; - MockHttpClientFactory mock_factory; - EXPECT_CALL(mock_factory, Call) - .WillOnce(Return(ByMove(make_mock_client(kPayload1)))) - .WillOnce(Return(ByMove(make_mock_client(kPayload2)))); - - auto tested = AuthorizedUserCredentialsTester::MakeAuthorizedUserCredentials( - oauth2_internal::AuthorizedUserCredentialsInfo{}, - mock_factory.AsStdFunction()); - auto const tp = std::chrono::system_clock::now(); - auto initial = AuthorizedUserCredentialsTester::Header(tested, tp); - ASSERT_STATUS_OK(initial); - - auto cached = AuthorizedUserCredentialsTester::Header( - tested, tp + std::chrono::seconds(30)); - EXPECT_THAT(cached, IsOkAndHolds(*initial)); - - cached = AuthorizedUserCredentialsTester::Header( - tested, tp + std::chrono::seconds(300)); - EXPECT_THAT(cached, IsOkAndHolds(*initial)); - - auto uncached = AuthorizedUserCredentialsTester::Header( - tested, tp + std::chrono::hours(2)); - ASSERT_STATUS_OK(uncached); - EXPECT_NE(*initial, *uncached); -} - -} // namespace -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/compute_engine_credentials.cc b/google/cloud/storage/oauth2/compute_engine_credentials.cc deleted file mode 100644 index 088436bf8f074..0000000000000 --- a/google/cloud/storage/oauth2/compute_engine_credentials.cc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/compute_engine_credentials.h" -#include "google/cloud/internal/oauth2_cached_credentials.h" -#include "google/cloud/internal/oauth2_compute_engine_credentials.h" -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -StatusOr ParseMetadataServerResponse( - storage::internal::HttpResponse const& response) { - auto meta = google::cloud::oauth2_internal::ParseMetadataServerResponse( - response.payload); - return ServiceAccountMetadata{std::move(meta.scopes), std::move(meta.email)}; -} - -StatusOr -ParseComputeEngineRefreshResponse( - storage::internal::HttpResponse const& response, - std::chrono::system_clock::time_point now) { - // Response should have the attributes "access_token", "expires_in", and - // "token_type". - auto access_token = nlohmann::json::parse(response.payload, nullptr, false); - if (!access_token.is_object() || access_token.count("access_token") == 0 || - access_token.count("expires_in") == 0 || - access_token.count("token_type") == 0) { - auto payload = - response.payload + - "Could not find all required fields in response (access_token," - " expires_in, token_type) while trying to obtain an access token for" - " compute engine credentials."; - return AsStatus(storage::internal::HttpResponse{response.status_code, - payload, response.headers}); - } - std::string header = "Authorization: "; - header += access_token.value("token_type", ""); - header += ' '; - header += access_token.value("access_token", ""); - auto expires_in = std::chrono::seconds(access_token.value("expires_in", 0)); - auto new_expiration = now + expires_in; - - return RefreshingCredentialsWrapper::TemporaryToken{std::move(header), - new_expiration}; -} - -ComputeEngineCredentials:: - ComputeEngineCredentials(std::string service_account_email) - : ComputeEngineCredentials( - std::move(service_account_email), [](Options const& o) { - return rest_internal::MakeDefaultRestClient(std::string{}, o); - }) {} - -ComputeEngineCredentials:: - ComputeEngineCredentials(std::string service_account_email, - oauth2_internal::HttpClientFactory client_factory) - : impl_(std::make_shared( - std::move(service_account_email), Options{}, - std::move(client_factory))), - cached_(std::make_shared(impl_)) {} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/compute_engine_credentials.h b/google/cloud/storage/oauth2/compute_engine_credentials.h deleted file mode 100644 index 603fd331a4b06..0000000000000 --- a/google/cloud/storage/oauth2/compute_engine_credentials.h +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_COMPUTE_ENGINE_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_COMPUTE_ENGINE_CREDENTIALS_H - -#include "google/cloud/storage/internal/base64.h" -#include "google/cloud/storage/internal/compute_engine_util.h" -#include "google/cloud/storage/internal/curl/request_builder.h" -#include "google/cloud/storage/internal/http_response.h" -#include "google/cloud/storage/oauth2/credential_constants.h" -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/oauth2/refreshing_credentials_wrapper.h" -#include "google/cloud/storage/version.h" -#include "google/cloud/internal/curl_handle_factory.h" -#include "google/cloud/internal/getenv.h" -#include "google/cloud/internal/oauth2_cached_credentials.h" -#include "google/cloud/internal/oauth2_compute_engine_credentials.h" -#include "google/cloud/status.h" -#include -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -/** - * A helper struct that contains service account metadata. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -struct GOOGLE_CLOUD_CPP_DEPRECATED( - "This struct will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") ServiceAccountMetadata { - std::set scopes; - std::string email; -}; - -/** - * Parses a metadata server response JSON string into a ServiceAccountMetadata. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr ParseMetadataServerResponse( - storage::internal::HttpResponse const& response); - -/** - * Parses a refresh response JSON string into an authorization header. - * - * The header and the current time (for the expiration) form a TemporaryToken. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr -ParseComputeEngineRefreshResponse( - storage::internal::HttpResponse const& response, - std::chrono::system_clock::time_point now); - -/** - * Wrapper class for Google OAuth 2.0 GCE instance service account credentials. - * - * Takes a service account email address or alias (e.g. "default") and uses the - * Google Compute Engine instance's metadata server to obtain service account - * metadata and OAuth 2.0 access tokens as needed. Instances of this class - * should usually be created via the convenience methods declared in - * google_credentials.h. - * - * An HTTP Authorization header, with an access token as its value, can be - * obtained by calling the AuthorizationHeader() method; if the current access - * token is invalid or nearing expiration, this will class will first obtain a - * new access token before returning the Authorization header string. - * - * @see https://cloud.google.com/compute/docs/authentication#using for details - * on how to get started with Compute Engine service account credentials. - * - * @tparam HttpRequestBuilderType a dependency injection point. It makes it - * possible to mock internal libcurl wrappers. This should generally not - * be overridden except for testing. - * @tparam ClockType a dependency injection point to fetch the current time. - * This should generally not be overridden except for testing. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -template -class ComputeEngineCredentials; - -/// @copydoc ComputeEngineCredentials -template <> -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") - ComputeEngineCredentials : public Credentials { - public: - explicit ComputeEngineCredentials() : ComputeEngineCredentials("default") {} - explicit ComputeEngineCredentials(std::string service_account_email); - - StatusOr AuthorizationHeader() override { - return oauth2_internal::AuthenticationHeaderJoined(*cached_); - } - - std::string AccountEmail() const override { return impl_->AccountEmail(); } - - /** - * Returns the email or alias of this credential's service account. - * - * @note This class must query the Compute Engine instance's metadata server - * to fetch service account metadata. Because of this, if an alias (e.g. - * "default") was supplied in place of an actual email address when - * initializing this credential, that alias is returned as this credential's - * email address if the credential has not been refreshed yet. - */ - std::string service_account_email() { return impl_->service_account_email(); } - - /** - * Returns the set of scopes granted to this credential's service account. - * - * @note Because this class must query the Compute Engine instance's metadata - * server to fetch service account metadata, this method will return an empty - * set if the credential has not been refreshed yet. - */ - std::set scopes() const { return impl_->scopes(); } - - private: - friend struct ComputeEngineCredentialsTester; - ComputeEngineCredentials(std::string service_account_email, - oauth2_internal::HttpClientFactory client_factory); - - StatusOr AuthorizationHeaderForTesting( - std::chrono::system_clock::time_point tp) { - return oauth2_internal::AuthenticationHeaderJoined(*cached_, tp); - } - - std::shared_ptr impl_; - std::shared_ptr cached_; -}; - -/// @copydoc ComputeEngineCredentials -template -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") ComputeEngineCredentials - : public Credentials { - public: - explicit ComputeEngineCredentials() : ComputeEngineCredentials("default") {} - - explicit ComputeEngineCredentials(std::string service_account_email) - : clock_(), service_account_email_(std::move(service_account_email)) {} - - StatusOr AuthorizationHeader() override { - std::unique_lock lock(mu_); - return refreshing_creds_.AuthorizationHeader(clock_.now(), - [this] { return Refresh(); }); - } - - std::string AccountEmail() const override { - std::unique_lock lock(mu_); - // Force a refresh on the account info. - RetrieveServiceAccountInfo(); - return service_account_email_; - } - - /** - * Returns the email or alias of this credential's service account. - * - * @note This class must query the Compute Engine instance's metadata server - * to fetch service account metadata. Because of this, if an alias (e.g. - * "default") was supplied in place of an actual email address when - * initializing this credential, that alias is returned as this credential's - * email address if the credential has not been refreshed yet. - */ - std::string service_account_email() const { - std::unique_lock lock(mu_); - return service_account_email_; - } - - /** - * Returns the set of scopes granted to this credential's service account. - * - * @note Because this class must query the Compute Engine instance's metadata - * server to fetch service account metadata, this method will return an empty - * set if the credential has not been refreshed yet. - */ - std::set scopes() const { - std::unique_lock lock(mu_); - return scopes_; - } - - private: - /** - * Sends an HTTP GET request to the GCE metadata server. - * - * @see https://cloud.google.com/compute/docs/storing-retrieving-metadata for - * an overview of retrieving information from the GCE metadata server. - */ - StatusOr DoMetadataServerGetRequest( - std::string const& path, bool recursive) const { - // Allows mocking the metadata server hostname for testing. - std::string metadata_server_hostname = - google::cloud::storage::internal::GceMetadataHostname(); - - HttpRequestBuilderType builder( - "http://" + metadata_server_hostname + path, - rest_internal::GetDefaultCurlHandleFactory()); - builder.AddHeader("metadata-flavor: Google"); - if (recursive) builder.AddQueryParameter("recursive", "true"); - return std::move(builder).BuildRequest().MakeRequest(std::string{}); - } - - /** - * Fetches metadata for an instance's service account. - * - * @see - * https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances - * for more details. - */ - Status RetrieveServiceAccountInfo() const { - auto response = DoMetadataServerGetRequest( - "/computeMetadata/v1/instance/service-accounts/" + - service_account_email_ + "/", - true); - if (!response) { - return std::move(response).status(); - } - if (response->status_code >= 300) { - return AsStatus(*response); - } - - auto metadata = ParseMetadataServerResponse(*response); - if (!metadata) { - return metadata.status(); - } - service_account_email_ = std::move(metadata->email); - scopes_ = std::move(metadata->scopes); - return Status(); - } - - StatusOr Refresh() const { - auto status = RetrieveServiceAccountInfo(); - if (!status.ok()) { - return status; - } - - auto response = DoMetadataServerGetRequest( - "/computeMetadata/v1/instance/service-accounts/" + - service_account_email_ + "/token", - false); - if (!response) { - return std::move(response).status(); - } - if (response->status_code >= 300) { - return AsStatus(*response); - } - - return ParseComputeEngineRefreshResponse(*response, clock_.now()); - } - - ClockType clock_; - mutable std::mutex mu_; - RefreshingCredentialsWrapper refreshing_creds_; - mutable std::set scopes_; - mutable std::string service_account_email_; -}; - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_COMPUTE_ENGINE_CREDENTIALS_H diff --git a/google/cloud/storage/oauth2/compute_engine_credentials_test.cc b/google/cloud/storage/oauth2/compute_engine_credentials_test.cc deleted file mode 100644 index c0116adbda333..0000000000000 --- a/google/cloud/storage/oauth2/compute_engine_credentials_test.cc +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/compute_engine_credentials.h" -#include "google/cloud/storage/internal/compute_engine_util.h" -#include "google/cloud/storage/oauth2/credential_constants.h" -#include "google/cloud/storage/testing/mock_http_request.h" -#include "google/cloud/testing_util/mock_fake_clock.h" -#include "google/cloud/testing_util/mock_http_payload.h" -#include "google/cloud/testing_util/mock_rest_client.h" -#include "google/cloud/testing_util/mock_rest_response.h" -#include "google/cloud/testing_util/status_matchers.h" -#include -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -// Define a helper to test the specialization. -struct ComputeEngineCredentialsTester { - static StatusOr Header( - ComputeEngineCredentials<>& tested, - std::chrono::system_clock::time_point tp) { - return tested.AuthorizationHeaderForTesting(tp); - } - - static ComputeEngineCredentials<> MakeComputeEngineCredentials( - std::string service_account_email, - oauth2_internal::HttpClientFactory factory) { - return ComputeEngineCredentials<>(std::move(service_account_email), - std::move(factory)); - } -}; - -namespace { - -using ::google::cloud::storage::internal::GceMetadataHostname; -using ::google::cloud::storage::internal::HttpResponse; -using ::google::cloud::storage::testing::MockHttpRequest; -using ::google::cloud::storage::testing::MockHttpRequestBuilder; -using ::google::cloud::testing_util::FakeClock; -using ::google::cloud::testing_util::IsOk; -using ::google::cloud::testing_util::IsOkAndHolds; -using ::google::cloud::testing_util::MakeMockHttpPayloadSuccess; -using ::google::cloud::testing_util::MockRestClient; -using ::google::cloud::testing_util::MockRestResponse; -using ::google::cloud::testing_util::StatusIs; -using ::testing::_; -using ::testing::ByMove; -using ::testing::HasSubstr; -using ::testing::IsEmpty; -using ::testing::Not; -using ::testing::Return; -using ::testing::StrEq; -using ::testing::UnorderedElementsAre; - -class ComputeEngineCredentialsTest : public ::testing::Test { - protected: - void SetUp() override { - MockHttpRequestBuilder::mock_ = - std::make_shared(); - } - void TearDown() override { MockHttpRequestBuilder::mock_.reset(); } -}; - -/// @test Verify that we can create and refresh ComputeEngineCredentials. -TEST_F(ComputeEngineCredentialsTest, - RefreshingSendsCorrectRequestBodyAndParsesResponse) { - std::string alias = "default"; - std::string email = "foo@bar.baz"; - std::string hostname = GceMetadataHostname(); - std::string svc_acct_info_resp = R"""({ - "email": ")""" + email + R"""(", - "scopes": ["scope1","scope2"] - })"""; - std::string token_info_resp = R"""({ - "access_token": "mysupersecrettoken", - "expires_in": 3600, - "token_type": "tokentype" - })"""; - - auto first_mock_req_impl = std::make_shared(); - EXPECT_CALL(*first_mock_req_impl, MakeRequest) - .WillOnce(Return(HttpResponse{200, svc_acct_info_resp, {}})); - auto second_mock_req_impl = std::make_shared(); - EXPECT_CALL(*second_mock_req_impl, MakeRequest) - .WillOnce(Return(HttpResponse{200, token_info_resp, {}})); - - auto mock_req_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_req_builder, BuildRequest()) - .WillOnce([first_mock_req_impl] { - MockHttpRequest mock_request; - mock_request.mock = first_mock_req_impl; - return mock_request; - }) - .WillOnce([second_mock_req_impl] { - MockHttpRequest mock_request; - mock_request.mock = second_mock_req_impl; - return mock_request; - }); - - // Both requests add this header. - EXPECT_CALL(*mock_req_builder, AddHeader(StrEq("metadata-flavor: Google"))) - .Times(2); - EXPECT_CALL( - *mock_req_builder, - Constructor(StrEq(std::string("http://") + hostname + - "/computeMetadata/v1/instance/service-accounts/" + - email + "/token"), - _, _)) - .Times(1); - // Only the call to retrieve service account info sends this query param. - EXPECT_CALL(*mock_req_builder, - AddQueryParameter(StrEq("recursive"), StrEq("true"))) - .Times(1); - EXPECT_CALL( - *mock_req_builder, - Constructor( - StrEq(std::string("http://") + hostname + - "/computeMetadata/v1/instance/service-accounts/" + alias + "/"), - _, _)) - .Times(1); - - ComputeEngineCredentials credentials(alias); - // Calls Refresh to obtain the access token for our authorization header. - EXPECT_EQ("Authorization: tokentype mysupersecrettoken", - credentials.AuthorizationHeader().value()); - // Make sure we obtain the scopes and email from the metadata server. - EXPECT_EQ(email, credentials.service_account_email()); - EXPECT_THAT(credentials.scopes(), UnorderedElementsAre("scope1", "scope2")); -} - -/// @test Parsing a refresh response with missing fields results in failure. -TEST_F(ComputeEngineCredentialsTest, ParseComputeEngineRefreshResponseInvalid) { - std::string token_info_resp = R"""({})"""; - // Does not have access_token. - std::string token_info_resp2 = R"""({ - "expires_in": 3600, - "token_type": "tokentype" -)"""; - - FakeClock::reset_clock(1000); - auto status = ParseComputeEngineRefreshResponse( - HttpResponse{400, token_info_resp, {}}, FakeClock::now()); - EXPECT_THAT(status, - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); - - status = ParseComputeEngineRefreshResponse( - HttpResponse{400, token_info_resp2, {}}, FakeClock::now()); - EXPECT_THAT(status, - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); - - EXPECT_THAT(ParseComputeEngineRefreshResponse( - HttpResponse{400, R"js("valid-json-but-not-object")js", {}}, - FakeClock::now()), - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); -} - -/// @test Parsing a refresh response yields a TemporaryToken. -TEST_F(ComputeEngineCredentialsTest, ParseComputeEngineRefreshResponse) { - std::string token_info_resp = R"""({ - "access_token": "mysupersecrettoken", - "expires_in": 3600, - "token_type": "tokentype" -})"""; - - auto expires_in = 3600; - auto clock_value = 2000; - FakeClock::reset_clock(clock_value); - - auto status = ParseComputeEngineRefreshResponse( - HttpResponse{200, token_info_resp, {}}, FakeClock::now()); - EXPECT_STATUS_OK(status); - auto token = *status; - EXPECT_EQ( - std::chrono::time_point_cast(token.expiration_time) - .time_since_epoch() - .count(), - clock_value + expires_in); - EXPECT_EQ(token.token, "Authorization: tokentype mysupersecrettoken"); -} - -/// @test Parsing a metadata server response with missing fields results in -/// failure. -TEST_F(ComputeEngineCredentialsTest, ParseMetadataServerResponseInvalid) { - std::string svc_acct_info_resp1 = R"""({})"""; - std::string svc_acct_info_resp2 = R"""({"scopes": ["scope1","scope2"]})"""; - std::string svc_acct_info_resp3 = R"""({"email": "foo@bar"})"""; - std::string svc_acct_info_resp4 = - R"""({"email": "foo@bar", "scopes": "scope1\nscope2\n"})"""; - - auto status = - ParseMetadataServerResponse(HttpResponse{200, svc_acct_info_resp1, {}}); - EXPECT_THAT(status, IsOk()); - EXPECT_THAT(status->email, IsEmpty()); - EXPECT_THAT(status->scopes, IsEmpty()); - - status = - ParseMetadataServerResponse(HttpResponse{400, svc_acct_info_resp2, {}}); - EXPECT_THAT(status, IsOk()); - EXPECT_THAT(status->email, IsEmpty()); - EXPECT_THAT(status->scopes, UnorderedElementsAre("scope1", "scope2")); - - status = - ParseMetadataServerResponse(HttpResponse{400, svc_acct_info_resp3, {}}); - EXPECT_THAT(status, IsOk()); - EXPECT_THAT(status->email, "foo@bar"); - EXPECT_THAT(status->scopes, IsEmpty()); - - status = - ParseMetadataServerResponse(HttpResponse{400, svc_acct_info_resp4, {}}); - EXPECT_THAT(status, IsOk()); - EXPECT_THAT(status->email, "foo@bar"); - EXPECT_THAT(status->scopes, UnorderedElementsAre("scope1", "scope2")); - - status = ParseMetadataServerResponse( - HttpResponse{400, R"js("valid-json-but-not-an-object")js", {}}); - EXPECT_THAT(status, IsOk()); - EXPECT_THAT(status->email, IsEmpty()); - EXPECT_THAT(status->scopes, IsEmpty()); -} - -/// @test Parsing a metadata server response yields a ServiceAccountMetadata. -TEST_F(ComputeEngineCredentialsTest, ParseMetadataServerResponse) { - std::string email = "foo@bar.baz"; - std::string svc_acct_info_resp = R"""({ - "email": ")""" + email + R"""(", - "scopes": ["scope1","scope2"] - })"""; - - auto status = - ParseMetadataServerResponse(HttpResponse{200, svc_acct_info_resp, {}}); - EXPECT_STATUS_OK(status); - auto metadata = *status; - EXPECT_EQ(metadata.email, email); - EXPECT_TRUE(metadata.scopes.count("scope1")); - EXPECT_TRUE(metadata.scopes.count("scope2")); -} - -/// @test Mock a failed refresh response during RetrieveServiceAccountInfo. -TEST_F(ComputeEngineCredentialsTest, FailedRetrieveServiceAccountInfo) { - std::string alias = "default"; - std::string hostname = GceMetadataHostname(); - - auto first_mock_req_impl = std::make_shared(); - EXPECT_CALL(*first_mock_req_impl, MakeRequest) - // Fail the first call to DoMetadataServerGetRequest immediately. - .WillOnce(Return(Status(StatusCode::kAborted, "Fake Curl error"))) - // Likewise, except with a >= 300 status code. - .WillOnce(Return(HttpResponse{400, "", {}})); - - auto mock_req_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_req_builder, BuildRequest()) - .Times(2) - .WillRepeatedly([first_mock_req_impl] { - MockHttpRequest mock_request; - mock_request.mock = first_mock_req_impl; - return mock_request; - }); - - EXPECT_CALL(*mock_req_builder, AddHeader(StrEq("metadata-flavor: Google"))) - .Times(2); - EXPECT_CALL(*mock_req_builder, - AddQueryParameter(StrEq("recursive"), StrEq("true"))) - .Times(2); - EXPECT_CALL( - *mock_req_builder, - Constructor( - StrEq(std::string("http://") + hostname + - "/computeMetadata/v1/instance/service-accounts/" + alias + "/"), - _, _)) - .Times(2); - - ComputeEngineCredentials credentials(alias); - // Response 1 - auto status = credentials.AuthorizationHeader(); - EXPECT_THAT(status, Not(IsOk())); - // Response 2 - status = credentials.AuthorizationHeader(); - EXPECT_THAT(status, Not(IsOk())); -} - -/// @test Mock a failed refresh response. -TEST_F(ComputeEngineCredentialsTest, FailedRefresh) { - std::string alias = "default"; - std::string email = "foo@bar.baz"; - std::string hostname = GceMetadataHostname(); - std::string svc_acct_info_resp = R"""({ - "email": ")""" + email + R"""(", - "scopes": ["scope1","scope2"] - })"""; - // Missing fields. - std::string token_info_resp = R"""({ - "expires_in": 3600, - "token_type": "tokentype" - })"""; - - auto mock_req = std::make_shared(); - EXPECT_CALL(*mock_req, MakeRequest) - // Fail the first call to RetrieveServiceAccountInfo immediately. - .WillOnce(Return(Status(StatusCode::kAborted, "Fake Curl error"))) - // Make the call to RetrieveServiceAccountInfo return a good response, - // but fail the token request immediately. - .WillOnce(Return(HttpResponse{200, svc_acct_info_resp, {}})) - .WillOnce(Return(Status(StatusCode::kAborted, "Fake Curl error"))) - // Likewise, except test with a >= 300 status code. - .WillOnce(Return(HttpResponse{200, svc_acct_info_resp, {}})) - .WillOnce(Return(HttpResponse{400, "", {}})) - // Parse with an invalid token response. - .WillOnce(Return(HttpResponse{200, svc_acct_info_resp, {}})) - .WillOnce(Return(HttpResponse{1, token_info_resp, {}})); - - auto mock_req_builder = MockHttpRequestBuilder::mock_; - auto mock_matcher = [mock_req] { - MockHttpRequest mock_request; - mock_request.mock = mock_req; - return mock_request; - }; - EXPECT_CALL(*mock_req_builder, BuildRequest()) - .Times(7) - .WillRepeatedly(mock_matcher); - - // This is added for all 7 requests. - EXPECT_CALL(*mock_req_builder, AddHeader(StrEq("metadata-flavor: Google"))) - .Times(7); - // These requests happen after RetrieveServiceAccountInfo, so they only - // occur 3 times. - EXPECT_CALL( - *mock_req_builder, - Constructor(StrEq(std::string("http://") + hostname + - "/computeMetadata/v1/instance/service-accounts/" + - email + "/token"), - _, _)) - .Times(3); - // This is only set when not retrieving the token. - EXPECT_CALL(*mock_req_builder, - AddQueryParameter(StrEq("recursive"), StrEq("true"))) - .Times(4); - // For the first expected failures, the alias is used until the metadata - // request succeeds. Then the email is refreshed. - EXPECT_CALL( - *mock_req_builder, - Constructor( - StrEq(std::string("http://") + hostname + - "/computeMetadata/v1/instance/service-accounts/" + alias + "/"), - _, _)) - .Times(2); - EXPECT_CALL( - *mock_req_builder, - Constructor( - StrEq(std::string("http://") + hostname + - "/computeMetadata/v1/instance/service-accounts/" + email + "/"), - _, _)) - .Times(2); - - ComputeEngineCredentials credentials(alias); - // Response 1 - auto status = credentials.AuthorizationHeader(); - EXPECT_THAT(status, StatusIs(StatusCode::kAborted)); - // Response 2 - status = credentials.AuthorizationHeader(); - EXPECT_THAT(status, Not(IsOk())); - // Response 3 - status = credentials.AuthorizationHeader(); - EXPECT_THAT(status, Not(IsOk())); - // Response 4 - status = credentials.AuthorizationHeader(); - EXPECT_THAT(status, - StatusIs(Not(StatusCode::kOk), - HasSubstr("Could not find all required fields"))); -} - -/// @test Verify that we can force a refresh of the service account email. -TEST_F(ComputeEngineCredentialsTest, AccountEmail) { - std::string alias = "default"; - std::string email = "foo@bar.baz"; - std::string hostname = GceMetadataHostname(); - std::string svc_acct_info_resp = R"""({ - "email": ")""" + email + R"""(", - "scopes": ["scope1","scope2"] - })"""; - - auto first_mock_req_impl = std::make_shared(); - EXPECT_CALL(*first_mock_req_impl, MakeRequest) - .WillOnce(Return(HttpResponse{200, svc_acct_info_resp, {}})); - - auto mock_req_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_req_builder, BuildRequest()) - .WillOnce([first_mock_req_impl] { - MockHttpRequest mock_request; - mock_request.mock = first_mock_req_impl; - return mock_request; - }); - - // Both requests add this header. - EXPECT_CALL(*mock_req_builder, AddHeader(StrEq("metadata-flavor: Google"))) - .Times(1); - // Only the call to retrieve service account info sends this query param. - EXPECT_CALL(*mock_req_builder, - AddQueryParameter(StrEq("recursive"), StrEq("true"))) - .Times(1); - EXPECT_CALL( - *mock_req_builder, - Constructor( - StrEq(std::string("http://") + hostname + - "/computeMetadata/v1/instance/service-accounts/" + alias + "/"), - _, _)) - .Times(1); - - ComputeEngineCredentials credentials(alias); - EXPECT_EQ(credentials.service_account_email(), alias); - auto refreshed_email = credentials.AccountEmail(); - EXPECT_EQ(email, refreshed_email); - EXPECT_EQ(credentials.service_account_email(), refreshed_email); -} - -TEST_F(ComputeEngineCredentialsTest, Caching) { - // We need to mock the Compute Engine Metadata Service or this would be an - // integration test that only runs on GCE. - auto make_mock_client = [](std::string const& payload) { - auto response = std::make_unique(); - EXPECT_CALL(*response, StatusCode) - .WillRepeatedly( - Return(google::cloud::rest_internal::HttpStatusCode::kOk)); - EXPECT_CALL(std::move(*response), ExtractPayload) - .WillOnce(Return(ByMove(MakeMockHttpPayloadSuccess(payload)))); - auto mock = std::make_unique(); - EXPECT_CALL(*mock, Get) - .WillOnce(Return(ByMove(std::unique_ptr( - std::move(response))))); - return std::unique_ptr(std::move(mock)); - }; - - // We expect a call to the metadata service to get the service account - // metadata. Then one call to get the token (which is cached), and finally a - // call the refresh the token. - auto constexpr kPayload0 = - R"""({"email": "test-only-email", "scopes": ["s1", "s2]})"""; - auto constexpr kPayload1 = R"""({ - "access_token": "test-token-1", - "expires_in": 3600, - "token_type": "tokentype" - })"""; - auto constexpr kPayload2 = R"""({ - "access_token": "test-token-2", - "expires_in": 3600, - "token_type": "tokentype" - })"""; - - using MockHttpClientFactory = - ::testing::MockFunction( - Options const&)>; - MockHttpClientFactory mock_factory; - EXPECT_CALL(mock_factory, Call) - .WillOnce(Return(ByMove(make_mock_client(kPayload0)))) - .WillOnce(Return(ByMove(make_mock_client(kPayload1)))) - .WillOnce(Return(ByMove(make_mock_client(kPayload2)))); - - auto tested = ComputeEngineCredentialsTester::MakeComputeEngineCredentials( - "test-only", mock_factory.AsStdFunction()); - auto const tp = std::chrono::system_clock::now(); - auto initial = ComputeEngineCredentialsTester::Header(tested, tp); - ASSERT_STATUS_OK(initial); - - auto cached = ComputeEngineCredentialsTester::Header( - tested, tp + std::chrono::seconds(30)); - EXPECT_THAT(cached, IsOkAndHolds(*initial)); - - cached = ComputeEngineCredentialsTester::Header( - tested, tp + std::chrono::seconds(300)); - EXPECT_THAT(cached, IsOkAndHolds(*initial)); - - auto uncached = ComputeEngineCredentialsTester::Header( - tested, tp + std::chrono::hours(2)); - ASSERT_STATUS_OK(uncached); - EXPECT_NE(*initial, *uncached); -} - -} // namespace -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/credential_constants.h b/google/cloud/storage/oauth2/credential_constants.h deleted file mode 100644 index b9081f9d3afa2..0000000000000 --- a/google/cloud/storage/oauth2/credential_constants.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_CREDENTIAL_CONSTANTS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_CREDENTIAL_CONSTANTS_H - -#include "google/cloud/storage/version.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -/** - * Supported signing algorithms used in JWT auth flows. - * - * We currently only support RSA with SHA-256, but use this enum for - * readability and easy addition of support for other algorithms. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -enum class GOOGLE_CLOUD_CPP_DEPRECATED( - "This enum will be removed in v4.0.0 and later. Prefer using the unified " - "credentials documented in @ref guac.") JwtSigningAlgorithms { - // NOLINTNEXTLINE(readability-identifier-naming) - RS256 -}; - -/** - * The max lifetime in seconds of an access token. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This constant will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -constexpr std::chrono::seconds GoogleOAuthAccessTokenLifetime() { - return std::chrono::seconds(3600); -} - -/** - * Returns the slack to consider when checking if an access token is expired. - * - * This time should be subtracted from a token's expiration time when checking - * if it is expired. This prevents race conditions where, for example, one might - * check expiration time one second before the expiration, see that the token is - * still valid, then attempt to use it two seconds later and receive an - * error. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This constant will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -constexpr std::chrono::seconds GoogleOAuthAccessTokenExpirationSlack() { - return std::chrono::seconds(300); -} - -/** - * The endpoint to fetch an OAuth 2.0 access token from. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This constant will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -inline char const* GoogleOAuthRefreshEndpoint() { - static constexpr char kEndpoint[] = "https://oauth2.googleapis.com/token"; - return kEndpoint; -} - -/** - * String representing the "cloud-platform" OAuth 2.0 scope. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This constant will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -inline char const* GoogleOAuthScopeCloudPlatform() { - static constexpr char kScope[] = - "https://www.googleapis.com/auth/cloud-platform"; - return kScope; -} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_CREDENTIAL_CONSTANTS_H diff --git a/google/cloud/storage/oauth2/credentials.cc b/google/cloud/storage/oauth2/credentials.cc deleted file mode 100644 index a2ce6d3a0629c..0000000000000 --- a/google/cloud/storage/oauth2/credentials.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/internal/make_status.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -StatusOr> Credentials::SignBlob( - SigningAccount const&, std::string const&) const { - return google::cloud::internal::UnimplementedError( - "The current credentials cannot sign blobs locally", GCP_ERROR_INFO()); -} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/credentials.h b/google/cloud/storage/oauth2/credentials.h deleted file mode 100644 index 1c8570dc6b895..0000000000000 --- a/google/cloud/storage/oauth2/credentials.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_CREDENTIALS_H - -#include "google/cloud/storage/signed_url_options.h" -#include "google/cloud/storage/version.h" -#include "google/cloud/status.h" -#include "google/cloud/status_or.h" -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -/** - * Authentication components for Google Cloud Storage. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -namespace oauth2 { - -/** - * Interface for OAuth 2.0 credentials used to access Google Cloud services. - * - * Instantiating a specific kind of `Credentials` should usually be done via the - * convenience methods declared in google_credentials.h. - * - * @see https://cloud.google.com/docs/authentication/ for an overview of - * authenticating to Google Cloud Platform APIs. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") Credentials { - public: - virtual ~Credentials() = default; - - /** - * Attempts to obtain a value for the Authorization HTTP header. - * - * If unable to obtain a value for the Authorization header, which could - * happen for `Credentials` that need to be periodically refreshed, the - * underlying `Status` will indicate failure details from the refresh HTTP - * request. Otherwise, the returned value will contain the Authorization - * header to be used in HTTP requests. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - virtual StatusOr AuthorizationHeader() = 0; - - /** - * Try to sign @p string_to_sign using @p service_account. - * - * Some %Credentials types can locally sign a blob, most often just on behalf - * of an specific service account. This function returns an error if the - * credentials cannot sign the blob at all, or if the service account is a - * mismatch. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - virtual StatusOr> SignBlob( - SigningAccount const& service_account, - std::string const& string_to_sign) const; - - /** - * Return the account's email associated with these credentials, if any. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - virtual std::string AccountEmail() const { return std::string{}; } - - /** - * Return the account's key_id associated with these credentials, if any. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - virtual std::string KeyId() const { return std::string{}; } -}; - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_CREDENTIALS_H diff --git a/google/cloud/storage/oauth2/google_application_default_credentials_file.cc b/google/cloud/storage/oauth2/google_application_default_credentials_file.cc deleted file mode 100644 index 2cf03f61b76c5..0000000000000 --- a/google/cloud/storage/oauth2/google_application_default_credentials_file.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/google_application_default_credentials_file.h" -#include "google/cloud/internal/oauth2_google_application_default_credentials_file.h" - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -char const* GoogleAdcEnvVar() { - return google::cloud::oauth2_internal::GoogleAdcEnvVar(); -} - -std::string GoogleAdcFilePathFromEnvVarOrEmpty() { - return oauth2_internal::GoogleAdcFilePathFromEnvVarOrEmpty(); -} - -std::string GoogleAdcFilePathFromWellKnownPathOrEmpty() { - return oauth2_internal::GoogleAdcFilePathFromWellKnownPathOrEmpty(); -} - -char const* GoogleGcloudAdcFileEnvVar() { - return google::cloud::oauth2_internal::GoogleGcloudAdcFileEnvVar(); -} - -char const* GoogleAdcHomeEnvVar() { - return google::cloud::oauth2_internal::GoogleAdcHomeEnvVar(); -} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/google_application_default_credentials_file.h b/google/cloud/storage/oauth2/google_application_default_credentials_file.h deleted file mode 100644 index 5c9b41d1c053c..0000000000000 --- a/google/cloud/storage/oauth2/google_application_default_credentials_file.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_GOOGLE_APPLICATION_DEFAULT_CREDENTIALS_FILE_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_GOOGLE_APPLICATION_DEFAULT_CREDENTIALS_FILE_H - -#include "google/cloud/storage/version.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -/** - * Returns the Application Default %Credentials environment variable name. - * - * This environment variable should be checked for a valid file path when - * attempting to load Google Application Default %Credentials. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -char const* GoogleAdcEnvVar(); - -/** - * Returns the path to the Application Default %Credentials file, if set. - * - * If the Application Default %Credentials environment variable is set, we check - * the path specified by its value for a file containing ADCs. Returns an - * empty string if no such path exists or the environment variable is not set. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -std::string GoogleAdcFilePathFromEnvVarOrEmpty(); - -/** - * Returns the path to the Application Default %Credentials file, if set. - * - * If the gcloud utility has configured an Application Default %Credentials - * file, the path to that file is returned. Returns an empty string if no such - * file exists at the well known path. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -std::string GoogleAdcFilePathFromWellKnownPathOrEmpty(); - -/** - * Returns the environment variable to override the gcloud ADC path. - * - * This environment variable is used for testing to override the path that - * should be searched for the gcloud Application Default %Credentials file. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -char const* GoogleGcloudAdcFileEnvVar(); - -/** - * Returns the environment variable used to construct the well known ADC path. - * - * The directory containing a user's application configuration data, indicated - * by this environment variable, varies across environments. That directory is - * used when constructing the well known path of the Application Default - * Credentials file. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -char const* GoogleAdcHomeEnvVar(); - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_GOOGLE_APPLICATION_DEFAULT_CREDENTIALS_FILE_H diff --git a/google/cloud/storage/oauth2/google_application_default_credentials_file_test.cc b/google/cloud/storage/oauth2/google_application_default_credentials_file_test.cc deleted file mode 100644 index 02f01a7ba2f36..0000000000000 --- a/google/cloud/storage/oauth2/google_application_default_credentials_file_test.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google_application_default_credentials_file.h" -#include "google/cloud/testing_util/scoped_environment.h" -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { -namespace { - -using ::google::cloud::testing_util::ScopedEnvironment; -using ::testing::HasSubstr; - -class DefaultServiceAccountFileTest : public ::testing::Test { - public: - DefaultServiceAccountFileTest() - : home_env_var_(GoogleAdcHomeEnvVar(), {}), - adc_env_var_(GoogleAdcEnvVar(), {}), - gcloud_path_override_env_var_(GoogleGcloudAdcFileEnvVar(), {}) {} - - protected: - ScopedEnvironment home_env_var_; - ScopedEnvironment adc_env_var_; - ScopedEnvironment gcloud_path_override_env_var_; -}; - -/// @test Verify that the specified path is given when the ADC env var is set. -TEST_F(DefaultServiceAccountFileTest, AdcEnvironmentVariableSet) { - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), "/foo/bar/baz"); - EXPECT_EQ("/foo/bar/baz", GoogleAdcFilePathFromEnvVarOrEmpty()); -} - -/// @test Verify that an empty string is given when the ADC env var is unset. -TEST_F(DefaultServiceAccountFileTest, AdcEnvironmentVariableNotSet) { - EXPECT_EQ(GoogleAdcFilePathFromEnvVarOrEmpty(), ""); -} - -/// @test Verify that the gcloud ADC file path can be overridden for testing. -TEST_F(DefaultServiceAccountFileTest, GcloudAdcPathOverrideViaEnvVar) { - ScopedEnvironment gcloud_path_override_env_var(GoogleGcloudAdcFileEnvVar(), - "/foo/bar/baz"); - EXPECT_EQ(GoogleAdcFilePathFromWellKnownPathOrEmpty(), "/foo/bar/baz"); -} - -/// @test Verify that the gcloud ADC file path is given when HOME is set. -TEST_F(DefaultServiceAccountFileTest, HomeSet) { - ScopedEnvironment home_env_var(GoogleAdcHomeEnvVar(), "/foo/bar/baz"); - - auto actual = GoogleAdcFilePathFromWellKnownPathOrEmpty(); - - EXPECT_THAT(actual, HasSubstr("/foo/bar/baz")); - // The rest of the path differs depending on the OS; just make sure that we - // appended this suffix of the path to the path prefix set above. - EXPECT_THAT(actual, HasSubstr("gcloud/application_default_credentials.json")); -} - -/// @test Verify that the gcloud ADC file path is not given when HOME is unset. -TEST_F(DefaultServiceAccountFileTest, HomeNotSet) { - EXPECT_EQ(GoogleAdcFilePathFromWellKnownPathOrEmpty(), ""); -} - -} // namespace -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/google_credentials.cc b/google/cloud/storage/oauth2/google_credentials.cc deleted file mode 100644 index 357db5c71ac41..0000000000000 --- a/google/cloud/storage/oauth2/google_credentials.cc +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/google_credentials.h" -#include "google/cloud/storage/internal/make_jwt_assertion.h" -#include "google/cloud/storage/oauth2/anonymous_credentials.h" -#include "google/cloud/storage/oauth2/authorized_user_credentials.h" -#include "google/cloud/storage/oauth2/compute_engine_credentials.h" -#include "google/cloud/storage/oauth2/google_application_default_credentials_file.h" -#include "google/cloud/storage/oauth2/service_account_credentials.h" -#include "google/cloud/internal/filesystem.h" -#include "google/cloud/internal/make_status.h" -#include "google/cloud/internal/throw_delegate.h" -#include -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -char constexpr kAdcLink[] = - "https://developers.google.com/identity/protocols/" - "application-default-credentials"; - -// Parses the JSON or P12 file at `path` and creates the appropriate -// Credentials type. -// -// If `service_account_scopes` or `service_account_subject` are specified, the -// file at `path` must be a P12 service account or a JSON service account. If -// a different type of credential file is found, this function returns -// nullptr to indicate a service account file wasn't found. -StatusOr> LoadCredsFromPath( - std::string const& path, bool non_service_account_ok, - absl::optional> service_account_scopes, - absl::optional service_account_subject, - Options const& options) { - std::ifstream ifs(path); - if (!ifs.is_open()) { - // We use kUnknown here because we don't know if the file does not exist, or - // if we were unable to open it for some other reason. - return google::cloud::internal::UnknownError( - "Cannot open credentials file " + path, GCP_ERROR_INFO()); - } - std::string contents(std::istreambuf_iterator{ifs}, {}); - auto cred_json = nlohmann::json::parse(contents, nullptr, false); - if (!cred_json.is_object()) { - // This is not a JSON file, try to load it as a P12 service account. - auto info = ParseServiceAccountP12File(path); - if (!info) return std::move(info).status(); - info->subject = std::move(service_account_subject); - info->scopes = std::move(service_account_scopes); - auto credentials = std::make_unique>(*info); - return std::unique_ptr(std::move(credentials)); - } - std::string cred_type = cred_json.value("type", "no type given"); - // If non_service_account_ok==false and the cred_type is authorized_user, - // we'll return "Unsupported credential type (authorized_user)". - if (cred_type == "authorized_user" && non_service_account_ok) { - if (service_account_scopes || service_account_subject) { - // No ptr indicates that the file we found was not a service account file. - return StatusOr>(nullptr); - } - auto info = ParseAuthorizedUserCredentials(contents, path); - if (!info) { - return info.status(); - } - std::unique_ptr ptr = - std::make_unique>(*info); - return StatusOr>(std::move(ptr)); - } - if (cred_type == "service_account") { - auto info = ParseServiceAccountCredentials(contents, path); - if (!info) { - return info.status(); - } - info->subject = std::move(service_account_subject); - info->scopes = std::move(service_account_scopes); - std::unique_ptr ptr = - std::make_unique>(*info, options); - return StatusOr>(std::move(ptr)); - } - return google::cloud::internal::InvalidArgumentError( - "Unsupported credential type (" + cred_type + - ") when reading Application Default Credentials file from " + path + - ".", - GCP_ERROR_INFO()); -} - -// Tries to load the file at the path specified by the value of the Application -// Default %Credentials environment variable and to create the appropriate -// Credentials type. -// -// Returns nullptr if the environment variable is not set or the path does not -// exist. -// -// If `service_account_scopes` or `service_account_subject` are specified, the -// found file must be a P12 service account or a JSON service account. If a -// different type of credential file is found, this function returns nullptr -// to indicate a service account file wasn't found. -StatusOr> MaybeLoadCredsFromAdcPaths( - bool non_service_account_ok, - absl::optional> service_account_scopes, - absl::optional service_account_subject, - Options const& options = {}) { - // 1) Check if the GOOGLE_APPLICATION_CREDENTIALS environment variable is set. - auto path = GoogleAdcFilePathFromEnvVarOrEmpty(); - if (path.empty()) { - // 2) If no path was specified via environment variable, check if the - // gcloud ADC file exists. - path = GoogleAdcFilePathFromWellKnownPathOrEmpty(); - if (path.empty()) { - return StatusOr>(nullptr); - } - // Just because we had the necessary information to build the path doesn't - // mean that a file exists there. - std::error_code ec; - auto adc_file_status = google::cloud::internal::status(path, ec); - if (!google::cloud::internal::exists(adc_file_status)) { - return StatusOr>(nullptr); - } - } - - // If the path was specified, try to load that file; explicitly fail if it - // doesn't exist or can't be read and parsed. - return LoadCredsFromPath(path, non_service_account_ok, - std::move(service_account_scopes), - std::move(service_account_subject), options); -} - -StatusOr> GoogleDefaultCredentials( - Options const& options) { - // 1 and 2) Check if the GOOGLE_APPLICATION_CREDENTIALS environment variable - // is set or if the gcloud ADC file exists. - auto creds = MaybeLoadCredsFromAdcPaths(true, {}, {}, options); - if (!creds) { - return StatusOr>(creds.status()); - } - if (*creds) { - return StatusOr>(std::move(*creds)); - } - - // 3) Use the implicit environment-based credentials (GCE, GAE Flexible, - // Cloud Run or GKE Environment). - return StatusOr>( - std::make_shared>()); -} - -std::shared_ptr CreateAnonymousCredentials() { - return std::make_shared(); -} - -StatusOr> -CreateAuthorizedUserCredentialsFromJsonFilePath(std::string const& path) { - std::ifstream is(path); - std::string contents(std::istreambuf_iterator{is}, {}); - auto info = ParseAuthorizedUserCredentials(contents, path); - if (!info) { - return StatusOr>(info.status()); - } - return StatusOr>( - std::make_shared>(*info)); -} - -StatusOr> -CreateAuthorizedUserCredentialsFromJsonContents(std::string const& contents, - Options const& options) { - auto info = ParseAuthorizedUserCredentials(contents, "memory"); - if (!info) { - return StatusOr>(info.status()); - } - return StatusOr>( - std::make_shared>(*info, options)); -} - -StatusOr> -CreateServiceAccountCredentialsFromFilePath(std::string const& path) { - return CreateServiceAccountCredentialsFromFilePath(path, {}, {}); -} - -StatusOr> -CreateServiceAccountCredentialsFromFilePath( - std::string const& path, absl::optional> scopes, - absl::optional subject) { - auto credentials = - CreateServiceAccountCredentialsFromJsonFilePath(path, scopes, subject); - if (credentials) { - return credentials; - } - return CreateServiceAccountCredentialsFromP12FilePath(path, std::move(scopes), - std::move(subject)); -} - -StatusOr> -CreateServiceAccountCredentialsFromJsonFilePath(std::string const& path) { - return CreateServiceAccountCredentialsFromJsonFilePath(path, {}, {}); -} - -StatusOr> -CreateServiceAccountCredentialsFromJsonFilePath( - std::string const& path, absl::optional> scopes, - absl::optional subject, Options const& options) { - std::ifstream is(path); - std::string contents(std::istreambuf_iterator{is}, {}); - auto info = ParseServiceAccountCredentials(contents, path); - if (!info) { - return StatusOr>(info.status()); - } - // These are supplied as extra parameters to this method, not in the JSON - // file. - info->subject = std::move(subject); - info->scopes = std::move(scopes); - return StatusOr>( - std::make_shared>(*info, options)); -} - -StatusOr> -CreateServiceAccountCredentialsFromP12FilePath( - std::string const& path, absl::optional> scopes, - absl::optional subject, Options const& options) { - auto info = ParseServiceAccountP12File(path); - if (!info) { - return StatusOr>(info.status()); - } - // These are supplied as extra parameters to this method, not in the P12 - // file. - info->subject = std::move(subject); - info->scopes = std::move(scopes); - return StatusOr>( - std::make_shared>(*info, options)); -} - -StatusOr> -CreateServiceAccountCredentialsFromP12FilePath(std::string const& path) { - return CreateServiceAccountCredentialsFromP12FilePath(path, {}, {}); -} - -StatusOr> -CreateServiceAccountCredentialsFromDefaultPaths(Options const& options) { - return CreateServiceAccountCredentialsFromDefaultPaths({}, {}, options); -} - -StatusOr> -CreateServiceAccountCredentialsFromDefaultPaths( - absl::optional> scopes, - absl::optional subject, Options const& options) { - auto creds = MaybeLoadCredsFromAdcPaths(false, std::move(scopes), - std::move(subject), options); - if (!creds) { - return StatusOr>(creds.status()); - } - if (*creds) { - return StatusOr>(std::move(*creds)); - } - - // We've exhausted all search points, thus credentials cannot be constructed. - return google::cloud::internal::UnknownError( - "Could not create service account credentials using Application" - "Default Credentials paths. For more information, please see " + - std::string(kAdcLink), - GCP_ERROR_INFO()); -} - -StatusOr> -CreateServiceAccountCredentialsFromJsonContents(std::string const& contents, - Options const& options) { - return CreateServiceAccountCredentialsFromJsonContents(contents, {}, {}, - options); -} - -StatusOr> -CreateServiceAccountCredentialsFromJsonContents( - std::string const& contents, absl::optional> scopes, - absl::optional subject, Options const& options) { - auto info = ParseServiceAccountCredentials(contents, "memory"); - if (!info) { - return StatusOr>(info.status()); - } - std::chrono::system_clock::time_point now; - auto components = AssertionComponentsFromInfo(*info, now); - auto jwt_assertion = internal::MakeJWTAssertionNoThrow( - components.first, components.second, info->private_key); - if (!jwt_assertion) return std::move(jwt_assertion).status(); - - // These are supplied as extra parameters to this method, not in the JSON - // file. - info->subject = std::move(subject); - info->scopes = std::move(scopes); - return StatusOr>( - std::make_shared>(*info, options)); -} - -std::shared_ptr CreateComputeEngineCredentials() { - return std::make_shared>(); -} - -std::shared_ptr CreateComputeEngineCredentials( - std::string const& service_account_email) { - return std::make_shared>(service_account_email); -} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/google_credentials.h b/google/cloud/storage/oauth2/google_credentials.h deleted file mode 100644 index dd891ac1acad6..0000000000000 --- a/google/cloud/storage/oauth2/google_credentials.h +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_GOOGLE_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_GOOGLE_CREDENTIALS_H - -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/version.h" -#include "google/cloud/optional.h" -#include "google/cloud/options.h" -#include "absl/types/optional.h" -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -/** - * Produces a Credentials type based on the runtime environment. - * - * If the GOOGLE_APPLICATION_CREDENTIALS environment variable is set, the JSON - * file it points to will be loaded and used to create a credential of the - * specified type. Otherwise, if running on a Google-hosted environment (e.g. - * Compute Engine), credentials for the environment's default service - * account will be used. - * - * @see https://cloud.google.com/docs/authentication/production for details - * about Application Default %Credentials. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> GoogleDefaultCredentials( - Options const& options = {}); - -///@{ -/** - * @name Functions to manually create specific credential types. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - -/** - * Creates an AnonymousCredentials. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -std::shared_ptr CreateAnonymousCredentials(); - -/** - * Creates an AuthorizedUserCredentials from a JSON file at the specified path. - * - * @note It is strongly preferred to instead use service account credentials - * with Cloud Storage client libraries. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateAuthorizedUserCredentialsFromJsonFilePath(std::string const& path); - -/** - * Creates an AuthorizedUserCredentials from a JSON string. - * - * @note It is strongly preferred to instead use service account credentials - * with Cloud Storage client libraries. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateAuthorizedUserCredentialsFromJsonContents(std::string const& contents, - Options const& options = {}); - -///@{ -/** - * @name Load service account key files. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - -/** - * Creates a ServiceAccountCredentials from a file at the specified path. - * - * @note This function automatically detects if the file is a JSON or P12 (aka - * PFX aka PKCS#12) file and tries to load the file as a service account - * credential. We strongly recommend that applications use JSON files for - * service account key files. - * - * These credentials use the cloud-platform OAuth 2.0 scope, defined by - * `GoogleOAuthScopeCloudPlatform()`. To specify alternate scopes, use the - * overloaded version of this function. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromFilePath(std::string const& path); - -/** - * Creates a ServiceAccountCredentials from a file at the specified path. - * - * @note This function automatically detects if the file is a JSON or P12 (aka - * PFX aka PKCS#12) file and tries to load the file as a service account - * credential. We strongly recommend that applications use JSON files for - * service account key files. - * - * @param path the path to the file containing service account JSON credentials. - * @param scopes the scopes to request during the authorization grant. If - * omitted, the cloud-platform scope, defined by - * `GoogleOAuthScopeCloudPlatform()`, is used as a default. - * @param subject for domain-wide delegation; the email address of the user for - * which to request delegated access. If omitted, no "subject" attribute is - * included in the authorization grant. - * - * @see https://developers.google.com/identity/protocols/googlescopes for a list - * of OAuth 2.0 scopes used with Google APIs. - * - * @see https://developers.google.com/identity/protocols/OAuth2ServiceAccount - * for more information about domain-wide delegation. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromFilePath( - std::string const& path, absl::optional> scopes, - absl::optional subject); - -/** - * Creates a ServiceAccountCredentials from a JSON file at the specified path. - * - * These credentials use the cloud-platform OAuth 2.0 scope, defined by - * `GoogleOAuthScopeCloudPlatform()`. To specify alternate scopes, use the - * overloaded version of this function. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromJsonFilePath(std::string const& path); - -/** - * Creates a ServiceAccountCredentials from a JSON file at the specified path. - * - * @param path the path to the file containing service account JSON credentials. - * @param scopes the scopes to request during the authorization grant. If - * omitted, the cloud-platform scope, defined by - * `GoogleOAuthScopeCloudPlatform()`, is used as a default. - * @param subject for domain-wide delegation; the email address of the user for - * which to request delegated access. If omitted, no "subject" attribute is - * included in the authorization grant. - * @param options any configuration needed for the transport channel to - * Google's authentication servers. - * - * @see https://developers.google.com/identity/protocols/googlescopes for a list - * of OAuth 2.0 scopes used with Google APIs. - * - * @see https://developers.google.com/identity/protocols/OAuth2ServiceAccount - * for more information about domain-wide delegation. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromJsonFilePath( - std::string const& path, absl::optional> scopes, - absl::optional subject, Options const& options = {}); - -/** - * Creates a ServiceAccountCredentials from a P12 file at the specified path. - * - * These credentials use the cloud-platform OAuth 2.0 scope, defined by - * `GoogleOAuthScopeCloudPlatform()`. To specify alternate scopes, use the - * overloaded version of this function. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromP12FilePath(std::string const& path); - -/** - * Creates a ServiceAccountCredentials from a P12 file at the specified path. - * - * @param path the path to the file containing service account JSON credentials. - * @param scopes the scopes to request during the authorization grant. If - * omitted, the cloud-platform scope, defined by - * `GoogleOAuthScopeCloudPlatform()`, is used as a default. - * @param subject for domain-wide delegation; the email address of the user for - * which to request delegated access. If omitted, no "subject" attribute is - * included in the authorization grant. - * @param options any configuration needed for the transport channel to - * Google's authentication servers. - * - * @see https://developers.google.com/identity/protocols/googlescopes for a list - * of OAuth 2.0 scopes used with Google APIs. - * - * @see https://developers.google.com/identity/protocols/OAuth2ServiceAccount - * for more information about domain-wide delegation. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromP12FilePath( - std::string const& path, absl::optional> scopes, - absl::optional subject, Options const& options = {}); -///@} - -/** - * Produces a ServiceAccountCredentials type by trying to load the standard - * Application Default %Credentials paths. - * - * If the GOOGLE_APPLICATION_CREDENTIALS environment variable is set, the JSON - * or P12 file it points to will be loaded. Otherwise, if the gcloud utility - * has configured an Application Default %Credentials file, that file is - * loaded. The loaded file is used to create a ServiceAccountCredentials. - * - * @param options any configuration needed for the transport channel to - * Google's authentication servers. - * - * @see https://cloud.google.com/docs/authentication/production for details - * about Application Default %Credentials. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromDefaultPaths(Options const& options = {}); - -/** - * Produces a ServiceAccountCredentials type by trying to load the standard - * Application Default %Credentials paths. - * - * If the GOOGLE_APPLICATION_CREDENTIALS environment variable is set, the JSON - * or P12 file it points to will be loaded. Otherwise, if the gcloud utility - * has configured an Application Default %Credentials file, that file is - * loaded. The loaded file is used to create a ServiceAccountCredentials. - * - * @param scopes the scopes to request during the authorization grant. If - * omitted, the cloud-platform scope, defined by - * `GoogleOAuthScopeCloudPlatform()`, is used as a default. - * @param subject for domain-wide delegation; the email address of the user for - * which to request delegated access. If omitted, no "subject" attribute is - * included in the authorization grant. - * @param options any configuration needed for the transport channel to - * Google's authentication servers. - * - * @see https://developers.google.com/identity/protocols/googlescopes for a list - * of OAuth 2.0 scopes used with Google APIs. - * - * @see https://cloud.google.com/docs/authentication/production for details - * about Application Default %Credentials. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromDefaultPaths( - absl::optional> scopes, - absl::optional subject, Options const& options = {}); - -/** - * Creates a ServiceAccountCredentials from a JSON string. - * - * These credentials use the cloud-platform OAuth 2.0 scope, defined by - * `GoogleOAuthScopeCloudPlatform()`. To specify an alternate set of scopes, use - * the overloaded version of this function. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromJsonContents(std::string const& contents, - Options const& options = {}); - -/** - * Creates a ServiceAccountCredentials from a JSON string. - * - * @param contents the string containing the JSON contents of a service account - * credentials file. - * @param scopes the scopes to request during the authorization grant. If - * omitted, the cloud-platform scope, defined by - * `GoogleOAuthScopeCloudPlatform()`, is used as a default. - * @param subject for domain-wide delegation; the email address of the user for - * which to request delegated access. If omitted, no "subject" attribute is - * included in the authorization grant. - * @param options any configuration needed for the transport channel to - * Google's authentication servers. - * - * @see https://developers.google.com/identity/protocols/googlescopes for a list - * of OAuth 2.0 scopes used with Google APIs. - * - * @see https://developers.google.com/identity/protocols/OAuth2ServiceAccount - * for more information about domain-wide delegation. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr> -CreateServiceAccountCredentialsFromJsonContents( - std::string const& contents, absl::optional> scopes, - absl::optional subject, Options const& options = {}); - -/** - * Creates a ComputeEngineCredentials for the VM's default service account. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -std::shared_ptr CreateComputeEngineCredentials(); - -/** - * Creates a ComputeEngineCredentials for the VM's specified service account. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -std::shared_ptr CreateComputeEngineCredentials( - std::string const& service_account_email); - -///@} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_GOOGLE_CREDENTIALS_H diff --git a/google/cloud/storage/oauth2/google_credentials_test.cc b/google/cloud/storage/oauth2/google_credentials_test.cc deleted file mode 100644 index 909fba2e3df7e..0000000000000 --- a/google/cloud/storage/oauth2/google_credentials_test.cc +++ /dev/null @@ -1,562 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/google_credentials.h" -#include "google/cloud/storage/internal/compute_engine_util.h" -#include "google/cloud/storage/oauth2/anonymous_credentials.h" -#include "google/cloud/storage/oauth2/authorized_user_credentials.h" -#include "google/cloud/storage/oauth2/compute_engine_credentials.h" -#include "google/cloud/storage/oauth2/google_application_default_credentials_file.h" -#include "google/cloud/storage/oauth2/service_account_credentials.h" -#include "google/cloud/storage/testing/constants.h" -#include "google/cloud/storage/testing/write_base64.h" -#include "google/cloud/internal/filesystem.h" -#include "google/cloud/testing_util/scoped_environment.h" -#include "google/cloud/testing_util/status_matchers.h" -#include "absl/strings/match.h" -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { -namespace { - -using ::google::cloud::storage::internal::GceCheckOverrideEnvVar; -using ::google::cloud::storage::testing::kP12KeyFileContents; -using ::google::cloud::storage::testing::kP12ServiceAccountId; -using ::google::cloud::storage::testing::WriteBase64AsBinary; -using ::google::cloud::testing_util::IsOk; -using ::google::cloud::testing_util::ScopedEnvironment; -using ::google::cloud::testing_util::StatusIs; -using ::testing::AllOf; -using ::testing::HasSubstr; -using ::testing::Not; -using ::testing::NotNull; - -class GoogleCredentialsTest : public ::testing::Test { - public: - // Make sure other higher-precedence credentials (ADC env var, gcloud ADC from - // well-known path) aren't loaded. - GoogleCredentialsTest() - : home_env_var_(GoogleAdcHomeEnvVar(), {}), - adc_env_var_(GoogleAdcEnvVar(), {}), - gcloud_path_override_env_var_(GoogleGcloudAdcFileEnvVar(), {}), - gce_check_override_env_var_(GceCheckOverrideEnvVar(), {}) {} - - protected: - ScopedEnvironment home_env_var_; - ScopedEnvironment adc_env_var_; - ScopedEnvironment gcloud_path_override_env_var_; - ScopedEnvironment gce_check_override_env_var_; -}; - -std::string const kAuthorizedUserCredFilename = "authorized-user.json"; -std::string const kAuthorizedUserCredContents = R"""({ - "client_id": "test-invalid-test-invalid.apps.googleusercontent.com", - "client_secret": "invalid-invalid-invalid", - "refresh_token": "1/test-test-test", - "type": "authorized_user" -})"""; - -void SetupAuthorizedUserCredentialsFileForTest(std::string const& filename) { - std::ofstream os(filename); - os << kAuthorizedUserCredContents; - os.close(); -} - -/** - * @test Verify `GoogleDefaultCredentials()` loads authorized user credentials. - * - * This test only verifies the right type of object is created, the unit tests - * for `AuthorizedUserCredentials` already check that once loaded the class - * works correctly. Testing here would be redundant. Furthermore, calling - * `AuthorizationHeader()` initiates the key verification workflow, that - * requires valid keys and contacting Google's production servers, and would - * make this an integration test. - */ -TEST_F(GoogleCredentialsTest, LoadValidAuthorizedUserCredentialsViaEnvVar) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupAuthorizedUserCredentialsFileForTest(filename); - - // Test that the authorized user credentials are loaded as the default when - // specified via the well known environment variable. - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - auto creds = GoogleDefaultCredentials(); - ASSERT_STATUS_OK(creds); - // Need to create a temporary for the pointer because clang-tidy warns about - // using expressions with (potential) side-effects inside typeid(). - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(AuthorizedUserCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, LoadValidAuthorizedUserCredentialsViaGcloudFile) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupAuthorizedUserCredentialsFileForTest(filename); - // Test that the authorized user credentials are loaded as the default when - // stored in the well known gcloud ADC file path. - ScopedEnvironment gcloud_path_override_env_var(GoogleGcloudAdcFileEnvVar(), - filename.c_str()); - auto creds = GoogleDefaultCredentials(); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(AuthorizedUserCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, LoadValidAuthorizedUserCredentialsFromFilename) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupAuthorizedUserCredentialsFileForTest(filename); - auto creds = CreateAuthorizedUserCredentialsFromJsonFilePath(filename); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(AuthorizedUserCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, LoadValidAuthorizedUserCredentialsFromContents) { - // Test that the authorized user credentials are loaded from a string - // representing JSON contents. - auto creds = CreateAuthorizedUserCredentialsFromJsonContents( - kAuthorizedUserCredContents); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(AuthorizedUserCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, - LoadInvalidAuthorizedUserCredentialsFromFilename) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), "invalid-credentials.json"); - std::ofstream os(filename); - std::string contents_str = R"""( not-a-json-object-string )"""; - os << contents_str; - os.close(); - auto creds = CreateAuthorizedUserCredentialsFromJsonFilePath(filename); - EXPECT_THAT(creds, Not(IsOk())); -} - -TEST_F(GoogleCredentialsTest, - LoadInvalidAuthorizedUserCredentialsFromJsonContents) { - std::string contents_str = R"""( not-a-json-object-string )"""; - auto creds = CreateAuthorizedUserCredentialsFromJsonContents(contents_str); - EXPECT_THAT(creds, Not(IsOk())); -} - -/** - * @test Verify `GoogleDefaultCredentials()` loads service account credentials. - * - * This test only verifies the right type of object is created, the unit tests - * for `ServiceAccountCredentials` already check that once loaded the class - * works correctly. Testing here would be redundant. Furthermore, calling - * `AuthorizationHeader()` initiates the key verification workflow, that - * requires valid keys and contacting Google's production servers, and would - * make this an integration test. - */ - -std::string const kServiceAccountCredFilename = "service-account.json"; -std::string const kServiceAccountCredContents = R"""({ - "type": "service_account", - "project_id": "foo-project", - "private_key_id": "a1a111aa1111a11a11a11aa111a111a1a1111111", - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCltiF2oP3KJJ+S\ntTc1McylY+TuAi3AdohX7mmqIjd8a3eBYDHs7FlnUrFC4CRijCr0rUqYfg2pmk4a\n6TaKbQRAhWDJ7XD931g7EBvCtd8+JQBNWVKnP9ByJUaO0hWVniM50KTsWtyX3up/\nfS0W2R8Cyx4yvasE8QHH8gnNGtr94iiORDC7De2BwHi/iU8FxMVJAIyDLNfyk0hN\neheYKfIDBgJV2v6VaCOGWaZyEuD0FJ6wFeLybFBwibrLIBE5Y/StCrZoVZ5LocFP\nT4o8kT7bU6yonudSCyNMedYmqHj/iF8B2UN1WrYx8zvoDqZk0nxIglmEYKn/6U7U\ngyETGcW9AgMBAAECggEAC231vmkpwA7JG9UYbviVmSW79UecsLzsOAZnbtbn1VLT\nPg7sup7tprD/LXHoyIxK7S/jqINvPU65iuUhgCg3Rhz8+UiBhd0pCH/arlIdiPuD\n2xHpX8RIxAq6pGCsoPJ0kwkHSw8UTnxPV8ZCPSRyHV71oQHQgSl/WjNhRi6PQroB\nSqc/pS1m09cTwyKQIopBBVayRzmI2BtBxyhQp9I8t5b7PYkEZDQlbdq0j5Xipoov\n9EW0+Zvkh1FGNig8IJ9Wp+SZi3rd7KLpkyKPY7BK/g0nXBkDxn019cET0SdJOHQG\nDiHiv4yTRsDCHZhtEbAMKZEpku4WxtQ+JjR31l8ueQKBgQDkO2oC8gi6vQDcx/CX\nZ23x2ZUyar6i0BQ8eJFAEN+IiUapEeCVazuxJSt4RjYfwSa/p117jdZGEWD0GxMC\n+iAXlc5LlrrWs4MWUc0AHTgXna28/vii3ltcsI0AjWMqaybhBTTNbMFa2/fV2OX2\nUimuFyBWbzVc3Zb9KAG4Y7OmJQKBgQC5324IjXPq5oH8UWZTdJPuO2cgRsvKmR/r\n9zl4loRjkS7FiOMfzAgUiXfH9XCnvwXMqJpuMw2PEUjUT+OyWjJONEK4qGFJkbN5\n3ykc7p5V7iPPc7Zxj4mFvJ1xjkcj+i5LY8Me+gL5mGIrJ2j8hbuv7f+PWIauyjnp\nNx/0GVFRuQKBgGNT4D1L7LSokPmFIpYh811wHliE0Fa3TDdNGZnSPhaD9/aYyy78\nLkxYKuT7WY7UVvLN+gdNoVV5NsLGDa4cAV+CWPfYr5PFKGXMT/Wewcy1WOmJ5des\nAgMC6zq0TdYmMBN6WpKUpEnQtbmh3eMnuvADLJWxbH3wCkg+4xDGg2bpAoGAYRNk\nMGtQQzqoYNNSkfus1xuHPMA8508Z8O9pwKU795R3zQs1NAInpjI1sOVrNPD7Ymwc\nW7mmNzZbxycCUL/yzg1VW4P1a6sBBYGbw1SMtWxun4ZbnuvMc2CTCh+43/1l+FHe\nMmt46kq/2rH2jwx5feTbOE6P6PINVNRJh/9BDWECgYEAsCWcH9D3cI/QDeLG1ao7\nrE2NcknP8N783edM07Z/zxWsIsXhBPY3gjHVz2LDl+QHgPWhGML62M0ja/6SsJW3\nYvLLIc82V7eqcVJTZtaFkuht68qu/Jn1ezbzJMJ4YXDYo1+KFi+2CAGR06QILb+I\nlUtj+/nH3HDQjM4ltYfTPUg=\n-----END PRIVATE KEY-----\n", - "client_email": "foo-email@foo-project.iam.gserviceaccount.com", - "client_id": "100000000000000000001", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://accounts.google.com/o/oauth2/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/foo-email%40foo-project.iam.gserviceaccount.com" -})"""; - -std::string const kServiceAccountCredInvalidPrivateKey = R"""({ - "type": "service_account", - "project_id": "foo-project", - "private_key_id": "a1a111aa1111a11a11a11aa111a111a1a1111111", - "private_key": "an-invalid-private-key", - "client_email": "foo-email@foo-project.iam.gserviceaccount.com", - "client_id": "100000000000000000001", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://accounts.google.com/o/oauth2/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/foo-email%40foo-project.iam.gserviceaccount.com" -})"""; - -void SetupServiceAccountCredentialsFileForTest(std::string const& filename) { - std::ofstream os(filename); - os << kServiceAccountCredContents; - os.close(); -} - -TEST_F(GoogleCredentialsTest, LoadValidServiceAccountCredentialsViaEnvVar) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupServiceAccountCredentialsFileForTest(filename); - - // Test that the service account credentials are loaded as the default when - // specified via the well known environment variable. - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - auto creds = GoogleDefaultCredentials(); - ASSERT_STATUS_OK(creds); - // Need to create a temporary for the pointer because clang-tidy warns about - // using expressions with (potential) side-effects inside typeid(). - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, LoadValidServiceAccountCredentialsViaGcloudFile) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupServiceAccountCredentialsFileForTest(filename); - - // Test that the service account credentials are loaded as the default when - // stored in the well known gcloud ADC file path. - ScopedEnvironment gcloud_path_override_env_var(GoogleGcloudAdcFileEnvVar(), - filename.c_str()); - auto creds = GoogleDefaultCredentials(); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, LoadValidServiceAccountCredentialsFromFilename) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kServiceAccountCredFilename); - SetupServiceAccountCredentialsFileForTest(filename); - - // Test that the service account credentials are loaded from a file. - auto creds = CreateServiceAccountCredentialsFromJsonFilePath(filename); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); - - // Test the wrapper function. - creds = CreateServiceAccountCredentialsFromFilePath(filename); - ASSERT_STATUS_OK(creds); - auto* ptr2 = creds->get(); - EXPECT_EQ(typeid(*ptr2), typeid(ServiceAccountCredentials<>)); - - creds = CreateServiceAccountCredentialsFromFilePath( - filename, {{"https://www.googleapis.com/auth/devstorage.full_control"}}, - "user@foo.bar"); - ASSERT_STATUS_OK(creds); - auto* ptr3 = creds->get(); - EXPECT_EQ(typeid(*ptr3), typeid(ServiceAccountCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, - LoadValidServiceAccountCredentialsFromFilenameWithOptionalArgs) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kServiceAccountCredFilename); - SetupServiceAccountCredentialsFileForTest(filename); - - // Test that the service account credentials are loaded from a file. - auto creds = CreateServiceAccountCredentialsFromJsonFilePath( - filename, {{"https://www.googleapis.com/auth/devstorage.full_control"}}, - "user@foo.bar"); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, - LoadInvalidServiceAccountCredentialsFromFilename) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), "invalid-credentials.json"); - std::ofstream os(filename); - std::string contents_str = R"""( not-a-json-object-string )"""; - os << contents_str; - os.close(); - - auto creds = CreateServiceAccountCredentialsFromJsonFilePath( - filename, {{"https://www.googleapis.com/auth/devstorage.full_control"}}, - "user@foo.bar"); - EXPECT_THAT(creds, Not(IsOk())); -} - -TEST_F(GoogleCredentialsTest, - LoadValidServiceAccountCredentialsFromDefaultPathsViaEnvVar) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupServiceAccountCredentialsFileForTest(filename); - - // Test that the service account credentials are loaded as the default when - // specified via the well known environment variable. - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - auto creds = CreateServiceAccountCredentialsFromDefaultPaths(); - ASSERT_STATUS_OK(creds); - // Need to create a temporary for the pointer because clang-tidy warns about - // using expressions with (potential) side-effects inside typeid(). - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, - LoadValidServiceAccountCredentialsFromDefaultPathsViaGcloudFile) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupServiceAccountCredentialsFileForTest(filename); - - // Test that the service account credentials are loaded as the default when - // stored in the well known gcloud ADC file path. - ScopedEnvironment gcloud_path_override_env_var(GoogleGcloudAdcFileEnvVar(), - filename.c_str()); - auto creds = CreateServiceAccountCredentialsFromDefaultPaths(); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, - LoadValidServiceAccountCredentialsFromDefaultPathsWithOptionalArgs) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupServiceAccountCredentialsFileForTest(filename); - - // Test that the service account credentials are loaded as the default when - // specified via the well known environment variable. - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - auto creds = CreateServiceAccountCredentialsFromDefaultPaths( - {{"https://www.googleapis.com/auth/devstorage.full_control"}}, - "user@foo.bar"); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); -} - -TEST_F( - GoogleCredentialsTest, - DoNotLoadAuthorizedUserCredentialsFromCreateServiceAccountCredentialsFromDefaultPaths) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), kAuthorizedUserCredFilename); - SetupAuthorizedUserCredentialsFileForTest(filename); - - // Test that the authorized user credentials are loaded as the default when - // specified via the well known environment variable. - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - auto creds = CreateServiceAccountCredentialsFromDefaultPaths(); - EXPECT_THAT(creds, StatusIs(Not(StatusCode::kOk), - HasSubstr("Unsupported credential type"))); -} - -TEST_F(GoogleCredentialsTest, - MissingCredentialsCreateServiceAccountCredentialsFromDefaultPaths) { - // The developer may have configured something that are not service account - // credentials in the well-known path. Change the search location to a - // directory that should have have developer configuration files. - ScopedEnvironment home_env_var(GoogleAdcHomeEnvVar(), ::testing::TempDir()); - // Test that when CreateServiceAccountCredentialsFromDefaultPaths cannot - // find any credentials, it fails. - auto creds = CreateServiceAccountCredentialsFromDefaultPaths(); - EXPECT_THAT( - creds, - StatusIs(Not(StatusCode::kOk), - HasSubstr("Could not create service account credentials"))); -} - -TEST_F(GoogleCredentialsTest, LoadValidServiceAccountCredentialsFromContents) { - // Test that the service account credentials are loaded from a string - // representing JSON contents. - auto creds = CreateServiceAccountCredentialsFromJsonContents( - kServiceAccountCredContents, - {{"https://www.googleapis.com/auth/devstorage.full_control"}}, - "user@foo.bar"); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, - LoadInvalidServiceAccountCredentialsFromContents) { - // Test that providing invalid contents returns a failure status. - auto creds = CreateServiceAccountCredentialsFromJsonContents( - "not-a-valid-jason-object", - {{"https://www.googleapis.com/auth/devstorage.full_control"}}, - "user@foo.bar"); - EXPECT_THAT(creds, Not(IsOk())); -} - -TEST_F(GoogleCredentialsTest, - LoadInvalidServiceAccountCredentialsWithInvalidKey) { - // Test that providing invalid private_key returns a failure status. - auto creds = CreateServiceAccountCredentialsFromJsonContents( - kServiceAccountCredInvalidPrivateKey); - EXPECT_THAT(creds, Not(IsOk())); -} - -TEST_F(GoogleCredentialsTest, LoadComputeEngineCredentialsFromADCFlow) { - ScopedEnvironment gcloud_path_override_env_var(GoogleGcloudAdcFileEnvVar(), - ""); - // If the ADC flow thinks we're on a GCE instance, it should return - // ComputeEngineCredentials. - ScopedEnvironment gce_check_override_env_var(GceCheckOverrideEnvVar(), "1"); - - auto creds = GoogleDefaultCredentials(); - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ComputeEngineCredentials<>)); -} - -TEST_F(GoogleCredentialsTest, CreateComputeEngineCredentialsWithDefaultEmail) { - auto credentials = CreateComputeEngineCredentials(); - auto* ptr = credentials.get(); - EXPECT_EQ(typeid(*ptr), typeid(ComputeEngineCredentials<>)); - EXPECT_EQ( - std::string("default"), - dynamic_cast*>(ptr)->service_account_email()); -} - -TEST_F(GoogleCredentialsTest, CreateComputeEngineCredentialsWithExplicitEmail) { - auto credentials = CreateComputeEngineCredentials("foo@bar.baz"); - auto* ptr = credentials.get(); - EXPECT_EQ(typeid(*ptr), typeid(ComputeEngineCredentials<>)); - EXPECT_EQ( - std::string("foo@bar.baz"), - dynamic_cast*>(ptr)->service_account_email()); -} - -TEST_F(GoogleCredentialsTest, LoadUnknownTypeCredentials) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), "unknown-type-credentials.json"); - std::ofstream os(filename); - std::string contents_str = R"""({ - "type": "unknown_type" -})"""; - os << contents_str; - os.close(); - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - - auto creds = GoogleDefaultCredentials(); - EXPECT_THAT(creds, StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr("Unsupported credential type"), - HasSubstr(filename)))); -} - -TEST_F(GoogleCredentialsTest, LoadInvalidCredentials) { - char const* const test_cases[] = { - R"""( not-a-json-object-string )""", - R"js("valid-json-but-not-an-object")js", - }; - for (auto const* contents : test_cases) { - SCOPED_TRACE("Testing with: " + std::string{contents}); - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), "invalid-credentials.json"); - std::ofstream os(filename); - os << contents; - os.close(); - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - - auto creds = GoogleDefaultCredentials(); - EXPECT_THAT(creds, - StatusIs(StatusCode::kInvalidArgument, HasSubstr(filename))); - } -} - -TEST_F(GoogleCredentialsTest, LoadInvalidAuthorizedUserCredentialsViaADC) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), "invalid-au-credentials.json"); - std::ofstream os(filename); - std::string contents_str = R"""("type": "authorized_user")"""; - os << contents_str; - os.close(); - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - - auto creds = GoogleDefaultCredentials(); - EXPECT_THAT(creds, StatusIs(StatusCode::kInvalidArgument)); -} - -TEST_F(GoogleCredentialsTest, LoadInvalidServiceAccountCredentialsViaADC) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), "invalid-au-credentials.json"); - std::ofstream os(filename); - std::string contents_str = R"""("type": "service_account")"""; - os << contents_str; - os.close(); - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - - auto creds = GoogleDefaultCredentials(); - EXPECT_THAT(creds, StatusIs(StatusCode::kInvalidArgument)); -} - -TEST_F(GoogleCredentialsTest, MissingCredentialsViaEnvVar) { - char const filename[] = "missing-credentials.json"; - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename); - - auto creds = GoogleDefaultCredentials(); - EXPECT_THAT(creds, StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr("Cannot open credentials file"), - HasSubstr(filename)))); -} - -TEST_F(GoogleCredentialsTest, MissingCredentialsViaGcloudFilePath) { - char const filename[] = "missing-credentials.json"; - - // The method to create default credentials should see that no file exists at - // this path, then continue trying to load the other credential types, - // eventually finding no valid credentials and hitting a runtime error. - ScopedEnvironment gcloud_path_override_env_var(GoogleGcloudAdcFileEnvVar(), - filename); - ScopedEnvironment gcloud_metadata_host_override_env_var( - internal::GceMetadataHostnameEnvVar(), "invalid.google.internal"); - - auto creds = GoogleDefaultCredentials(); - ASSERT_STATUS_OK(creds); - ASSERT_THAT(*creds, NotNull()); - auto header = (*creds)->AuthorizationHeader(); - EXPECT_THAT(header, StatusIs(Not(StatusCode::kOk))); -} - -TEST_F(GoogleCredentialsTest, LoadP12Credentials) { - // Ensure that the parser detects that it's not a JSON file and parses it as - // a P12 file. - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), "credentials.p12"); - WriteBase64AsBinary(filename, kP12KeyFileContents); - ScopedEnvironment adc_env_var(GoogleAdcEnvVar(), filename.c_str()); - - auto creds = GoogleDefaultCredentials(); - EXPECT_EQ(0, std::remove(filename.c_str())); - - if (creds.status().code() == StatusCode::kInvalidArgument) { - if (absl::StrContains(creds.status().message(), "error:0308010C")) { - // With OpenSSL 3.0 the PKCS#12 files may not be supported by default. - GTEST_SKIP() << "PKCS#12 support unavailable, skipping test"; - } -#if _WIN32 - // On Windows, the OS may not have the necessary providers to support - // PKCS#12. Unfortunately the error message is not as unambiguous, so we use - // the function that fails instead. - auto const& metadata = creds.status().error_info().metadata(); - auto const l = metadata.find("gcloud-cpp.source.function"); - if (l != metadata.end() && l->second == "GetCertificatePrivateKey") { - GTEST_SKIP() << "PKCS#12 support unavailable, skipping test"; - } -#endif // _WIN32 - } - ASSERT_STATUS_OK(creds); - auto* ptr = creds->get(); - EXPECT_EQ(typeid(*ptr), typeid(ServiceAccountCredentials<>)); - EXPECT_EQ(kP12ServiceAccountId, ptr->AccountEmail()); - EXPECT_FALSE(ptr->KeyId().empty()); -} - -} // namespace -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/refreshing_credentials_wrapper.cc b/google/cloud/storage/oauth2/refreshing_credentials_wrapper.cc deleted file mode 100644 index ddb055c3512c5..0000000000000 --- a/google/cloud/storage/oauth2/refreshing_credentials_wrapper.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/refreshing_credentials_wrapper.h" -#include "google/cloud/storage/oauth2/credential_constants.h" -#include "google/cloud/internal/oauth2_refreshing_credentials_wrapper.h" -#include "absl/strings/str_split.h" -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -RefreshingCredentialsWrapper::RefreshingCredentialsWrapper() - : impl_(std::make_unique()) { -} - -std::pair RefreshingCredentialsWrapper::SplitToken( - std::string const& token) { - std::pair split_token = absl::StrSplit(token, ": "); - return split_token; -} - -bool RefreshingCredentialsWrapper::IsExpired( - std::chrono::system_clock::time_point now) const { - return now > - (impl_->token_.expiration - GoogleOAuthAccessTokenExpirationSlack()); -} - -bool RefreshingCredentialsWrapper::IsValid( - std::chrono::system_clock::time_point now) const { - return !impl_->token_.token.empty() && !IsExpired(now); -} - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/refreshing_credentials_wrapper.h b/google/cloud/storage/oauth2/refreshing_credentials_wrapper.h deleted file mode 100644 index 2f63398024904..0000000000000 --- a/google/cloud/storage/oauth2/refreshing_credentials_wrapper.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_REFRESHING_CREDENTIALS_WRAPPER_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_REFRESHING_CREDENTIALS_WRAPPER_H - -#include "google/cloud/storage/version.h" -#include "google/cloud/internal/oauth2_refreshing_credentials_wrapper.h" -#include "google/cloud/status.h" -#include "google/cloud/status_or.h" -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -/** - * Wrapper for refreshable parts of a Credentials object. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") - RefreshingCredentialsWrapper { - public: - RefreshingCredentialsWrapper(); - - struct GOOGLE_CLOUD_CPP_DEPRECATED( - "This struct will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") TemporaryToken { - std::string token; - std::chrono::system_clock::time_point expiration_time; - }; - - template - StatusOr AuthorizationHeader( - std::chrono::system_clock::time_point, RefreshFunctor refresh_fn) const { - auto refresh_fn_wrapper = - [refresh_fn]() -> StatusOr { - auto temp_token = refresh_fn(); - if (!temp_token.ok()) return temp_token.status(); - auto token = SplitToken(temp_token->token); - return google::cloud::AccessToken{std::move(token.second), - temp_token->expiration_time}; - }; - auto header = impl_->AuthorizationHeader(refresh_fn_wrapper); - if (!header.ok()) return std::move(header).status(); - return header->first + ": " + header->second; - } - - /** - * Returns whether the current access token should be considered expired. - * - * When determining if a Credentials object needs to be refreshed, the IsValid - * method should be used instead; there may be cases where a Credentials is - * not expired but should be considered invalid. - * - * If a Credentials is close to expiration but not quite expired, this method - * may still return false. This helps prevent the case where an access token - * expires between when it is obtained and when it is used. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - bool IsExpired(std::chrono::system_clock::time_point now) const; - - /** - * Returns whether the current access token should be considered valid. - * - * This method should be used to determine whether a Credentials object needs - * to be refreshed. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - bool IsValid(std::chrono::system_clock::time_point now) const; - - private: - static std::pair SplitToken( - std::string const& token); - - std::unique_ptr impl_; -}; - -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_REFRESHING_CREDENTIALS_WRAPPER_H diff --git a/google/cloud/storage/oauth2/service_account_credentials.cc b/google/cloud/storage/oauth2/service_account_credentials.cc deleted file mode 100644 index 25a1bc4ce2f4e..0000000000000 --- a/google/cloud/storage/oauth2/service_account_credentials.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/service_account_credentials.h" -#include "google/cloud/storage/internal/base64.h" -#include "google/cloud/storage/internal/make_jwt_assertion.h" -#include "google/cloud/internal/absl_str_join_quiet.h" -#include "google/cloud/internal/oauth2_cached_credentials.h" -#include "google/cloud/internal/oauth2_service_account_credentials.h" -#include "google/cloud/internal/parse_service_account_p12_file.h" -#include "google/cloud/internal/sign_using_sha256.h" -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -auto constexpr kP12PrivateKeyIdMarker = "--unknown--"; - -StatusOr ParseServiceAccountCredentials( - std::string const& content, std::string const& source, - std::string const& default_token_uri) { - auto info = oauth2_internal::ParseServiceAccountCredentials( - content, source, default_token_uri); - if (!info.ok()) return info.status(); - return ServiceAccountCredentialsInfo{info->client_email, info->private_key_id, - info->private_key, info->token_uri, - info->scopes, info->subject}; -} - -StatusOr ParseServiceAccountP12File( - std::string const& source, std::string const&) { - auto info = oauth2_internal::ParseServiceAccountP12File(source); - if (!info.ok()) return info.status(); - return ServiceAccountCredentialsInfo{info->client_email, info->private_key_id, - info->private_key, info->token_uri, - info->scopes, info->subject}; -} - -std::pair AssertionComponentsFromInfo( - ServiceAccountCredentialsInfo const& info, - std::chrono::system_clock::time_point now) { - return oauth2_internal::AssertionComponentsFromInfo( - internal::MapServiceAccountCredentialsInfo(info), now); -} - -std::string MakeJWTAssertion(std::string const& header, - std::string const& payload, - std::string const& pem_contents) { - return internal::MakeJWTAssertionNoThrow(header, payload, pem_contents) - .value(); -} - -std::string CreateServiceAccountRefreshPayload( - ServiceAccountCredentialsInfo const& info, std::string const&, - std::chrono::system_clock::time_point now) { - auto params = oauth2_internal::CreateServiceAccountRefreshPayload( - internal::MapServiceAccountCredentialsInfo(info), now); - return absl::StrJoin(params, "&", absl::PairFormatter("=")); -} - -StatusOr -ParseServiceAccountRefreshResponse( - storage::internal::HttpResponse const& response, - std::chrono::system_clock::time_point now) { - auto access_token = nlohmann::json::parse(response.payload, nullptr, false); - if (access_token.is_discarded() || access_token.count("access_token") == 0 || - access_token.count("expires_in") == 0 || - access_token.count("token_type") == 0) { - auto payload = - response.payload + - "Could not find all required fields in response (access_token," - " expires_in, token_type) while trying to obtain an access token for" - " service account credentials."; - return AsStatus(storage::internal::HttpResponse{response.status_code, - payload, response.headers}); - } - // Response should have the attributes "access_token", "expires_in", and - // "token_type". - std::string header = - "Authorization: " + access_token.value("token_type", "") + " " + - access_token.value("access_token", ""); - auto expires_in = std::chrono::seconds(access_token.value("expires_in", 0)); - auto new_expiration = now + expires_in; - - return RefreshingCredentialsWrapper::TemporaryToken{std::move(header), - new_expiration}; -} - -StatusOr MakeSelfSignedJWT( - ServiceAccountCredentialsInfo const& info, - std::chrono::system_clock::time_point tp) { - auto mapped = internal::MapServiceAccountCredentialsInfo(info); - return ::google::cloud::oauth2_internal::MakeSelfSignedJWT(mapped, tp); -} - -bool ServiceAccountUseOAuth(ServiceAccountCredentialsInfo const& info) { - if (info.private_key_id == kP12PrivateKeyIdMarker) return true; - // Self-signed JWTs do not work in GCS if they have scopes. - if (info.scopes.has_value()) return true; - auto disable_jwt = google::cloud::internal::GetEnv( - "GOOGLE_CLOUD_CPP_EXPERIMENTAL_DISABLE_SELF_SIGNED_JWT"); - return disable_jwt.has_value(); -} - -ServiceAccountCredentials:: - ServiceAccountCredentials(ServiceAccountCredentialsInfo info, - Options options) - : impl_(std::make_unique( - std::make_unique( - internal::MapServiceAccountCredentialsInfo(std::move(info)), - std::move(options), [](Options const& o) { - return rest_internal::MakeDefaultRestClient(std::string{}, o); - }))) {} - -} // namespace oauth2 - -namespace internal { - -oauth2_internal::ServiceAccountCredentialsInfo MapServiceAccountCredentialsInfo( - oauth2::ServiceAccountCredentialsInfo info) { - // Storage has more stringent requirements w.r.t. self-signed JWTs - // than most services. Any scope makes the self-signed JWTs unusable with - // storage, but they remain usable with other services. We need to disable - // self-signed JWTs in the implementation class as it is unaware of the - // storage service limitations. - auto enable_self_signed_jwt = !ServiceAccountUseOAuth(info); - return {std::move(info.client_email), std::move(info.private_key_id), - std::move(info.private_key), std::move(info.token_uri), - std::move(info.scopes), std::move(info.subject), - enable_self_signed_jwt, /*.universe_domain=*/absl::nullopt, - /*.project_id=*/absl::nullopt}; -} - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/oauth2/service_account_credentials.h b/google/cloud/storage/oauth2/service_account_credentials.h deleted file mode 100644 index 9bc28128beee8..0000000000000 --- a/google/cloud/storage/oauth2/service_account_credentials.h +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_SERVICE_ACCOUNT_CREDENTIALS_H -#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_SERVICE_ACCOUNT_CREDENTIALS_H - -#include "google/cloud/storage/internal/base64.h" -#include "google/cloud/storage/internal/curl/request_builder.h" -#include "google/cloud/storage/internal/http_response.h" -#include "google/cloud/storage/oauth2/credential_constants.h" -#include "google/cloud/storage/oauth2/credentials.h" -#include "google/cloud/storage/oauth2/refreshing_credentials_wrapper.h" -#include "google/cloud/storage/version.h" -#include "google/cloud/internal/curl_handle_factory.h" -#include "google/cloud/internal/getenv.h" -#include "google/cloud/internal/make_status.h" -#include "google/cloud/internal/oauth2_service_account_credentials.h" -#include "google/cloud/internal/sha256_hash.h" -#include "google/cloud/internal/sign_using_sha256.h" -#include "google/cloud/optional.h" -#include "google/cloud/options.h" -#include "google/cloud/status_or.h" -#include "absl/types/optional.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -/** - * Object to hold information used to instantiate an ServiceAccountCredentials. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -struct GOOGLE_CLOUD_CPP_DEPRECATED( - "This struct will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") - ServiceAccountCredentialsInfo { - std::string client_email; - std::string private_key_id; - std::string private_key; - std::string token_uri; - // If no set is supplied, a default set of scopes will be used. - absl::optional> scopes; - // See https://developers.google.com/identity/protocols/OAuth2ServiceAccount. - absl::optional subject; -}; - -/** - * Parses the contents of a JSON keyfile into a ServiceAccountCredentialsInfo. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr ParseServiceAccountCredentials( - std::string const& content, std::string const& source, - std::string const& default_token_uri = GoogleOAuthRefreshEndpoint()); - -/** - * Parses the contents of a P12 keyfile into a ServiceAccountCredentialsInfo. - * - * @warning We strongly recommend that applications use JSON keyfiles instead. - * - * @note Note that P12 keyfiles do not contain the `client_email` for the - * service account, the application must obtain this through some other means - * and provide them to the function. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr ParseServiceAccountP12File( - std::string const& source, - std::string const& default_token_uri = GoogleOAuthRefreshEndpoint()); - -/** - * Parses a refresh response JSON string and uses the current time to create a - * TemporaryToken. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr -ParseServiceAccountRefreshResponse( - storage::internal::HttpResponse const& response, - std::chrono::system_clock::time_point now); - -/** - * Splits a ServiceAccountCredentialsInfo into header and payload components - * and uses the current time to make a JWT assertion. - * - * @see - * https://cloud.google.com/endpoints/docs/frameworks/java/troubleshoot-jwt - * - * @see https://tools.ietf.org/html/rfc7523 - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -std::pair AssertionComponentsFromInfo( - ServiceAccountCredentialsInfo const& info, - std::chrono::system_clock::time_point now); - -/** - * Given a key and a JSON header and payload, creates a JWT assertion string. - * - * @see https://tools.ietf.org/html/rfc7519 - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -std::string MakeJWTAssertion(std::string const& header, - std::string const& payload, - std::string const& pem_contents); - -/** - * Uses a ServiceAccountCredentialsInfo and the current time to construct a JWT - * assertion. - * - * The assertion combined with the grant type is used to create the refresh - * payload. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -std::string CreateServiceAccountRefreshPayload( - ServiceAccountCredentialsInfo const& info, std::string const& grant_type, - std::chrono::system_clock::time_point now); - -/** - * Make a self-signed JWT from the service account. - * - * [Self-signed JWTs] bypass the intermediate step of exchanging client - * assertions for OAuth tokens. The advantages of self-signed JTWs include: - * - * - They are more efficient, as they require more or less the same amount of - * local work, and save a round-trip to the token endpoint, typically - * https://oauth2.googleapis.com/token. - * - While this service is extremely reliable, removing external dependencies in - * the critical path almost always improves reliability. - * - They work better in VPC-SC environments and other environments with limited - * Internet access. - * - * @warning At this time only scope-based self-signed JWTs are supported. - * - * [Self-signed JWTs]: https://google.aip.dev/auth/4111 - * - * @param info the parsed service account information, see - * `ParseServiceAccountCredentials()` - * @param tp the current time - * @return a bearer token for authentication. Include this value in the - * `Authorization` header with the "Bearer" type. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -StatusOr MakeSelfSignedJWT( - ServiceAccountCredentialsInfo const& info, - std::chrono::system_clock::time_point tp); - -/** - * Return true if we need to use the OAuth path to create tokens - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -bool ServiceAccountUseOAuth(ServiceAccountCredentialsInfo const& info); - -/** - * Wrapper class for Google OAuth 2.0 service account credentials. - * - * Takes a ServiceAccountCredentialsInfo and obtains access tokens from the - * Google Authorization Service as needed. Instances of this class should - * usually be created via the convenience methods declared in - * google_credentials.h. - * - * An HTTP Authorization header, with an access token as its value, - * can be obtained by calling the AuthorizationHeader() method; if the current - * access token is invalid or nearing expiration, this will class will first - * obtain a new access token before returning the Authorization header string. - * - * @see https://developers.google.com/identity/protocols/OAuth2ServiceAccount - * for an overview of using service accounts with Google's OAuth 2.0 system. - * - * @see https://cloud.google.com/storage/docs/reference/libraries for details on - * how to obtain and get started with service account credentials. - * - * @tparam HttpRequestBuilderType a dependency injection point. It makes it - * possible to mock internal libcurl wrappers. This should generally not be - * overridden except for testing. - * @tparam ClockType a dependency injection point to fetch the current time. - * This should generally not be overridden except for testing. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ -template -class ServiceAccountCredentials; - -/// @copydoc ServiceAccountCredentials -template <> -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") - ServiceAccountCredentials : public Credentials { - public: - explicit ServiceAccountCredentials(ServiceAccountCredentialsInfo info) - : ServiceAccountCredentials(std::move(info), {}) {} - ServiceAccountCredentials(ServiceAccountCredentialsInfo info, - Options options); - - StatusOr AuthorizationHeader() override { - return oauth2_internal::AuthenticationHeaderJoined(*impl_); - } - - /** - * Create a RSA SHA256 signature of the blob using the Credential object. - * - * @param signing_account the desired service account which should sign - * @p blob. If not set, uses this object's account. If set, it must match - * this object's service account. - * @param blob the string to sign. Note that sometimes the application must - * Base64-encode the data before signing. - * @return the signed blob as raw bytes. An error if the @p signing_account - * does not match the email for the credential's account. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - StatusOr> SignBlob( - SigningAccount const& signing_account, - std::string const& blob) const override { - return impl_->SignBlob((signing_account.has_value() - ? signing_account.value() - : absl::optional(absl::nullopt)), - blob); - } - - std::string AccountEmail() const override { return impl_->AccountEmail(); } - std::string KeyId() const override { return impl_->KeyId(); } - - private: - friend struct ServiceAccountCredentialsTester; - StatusOr AuthorizationHeaderForTesting( - std::chrono::system_clock::time_point tp) { - return oauth2_internal::AuthenticationHeaderJoined(*impl_, tp); - } - std::unique_ptr impl_; -}; - -/// @copydoc ServiceAccountCredentials -template -class GOOGLE_CLOUD_CPP_DEPRECATED( - "This class will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") ServiceAccountCredentials - : public Credentials { - public: - explicit ServiceAccountCredentials(ServiceAccountCredentialsInfo info) - : ServiceAccountCredentials(std::move(info), {}) {} - ServiceAccountCredentials(ServiceAccountCredentialsInfo info, Options options) - : info_(std::move(info)), options_(std::move(options)), clock_() {} - - StatusOr AuthorizationHeader() override { - std::unique_lock lock(mu_); - return refreshing_creds_.AuthorizationHeader(clock_.now(), - [this] { return Refresh(); }); - } - - /** - * Create a RSA SHA256 signature of the blob using the Credential object. - * - * @param signing_account the desired service account which should sign - * @p blob. If not set, uses this object's account. If set, it must match - * this object's service account. - * @param blob the string to sign. Note that sometimes the application must - * Base64-encode the data before signing. - * @return the signed blob as raw bytes. An error if the @p signing_account - * does not match the email for the credential's account. - * - * @deprecated Prefer using the unified credentials documented in @ref guac - */ - StatusOr> SignBlob( - SigningAccount const& signing_account, - std::string const& blob) const override { - if (signing_account.has_value() && - signing_account.value() != info_.client_email) { - return google::cloud::internal::InvalidArgumentError( - "The current_credentials cannot sign blobs for " + - signing_account.value(), - GCP_ERROR_INFO()); - } - return google::cloud::internal::SignUsingSha256(blob, info_.private_key); - } - - std::string AccountEmail() const override { return info_.client_email; } - std::string KeyId() const override { return info_.private_key_id; } - - private: - bool UseOAuth() const { return ServiceAccountUseOAuth(info_); } - - StatusOr Refresh() { - if (UseOAuth()) return RefreshOAuth(); - return RefreshSelfSigned(); - } - - StatusOr RefreshOAuth() const { - HttpRequestBuilderType builder( - info_.token_uri, rest_internal::GetDefaultCurlHandleFactory(options_)); - builder.AddHeader("Content-Type: application/x-www-form-urlencoded"); - // This is the value of grant_type for JSON-formatted service account - // keyfiles downloaded from Cloud Console. - std::string grant_type("grant_type="); - grant_type += - builder.MakeEscapedString("urn:ietf:params:oauth:grant-type:jwt-bearer") - .get(); - - auto payload = - CreateServiceAccountRefreshPayload(info_, grant_type, clock_.now()); - auto response = std::move(builder).BuildRequest().MakeRequest(payload); - if (!response) return std::move(response).status(); - if (response->status_code >= 300) return AsStatus(*response); - return ParseServiceAccountRefreshResponse(*response, clock_.now()); - } - - StatusOr RefreshSelfSigned() - const { - auto const tp = clock_.now(); - auto token = MakeSelfSignedJWT(info_, tp); - if (!token) return std::move(token).status(); - return RefreshingCredentialsWrapper::TemporaryToken{ - "Authorization: Bearer " + *token, - tp + GoogleOAuthAccessTokenLifetime()}; - } - - ServiceAccountCredentialsInfo info_; - Options options_; - mutable std::mutex mu_; - RefreshingCredentialsWrapper refreshing_creds_; - ClockType clock_; -}; - -} // namespace oauth2 - -namespace internal { - -GOOGLE_CLOUD_CPP_DEPRECATED( - "This function will be removed in v4.0.0 and later. Prefer using the " - "unified credentials documented in @ref guac.") -oauth2_internal::ServiceAccountCredentialsInfo MapServiceAccountCredentialsInfo( - oauth2::ServiceAccountCredentialsInfo info); - -} // namespace internal -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google - -#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_SERVICE_ACCOUNT_CREDENTIALS_H diff --git a/google/cloud/storage/oauth2/service_account_credentials_test.cc b/google/cloud/storage/oauth2/service_account_credentials_test.cc deleted file mode 100644 index 948095d24e30f..0000000000000 --- a/google/cloud/storage/oauth2/service_account_credentials_test.cc +++ /dev/null @@ -1,886 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/oauth2/service_account_credentials.h" -#include "google/cloud/storage/oauth2/credential_constants.h" -#include "google/cloud/storage/oauth2/google_credentials.h" -#include "google/cloud/storage/testing/constants.h" -#include "google/cloud/storage/testing/mock_http_request.h" -#include "google/cloud/storage/testing/write_base64.h" -#include "google/cloud/common_options.h" -#include "google/cloud/credentials.h" -#include "google/cloud/internal/base64_transforms.h" -#include "google/cloud/internal/filesystem.h" -#include "google/cloud/internal/random.h" -#include "google/cloud/internal/sign_using_sha256.h" -#include "google/cloud/testing_util/mock_fake_clock.h" -#include "google/cloud/testing_util/scoped_environment.h" -#include "google/cloud/testing_util/status_matchers.h" -#include "absl/strings/match.h" -#include "absl/strings/str_split.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace oauth2 { - -// Define a helper to test the specialization. -struct ServiceAccountCredentialsTester { - static StatusOr Header( - ServiceAccountCredentials<>& tested, - std::chrono::system_clock::time_point tp) { - return tested.AuthorizationHeaderForTesting(tp); - } -}; - -namespace { - -using ::google::cloud::internal::SignUsingSha256; -using ::google::cloud::internal::UrlsafeBase64Decode; -using ::google::cloud::internal::UrlsafeBase64Encode; -using ::google::cloud::storage::internal::HttpResponse; -using ::google::cloud::storage::testing::kP12KeyFileContents; -using ::google::cloud::storage::testing::kP12ServiceAccountId; -using ::google::cloud::storage::testing::kWellFormattedKey; -using ::google::cloud::storage::testing::MockHttpRequest; -using ::google::cloud::storage::testing::MockHttpRequestBuilder; -using ::google::cloud::storage::testing::WriteBase64AsBinary; -using ::google::cloud::testing_util::FakeClock; -using ::google::cloud::testing_util::IsOk; -using ::google::cloud::testing_util::IsOkAndHolds; -using ::google::cloud::testing_util::ScopedEnvironment; -using ::google::cloud::testing_util::StatusIs; -using ::testing::_; -using ::testing::An; -using ::testing::AtLeast; -using ::testing::ElementsAre; -using ::testing::ElementsAreArray; -using ::testing::HasSubstr; -using ::testing::Not; -using ::testing::Return; -using ::testing::StrEq; - -constexpr char kScopeForTest0[] = - "https://www.googleapis.com/auth/devstorage.full_control"; -constexpr char kScopeForTest1[] = - "https://www.googleapis.com/auth/cloud-platform"; -constexpr std::time_t kFixedJwtTimestamp = 1530060324; -constexpr char kGrantParamUnescaped[] = - "urn:ietf:params:oauth:grant-type:jwt-bearer"; -constexpr char kGrantParamEscaped[] = - "urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer"; - -auto constexpr kProjectId = "foo-project"; -auto constexpr kPrivateKeyId = "a1a111aa1111a11a11a11aa111a111a1a1111111"; -auto constexpr kClientEmail = "foo-email@foo-project.iam.gserviceaccount.com"; -auto constexpr kClientId = "100000000000000000001"; -auto constexpr kAuthUri = "https://accounts.google.com/o/oauth2/auth"; -auto constexpr kTokenUri = "https://oauth2.googleapis.com/token"; -auto constexpr kAuthProviderX509CerlUrl = - "https://www.googleapis.com/oauth2/v1/certs"; -auto constexpr kClientX509CertUrl = - "https://www.googleapis.com/robot/v1/metadata/x509/" - "foo-email%40foo-project.iam.gserviceaccount.com"; -constexpr char kSubjectForGrant[] = "user@foo.bar"; - -std::string MakeTestContents() { - return nlohmann::json{ - {"type", "service_account"}, - {"project_id", kProjectId}, - {"private_key_id", kPrivateKeyId}, - {"private_key", kWellFormattedKey}, - {"client_email", kClientEmail}, - {"client_id", kClientId}, - {"auth_uri", kAuthUri}, - {"token_uri", kTokenUri}, - {"auth_provider_x509_cert_url", kAuthProviderX509CerlUrl}, - {"client_x509_cert_url", kClientX509CertUrl}, - } - .dump(); -} - -class ServiceAccountCredentialsTest : public ::testing::Test { - protected: - void SetUp() override { - MockHttpRequestBuilder::mock_ = - std::make_shared(); - FakeClock::reset_clock(kFixedJwtTimestamp); - } - void TearDown() override { MockHttpRequestBuilder::mock_.reset(); } - - std::string CreateRandomFileName() { - // When running on the internal Google CI systems we cannot write to the - // local directory, GTest has a good temporary directory in that case. - return google::cloud::internal::PathAppend( - ::testing::TempDir(), - google::cloud::internal::Sample( - generator_, 8, "abcdefghijklmnopqrstuvwxyz0123456789")); - } - - google::cloud::internal::DefaultPRNG generator_ = - google::cloud::internal::MakeDefaultPRNG(); -}; - -TEST_F(ServiceAccountCredentialsTest, MultipleScopes) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - auto expected_info = *info; - // .scopes is a `std::set` so we need to preserve order. - ASSERT_LT(std::string{kScopeForTest1}, kScopeForTest0); - expected_info.scopes = {std::string{kScopeForTest1} + " " + kScopeForTest0}; - expected_info.subject = std::string(kSubjectForGrant); - auto const now = std::chrono::system_clock::now(); - auto const expected_components = - AssertionComponentsFromInfo(expected_info, now); - - auto actual_info = *info; - actual_info.scopes = {kScopeForTest0, kScopeForTest1}; - actual_info.subject = std::string(kSubjectForGrant); - auto const actual_components = AssertionComponentsFromInfo(actual_info, now); - EXPECT_EQ(actual_components, expected_components); -} - -/// @test Verify that we refresh service account credentials appropriately. -TEST_F(ServiceAccountCredentialsTest, - RefreshCalledOnlyWhenAccessTokenIsMissingOrInvalid) { - ScopedEnvironment disable_self_signed_jwt( - "GOOGLE_CLOUD_CPP_EXPERIMENTAL_DISABLE_SELF_SIGNED_JWT", "1"); - - // Prepare two responses, the first one is used but becomes immediately - // expired, resulting in another refresh next time the caller tries to get - // an authorization header. - std::string r1 = R"""({ - "token_type": "Type", - "access_token": "access-token-r1", - "expires_in": 0 -})"""; - std::string r2 = R"""({ - "token_type": "Type", - "access_token": "access-token-r2", - "expires_in": 1000 -})"""; - - // Now setup the builder to return those responses. - auto mock_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_builder, BuildRequest()) - .WillOnce([&] { - MockHttpRequest request; - EXPECT_CALL(*request.mock, MakeRequest) - .WillOnce(Return(HttpResponse{200, r1, {}})); - return request; - }) - .WillOnce([&] { - MockHttpRequest request; - EXPECT_CALL(*request.mock, MakeRequest) - .WillOnce(Return(HttpResponse{200, r2, {}})); - return request; - }); - EXPECT_CALL(*mock_builder, AddHeader(An())) - .Times(AtLeast(1)); - EXPECT_CALL(*mock_builder, Constructor(GoogleOAuthRefreshEndpoint(), _, _)) - .Times(AtLeast(1)); - EXPECT_CALL(*mock_builder, MakeEscapedString(An())) - .WillRepeatedly([](std::string const& s) -> std::unique_ptr { - EXPECT_EQ(kGrantParamUnescaped, s); - auto t = std::unique_ptr(new char[sizeof(kGrantParamEscaped)]); - std::copy(kGrantParamEscaped, - kGrantParamEscaped + sizeof(kGrantParamEscaped), t.get()); - return t; - }); - - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - ServiceAccountCredentials credentials(*info); - // Calls Refresh to obtain the access token for our authorization header. - EXPECT_EQ("Authorization: Type access-token-r1", - credentials.AuthorizationHeader().value()); - // Token is expired, resulting in another call to Refresh. - EXPECT_EQ("Authorization: Type access-token-r2", - credentials.AuthorizationHeader().value()); - // Token still valid; should return cached token instead of calling Refresh. - EXPECT_EQ("Authorization: Type access-token-r2", - credentials.AuthorizationHeader().value()); -} - -/// @test Verify that parsing a service account JSON string works. -TEST_F(ServiceAccountCredentialsTest, ParseSimple) { - std::string contents = R"""({ - "type": "service_account", - "private_key_id": "not-a-key-id-just-for-testing", - "private_key": "not-a-valid-key-just-for-testing", - "client_email": "test-only@test-group.example.com", - "token_uri": "https://oauth2.googleapis.com/test_endpoint" -})"""; - - auto actual = - ParseServiceAccountCredentials(contents, "test-data", "unused-uri"); - ASSERT_STATUS_OK(actual); - EXPECT_EQ("not-a-key-id-just-for-testing", actual->private_key_id); - EXPECT_EQ("not-a-valid-key-just-for-testing", actual->private_key); - EXPECT_EQ("test-only@test-group.example.com", actual->client_email); - EXPECT_EQ("https://oauth2.googleapis.com/test_endpoint", actual->token_uri); -} - -/// @test Verify that parsing a service account JSON string works. -TEST_F(ServiceAccountCredentialsTest, ParseUsesExplicitDefaultTokenUri) { - // No token_uri attribute here, so the default passed below should be used. - std::string contents = R"""({ - "type": "service_account", - "private_key_id": "not-a-key-id-just-for-testing", - "private_key": "not-a-valid-key-just-for-testing", - "client_email": "test-only@test-group.example.com" -})"""; - - auto actual = ParseServiceAccountCredentials( - contents, "test-data", "https://oauth2.googleapis.com/test_endpoint"); - ASSERT_STATUS_OK(actual); - EXPECT_EQ("not-a-key-id-just-for-testing", actual->private_key_id); - EXPECT_EQ("not-a-valid-key-just-for-testing", actual->private_key); - EXPECT_EQ("test-only@test-group.example.com", actual->client_email); - EXPECT_EQ("https://oauth2.googleapis.com/test_endpoint", actual->token_uri); -} - -/// @test Verify that parsing a service account JSON string works. -TEST_F(ServiceAccountCredentialsTest, ParseUsesImplicitDefaultTokenUri) { - // No token_uri attribute here. - std::string contents = R"""({ - "type": "service_account", - "private_key_id": "not-a-key-id-just-for-testing", - "private_key": "not-a-valid-key-just-for-testing", - "client_email": "test-only@test-group.example.com" -})"""; - - // No token_uri passed in here, either. - auto actual = ParseServiceAccountCredentials(contents, "test-data"); - ASSERT_STATUS_OK(actual); - EXPECT_EQ("not-a-key-id-just-for-testing", actual->private_key_id); - EXPECT_EQ("not-a-valid-key-just-for-testing", actual->private_key); - EXPECT_EQ("test-only@test-group.example.com", actual->client_email); - EXPECT_EQ(std::string(GoogleOAuthRefreshEndpoint()), actual->token_uri); -} - -/// @test Verify that invalid contents result in a readable error. -TEST_F(ServiceAccountCredentialsTest, ParseInvalidContentsFails) { - EXPECT_THAT(ParseServiceAccountCredentials(R"""( not-a-valid-json-string )""", - "test-as-a-source"), - StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr("Invalid ServiceAccountCredentials"), - HasSubstr("test-as-a-source")))); - - EXPECT_THAT(ParseServiceAccountCredentials( - R"""("valid-json-but-not-an-object")""", "test-as-a-source"), - StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr("Invalid ServiceAccountCredentials"), - HasSubstr("test-as-a-source")))); -} - -/// @test Parsing a service account JSON string should detect empty fields. -TEST_F(ServiceAccountCredentialsTest, ParseEmptyFieldFails) { - std::string contents = R"""({ - "type": "service_account", - "private_key": "not-a-valid-key-just-for-testing", - "client_email": "test-only@test-group.example.com", - "token_uri": "https://oauth2.googleapis.com/token" -})"""; - - for (auto const& field : {"private_key", "client_email", "token_uri"}) { - auto json = nlohmann::json::parse(contents); - json[field] = ""; - auto actual = ParseServiceAccountCredentials(json.dump(), "test-data", ""); - EXPECT_THAT(actual, - StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr(field), HasSubstr(" field is empty"), - HasSubstr("test-data")))); - } -} - -/// @test Parsing a service account JSON string should detect missing fields. -TEST_F(ServiceAccountCredentialsTest, ParseMissingFieldFails) { - std::string contents = R"""({ - "type": "service_account", - "private_key": "not-a-valid-key-just-for-testing", - "client_email": "test-only@test-group.example.com", - "token_uri": "https://oauth2.googleapis.com/token" -})"""; - - for (auto const& field : {"private_key", "client_email"}) { - auto json = nlohmann::json::parse(contents); - json.erase(field); - auto actual = ParseServiceAccountCredentials(json.dump(), "test-data", ""); - EXPECT_THAT(actual, - StatusIs(Not(StatusCode::kOk), - AllOf(HasSubstr(field), HasSubstr(" field is missing"), - HasSubstr("test-data")))); - } -} - -/// @test Parsing a service account JSON string allows an optional field. -TEST_F(ServiceAccountCredentialsTest, ParseOptionalField) { - std::string contents = R"""({ - "type": "service_account", - "private_key_id": "", - "private_key": "not-a-valid-key-just-for-testing", - "client_email": "test-only@test-group.example.com", - "token_uri": "https://oauth2.googleapis.com/token" -})"""; - - auto json = nlohmann::json::parse(contents); - auto actual = ParseServiceAccountCredentials(json.dump(), "test-data", ""); - ASSERT_STATUS_OK(actual.status()); -} - -/// @test Verify that the options are used in the constructor. -TEST_F(ServiceAccountCredentialsTest, UsesCARootsInfo) { - ScopedEnvironment disable_self_signed_jwt( - "GOOGLE_CLOUD_CPP_EXPERIMENTAL_DISABLE_SELF_SIGNED_JWT", "1"); - - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - - auto mock_builder = MockHttpRequestBuilder::mock_; - EXPECT_CALL(*mock_builder, BuildRequest()).WillOnce([&] { - MockHttpRequest result; - EXPECT_CALL(*result.mock, MakeRequest).WillOnce([](std::string const&) { - nlohmann::json response{{"token_type", "Mock-Type"}, - {"access_token", "fake-token"}, - {"expires_in", 3600}}; - return HttpResponse{200, response.dump(), {}}; - }); - return result; - }); - - // This is the key check in this test, verify the constructor is called with - // the right parameters. - auto const cainfo = std::string{"fake-cainfo-path-aka-roots-pem"}; - EXPECT_CALL(*mock_builder, Constructor(GoogleOAuthRefreshEndpoint(), - absl::make_optional(cainfo), _)) - .Times(AtLeast(1)); - - auto const expected_header = - std::string{"Content-Type: application/x-www-form-urlencoded"}; - EXPECT_CALL(*mock_builder, AddHeader(StrEq(expected_header))) - .Times(AtLeast(1)); - EXPECT_CALL(*mock_builder, MakeEscapedString(An())) - .WillRepeatedly([](std::string const& s) -> std::unique_ptr { - EXPECT_EQ(kGrantParamUnescaped, s); - auto t = std::unique_ptr(new char[sizeof(kGrantParamEscaped)]); - std::copy(kGrantParamEscaped, - kGrantParamEscaped + sizeof(kGrantParamEscaped), t.get()); - return t; - }); - - ServiceAccountCredentials credentials( - *info, Options{}.set(cainfo)); - // Call Refresh to obtain the access token for our authorization header. - auto authorization_header = credentials.AuthorizationHeader(); - ASSERT_STATUS_OK(authorization_header); - EXPECT_EQ("Authorization: Mock-Type fake-token", *authorization_header); -} - -/// @test Verify that we can create sign blobs using a service account. -TEST_F(ServiceAccountCredentialsTest, SignBlob) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - ServiceAccountCredentials credentials( - *info); - - std::string blob = R"""(GET -rmYdCNHKFXam78uCt7xQLw== -text/plain -1388534400 -x-goog-encryption-algorithm:AES256 -x-goog-meta-foo:bar,baz -/bucket/objectname)"""; - - auto actual = credentials.SignBlob(SigningAccount(), blob); - ASSERT_STATUS_OK(actual); - - // To generate the expected output I used: - // `openssl dgst -sha256 -sign private.pem blob.txt | openssl base64 -A` - // where `blob.txt` contains the `blob` string, and `private.pem` contains - // the private key embedded in `kJsonKeyfileContents`. - std::string expected_signed = - "Zsy8o5ci07DQTvO/" - "SVr47PKsCXvN+" - "FzXga0iYrReAnngdZYewHdcAnMQ8bZvFlTM8HY3msrRw64Jc6hoXVL979An5ugXoZ1ol/" - "DT1KlKp3l9E0JSIbqL88ogpElTxFvgPHOtHOUsy2mzhqOVrNSXSj4EM50gKHhvHKSbFq8Pcj" - "lAkROtq5gqp5t0OFd7EMIaRH+tekVUZjQPfFT/" - "hRW9bSCCV8w1Ex+" - "QxmB5z7P7zZn2pl7JAcL850emTo8f2tfv1xXWQGhACvIJeMdPmyjbc04Ye4M8Ljpkg3YhE6l" - "4GwC2MnI8TkuoHe4Bj2MvA8mM8TVwIvpBs6Etsj6Jdaz4rg=="; - EXPECT_EQ(expected_signed, internal::Base64Encode(*actual)); -} - -/// @test Verify that signing blobs fails with invalid e-mail. -TEST_F(ServiceAccountCredentialsTest, SignBlobFailure) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - ServiceAccountCredentials credentials( - *info); - - auto actual = - credentials.SignBlob(SigningAccount("fake@fake.com"), "test-blob"); - EXPECT_THAT( - actual, - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("The current_credentials cannot sign blobs for "))); -} - -/// @test Verify that we can get the client id from a service account. -TEST_F(ServiceAccountCredentialsTest, ClientId) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - ServiceAccountCredentials credentials( - *info); - - EXPECT_EQ("foo-email@foo-project.iam.gserviceaccount.com", - credentials.AccountEmail()); - EXPECT_EQ("a1a111aa1111a11a11a11aa111a111a1a1111111", credentials.KeyId()); -} - -char const kP12KeyFileMissingCerts[] = - "MIIDzAIBAzCCA5IGCSqGSIb3DQEHAaCCA4MEggN/MIIDezCCA3cGCSqGSIb3DQEH" - "BqCCA2gwggNkAgEAMIIDXQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQILaGB" - "fWhJ2V0CAggAgIIDMM5EI/ck4VQD4JyGchVPbgd5HQjFbn+HThIoxBYpMPEK+iT7" - "t32idiirDi0qH+6nZancp69nnKhjpAOnMLSjCvba7HDFzi/op7fgf9hnwupEOahv" - "4b8Wv0S9ePTqsLfJy8tJzOAPYKOJO7HGSeZanWh2HpyCd2g1K1dBXsqsabTtJBsF" - "TSGsfUg08/SMT5o12BlMk/wjzUrcSNQxntyPXLfjO1uZ0gFjFO6xsFyclVWr8Zax" - "7fTA6SLdgeE1Iu2+mS1ohwNNzeBrCU6kXVzgw1GSn0UV0ZGbANRWDZZThWzQs9UW" - "sn8l1fr70OZ4JhUwPZe9g0Tu7EeGNPkM5dW1Lr3izKNtYdInBD/1J7wGxsmomsU3" - "khIH2FMqqYX7NFkI0TZiHpLYk2bQmMnfFbBDlXluzO2iLvBY5FPUCn5W4ZPAJlFs" - "Ryo/OytciwJUIRoz76CIg3TmzM1b+RLBMEr6lAsD1za3fcTMwbsBeYY0FEFfb/I6" - "ddmJTxjbCLPLekgkV7MIFSWPiL4t2eXR3rlu1Vnoys0aTWmFtJhEOI16Q1bkJ9L1" - "c/KXHm/Srccm8hTazNYQewHRXWiAvigg6slRnx1I36Z0TMbnikDVCRH8cjFsMKO5" - "/qNMKSsZ6EAePHYAu4N5CpqaTl0hjHI8sW+CDzzmGOn8Acb00gJ+DOu+wiTZtJYS" - "GIZogs7PluMJ7cU1Ju38OixWbQDvfDdloQ/7kZrM6DoEKhvC2bwMwlfxin9jUwjJ" - "98dtdAwQVgckvnYYVpqKnn/dlkiStaiZFKx27kw6o2oobcDrkg0wtOZFeX8k0SXZ" - "ekcmMc5Xfl+5HyJxH5ni8UmHyOHAM8dNjpnzCD9J2K0U7z8kdzslZ95X5MAxYIUa" - "r50tIaWHxeLLYYZUi+nyjNbMZ+yvAqOjQqI1mIcYZurHRPRIHVi2x4nfcKKQIkxn" - "UTF9d3VWbkWoJ1qfe0OSpWg4RrdgDCSB1BlF0gQHEsDTT5/xoZIEoUV8t6TYTVCe" - "axreBYxLhvROONz94v6GD6Eb4kakbSObn8NuBiWnaPevFyEF5YluKR87MbZRQY0Z" - "yJ/4PuEhDIioRdY7ujAxMCEwCQYFKw4DAhoFAAQU4/UMFJQGUvgPuTXRKp0gVU4B" - "GbkECPTYJIica3DWAgIIAA=="; - -char const kP12KeyFileMissingKey[] = - "MIIDzAIBAzCCA5IGCSqGSIb3DQEHAaCCA4MEggN/MIIDezCCA3cGCSqGSIb3DQEH" - "BqCCA2gwggNkAgEAMIIDXQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQILaGB" - "fWhJ2V0CAggAgIIDMM5EI/ck4VQD4JyGchVPbgd5HQjFbn+HThIoxBYpMPEK+iT7" - "t32idiirDi0qH+6nZancp69nnKhjpAOnMLSjCvba7HDFzi/op7fgf9hnwupEOahv" - "4b8Wv0S9ePTqsLfJy8tJzOAPYKOJO7HGSeZanWh2HpyCd2g1K1dBXsqsabTtJBsF" - "TSGsfUg08/SMT5o12BlMk/wjzUrcSNQxntyPXLfjO1uZ0gFjFO6xsFyclVWr8Zax" - "7fTA6SLdgeE1Iu2+mS1ohwNNzeBrCU6kXVzgw1GSn0UV0ZGbANRWDZZThWzQs9UW" - "sn8l1fr70OZ4JhUwPZe9g0Tu7EeGNPkM5dW1Lr3izKNtYdInBD/1J7wGxsmomsU3" - "khIH2FMqqYX7NFkI0TZiHpLYk2bQmMnfFbBDlXluzO2iLvBY5FPUCn5W4ZPAJlFs" - "Ryo/OytciwJUIRoz76CIg3TmzM1b+RLBMEr6lAsD1za3fcTMwbsBeYY0FEFfb/I6" - "ddmJTxjbCLPLekgkV7MIFSWPiL4t2eXR3rlu1Vnoys0aTWmFtJhEOI16Q1bkJ9L1" - "c/KXHm/Srccm8hTazNYQewHRXWiAvigg6slRnx1I36Z0TMbnikDVCRH8cjFsMKO5" - "/qNMKSsZ6EAePHYAu4N5CpqaTl0hjHI8sW+CDzzmGOn8Acb00gJ+DOu+wiTZtJYS" - "GIZogs7PluMJ7cU1Ju38OixWbQDvfDdloQ/7kZrM6DoEKhvC2bwMwlfxin9jUwjJ" - "98dtdAwQVgckvnYYVpqKnn/dlkiStaiZFKx27kw6o2oobcDrkg0wtOZFeX8k0SXZ" - "ekcmMc5Xfl+5HyJxH5ni8UmHyOHAM8dNjpnzCD9J2K0U7z8kdzslZ95X5MAxYIUa" - "r50tIaWHxeLLYYZUi+nyjNbMZ+yvAqOjQqI1mIcYZurHRPRIHVi2x4nfcKKQIkxn" - "UTF9d3VWbkWoJ1qfe0OSpWg4RrdgDCSB1BlF0gQHEsDTT5/xoZIEoUV8t6TYTVCe" - "axreBYxLhvROONz94v6GD6Eb4kakbSObn8NuBiWnaPevFyEF5YluKR87MbZRQY0Z" - "yJ/4PuEhDIioRdY7ujAxMCEwCQYFKw4DAhoFAAQU4/UMFJQGUvgPuTXRKp0gVU4B" - "GbkECPTYJIica3DWAgIIAA=="; - -/// @test Verify that parsing a service account JSON string works. -TEST_F(ServiceAccountCredentialsTest, ParseSimpleP12) { - auto filename = CreateRandomFileName() + ".p12"; - WriteBase64AsBinary(filename, kP12KeyFileContents); - - auto info = ParseServiceAccountP12File(filename); - EXPECT_EQ(0, std::remove(filename.c_str())); - - if (info.status().code() == StatusCode::kInvalidArgument) { - if (absl::StrContains(info.status().message(), "error:0308010C")) { - // With OpenSSL 3.0 the PKCS#12 files may not be supported by default. - GTEST_SKIP() << "PKCS#12 support unavailable, skipping test"; - } -#if _WIN32 - // On Windows, the OS may not have the necessary providers to support - // PKCS#12. Unfortunately the error message is not as unambiguous, so we use - // the function that fails instead. - auto const& metadata = info.status().error_info().metadata(); - auto const l = metadata.find("gcloud-cpp.source.function"); - if (l != metadata.end() && l->second == "GetCertificatePrivateKey") { - GTEST_SKIP() << "PKCS#12 support unavailable, skipping test"; - } -#endif // _WIN32 - } - ASSERT_STATUS_OK(info); - - EXPECT_EQ(kP12ServiceAccountId, info->client_email); - EXPECT_FALSE(info->private_key.empty()); - - ServiceAccountCredentials<> credentials(*info); - - auto signed_blob = credentials.SignBlob(SigningAccount(), "test-blob"); - EXPECT_STATUS_OK(signed_blob); -} - -TEST_F(ServiceAccountCredentialsTest, ParseP12MissingKey) { - std::string filename = CreateRandomFileName() + ".p12"; - WriteBase64AsBinary(filename, kP12KeyFileMissingKey); - auto info = ParseServiceAccountP12File(filename); - EXPECT_THAT(info, Not(IsOk())); -} - -TEST_F(ServiceAccountCredentialsTest, ParseP12MissingCerts) { - std::string filename = google::cloud::internal::PathAppend( - ::testing::TempDir(), CreateRandomFileName() + ".p12"); - WriteBase64AsBinary(filename, kP12KeyFileMissingCerts); - auto info = ParseServiceAccountP12File(filename); - EXPECT_THAT(info, Not(IsOk())); -} - -TEST_F(ServiceAccountCredentialsTest, CreateFromP12MissingFile) { - std::string filename = CreateRandomFileName(); - // Loading a non-existent file should fail. - auto actual = CreateServiceAccountCredentialsFromP12FilePath(filename); - EXPECT_THAT(actual, Not(IsOk())); -} - -TEST_F(ServiceAccountCredentialsTest, CreateFromP12EmptyFile) { - std::string filename = CreateRandomFileName(); - std::ofstream(filename).close(); - - // Loading an empty file should fail. - auto actual = CreateServiceAccountCredentialsFromP12FilePath(filename); - EXPECT_THAT(actual, Not(IsOk())); - - EXPECT_EQ(0, std::remove(filename.c_str())); -} - -TEST_F(ServiceAccountCredentialsTest, CreateFromP12ValidFile) { - std::string filename = CreateRandomFileName() + ".p12"; - WriteBase64AsBinary(filename, kP12KeyFileContents); - - auto actual = CreateServiceAccountCredentialsFromP12FilePath(filename); - EXPECT_EQ(0, std::remove(filename.c_str())); - - if (actual.status().code() == StatusCode::kInvalidArgument) { - if (absl::StrContains(actual.status().message(), "error:0308010C")) { - // With OpenSSL 3.0 the PKCS#12 files may not be supported by default. - GTEST_SKIP() << "PKCS#12 support unavailable, skipping test"; - } -#if _WIN32 - // On Windows, the OS may not have the necessary providers to support - // PKCS#12. Unfortunately the error message is not as unambiguous, so we use - // the function that fails instead. - auto const& metadata = actual.status().error_info().metadata(); - auto const l = metadata.find("gcloud-cpp.source.function"); - if (l != metadata.end() && l->second == "GetCertificatePrivateKey") { - GTEST_SKIP() << "PKCS#12 support unavailable, skipping test"; - } -#endif // _WIN32 - } - EXPECT_STATUS_OK(actual); -} - -/// @test Verify we can obtain JWT assertion components given the info parsed -/// from a keyfile. -TEST_F(ServiceAccountCredentialsTest, AssertionComponentsFromInfo) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - auto const clock_value_1 = 10000; - FakeClock::now_value_ = clock_value_1; - auto components = AssertionComponentsFromInfo(*info, FakeClock::now()); - - auto header = nlohmann::json::parse(components.first); - EXPECT_EQ("RS256", header.value("alg", "")); - EXPECT_EQ("JWT", header.value("typ", "")); - EXPECT_EQ(info->private_key_id, header.value("kid", "")); - - auto payload = nlohmann::json::parse(components.second); - EXPECT_EQ(clock_value_1, payload.value("iat", 0)); - EXPECT_EQ(clock_value_1 + 3600, payload.value("exp", 0)); - EXPECT_EQ(info->client_email, payload.value("iss", "")); - EXPECT_EQ(info->token_uri, payload.value("aud", "")); -} - -/// @test Verify we can construct a JWT assertion given the info parsed from a -/// keyfile. -TEST_F(ServiceAccountCredentialsTest, MakeJWTAssertion) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - FakeClock::reset_clock(kFixedJwtTimestamp); - auto components = AssertionComponentsFromInfo(*info, FakeClock::now()); - auto assertion = - MakeJWTAssertion(components.first, components.second, info->private_key); - - auto const body = UrlsafeBase64Encode(components.first) + "." + - UrlsafeBase64Encode(components.second); - auto signed_body = - google::cloud::internal::SignUsingSha256(body, info->private_key); - ASSERT_STATUS_OK(signed_body); - - std::vector actual_tokens = absl::StrSplit(assertion, '.'); - EXPECT_THAT(actual_tokens, ElementsAre(UrlsafeBase64Encode(components.first), - UrlsafeBase64Encode(components.second), - UrlsafeBase64Encode(*signed_body))); -} - -/// @test Verify we can construct a service account refresh payload given the -/// info parsed from a keyfile. -TEST_F(ServiceAccountCredentialsTest, CreateServiceAccountRefreshPayload) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - FakeClock::reset_clock(kFixedJwtTimestamp); - auto components = AssertionComponentsFromInfo(*info, FakeClock::now()); - auto assertion = - MakeJWTAssertion(components.first, components.second, info->private_key); - auto actual_payload = CreateServiceAccountRefreshPayload( - *info, kGrantParamEscaped, FakeClock::now()); - - EXPECT_THAT(actual_payload, HasSubstr(std::string("assertion=") + assertion)); - EXPECT_THAT(actual_payload, HasSubstr(kGrantParamUnescaped)); -} - -/// @test Parsing a refresh response with missing fields results in failure. -TEST_F(ServiceAccountCredentialsTest, - ParseServiceAccountRefreshResponseInvalid) { - ScopedEnvironment disable_self_signed_jwt( - "GOOGLE_CLOUD_CPP_EXPERIMENTAL_DISABLE_SELF_SIGNED_JWT", "1"); - - std::string r1 = R"""({})"""; - // Does not have access_token. - std::string r2 = R"""({ - "token_type": "Type", - "id_token": "id-token-value", - "expires_in": 1000 -})"""; - - FakeClock::reset_clock(1000); - auto status = ParseServiceAccountRefreshResponse(HttpResponse{400, r1, {}}, - FakeClock::now()); - EXPECT_THAT(status, - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); - - status = ParseServiceAccountRefreshResponse(HttpResponse{400, r2, {}}, - FakeClock::now()); - EXPECT_THAT(status, - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); - - EXPECT_THAT( - ParseServiceAccountRefreshResponse( - HttpResponse{400, R"js("valid-json-but-not-an-object")js", {}}, - FakeClock::now()), - StatusIs(StatusCode::kInvalidArgument, - HasSubstr("Could not find all required fields"))); -} - -/// @test Parsing a refresh response yields a TemporaryToken. -TEST_F(ServiceAccountCredentialsTest, ParseServiceAccountRefreshResponse) { - ScopedEnvironment disable_self_signed_jwt( - "GOOGLE_CLOUD_CPP_EXPERIMENTAL_DISABLE_SELF_SIGNED_JWT", "1"); - - std::string r1 = R"""({ - "token_type": "Type", - "access_token": "access-token-r1", - "expires_in": 1000 -})"""; - - auto expires_in = 1000; - FakeClock::reset_clock(2000); - auto status = ParseServiceAccountRefreshResponse(HttpResponse{200, r1, {}}, - FakeClock::now()); - EXPECT_STATUS_OK(status); - auto token = *status; - EXPECT_EQ( - std::chrono::time_point_cast(token.expiration_time) - .time_since_epoch() - .count(), - FakeClock::now_value_ + expires_in); - EXPECT_EQ(token.token, "Authorization: Type access-token-r1"); -} - -TEST_F(ServiceAccountCredentialsTest, MakeSelfSignedJWT) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - auto const now = std::chrono::system_clock::now(); - auto actual = MakeSelfSignedJWT(*info, now); - ASSERT_STATUS_OK(actual); - - std::vector components = absl::StrSplit(*actual, '.'); - std::vector decoded(components.size()); - std::transform(components.begin(), components.end(), decoded.begin(), - [](std::string const& e) { - auto v = UrlsafeBase64Decode(e).value(); - return std::string{v.begin(), v.end()}; - }); - ASSERT_THAT(3, decoded.size()); - auto const header = nlohmann::json::parse(decoded[0], nullptr); - ASSERT_FALSE(header.is_null()) << "header=" << decoded[0]; - auto const payload = nlohmann::json::parse(decoded[1], nullptr); - ASSERT_FALSE(payload.is_null()) << "payload=" << decoded[1]; - - auto const expected_header = nlohmann::json{ - {"alg", "RS256"}, {"typ", "JWT"}, {"kid", info->private_key_id}}; - - auto const iat = - std::chrono::duration_cast(now.time_since_epoch()); - auto const exp = iat + std::chrono::hours(1); - auto const expected_payload = nlohmann::json{ - {"iss", info->client_email}, - {"sub", info->client_email}, - {"iat", iat.count()}, - {"exp", exp.count()}, - {"scope", "https://www.googleapis.com/auth/cloud-platform"}, - }; - - ASSERT_EQ(expected_header, header) << "header=" << header; - ASSERT_EQ(expected_payload, payload) << "payload=" << payload; - - auto signature = - SignUsingSha256(components[0] + '.' + components[1], info->private_key); - ASSERT_STATUS_OK(signature); - EXPECT_THAT(*signature, - ElementsAreArray(decoded[2].begin(), decoded[2].end())); -} - -TEST_F(ServiceAccountCredentialsTest, MakeSelfSignedJWTWithScopes) { - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - info->scopes = std::set{"test-only-s1", "test-only-s2"}; - - auto const now = std::chrono::system_clock::now(); - auto actual = MakeSelfSignedJWT(*info, now); - ASSERT_STATUS_OK(actual); - - std::vector components = absl::StrSplit(*actual, '.'); - std::vector decoded(components.size()); - std::transform(components.begin(), components.end(), decoded.begin(), - [](std::string const& e) { - auto v = UrlsafeBase64Decode(e).value(); - return std::string{v.begin(), v.end()}; - }); - ASSERT_THAT(3, decoded.size()); - auto const header = nlohmann::json::parse(decoded[0], nullptr); - ASSERT_FALSE(header.is_null()) << "header=" << decoded[0]; - auto const payload = nlohmann::json::parse(decoded[1], nullptr); - ASSERT_FALSE(payload.is_null()) << "payload=" << decoded[1]; - - auto const expected_header = nlohmann::json{ - {"alg", "RS256"}, {"typ", "JWT"}, {"kid", info->private_key_id}}; - - auto const iat = - std::chrono::duration_cast(now.time_since_epoch()); - auto const exp = iat + std::chrono::hours(1); - auto const expected_payload = nlohmann::json{ - {"iss", info->client_email}, - {"sub", info->client_email}, - {"iat", iat.count()}, - {"exp", exp.count()}, - {"scope", "test-only-s1 test-only-s2"}, - }; - - ASSERT_EQ(expected_header, header) << "header=" << header; - ASSERT_EQ(expected_payload, payload) << "payload=" << payload; - - auto signature = - SignUsingSha256(components[0] + '.' + components[1], info->private_key); - ASSERT_STATUS_OK(signature); - EXPECT_THAT(*signature, - ElementsAreArray(decoded[2].begin(), decoded[2].end())); -} - -TEST_F(ServiceAccountCredentialsTest, UseOauth) { - std::string filename = CreateRandomFileName() + ".p12"; - WriteBase64AsBinary(filename, kP12KeyFileContents); - - auto p12_info = ParseServiceAccountP12File(filename); - EXPECT_EQ(0, std::remove(filename.c_str())); - - auto json_info_without_scopes = - ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(json_info_without_scopes); - ASSERT_FALSE(json_info_without_scopes->scopes.has_value()); - auto json_info_with_scopes = *json_info_without_scopes; - json_info_with_scopes.scopes = - std::set{{GoogleOAuthScopeCloudPlatform()}}; - - struct TestCase { - std::string name; - ServiceAccountCredentialsInfo info; - absl::optional environment; - bool expected; - }; - - std::vector cases = { - {"JSON/with-scopes/no-env", json_info_with_scopes, absl::nullopt, true}, - {"JSON/with-scopes/env", json_info_with_scopes, "1", true}, - {"JSON/no-scopes/no-env", *json_info_without_scopes, absl::nullopt, - false}, - {"JSON/no-scopes/env", *json_info_without_scopes, "1", true}, - }; - if (p12_info) { - // Some environments do not support PKCS$12, we need to test the other - // cases. - cases.push_back({"P12/no-env", *p12_info, absl::nullopt, true}); - cases.push_back({"P12/env", *p12_info, "1", true}); - } - - for (auto const& test : cases) { - SCOPED_TRACE("Testing for " + test.name); - ScopedEnvironment env( - "GOOGLE_CLOUD_CPP_EXPERIMENTAL_DISABLE_SELF_SIGNED_JWT", - test.environment); - EXPECT_EQ(test.expected, ServiceAccountUseOAuth(test.info)); - } -} - -TEST_F(ServiceAccountCredentialsTest, CachingWithSelfSignedJwt) { - auto clear = ScopedEnvironment( - "GOOGLE_CLOUD_CPP_EXPERIMENTAL_DISABLE_SELF_SIGNED_JWT", absl::nullopt); - auto info = ParseServiceAccountCredentials(MakeTestContents(), "test"); - ASSERT_STATUS_OK(info); - - ServiceAccountCredentials<> tested(*info); - auto tp = std::chrono::system_clock::now(); - auto initial = ServiceAccountCredentialsTester::Header(tested, tp); - ASSERT_STATUS_OK(initial); - - auto cached = ServiceAccountCredentialsTester::Header( - tested, tp + std::chrono::seconds(30)); - EXPECT_THAT(cached, IsOkAndHolds(*initial)); - - cached = ServiceAccountCredentialsTester::Header( - tested, tp + std::chrono::seconds(300)); - EXPECT_THAT(cached, IsOkAndHolds(*initial)); - - auto uncached = ServiceAccountCredentialsTester::Header( - tested, tp + std::chrono::hours(2)); - ASSERT_STATUS_OK(uncached); - EXPECT_NE(*initial, *uncached); -} - -} // namespace -} // namespace oauth2 -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/storage/options.h b/google/cloud/storage/options.h index 4a5204e58dfb1..f64f5d1f28e51 100644 --- a/google/cloud/storage/options.h +++ b/google/cloud/storage/options.h @@ -16,7 +16,6 @@ #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OPTIONS_H #include "google/cloud/storage/idempotency_policy.h" -#include "google/cloud/storage/oauth2/credentials.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/version.h" #include "google/cloud/backoff_policy.h" @@ -89,18 +88,6 @@ struct IamEndpointOption { using Type = std::string; }; -/** - * Configure oauth2::Credentials for the GCS client library. - * - * @ingroup storage-options - * - * @deprecated prefer using `google::cloud::UnifiedCredentialsOption` and the - * unified credentials documented in @ref guac - */ -struct Oauth2CredentialsOption { - using Type = std::shared_ptr; -}; - /** * Set the Google Cloud Platform project id. * @@ -332,9 +319,8 @@ struct IdempotencyPolicyOption { /// The complete list of options accepted by `storage::Client`. using ClientOptionList = ::google::cloud::OptionList< - RestEndpointOption, IamEndpointOption, Oauth2CredentialsOption, - ProjectIdOption, ProjectIdOption, ConnectionPoolSizeOption, - DownloadBufferSizeOption, UploadBufferSizeOption, + RestEndpointOption, IamEndpointOption, ProjectIdOption, ProjectIdOption, + ConnectionPoolSizeOption, DownloadBufferSizeOption, UploadBufferSizeOption, EnableCurlSslLockingOption, EnableCurlSigpipeHandlerOption, MaximumCurlSocketRecvSizeOption, MaximumCurlSocketSendSizeOption, TransferStallTimeoutOption, RetryPolicyOption, BackoffPolicyOption, diff --git a/google/cloud/storage/parallel_uploads_test.cc b/google/cloud/storage/parallel_uploads_test.cc index 5542b31641a57..68a0f40f27834 100644 --- a/google/cloud/storage/parallel_uploads_test.cc +++ b/google/cloud/storage/parallel_uploads_test.cc @@ -14,7 +14,6 @@ #include "google/cloud/storage/internal/bucket_metadata_parser.h" #include "google/cloud/storage/internal/object_metadata_parser.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/parallel_upload.h" #include "google/cloud/storage/retry_policy.h" #include "google/cloud/storage/testing/canonical_errors.h" diff --git a/google/cloud/storage/storage_client_unit_tests.bzl b/google/cloud/storage/storage_client_unit_tests.bzl index 065d4f6240a4b..54c1c64a555b6 100644 --- a/google/cloud/storage/storage_client_unit_tests.bzl +++ b/google/cloud/storage/storage_client_unit_tests.bzl @@ -41,7 +41,6 @@ storage_client_unit_tests = [ "hashing_options_test.cc", "hmac_key_metadata_test.cc", "idempotency_policy_test.cc", - "internal/access_token_credentials_test.cc", "internal/base64_test.cc", "internal/bucket_acl_requests_test.cc", "internal/bucket_requests_test.cc", @@ -68,7 +67,6 @@ storage_client_unit_tests = [ "internal/hash_values_test.cc", "internal/hmac_key_requests_test.cc", "internal/http_response_test.cc", - "internal/impersonate_service_account_credentials_test.cc", "internal/logging_stub_test.cc", "internal/make_jwt_assertion_test.cc", "internal/md5hash_test.cc", @@ -92,7 +90,6 @@ storage_client_unit_tests = [ "internal/tracing_connection_test.cc", "internal/tracing_object_read_source_test.cc", "internal/tuple_filter_test.cc", - "internal/unified_rest_credentials_test.cc", "lifecycle_rule_test.cc", "list_buckets_extended_reader_test.cc", "list_buckets_reader_test.cc", @@ -100,12 +97,6 @@ storage_client_unit_tests = [ "list_objects_and_prefixes_reader_test.cc", "list_objects_reader_test.cc", "notification_metadata_test.cc", - "oauth2/anonymous_credentials_test.cc", - "oauth2/authorized_user_credentials_test.cc", - "oauth2/compute_engine_credentials_test.cc", - "oauth2/google_application_default_credentials_file_test.cc", - "oauth2/google_credentials_test.cc", - "oauth2/service_account_credentials_test.cc", "object_access_control_test.cc", "object_metadata_test.cc", "object_retention_test.cc", diff --git a/google/cloud/storage/testing/mock_client.h b/google/cloud/storage/testing/mock_client.h index 0f63951d28920..1faa1d99b4f27 100644 --- a/google/cloud/storage/testing/mock_client.h +++ b/google/cloud/storage/testing/mock_client.h @@ -176,9 +176,6 @@ class MockClient : public google::cloud::storage::internal::StorageConnection { (internal::GetNotificationRequest const&), (override)); MOCK_METHOD(StatusOr, DeleteNotification, (internal::DeleteNotificationRequest const&), (override)); - MOCK_METHOD( - StatusOr, AuthorizationHeader, - (std::shared_ptr const&)); MOCK_METHOD(std::vector, InspectStackStructure, (), (const, override)); diff --git a/google/cloud/storage/tests/CMakeLists.txt b/google/cloud/storage/tests/CMakeLists.txt index 80370150ddd23..a1fc1638054a8 100644 --- a/google/cloud/storage/tests/CMakeLists.txt +++ b/google/cloud/storage/tests/CMakeLists.txt @@ -72,11 +72,8 @@ set(storage_client_integration_tests tracing_integration_test.cc) set(storage_client_integration_tests_production # cmake-format: sort - alternative_endpoint_integration_test.cc - key_file_integration_test.cc - mtls_object_basic_crud_integration_test.cc - signed_url_integration_test.cc - unified_credentials_integration_test.cc) + alternative_endpoint_integration_test.cc key_file_integration_test.cc + mtls_object_basic_crud_integration_test.cc signed_url_integration_test.cc) list(APPEND storage_client_integration_tests ${storage_client_integration_tests_production}) list(SORT storage_client_integration_tests) diff --git a/google/cloud/storage/tests/create_client_integration_test.cc b/google/cloud/storage/tests/create_client_integration_test.cc index fafdeedebd5fb..6d385b36ea6b3 100644 --- a/google/cloud/storage/tests/create_client_integration_test.cc +++ b/google/cloud/storage/tests/create_client_integration_test.cc @@ -13,7 +13,6 @@ // limitations under the License. #include "google/cloud/storage/client.h" -#include "google/cloud/storage/internal/unified_rest_credentials.h" #include "google/cloud/storage/testing/storage_integration_test.h" #include "google/cloud/storage/testing/temp_file.h" #include "google/cloud/credentials.h" @@ -72,12 +71,6 @@ TEST_F(CreateClientIntegrationTest, DefaultWorks) { } TEST_F(CreateClientIntegrationTest, SettingPolicies) { - auto credentials = oauth2::CreateAnonymousCredentials(); - if (!UsingEmulator()) { - auto c = oauth2::GoogleDefaultCredentials(); - ASSERT_THAT(c, IsOk()); - credentials = *std::move(c); - } auto client = Client( Options{} .set( diff --git a/google/cloud/storage/tests/key_file_integration_test.cc b/google/cloud/storage/tests/key_file_integration_test.cc index fe7f8ee98f0b8..b74eb23baaafd 100644 --- a/google/cloud/storage/tests/key_file_integration_test.cc +++ b/google/cloud/storage/tests/key_file_integration_test.cc @@ -70,12 +70,9 @@ class KeyFileIntegrationTest TEST_P(KeyFileIntegrationTest, ObjectWriteSignAndReadDefaultAccount) { if (UsingGrpc()) GTEST_SKIP(); - auto credentials = - oauth2::CreateServiceAccountCredentialsFromFilePath(key_filename_); - ASSERT_STATUS_OK(credentials); - + auto credentials = MakeServiceAccountCredentialsFromFile(key_filename_); auto client = MakeIntegrationTestClient( - Options{}.set(*credentials)); + Options{}.set(credentials)); auto object_name = MakeRandomObjectName(); std::string expected = LoremIpsum(); @@ -98,12 +95,9 @@ TEST_P(KeyFileIntegrationTest, ObjectWriteSignAndReadDefaultAccount) { TEST_P(KeyFileIntegrationTest, ObjectWriteSignAndReadExplicitAccount) { if (UsingGrpc()) GTEST_SKIP(); - auto credentials = - oauth2::CreateServiceAccountCredentialsFromFilePath(key_filename_); - ASSERT_STATUS_OK(credentials); - + auto credentials = MakeServiceAccountCredentialsFromFile(key_filename_); auto client = MakeIntegrationTestClient( - Options{}.set(*credentials)); + Options{}.set(credentials)); auto object_name = MakeRandomObjectName(); std::string expected = LoremIpsum(); diff --git a/google/cloud/storage/tests/service_account_credentials_integration_test.cc b/google/cloud/storage/tests/service_account_credentials_integration_test.cc index 5959030c32f29..08fc4c0d3670b 100644 --- a/google/cloud/storage/tests/service_account_credentials_integration_test.cc +++ b/google/cloud/storage/tests/service_account_credentials_integration_test.cc @@ -12,17 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "google/cloud/storage/oauth2/google_credentials.h" #include "google/cloud/storage/testing/retry_http_request.h" #include "google/cloud/storage/testing/storage_integration_test.h" #include "google/cloud/internal/getenv.h" +#include "google/cloud/internal/oauth2_service_account_credentials.h" #include "google/cloud/internal/rest_request.h" +#include "google/cloud/internal/unified_rest_credentials.h" #include "google/cloud/testing_util/status_matchers.h" #include "absl/strings/str_split.h" #include #include #include -#include #include #include @@ -43,20 +43,21 @@ TEST_F(ServiceAccountCredentialsTest, UserInfoOAuth2) { "GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON"); if (UsingEmulator() || !filename) GTEST_SKIP(); - auto credentials = oauth2::CreateServiceAccountCredentialsFromFilePath( - *filename, /*scopes=*/ - std::set({"https://www.googleapis.com/auth/userinfo.email", - "https://www.googleapis.com/auth/cloud-platform"}), - /*subject=*/absl::nullopt); - ASSERT_STATUS_OK(credentials); + auto options = Options{} + .set({}) + .set(std::vector( + {"https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/cloud-platform"})); + + auto credentials = MakeServiceAccountCredentialsFromFile(*filename, options); + auto sa_creds = rest_internal::MapCredentials(*credentials); auto constexpr kUrl = "https://www.googleapis.com/userinfo/v2/me"; - auto factory = [c = *credentials]() { - auto authorization = c->AuthorizationHeader(); + auto factory = [c = sa_creds]() { + auto authorization = c->GetToken(std::chrono::system_clock::now()); if (!authorization) return rest_internal::RestRequest(); - std::pair p = - absl::StrSplit(*authorization, absl::MaxSplits(": ", 1)); - return rest_internal::RestRequest().AddHeader(std::move(p)); + return rest_internal::RestRequest().AddHeader( + std::make_pair("Authorization", "Bearer " + authorization->token)); }; auto response = RetryHttpGet(kUrl, factory); diff --git a/google/cloud/storage/tests/signed_url_conformance_test.cc b/google/cloud/storage/tests/signed_url_conformance_test.cc index d50dbb2014b08..002d0ccaecaf8 100644 --- a/google/cloud/storage/tests/signed_url_conformance_test.cc +++ b/google/cloud/storage/tests/signed_url_conformance_test.cc @@ -21,6 +21,7 @@ #include "google/cloud/internal/format_time_point.h" #include "google/cloud/internal/getenv.h" #include "google/cloud/internal/time_utils.h" +#include "google/cloud/internal/unified_rest_credentials.h" #include "google/cloud/terminate_handler.h" #include "google/cloud/testing_util/scoped_environment.h" #include "google/cloud/testing_util/status_matchers.h" @@ -87,13 +88,16 @@ class V4PostPolicyConformanceTest : public V4SignedUrlConformanceTest {}; TEST_P(V4SignedUrlConformanceTest, V4SignJson) { testing_util::ScopedEnvironment endpoint("CLOUD_STORAGE_EMULATOR_ENDPOINT", absl::nullopt); - auto creds = oauth2::CreateServiceAccountCredentialsFromJsonFilePath( - service_account_key_filename_); - ASSERT_STATUS_OK(creds); + testing_util::ScopedEnvironment preserve_creds( + "GOOGLE_CLOUD_CPP_STORAGE_TESTING_PRESERVE_CREDENTIALS", "yes"); + auto credentials = + MakeServiceAccountCredentialsFromFile(service_account_key_filename_); + auto sa_creds = rest_internal::MapCredentials(*credentials); + std::string account_email = sa_creds->AccountEmail(); + + auto client = MakeIntegrationTestClient( + Options{}.set(credentials)); - std::string account_email = (*creds)->AccountEmail(); - auto client = - MakeIntegrationTestClient(Options{}.set(*creds)); std::string actual_canonical_request; std::string actual_string_to_sign; @@ -191,13 +195,15 @@ INSTANTIATE_TEST_SUITE_P( TEST_P(V4PostPolicyConformanceTest, V4PostPolicy) { testing_util::ScopedEnvironment endpoint("CLOUD_STORAGE_EMULATOR_ENDPOINT", absl::nullopt); - auto creds = oauth2::CreateServiceAccountCredentialsFromJsonFilePath( - service_account_key_filename_); - ASSERT_STATUS_OK(creds); - - std::string account_email = (*creds)->AccountEmail(); - auto client = - MakeIntegrationTestClient(Options{}.set(*creds)); + testing_util::ScopedEnvironment preserve_creds( + "GOOGLE_CLOUD_CPP_STORAGE_TESTING_PRESERVE_CREDENTIALS", "yes"); + auto credentials = + MakeServiceAccountCredentialsFromFile(service_account_key_filename_); + auto sa_creds = rest_internal::MapCredentials(*credentials); + std::string account_email = sa_creds->AccountEmail(); + + auto client = MakeIntegrationTestClient( + Options{}.set(credentials)); auto const& test_params = (*post_policy_tests)[GetParam()]; auto const& input = test_params.policyinput(); diff --git a/google/cloud/storage/tests/storage_client_integration_tests.bzl b/google/cloud/storage/tests/storage_client_integration_tests.bzl index 1536bb02c89ef..b3e9bf6911515 100644 --- a/google/cloud/storage/tests/storage_client_integration_tests.bzl +++ b/google/cloud/storage/tests/storage_client_integration_tests.bzl @@ -75,7 +75,6 @@ storage_client_integration_tests = [ "storage_include_test.cc", "thread_integration_test.cc", "tracing_integration_test.cc", - "unified_credentials_integration_test.cc", ] storage_client_integration_tests_production = [ @@ -83,5 +82,4 @@ storage_client_integration_tests_production = [ "key_file_integration_test.cc", "mtls_object_basic_crud_integration_test.cc", "signed_url_integration_test.cc", - "unified_credentials_integration_test.cc", ] diff --git a/google/cloud/storage/tests/storage_include_test.cc b/google/cloud/storage/tests/storage_include_test.cc index 5f0c341e37b12..abc4b3d15f478 100644 --- a/google/cloud/storage/tests/storage_include_test.cc +++ b/google/cloud/storage/tests/storage_include_test.cc @@ -13,7 +13,6 @@ // limitations under the License. #include "google/cloud/storage/client.h" -#include "google/cloud/storage/oauth2/google_credentials.h" #include int main() { diff --git a/google/cloud/storage/tests/unified_credentials_integration_test.cc b/google/cloud/storage/tests/unified_credentials_integration_test.cc deleted file mode 100644 index 68e6edf086954..0000000000000 --- a/google/cloud/storage/tests/unified_credentials_integration_test.cc +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "google/cloud/storage/client.h" -#include "google/cloud/storage/internal/unified_rest_credentials.h" -#include "google/cloud/storage/testing/storage_integration_test.h" -#include "google/cloud/storage/testing/temp_file.h" -#include "google/cloud/credentials.h" -#include "google/cloud/internal/getenv.h" -#include "google/cloud/testing_util/scoped_environment.h" -#include "google/cloud/testing_util/status_matchers.h" -#include -#ifndef _WIN32 -#include -#include -#endif -#include -#include -#include - -namespace google { -namespace cloud { -namespace storage { -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN -namespace { - -using ::google::cloud::MakeAccessTokenCredentials; -using ::google::cloud::MakeGoogleDefaultCredentials; -using ::google::cloud::MakeServiceAccountCredentials; -using ::google::cloud::UnifiedCredentialsOption; -using ::google::cloud::internal::GetEnv; -using ::google::cloud::storage::testing::TempFile; -using ::google::cloud::testing_util::IsOk; -using ::testing::IsEmpty; -using ::testing::Not; -using ::testing::StartsWith; - -// This is a properly formatted, but invalid, CA Certificate. We will use this -// as the *only* root of trust and try to contact *.google.com. This will -// (naturally) fail, which is the expected result. A separate test will verify -// that using a *valid* CA certificate (bundle) works. -// -// Created with: -// -// openssl genrsa -des3 -out cert.key 2048 -// openssl req -x509 -new -nodes -key cert.key -// -sha256 -days 3650 -out cert.pem -auto constexpr kCACertificate = R"""( ------BEGIN CERTIFICATE----- -MIIEPTCCAyWgAwIBAgIUXa/2HsbYrolo1Cox/1SOqLDnNYQwDQYJKoZIhvcNAQEL -BQAwga0xCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwI -TmV3IFlvcmsxGjAYBgNVBAoMEVRlc3QtT25seSBJbnZhbGlkMRIwEAYDVQQLDAlU -ZXN0LU9ubHkxGjAYBgNVBAMMEVRlc3QtT25seSBJbnZhbGlkMSwwKgYJKoZIhvcN -AQkBFh10ZXN0LW9ubHlAaW52YWxpZC5leGFtcGxlLmNvbTAeFw0yMTA1MjUxOTQy -MTdaFw0zMTA1MjMxOTQyMTdaMIGtMQswCQYDVQQGEwJVUzERMA8GA1UECAwITmV3 -IFlvcmsxETAPBgNVBAcMCE5ldyBZb3JrMRowGAYDVQQKDBFUZXN0LU9ubHkgSW52 -YWxpZDESMBAGA1UECwwJVGVzdC1Pbmx5MRowGAYDVQQDDBFUZXN0LU9ubHkgSW52 -YWxpZDEsMCoGCSqGSIb3DQEJARYddGVzdC1vbmx5QGludmFsaWQuZXhhbXBsZS5j -b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL66K2V2OQHcb2Ab7o -ucWqb3iOF1IGvc6lzC2XeqrqCvYF5HB9jK+cWHDmeGjJMoYI35S2fp+Wzh3Qek0Y -ilQBylUq91y2ZnqAmFu7gc83wWgWPHCkKPKTS5tcK5sQTbhBuaQBQs5hWCeNfZOy -AtAU2ysNde79DwSXfq/e8NLRvaKsS8etqiLyfIuDWfXHzIDgAgyyi49m67fYnLYx -y2W555Zh0vAnd4MQYh0SYQ64BgaUg59WLRYhsHN5r9D06JEur9uxMLmtiQy7UyL2 -xuw6u2y/+vcdTb9L9zaNEnITPl+3N23TG5KgBxLfKkHzNE7gS/w7ljS0ljNExuUO -UZutAgMBAAGjUzBRMB0GA1UdDgQWBBS7T7DuKDv1Hz1e693kY7gLXSu8PjAfBgNV -HSMEGDAWgBS7T7DuKDv1Hz1e693kY7gLXSu8PjAPBgNVHRMBAf8EBTADAQH/MA0G -CSqGSIb3DQEBCwUAA4IBAQC/4HmQwp7KjKF5FEnlHG5Chqob/yiPkwbMDV1yKA+i -ZMknQFgm8h0X4kLAbOiao71MPI5Zi5OQge6GoSXJJQYkesgdakPw6xkFfz9MsfCp -zMKm7sKIIGNaPMMqMJJ/cciCRIzXKl6gcOkZLlIFU1T2uE764Lc08yXIY7eXkVo1 -8w4Gv6nH7uaQiESa2wt3TWGISG9wdDkcVG01tTr4jzq77yWuC1Ela2UvQK7AIWbK -OB6Sby1bvjYv0kqjinu9AcSCHUHJ1sJaPD5DvAyKP7W01YedP4iqLxkTlIRjaikD -KlXA1yQW/ClmnHVg57SN1g1rvOJCcnHBnSbT7kGFqUol ------END CERTIFICATE----- -)"""; - -constexpr int kCurleAbortedByCallback = 42; -constexpr int kCurleOk = 0; - -class UnifiedCredentialsIntegrationTest - : public ::google::cloud::storage::testing::StorageIntegrationTest { - protected: - UnifiedCredentialsIntegrationTest() = default; - - void SetUp() override { - bucket_name_ = - GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_BUCKET_NAME").value_or(""); - project_id_ = GetEnv("GOOGLE_CLOUD_PROJECT").value_or(""); - service_account_ = - GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_SIGNING_SERVICE_ACCOUNT") - .value_or(""); - roots_pem_ = GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_ROOTS_PEM").value_or(""); - - ASSERT_THAT(bucket_name_, Not(IsEmpty())); - ASSERT_THAT(project_id_, Not(IsEmpty())); - ASSERT_THAT(service_account_, Not(IsEmpty())); - ASSERT_THAT(roots_pem_, Not(IsEmpty())); - - std::ifstream is(roots_pem_); - ca_certs_text_ = std::string{std::istreambuf_iterator{is}, {}}; - std::string begin_cert = "-----BEGIN CERTIFICATE-----"; - std::string end_cert = "-----END CERTIFICATE-----"; - size_t current = 0; - size_t begin; - while ((begin = ca_certs_text_.find(begin_cert, current)) != - std::string::npos) { - auto end = ca_certs_text_.find(end_cert, begin); - current = end + end_cert.length(); - ca_certs_.emplace_back(&ca_certs_text_[begin], current - begin); - } - ASSERT_THAT(ca_certs_, Not(IsEmpty())); - } - - static Client MakeTestClient(Options opts) { - return MakeIntegrationTestClient(std::move(opts)); - } - - std::string const& bucket_name() const { return bucket_name_; } - std::string const& project_id() const { return project_id_; } - std::string const& service_account() const { return service_account_; } - std::string roots_pem() const { return roots_pem_; } - std::string invalid_pem() const { return invalid_pem_.name(); } - std::string empty_file() const { return empty_file_.name(); } - - static Options TestOptions() { - return Options{}.set( - LimitedErrorCountRetryPolicy(3).clone()); - } - - Options EmptyTrustStoreOptions() { - return TestOptions() - // Use the trust store with no valid just an invalid CA certificate. - .set(invalid_pem()) - // Disable the default CAPath in libcurl, no effect on gRPC. - .set(empty_file()); - } - - Options CustomTrustStoreOptions() { - return TestOptions() - // Use the custom trust store with Google's root CA certificates. - .set(roots_pem()) - // Disable the default CAPath in libcurl, no effect on gRPC. - .set(empty_file()); - } - - Options ValidInMemoryTrustStoreOptions() { - return TestOptions() - // Populates in-memory trust store with Google's root CA certificates. - .set(ca_certs_); - } - - static Options InvalidInMemoryTrustStoreOptions() { - std::vector bogus_certs; - bogus_certs.emplace_back(kCACertificate); - - return TestOptions() - // Populates in-memory trust store with an invalid CA certificate. - .set(bogus_certs); - } - - Options ValidCAStoreFromSslCtxCallbackOptions() { - auto ssl_ctx_callback = [ca_certs = ca_certs_](void*, void* ssl_ctx, - void*) -> int { -#ifndef _WIN32 - struct BIOPtrCleanup { - int operator()(BIO* b) const { return BIO_free(b); } - }; - using BioPtr = std::unique_ptr; - struct X509InfoPtrCleanup { - void operator()(STACK_OF(X509_INFO) * i) const { - return sk_X509_INFO_pop_free(i, X509_INFO_free); - } - }; - using X509InfoPtr = - std::unique_ptr; - - X509_STORE* cert_store = - SSL_CTX_get_cert_store(static_cast(ssl_ctx)); - if (!cert_store) { - return kCurleAbortedByCallback; - } - - // Add each of the provided certs to the store. - for (auto const& cert : ca_certs) { - BioPtr buf{ - BIO_new_mem_buf(cert.data(), static_cast(cert.length()))}; - if (!buf) { - return kCurleAbortedByCallback; - } - X509InfoPtr info{ - PEM_X509_INFO_read_bio(buf.get(), nullptr, nullptr, nullptr)}; - if (!info) { - return kCurleAbortedByCallback; - } - - for (decltype(sk_X509_INFO_num(info.get())) i = 0; - i < sk_X509_INFO_num(info.get()); ++i) { - X509_INFO* value = sk_X509_INFO_value(info.get(), i); - if (value->x509) { - X509_STORE_add_cert(cert_store, value->x509); - } - if (value->crl) { - X509_STORE_add_crl(cert_store, value->crl); - } - } - } - return kCurleOk; -#else - return kCurleAbortedByCallback; -#endif - }; - - return TestOptions() - // Populates in-memory trust store with Google's root CA certificates. - .set(ssl_ctx_callback); - } - - static Options InvalidCAStoreFromSslCtxCallbackOptions() { - auto ssl_ctx_callback = [=](void*, void*, void*) -> int { - return kCurleAbortedByCallback; - }; - return TestOptions() - // Populates in-memory trust store with an invalid CA certificate. - .set(ssl_ctx_callback); - } - - void UseClient(Client client, std::string const& bucket_name, - std::string const& object_name, std::string const& payload) { - StatusOr meta = client.InsertObject( - bucket_name, object_name, payload, IfGenerationMatch(0)); - ASSERT_THAT(meta, IsOk()); - ScheduleForDelete(*meta); - - auto stream = client.ReadObject(bucket_name, object_name); - std::string actual(std::istreambuf_iterator{stream}, {}); - EXPECT_EQ(payload, actual); - } - - void ExpectInsertFailure(Client client, std::string const& bucket_name, - std::string const& object_name) { - auto meta = client.InsertObject(bucket_name, object_name, LoremIpsum(), - IfGenerationMatch(0)); - EXPECT_THAT(meta, Not(IsOk())); - if (meta) ScheduleForDelete(*meta); - } - - private: - std::string bucket_name_; - std::string project_id_; - std::string service_account_; - std::string roots_pem_; - std::string ca_certs_text_; - std::vector ca_certs_; - TempFile invalid_pem_{kCACertificate}; - TempFile empty_file_{std::string{}}; -}; - -TEST_F(UnifiedCredentialsIntegrationTest, GoogleDefaultCredentials) { - if (UsingEmulator()) GTEST_SKIP(); - auto client = MakeTestClient( - Options{}.set(MakeGoogleDefaultCredentials())); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, SAImpersonation) { - if (UsingEmulator()) GTEST_SKIP(); - - auto client = MakeTestClient(Options{}.set( - MakeImpersonateServiceAccountCredentials(MakeGoogleDefaultCredentials(), - service_account()))); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, SAImpersonationCustomTrustStore) { - if (UsingEmulator()) GTEST_SKIP(); - - auto client = - MakeTestClient(CustomTrustStoreOptions().set( - MakeImpersonateServiceAccountCredentials( - MakeGoogleDefaultCredentials(), service_account(), - CustomTrustStoreOptions()))); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, SAImpersonationEmptyTrustStore) { - if (UsingEmulator()) GTEST_SKIP(); - - auto client = - MakeTestClient(EmptyTrustStoreOptions().set( - MakeImpersonateServiceAccountCredentials( - MakeGoogleDefaultCredentials(), service_account(), - EmptyTrustStoreOptions()))); - - ASSERT_NO_FATAL_FAILURE( - ExpectInsertFailure(client, bucket_name(), MakeRandomObjectName())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, ServiceAccount) { - if (UsingEmulator()) GTEST_SKIP(); - auto keyfile = GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON"); - if (!keyfile.has_value()) GTEST_SKIP(); - - auto contents = [](std::string const& filename) { - std::ifstream is(filename); - return std::string{std::istreambuf_iterator{is}, {}}; - }(keyfile.value()); - - auto client = MakeTestClient(Options{}.set( - MakeServiceAccountCredentials(contents))); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, ServiceAccountCustomTrustStore) { - if (UsingEmulator()) GTEST_SKIP(); - auto keyfile = GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON"); - if (!keyfile.has_value()) GTEST_SKIP(); - - auto contents = [](std::string const& filename) { - std::ifstream is(filename); - return std::string{std::istreambuf_iterator{is}, {}}; - }(keyfile.value()); - - auto client = - MakeTestClient(CustomTrustStoreOptions().set( - MakeServiceAccountCredentials(contents, CustomTrustStoreOptions()))); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, ServiceAccountEmptyTrustStore) { - if (UsingEmulator()) GTEST_SKIP(); - auto keyfile = GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON"); - if (!keyfile.has_value()) GTEST_SKIP(); - - auto contents = [](std::string const& filename) { - std::ifstream is(filename); - return std::string{std::istreambuf_iterator{is}, {}}; - }(keyfile.value()); - - auto client = - MakeTestClient(EmptyTrustStoreOptions().set( - MakeServiceAccountCredentials(contents, EmptyTrustStoreOptions()))); - - ASSERT_NO_FATAL_FAILURE( - ExpectInsertFailure(client, bucket_name(), MakeRandomObjectName())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, AccessToken) { - if (UsingEmulator()) GTEST_SKIP(); - // First use the default credentials to obtain an access token, then use the - // access token to test the AccessTokenCredentials() function. In a real - // application one would fetch access tokens from something more interesting, - // like the IAM credentials service. This is just a reasonably easy way to get - // a working access token for the test. - auto default_credentials = oauth2::GoogleDefaultCredentials(); - ASSERT_THAT(default_credentials, IsOk()); - auto expiration = std::chrono::system_clock::now() + std::chrono::hours(1); - auto header = default_credentials.value()->AuthorizationHeader(); - ASSERT_THAT(header, IsOk()); - - auto constexpr kPrefix = "Authorization: Bearer "; - ASSERT_THAT(*header, StartsWith(kPrefix)); - auto token = header->substr(std::strlen(kPrefix)); - - auto client = MakeTestClient(Options{}.set( - MakeAccessTokenCredentials(token, expiration))); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, AccessTokenCustomTrustStore) { - if (UsingEmulator()) GTEST_SKIP(); - - // First use the default credentials to obtain an access token, then use the - // access token to test the AccessTokenCredentials() function. In a real - // application one would fetch access tokens from something more interesting, - // like the IAM credentials service. This is just a reasonably easy way to get - // a working access token for the test. - auto default_credentials = oauth2::GoogleDefaultCredentials(); - ASSERT_THAT(default_credentials, IsOk()); - auto expiration = std::chrono::system_clock::now() + std::chrono::hours(1); - auto header = default_credentials.value()->AuthorizationHeader(); - ASSERT_THAT(header, IsOk()); - - auto constexpr kPrefix = "Authorization: Bearer "; - ASSERT_THAT(*header, StartsWith(kPrefix)); - auto token = header->substr(std::strlen(kPrefix)); - - testing_util::ScopedEnvironment grpc_roots_pem( - "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH", absl::nullopt); - - auto client = - MakeTestClient(CustomTrustStoreOptions().set( - MakeAccessTokenCredentials(token, expiration))); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, AccessTokenEmptyTrustStore) { - if (UsingEmulator()) GTEST_SKIP(); - // First use the default credentials to obtain an access token, then use the - // access token to test the AccessTokenCredentials() function. In a real - // application one would fetch access tokens from something more interesting, - // like the IAM credentials service. This is just a reasonably easy way to get - // a working access token for the test. - auto default_credentials = oauth2::GoogleDefaultCredentials(); - ASSERT_THAT(default_credentials, IsOk()); - auto expiration = std::chrono::system_clock::now() + std::chrono::hours(1); - auto header = default_credentials.value()->AuthorizationHeader(); - ASSERT_THAT(header, IsOk()); - - auto constexpr kPrefix = "Authorization: Bearer "; - ASSERT_THAT(*header, StartsWith(kPrefix)); - auto token = header->substr(std::strlen(kPrefix)); - - auto client = MakeTestClient( - EmptyTrustStoreOptions() - .set( - MakeAccessTokenCredentials(token, expiration)) - .set(LimitedErrorCountRetryPolicy(2).clone())); - - EXPECT_NO_FATAL_FAILURE( - ExpectInsertFailure(client, bucket_name(), MakeRandomObjectName())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, ValidCAStoreInMemory) { - if (GetEnv("GOOGLE_CLOUD_CPP_STORAGE_GRPC_CONFIG") != "none") GTEST_SKIP(); - if (UsingEmulator()) GTEST_SKIP(); - auto keyfile = GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON"); - if (!keyfile.has_value()) GTEST_SKIP(); - - auto contents = [](std::string const& filename) { - std::ifstream is(filename); - return std::string{std::istreambuf_iterator{is}, {}}; - }(keyfile.value()); - - auto client = MakeTestClient( - ValidInMemoryTrustStoreOptions().set( - MakeServiceAccountCredentials(contents))); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, InvalidCAStoreInMemory) { - if (GetEnv("GOOGLE_CLOUD_CPP_STORAGE_GRPC_CONFIG") != "none") GTEST_SKIP(); - if (UsingEmulator()) GTEST_SKIP(); - auto keyfile = GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON"); - if (!keyfile.has_value()) GTEST_SKIP(); - - auto contents = [](std::string const& filename) { - std::ifstream is(filename); - return std::string{std::istreambuf_iterator{is}, {}}; - }(keyfile.value()); - - auto client = MakeTestClient( - InvalidInMemoryTrustStoreOptions().set( - MakeServiceAccountCredentials(contents))); - - EXPECT_NO_FATAL_FAILURE( - ExpectInsertFailure(client, bucket_name(), MakeRandomObjectName())); -} - -#ifndef _WIN32 -TEST_F(UnifiedCredentialsIntegrationTest, ValidCAStoreFromSslCtxCallback) { - if (GetEnv("GOOGLE_CLOUD_CPP_STORAGE_GRPC_CONFIG") != "none") GTEST_SKIP(); - if (UsingEmulator()) GTEST_SKIP(); - auto keyfile = GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON"); - if (!keyfile.has_value()) GTEST_SKIP(); - - auto contents = [](std::string const& filename) { - std::ifstream is(filename); - return std::string{std::istreambuf_iterator{is}, {}}; - }(keyfile.value()); - - auto client = MakeTestClient( - ValidCAStoreFromSslCtxCallbackOptions().set( - MakeServiceAccountCredentials(contents))); - - ASSERT_NO_FATAL_FAILURE( - UseClient(client, bucket_name(), MakeRandomObjectName(), LoremIpsum())); -} - -TEST_F(UnifiedCredentialsIntegrationTest, InvalidCAStoreFromSslCtxCallback) { - if (GetEnv("GOOGLE_CLOUD_CPP_STORAGE_GRPC_CONFIG") != "none") GTEST_SKIP(); - if (UsingEmulator()) GTEST_SKIP(); - auto keyfile = GetEnv("GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON"); - if (!keyfile.has_value()) GTEST_SKIP(); - - auto contents = [](std::string const& filename) { - std::ifstream is(filename); - return std::string{std::istreambuf_iterator{is}, {}}; - }(keyfile.value()); - - auto client = MakeTestClient( - InvalidCAStoreFromSslCtxCallbackOptions().set( - MakeServiceAccountCredentials(contents))); - - EXPECT_NO_FATAL_FAILURE( - ExpectInsertFailure(client, bucket_name(), MakeRandomObjectName())); -} -#endif - -} // namespace -GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END -} // namespace storage -} // namespace cloud -} // namespace google diff --git a/google/cloud/testing_util/credentials.h b/google/cloud/testing_util/credentials.h index 593d568d9fde5..d9bd69aebfdad 100644 --- a/google/cloud/testing_util/credentials.h +++ b/google/cloud/testing_util/credentials.h @@ -53,7 +53,7 @@ struct TestCredentialsVisitor : public internal::CredentialsVisitor { } void visit(internal::ServiceAccountConfig const& cfg) override { name = "ServiceAccountConfig"; - json_object = cfg.json_object(); + json_object = *cfg.json_object(); } void visit(internal::ExternalAccountConfig const& cfg) override { name = "ExternalAccountConfig";