Skip to content

Commit

Permalink
Revert "[call creds] revert grpc#37544 and grpc#37531 (grpc#37567)" (g…
Browse files Browse the repository at this point in the history
…rpc#37573)

The first commit is a pure revert of the revert, and the second one has the fix.

Closes grpc#37573

COPYBARA_INTEGRATE_REVIEW=grpc#37573 from markdroth:call_creds_roll_forward 2476329
PiperOrigin-RevId: 667672832
  • Loading branch information
markdroth authored and paulosjca committed Aug 27, 2024
1 parent 1ab668f commit a56f884
Show file tree
Hide file tree
Showing 17 changed files with 1,193 additions and 110 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions build_autogenerated.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/trace_flags.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 49 additions & 1 deletion src/core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4335,14 +4335,17 @@ grpc_cc_library(
deps = [
"arena_promise",
"context",
"default_event_engine",
"metadata",
"poll",
"pollset_set",
"ref_counted",
"time",
"useful",
"//:backoff",
"//:gpr",
"//:grpc_security_base",
"//:grpc_trace",
"//:httpcli",
"//:iomgr",
"//:orphanable",
Expand All @@ -4351,6 +4354,52 @@ grpc_cc_library(
],
)

grpc_cc_library(
name = "gcp_service_account_identity_credentials",
srcs = [
"lib/security/credentials/gcp_service_account_identity/gcp_service_account_identity_credentials.cc",
],
hdrs = [
"lib/security/credentials/gcp_service_account_identity/gcp_service_account_identity_credentials.h",
],
external_deps = [
"absl/functional:any_invocable",
"absl/status",
"absl/status:statusor",
"absl/strings",
],
language = "c++",
deps = [
"activity",
"arena_promise",
"closure",
"error",
"json",
"json_args",
"json_object_loader",
"json_reader",
"metadata",
"pollset_set",
"ref_counted",
"slice",
"status_conversion",
"status_helper",
"time",
"token_fetcher_credentials",
"unique_type_name",
"//:gpr",
"//:grpc_base",
"//:grpc_core_credentials_header",
"//:grpc_security_base",
"//:httpcli",
"//:iomgr",
"//:orphanable",
"//:promise",
"//:ref_counted_ptr",
"//:uri_parser",
],
)

grpc_cc_library(
name = "grpc_oauth2_credentials",
srcs = [
Expand Down Expand Up @@ -4436,7 +4485,6 @@ grpc_cc_library(
language = "c++",
deps = [
"closure",
"default_event_engine",
"env",
"error",
"error_utils",
Expand Down
2 changes: 2 additions & 0 deletions src/core/lib/debug/trace_flags.cc

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/core/lib/debug/trace_flags.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/core/lib/debug/trace_flags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ timer:
timer_check:
default: false
description: more detailed trace of timer logic in grpc internals.
token_fetcher_credentials:
default: false
description: Token fetcher call credentials framework, used for (e.g.) oauth2 token fetcher credentials.
tsi:
default: false
description: TSI transport security.
Expand Down
2 changes: 1 addition & 1 deletion src/core/lib/promise/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION auto CheckDelayed(Promise promise) {
delayed = true;
return Pending{};
}
return std::make_tuple(r.value(), delayed);
return std::make_tuple(std::move(r.value()), delayed);
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
#include <grpc/support/port_platform.h>
#include <grpc/support/string_util.h>

#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/security/credentials/credentials.h"
#include "src/core/lib/security/credentials/external/aws_external_account_credentials.h"
Expand Down Expand Up @@ -591,10 +590,7 @@ ExternalAccountCredentials::Create(
ExternalAccountCredentials::ExternalAccountCredentials(
Options options, std::vector<std::string> scopes,
std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine)
: event_engine_(
event_engine == nullptr
? grpc_event_engine::experimental::GetDefaultEventEngine()
: std::move(event_engine)),
: TokenFetcherCredentials(std::move(event_engine)),
options_(std::move(options)) {
if (scopes.empty()) {
scopes.push_back(GOOGLE_CLOUD_PLATFORM_DEFAULT_SCOPE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,6 @@ class ExternalAccountCredentials : public TokenFetcherCredentials {

absl::string_view audience() const { return options_.audience; }

grpc_event_engine::experimental::EventEngine& event_engine() const {
return *event_engine_;
}

private:
OrphanablePtr<FetchRequest> FetchToken(
Timestamp deadline,
Expand All @@ -204,7 +200,6 @@ class ExternalAccountCredentials : public TokenFetcherCredentials {
Timestamp deadline,
absl::AnyInvocable<void(absl::StatusOr<std::string>)> on_done) = 0;

std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine_;
Options options_;
std::vector<std::string> scopes_;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#include <grpc/support/json.h>
#include <grpc/support/port_platform.h>

#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/gprpp/load_file.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_internal.h"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
//
// Copyright 2024 gRPC authors.
//
// 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
//
// http://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 "src/core/lib/security/credentials/gcp_service_account_identity/gcp_service_account_identity_credentials.h"

#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"

#include <grpc/support/time.h>

#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/transport/metadata.h"
#include "src/core/lib/transport/status_conversion.h"
#include "src/core/lib/uri/uri_parser.h"
#include "src/core/util/json/json.h"
#include "src/core/util/json/json_args.h"
#include "src/core/util/json/json_object_loader.h"
#include "src/core/util/json/json_reader.h"

namespace grpc_core {

//
// JwtTokenFetcherCallCredentials
//

// State held for a pending HTTP request.
class JwtTokenFetcherCallCredentials::HttpFetchRequest final
: public TokenFetcherCredentials::FetchRequest {
public:
HttpFetchRequest(
JwtTokenFetcherCallCredentials* creds, Timestamp deadline,
absl::AnyInvocable<
void(absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>)>
on_done)
: on_done_(std::move(on_done)) {
GRPC_CLOSURE_INIT(&on_http_response_, OnHttpResponse, this, nullptr);
Ref().release(); // Ref held by HTTP request callback.
http_request_ = creds->StartHttpRequest(creds->pollent(), deadline,
&response_, &on_http_response_);
}

~HttpFetchRequest() override { grpc_http_response_destroy(&response_); }

void Orphan() override {
http_request_.reset();
Unref();
}

private:
static void OnHttpResponse(void* arg, grpc_error_handle error) {
RefCountedPtr<HttpFetchRequest> self(static_cast<HttpFetchRequest*>(arg));
if (!error.ok()) {
// TODO(roth): It shouldn't be necessary to explicitly set the
// status to UNAVAILABLE here. Once the HTTP client code is
// migrated to stop using legacy grpc_error APIs to create
// statuses, we should be able to just propagate the status as-is.
self->on_done_(absl::UnavailableError(StatusToString(error)));
return;
}
if (self->response_.status != 200) {
grpc_status_code status_code =
grpc_http2_status_to_grpc_status(self->response_.status);
if (status_code != GRPC_STATUS_UNAVAILABLE) {
status_code = GRPC_STATUS_UNAUTHENTICATED;
}
self->on_done_(absl::Status(static_cast<absl::StatusCode>(status_code),
absl::StrCat("JWT fetch failed with status ",
self->response_.status)));
return;
}
absl::string_view body(self->response_.body, self->response_.body_length);
// Parse JWT token based on https://datatracker.ietf.org/doc/html/rfc7519.
// We don't do full verification here, just enough to extract the
// expiration time.
// First, split the 3 '.'-delimited parts.
std::vector<absl::string_view> parts = absl::StrSplit(body, '.');
if (parts.size() != 3) {
self->on_done_(absl::UnauthenticatedError("error parsing JWT token"));
return;
}
// Base64-decode the payload.
std::string payload;
if (!absl::WebSafeBase64Unescape(parts[1], &payload)) {
self->on_done_(absl::UnauthenticatedError("error parsing JWT token"));
return;
}
// Parse as JSON.
auto json = JsonParse(payload);
if (!json.ok()) {
self->on_done_(absl::UnauthenticatedError("error parsing JWT token"));
return;
}
// Extract "exp" field.
struct ParsedPayload {
uint64_t exp = 0;

static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
static const auto kJsonLoader = JsonObjectLoader<ParsedPayload>()
.Field("exp", &ParsedPayload::exp)
.Finish();
return kJsonLoader;
}
};
auto parsed_payload = LoadFromJson<ParsedPayload>(*json, JsonArgs(), "");
if (!parsed_payload.ok()) {
self->on_done_(absl::UnauthenticatedError("error parsing JWT token"));
return;
}
gpr_timespec ts = gpr_time_0(GPR_CLOCK_REALTIME);
ts.tv_sec = parsed_payload->exp;
Timestamp expiration_time = Timestamp::FromTimespecRoundDown(ts);
// Return token object.
self->on_done_(MakeRefCounted<Token>(
Slice::FromCopiedString(absl::StrCat("Bearer ", body)),
expiration_time));
}

OrphanablePtr<HttpRequest> http_request_;
grpc_closure on_http_response_;
grpc_http_response response_;
absl::AnyInvocable<void(
absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>)>
on_done_;
};

OrphanablePtr<TokenFetcherCredentials::FetchRequest>
JwtTokenFetcherCallCredentials::FetchToken(
Timestamp deadline,
absl::AnyInvocable<
void(absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>)>
on_done) {
return MakeOrphanable<HttpFetchRequest>(this, deadline, std::move(on_done));
}

//
// GcpServiceAccountIdentityCallCredentials
//

std::string GcpServiceAccountIdentityCallCredentials::debug_string() {
return absl::StrCat("GcpServiceAccountIdentityCallCredentials(", audience_,
")");
}

UniqueTypeName GcpServiceAccountIdentityCallCredentials::type() const {
static UniqueTypeName::Factory kFactory("GcpServiceAccountIdentity");
return kFactory.Create();
}

OrphanablePtr<HttpRequest>
GcpServiceAccountIdentityCallCredentials::StartHttpRequest(
grpc_polling_entity* pollent, Timestamp deadline,
grpc_http_response* response, grpc_closure* on_complete) {
grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
const_cast<char*>("Google")};
grpc_http_request request;
memset(&request, 0, sizeof(grpc_http_request));
request.hdr_count = 1;
request.hdrs = &header;
// TODO(ctiller): Carry the memory quota in ctx and share it with the host
// channel. This would allow us to cancel an authentication query when under
// extreme memory pressure.
auto uri = URI::Create(
"http", "metadata.google.internal.",
"/computeMetadata/v1/instance/service-accounts/default/identity",
{{"audience", audience_}}, /*fragment=*/"");
CHECK_OK(uri); // params are hardcoded
auto http_request =
HttpRequest::Get(std::move(*uri), /*args=*/nullptr, pollent, &request,
deadline, on_complete, response,
RefCountedPtr<grpc_channel_credentials>(
grpc_insecure_credentials_create()));
http_request->Start();
return http_request;
}

} // namespace grpc_core
Loading

0 comments on commit a56f884

Please sign in to comment.