Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions google/cloud/google_cloud_cpp_rest_internal.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ google_cloud_cpp_rest_internal_hdrs = [
"internal/oauth2_anonymous_credentials.h",
"internal/oauth2_api_key_credentials.h",
"internal/oauth2_authorized_user_credentials.h",
"internal/oauth2_background_credentials.h",
"internal/oauth2_cached_credentials.h",
"internal/oauth2_compute_engine_credentials.h",
"internal/oauth2_credential_constants.h",
Expand Down
1 change: 1 addition & 0 deletions google/cloud/google_cloud_cpp_rest_internal.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ add_library(
internal/oauth2_api_key_credentials.h
internal/oauth2_authorized_user_credentials.cc
internal/oauth2_authorized_user_credentials.h
internal/oauth2_background_credentials.h
internal/oauth2_cached_credentials.cc
internal/oauth2_cached_credentials.h
internal/oauth2_compute_engine_credentials.cc
Expand Down
94 changes: 94 additions & 0 deletions google/cloud/internal/oauth2_background_credentials.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2026 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_INTERNAL_OAUTH2_BACKGROUND_CREDENTIALS_H
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_BACKGROUND_CREDENTIALS_H

#include "google/cloud/internal/http_header.h"
#include "google/cloud/internal/oauth2_credentials.h"
#include "google/cloud/internal/rest_pure_background_threads_impl.h"
#include "google/cloud/options.h"
#include "google/cloud/status_or.h"
#include "google/cloud/version.h"
#include "absl/types/optional.h"
#include <chrono>
#include <memory>
#include <string>

namespace google {
namespace cloud {
namespace oauth2_internal {
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN

// This decorator exists to manage the lifetime of any BackgroundThreads that
// may be needed by a child Credentials it decorates. This separation is
// necessary to help avoid a thread in the pool attempting to destroy the
// BackgroundThreads object, which would result in a thread attempting to join
// itself.
class BackgroundCredentials : public Credentials {
Comment thread
alvarowolfx marked this conversation as resolved.
public:
BackgroundCredentials(
std::unique_ptr<rest_internal::RestPureBackgroundThreads> background,
std::shared_ptr<Credentials> child)
: background_(std::move(background)), child_(std::move(child)) {}

StatusOr<std::vector<std::uint8_t>> SignBlob(
absl::optional<std::string> const& signing_service_account,
std::string const& string_to_sign) const override {
return child_->SignBlob(signing_service_account, string_to_sign);
}
std::string AccountEmail() const override { return child_->AccountEmail(); }
std::string KeyId() const override { return child_->KeyId(); }
StatusOr<std::string> universe_domain() const override {
return child_->universe_domain();
}
StatusOr<std::string> universe_domain(
google::cloud::Options const& options) const override {
return child_->universe_domain(options);
}
StatusOr<std::string> project_id() const override {
return child_->project_id();
}
StatusOr<std::string> project_id(Options const& options) const override {
return child_->project_id(options);
}
StatusOr<rest_internal::HttpHeader> Authorization(
std::chrono::system_clock::time_point tp) override {
return child_->Authorization(tp);
}
StatusOr<AccessToken> GetToken(
std::chrono::system_clock::time_point tp) override {
return child_->GetToken(tp);
}
Credentials::AllowedLocationsRequestType AllowedLocationsRequest()
const override {
return child_->AllowedLocationsRequest();
}
StatusOr<rest_internal::HttpHeader> AllowedLocations(
std::chrono::system_clock::time_point tp,
std::string_view endpoint) override {
return child_->AllowedLocations(tp, endpoint);
}

private:
std::unique_ptr<rest_internal::RestPureBackgroundThreads> background_;
std::shared_ptr<Credentials> child_;
};

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
} // namespace oauth2_internal
} // namespace cloud
} // namespace google

#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_BACKGROUND_CREDENTIALS_H
10 changes: 8 additions & 2 deletions google/cloud/internal/oauth2_decorate_credentials.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "google/cloud/internal/oauth2_decorate_credentials.h"
#include "google/cloud/common_options.h"
#include "google/cloud/internal/oauth2_background_credentials.h"
#include "google/cloud/internal/oauth2_cached_credentials.h"
#include "google/cloud/internal/oauth2_logging_credentials.h"
#include "google/cloud/internal/oauth2_regional_access_boundary_token_manager.h"
Expand Down Expand Up @@ -54,8 +55,13 @@ std::shared_ptr<oauth2_internal::Credentials> WithCaching(
std::shared_ptr<oauth2_internal::Credentials> WithRegionalAccessBoundary(
std::shared_ptr<oauth2_internal::Credentials> impl,
HttpClientFactory client_factory, Options options) {
return RegionalAccessBoundaryTokenManager::Create(
std::move(impl), std::move(client_factory), std::move(options));
auto background = std::make_unique<
rest_internal::AutomaticallyCreatedRestPureBackgroundThreads>();
auto rab = RegionalAccessBoundaryTokenManager::Create(
std::move(impl), std::move(client_factory), background->cq(),
std::move(options));
return std::make_shared<BackgroundCredentials>(std::move(background),
std::move(rab));
}

GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,46 +82,46 @@ class RegionalAccessBoundaryTokenManager::RefreshTokenLimitedTimeRetryPolicy
google::cloud::internal::LimitedTimeRetryPolicy<RetryTraits> impl_;
};

RegionalAccessBoundaryTokenManager::~RegionalAccessBoundaryTokenManager() {
if (failed_lookup_cooldown_.valid()) failed_lookup_cooldown_.cancel();
}

std::shared_ptr<RegionalAccessBoundaryTokenManager>
RegionalAccessBoundaryTokenManager::Create(std::shared_ptr<Credentials> child,
HttpClientFactory client_factory,
Options options) {
RegionalAccessBoundaryTokenManager::Create(
std::shared_ptr<Credentials> child, HttpClientFactory client_factory,
rest_internal::RestPureCompletionQueue cq, Options options) {
auto iam_stub = MakeMinimalIamCredentialsRestStub(child, options,
std::move(client_factory));
return std::shared_ptr<RegionalAccessBoundaryTokenManager>(
new RegionalAccessBoundaryTokenManager(
std::move(child), std::move(iam_stub),
std::make_unique<
rest_internal::AutomaticallyCreatedRestPureBackgroundThreads>(),
std::move(child), std::move(iam_stub), std::move(cq),
std::move(options), FailedLookupBackoffPolicy,
std::make_shared<Clock>()));
}

std::shared_ptr<RegionalAccessBoundaryTokenManager>
RegionalAccessBoundaryTokenManager::Create(
std::shared_ptr<Credentials> child,
std::shared_ptr<MinimalIamCredentialsRest> iam_stub, Options options) {
std::shared_ptr<MinimalIamCredentialsRest> iam_stub,
rest_internal::RestPureCompletionQueue cq, Options options) {
return std::shared_ptr<RegionalAccessBoundaryTokenManager>(
new RegionalAccessBoundaryTokenManager(
std::move(child), std::move(iam_stub),
std::make_unique<
rest_internal::AutomaticallyCreatedRestPureBackgroundThreads>(),
std::move(child), std::move(iam_stub), std::move(cq),
std::move(options), FailedLookupBackoffPolicy,
std::make_shared<Clock>()));
}

std::shared_ptr<RegionalAccessBoundaryTokenManager>
RegionalAccessBoundaryTokenManager::Create(
std::shared_ptr<Credentials> child,
std::shared_ptr<MinimalIamCredentialsRest> iam_stub, Options options,
std::shared_ptr<MinimalIamCredentialsRest> iam_stub,
rest_internal::RestPureCompletionQueue cq, Options options,
std::function<std::unique_ptr<BackoffPolicy>()>
failed_lookup_backoff_policy_fn,
std::shared_ptr<Clock> clock, AllowedLocationsResponse allowed_locations) {
return std::shared_ptr<RegionalAccessBoundaryTokenManager>(
new RegionalAccessBoundaryTokenManager(
std::move(child), std::move(iam_stub),
std::make_unique<
rest_internal::AutomaticallyCreatedRestPureBackgroundThreads>(),
std::move(child), std::move(iam_stub), std::move(cq),
std::move(options), std::move(failed_lookup_backoff_policy_fn),
std::move(clock), std::move(allowed_locations)));
}
Expand All @@ -136,13 +136,12 @@ RegionalAccessBoundaryTokenManager::FailedLookupBackoffPolicy() {
RegionalAccessBoundaryTokenManager::RegionalAccessBoundaryTokenManager(
std::shared_ptr<Credentials> child,
std::shared_ptr<MinimalIamCredentialsRest> iam_stub,
std::unique_ptr<rest_internal::RestPureBackgroundThreads> background,
Options options,
rest_internal::RestPureCompletionQueue cq, Options options,
std::function<std::unique_ptr<BackoffPolicy>()>
failed_lookup_backoff_policy_fn,
std::shared_ptr<Clock> clock, AllowedLocationsResponse allowed_locations)
: child_(std::move(child)),
background_(std::move(background)),
cq_(std::move(cq)),
options_(std::move(options)),
clock_(std::move(clock)),
retry_policy_(std::make_unique<RefreshTokenLimitedTimeRetryPolicy>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,16 @@ class RegionalAccessBoundaryTokenManager
static bool IsPermanentFailure(Status const&);
};

~RegionalAccessBoundaryTokenManager() override;

static std::shared_ptr<RegionalAccessBoundaryTokenManager> Create(
std::shared_ptr<Credentials> child, HttpClientFactory client_factory,
Options options);
rest_internal::RestPureCompletionQueue cq, Options options);

static std::shared_ptr<RegionalAccessBoundaryTokenManager> Create(
std::shared_ptr<Credentials> child,
std::shared_ptr<MinimalIamCredentialsRest> iam_stub, Options options);
std::shared_ptr<MinimalIamCredentialsRest> iam_stub,
rest_internal::RestPureCompletionQueue cq, Options options);

static std::chrono::seconds TtlGracePeriod();
static std::chrono::seconds TokenTtl();
Expand Down Expand Up @@ -126,7 +129,8 @@ class RegionalAccessBoundaryTokenManager
// Used for testing.
static std::shared_ptr<RegionalAccessBoundaryTokenManager> Create(
std::shared_ptr<Credentials> child,
std::shared_ptr<MinimalIamCredentialsRest> iam_stub, Options options,
std::shared_ptr<MinimalIamCredentialsRest> iam_stub,
rest_internal::RestPureCompletionQueue cq, Options options,
std::function<std::unique_ptr<BackoffPolicy>()>
failed_lookup_backoff_policy_fn,
std::shared_ptr<Clock> clock = std::make_shared<Clock>(),
Expand Down Expand Up @@ -159,8 +163,7 @@ class RegionalAccessBoundaryTokenManager
RegionalAccessBoundaryTokenManager(
std::shared_ptr<Credentials> child,
std::shared_ptr<MinimalIamCredentialsRest> iam_stub,
std::unique_ptr<rest_internal::RestPureBackgroundThreads> background,
Options options,
rest_internal::RestPureCompletionQueue cq, Options options,
std::function<std::unique_ptr<BackoffPolicy>()>
failed_lookup_backoff_policy_fn,
std::shared_ptr<Clock> clock = std::make_shared<Clock>(),
Expand Down Expand Up @@ -216,21 +219,20 @@ class RegionalAccessBoundaryTokenManager
self->failed_lookup_backoff_policy_ =
self->failed_lookup_backoff_policy_fn_();
}
self->failed_lookup_cooldown_ =
self->background_->cq().MakeRelativeTimer(
self->failed_lookup_backoff_policy_->OnCompletion());
self->failed_lookup_cooldown_ = self->cq_.MakeRelativeTimer(
self->failed_lookup_backoff_policy_->OnCompletion());
p.set_value(allowed_locations.status());
}
self->refresh_in_progress_ = false;
};

refresh_in_progress_ = true;
background_->cq().RunAsync(std::move(pending_refresh_fn));
cq_.RunAsync(std::move(pending_refresh_fn));
}

mutable std::mutex mu_;
std::shared_ptr<Credentials> child_;
std::unique_ptr<rest_internal::RestPureBackgroundThreads> background_;
rest_internal::RestPureCompletionQueue cq_;
Options options_;
std::shared_ptr<Clock> clock_;
std::unique_ptr<RefreshTokenRetryPolicy> retry_policy_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,21 @@ class RegionalAccessBoundaryTokenManagerTest : public ::testing::Test {
RegionalAccessBoundaryTokenManagerTest()
: mock_credentials_(std::make_shared<MockCredentials>()),
mock_iam_stub_(std::make_shared<MockMinimalIamCredentialsRest>()),
fake_clock_(std::make_shared<testing_util::FakeSystemClock>()) {}
fake_clock_(std::make_shared<testing_util::FakeSystemClock>()),
background_(std::make_unique<
rest_internal::
AutomaticallyCreatedRestPureBackgroundThreads>()) {}

std::shared_ptr<MockCredentials> mock_credentials_;
std::shared_ptr<MockMinimalIamCredentialsRest> mock_iam_stub_;
std::shared_ptr<testing_util::FakeSystemClock> fake_clock_;
std::unique_ptr<rest_internal::RestPureBackgroundThreads> background_;
};

TEST_F(RegionalAccessBoundaryTokenManagerTest,
GetAllowedLocationsHeaderNonApplicableEndpoints) {
auto manager = RegionalAccessBoundaryTokenManager::Create(mock_credentials_,
mock_iam_stub_, {});
auto manager = RegionalAccessBoundaryTokenManager::Create(
mock_credentials_, mock_iam_stub_, background_->cq(), {});

ServiceAccountAllowedLocationsRequest request;
auto header = manager->GetAllowedLocationsHeader(
Expand Down Expand Up @@ -151,8 +155,8 @@ TEST_F(RegionalAccessBoundaryTokenManagerTest,
allowed_locations.encoded_locations = "encoded-location";

auto manager = RegionalAccessBoundaryTokenManager::Create(
mock_credentials_, mock_iam_stub_, {}, backoff_fn.AsStdFunction(),
fake_clock_, allowed_locations);
mock_credentials_, mock_iam_stub_, background_->cq(), {},
backoff_fn.AsStdFunction(), fake_clock_, allowed_locations);

fake_clock_->AdvanceTime(std::chrono::seconds(1));

Expand Down Expand Up @@ -211,8 +215,8 @@ TEST_F(RegionalAccessBoundaryTokenManagerTest,
allowed_locations.encoded_locations = "0x0";

auto manager = RegionalAccessBoundaryTokenManager::Create(
mock_credentials_, mock_iam_stub_, {}, backoff_fn.AsStdFunction(),
fake_clock_, allowed_locations);
mock_credentials_, mock_iam_stub_, background_->cq(), {},
backoff_fn.AsStdFunction(), fake_clock_, allowed_locations);

fake_clock_->AdvanceTime(std::chrono::seconds(1));

Expand All @@ -239,8 +243,8 @@ TEST_F(RegionalAccessBoundaryTokenManagerTest,
return response;
});

auto manager = RegionalAccessBoundaryTokenManager::Create(mock_credentials_,
mock_iam_stub_, {});
auto manager = RegionalAccessBoundaryTokenManager::Create(
mock_credentials_, mock_iam_stub_, background_->cq(), {});

auto header = manager->AllowedLocations(std::chrono::system_clock::now(),
"service.googleapis.com");
Expand Down Expand Up @@ -286,7 +290,8 @@ TEST_F(RegionalAccessBoundaryTokenManagerTest,
});

auto manager = RegionalAccessBoundaryTokenManager::Create(
mock_credentials_, mock_iam_stub_, {}, backoff_fn.AsStdFunction());
mock_credentials_, mock_iam_stub_, background_->cq(), {},
backoff_fn.AsStdFunction());

auto header = manager->AllowedLocations(std::chrono::system_clock::now(),
"service.googleapis.com");
Expand Down Expand Up @@ -341,8 +346,8 @@ TEST_F(RegionalAccessBoundaryTokenManagerTest,
AllowedLocations(A<WorkloadIdentityAllowedLocationsRequest const&>()))
.Times(0);

auto manager = RegionalAccessBoundaryTokenManager::Create(mock_credentials_,
mock_iam_stub_, {});
auto manager = RegionalAccessBoundaryTokenManager::Create(
mock_credentials_, mock_iam_stub_, background_->cq(), {});
auto header = manager->AllowedLocations(std::chrono::system_clock::now(),
"service.googleapis.com");
EXPECT_THAT(header, IsOkAndHolds(IsEmpty()));
Expand Down Expand Up @@ -388,8 +393,8 @@ TEST_F(RegionalAccessBoundaryTokenManagerTest, DecoratorMethodPassThrough) {
.WillOnce(
Return(Credentials::AllowedLocationsRequestType{std::monostate{}}));

auto manager = RegionalAccessBoundaryTokenManager::Create(mock_credentials_,
mock_iam_stub_, {});
auto manager = RegionalAccessBoundaryTokenManager::Create(
mock_credentials_, mock_iam_stub_, background_->cq(), {});

(void)manager->SignBlob("sa", "string");
(void)manager->AccountEmail();
Expand Down
Loading
Loading