Adding support for STS Token Exchange Creds in core:
- IETF specification is available here: https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16
This commit is contained in:
parent
e6b29b3693
commit
189c2c8c30
1
grpc.def
1
grpc.def
|
|
@ -111,6 +111,7 @@ EXPORTS
|
|||
grpc_google_refresh_token_credentials_create
|
||||
grpc_access_token_credentials_create
|
||||
grpc_google_iam_credentials_create
|
||||
grpc_sts_credentials_create
|
||||
grpc_metadata_credentials_create_from_plugin
|
||||
grpc_secure_channel_create
|
||||
grpc_server_credentials_release
|
||||
|
|
|
|||
|
|
@ -328,6 +328,31 @@ GRPCAPI grpc_call_credentials* grpc_google_iam_credentials_create(
|
|||
const char* authorization_token, const char* authority_selector,
|
||||
void* reserved);
|
||||
|
||||
/** Options for creating STS Oauth Token Exchange credentials following the IETF
|
||||
draft https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16.
|
||||
Optional fields may be set to NULL. It is the responsibility of the caller to
|
||||
ensure that the subject and actor tokens are refreshed on disk at the
|
||||
specified paths. This API is used for experimental purposes for now and may
|
||||
change in the future. */
|
||||
typedef struct {
|
||||
const char* sts_endpoint_url; /* Required. */
|
||||
const char* resource; /* Optional. */
|
||||
const char* audience; /* Optional. */
|
||||
const char* scope; /* Optional. */
|
||||
const char* requested_token_type; /* Optional. */
|
||||
const char* subject_token_path; /* Required. */
|
||||
const char* subject_token_type; /* Required. */
|
||||
const char* actor_token_path; /* Optional. */
|
||||
const char* actor_token_type; /* Optional. */
|
||||
} grpc_sts_credentials_options;
|
||||
|
||||
/** Creates an STS credentials following the STS Token Exchanged specifed in the
|
||||
IETF draft https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-16.
|
||||
This API is used for experimental purposes for now and may change in the
|
||||
future. */
|
||||
GRPCAPI grpc_call_credentials* grpc_sts_credentials_create(
|
||||
const grpc_sts_credentials_options* options, void* reserved);
|
||||
|
||||
/** Callback function to be called by the metadata credentials plugin
|
||||
implementation when the metadata is ready.
|
||||
- user_data is the opaque pointer that was passed in the get_metadata method
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/security/credentials/jwt/json_token.h"
|
||||
|
||||
#include <string.h>
|
||||
|
|
@ -69,6 +70,7 @@ grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json* json) {
|
|||
BIO* bio = nullptr;
|
||||
const char* prop_value;
|
||||
int success = 0;
|
||||
grpc_error* error = GRPC_ERROR_NONE;
|
||||
|
||||
memset(&result, 0, sizeof(grpc_auth_json_key));
|
||||
result.type = GRPC_AUTH_JSON_TYPE_INVALID;
|
||||
|
|
@ -77,7 +79,8 @@ grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json* json) {
|
|||
goto end;
|
||||
}
|
||||
|
||||
prop_value = grpc_json_get_string_property(json, "type");
|
||||
prop_value = grpc_json_get_string_property(json, "type", &error);
|
||||
GRPC_LOG_IF_ERROR("JSON key parsing", error);
|
||||
if (prop_value == nullptr ||
|
||||
strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT)) {
|
||||
goto end;
|
||||
|
|
@ -92,7 +95,8 @@ grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json* json) {
|
|||
goto end;
|
||||
}
|
||||
|
||||
prop_value = grpc_json_get_string_property(json, "private_key");
|
||||
prop_value = grpc_json_get_string_property(json, "private_key", &error);
|
||||
GRPC_LOG_IF_ERROR("JSON key parsing", error);
|
||||
if (prop_value == nullptr) {
|
||||
goto end;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,23 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/security/util/json_util.h"
|
||||
#include "src/core/lib/surface/api_trace.h"
|
||||
|
||||
#include <grpc/grpc_security.h>
|
||||
#include <grpc/impl/codegen/slice.h>
|
||||
#include <grpc/slice.h>
|
||||
#include <grpc/support/alloc.h>
|
||||
#include <grpc/support/log.h>
|
||||
#include <grpc/support/string_util.h>
|
||||
|
||||
#include "src/core/lib/gpr/string.h"
|
||||
#include "src/core/lib/gprpp/inlined_vector.h"
|
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/iomgr/load_file.h"
|
||||
#include "src/core/lib/security/util/json_util.h"
|
||||
#include "src/core/lib/slice/slice_internal.h"
|
||||
#include "src/core/lib/surface/api_trace.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
//
|
||||
// Auth Refresh Token.
|
||||
//
|
||||
|
|
@ -45,6 +54,7 @@ grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
|
|||
grpc_auth_refresh_token result;
|
||||
const char* prop_value;
|
||||
int success = 0;
|
||||
grpc_error* error = GRPC_ERROR_NONE;
|
||||
|
||||
memset(&result, 0, sizeof(grpc_auth_refresh_token));
|
||||
result.type = GRPC_AUTH_JSON_TYPE_INVALID;
|
||||
|
|
@ -53,7 +63,8 @@ grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
|
|||
goto end;
|
||||
}
|
||||
|
||||
prop_value = grpc_json_get_string_property(json, "type");
|
||||
prop_value = grpc_json_get_string_property(json, "type", &error);
|
||||
GRPC_LOG_IF_ERROR("Parsing refresh token", error);
|
||||
if (prop_value == nullptr ||
|
||||
strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) {
|
||||
goto end;
|
||||
|
|
@ -218,8 +229,10 @@ void grpc_oauth2_token_fetcher_credentials::on_http_response(
|
|||
grpc_mdelem access_token_md = GRPC_MDNULL;
|
||||
grpc_millis token_lifetime;
|
||||
grpc_credentials_status status =
|
||||
grpc_oauth2_token_fetcher_credentials_parse_server_response(
|
||||
&r->response, &access_token_md, &token_lifetime);
|
||||
error == GRPC_ERROR_NONE
|
||||
? grpc_oauth2_token_fetcher_credentials_parse_server_response(
|
||||
&r->response, &access_token_md, &token_lifetime)
|
||||
: GRPC_CREDENTIALS_ERROR;
|
||||
// Update cache and grab list of pending requests.
|
||||
gpr_mu_lock(&mu_);
|
||||
token_fetch_pending_ = false;
|
||||
|
|
@ -234,14 +247,15 @@ void grpc_oauth2_token_fetcher_credentials::on_http_response(
|
|||
gpr_mu_unlock(&mu_);
|
||||
// Invoke callbacks for all pending requests.
|
||||
while (pending_request != nullptr) {
|
||||
grpc_error* new_error = GRPC_ERROR_NONE;
|
||||
if (status == GRPC_CREDENTIALS_OK) {
|
||||
grpc_credentials_mdelem_array_add(pending_request->md_array,
|
||||
access_token_md);
|
||||
} else {
|
||||
error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
|
||||
new_error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
|
||||
"Error occurred when fetching oauth2 token.", &error, 1);
|
||||
}
|
||||
GRPC_CLOSURE_SCHED(pending_request->on_request_metadata, error);
|
||||
GRPC_CLOSURE_SCHED(pending_request->on_request_metadata, new_error);
|
||||
grpc_polling_entity_del_from_pollset_set(
|
||||
pending_request->pollent, grpc_polling_entity_pollset_set(&pollent_));
|
||||
grpc_oauth2_pending_get_request_metadata* prev = pending_request;
|
||||
|
|
@ -356,7 +370,8 @@ class grpc_compute_engine_token_fetcher_credentials
|
|||
grpc_polling_entity* pollent,
|
||||
grpc_iomgr_cb_func response_cb,
|
||||
grpc_millis deadline) override {
|
||||
grpc_http_header header = {(char*)"Metadata-Flavor", (char*)"Google"};
|
||||
grpc_http_header header = {const_cast<char*>("Metadata-Flavor"),
|
||||
const_cast<char*>("Google")};
|
||||
grpc_httpcli_request request;
|
||||
memset(&request, 0, sizeof(grpc_httpcli_request));
|
||||
request.host = (char*)GRPC_COMPUTE_ENGINE_METADATA_HOST;
|
||||
|
|
@ -369,11 +384,14 @@ class grpc_compute_engine_token_fetcher_credentials
|
|||
grpc_resource_quota* resource_quota =
|
||||
grpc_resource_quota_create("oauth2_credentials");
|
||||
grpc_httpcli_get(http_context, pollent, resource_quota, &request, deadline,
|
||||
GRPC_CLOSURE_CREATE(response_cb, metadata_req,
|
||||
grpc_schedule_on_exec_ctx),
|
||||
GRPC_CLOSURE_INIT(&http_get_cb_closure_, response_cb,
|
||||
metadata_req, grpc_schedule_on_exec_ctx),
|
||||
&metadata_req->response);
|
||||
grpc_resource_quota_unref_internal(resource_quota);
|
||||
}
|
||||
|
||||
private:
|
||||
grpc_closure http_get_cb_closure_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
@ -401,8 +419,9 @@ void grpc_google_refresh_token_credentials::fetch_oauth2(
|
|||
grpc_credentials_metadata_request* metadata_req,
|
||||
grpc_httpcli_context* httpcli_context, grpc_polling_entity* pollent,
|
||||
grpc_iomgr_cb_func response_cb, grpc_millis deadline) {
|
||||
grpc_http_header header = {(char*)"Content-Type",
|
||||
(char*)"application/x-www-form-urlencoded"};
|
||||
grpc_http_header header = {
|
||||
const_cast<char*>("Content-Type"),
|
||||
const_cast<char*>("application/x-www-form-urlencoded")};
|
||||
grpc_httpcli_request request;
|
||||
char* body = nullptr;
|
||||
gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
|
||||
|
|
@ -419,11 +438,11 @@ void grpc_google_refresh_token_credentials::fetch_oauth2(
|
|||
extreme memory pressure. */
|
||||
grpc_resource_quota* resource_quota =
|
||||
grpc_resource_quota_create("oauth2_credentials_refresh");
|
||||
grpc_httpcli_post(
|
||||
httpcli_context, pollent, resource_quota, &request, body, strlen(body),
|
||||
deadline,
|
||||
GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx),
|
||||
&metadata_req->response);
|
||||
grpc_httpcli_post(httpcli_context, pollent, resource_quota, &request, body,
|
||||
strlen(body), deadline,
|
||||
GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb,
|
||||
metadata_req, grpc_schedule_on_exec_ctx),
|
||||
&metadata_req->response);
|
||||
grpc_resource_quota_unref_internal(resource_quota);
|
||||
gpr_free(body);
|
||||
}
|
||||
|
|
@ -472,6 +491,207 @@ grpc_call_credentials* grpc_google_refresh_token_credentials_create(
|
|||
.release();
|
||||
}
|
||||
|
||||
//
|
||||
// STS credentials.
|
||||
//
|
||||
|
||||
namespace grpc_core {
|
||||
|
||||
namespace {
|
||||
|
||||
void MaybeAddToBody(gpr_strvec* body_strvec, const char* field_name,
|
||||
const char* field) {
|
||||
if (field == nullptr || strlen(field) == 0) return;
|
||||
char* new_query;
|
||||
gpr_asprintf(&new_query, "&%s=%s", field_name, field);
|
||||
gpr_strvec_add(body_strvec, new_query);
|
||||
}
|
||||
|
||||
grpc_error* LoadTokenFile(const char* path, gpr_slice* token) {
|
||||
grpc_error* err = grpc_load_file(path, 1, token);
|
||||
if (err != GRPC_ERROR_NONE) return err;
|
||||
if (GRPC_SLICE_LENGTH(*token) == 0) {
|
||||
gpr_log(GPR_ERROR, "Token file %s is empty", path);
|
||||
err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Token file is empty.");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
class StsTokenFetcherCredentials
|
||||
: public grpc_oauth2_token_fetcher_credentials {
|
||||
public:
|
||||
StsTokenFetcherCredentials(grpc_uri* sts_url, // Ownership transfered.
|
||||
const grpc_sts_credentials_options* options)
|
||||
: sts_url_(sts_url),
|
||||
resource_(gpr_strdup(options->resource)),
|
||||
audience_(gpr_strdup(options->audience)),
|
||||
scope_(gpr_strdup(options->scope)),
|
||||
requested_token_type_(gpr_strdup(options->requested_token_type)),
|
||||
subject_token_path_(gpr_strdup(options->subject_token_path)),
|
||||
subject_token_type_(gpr_strdup(options->subject_token_type)),
|
||||
actor_token_path_(gpr_strdup(options->actor_token_path)),
|
||||
actor_token_type_(gpr_strdup(options->actor_token_type)) {}
|
||||
|
||||
~StsTokenFetcherCredentials() override { grpc_uri_destroy(sts_url_); }
|
||||
|
||||
private:
|
||||
void fetch_oauth2(grpc_credentials_metadata_request* metadata_req,
|
||||
grpc_httpcli_context* http_context,
|
||||
grpc_polling_entity* pollent,
|
||||
grpc_iomgr_cb_func response_cb,
|
||||
grpc_millis deadline) override {
|
||||
char* body = nullptr;
|
||||
size_t body_length = 0;
|
||||
grpc_error* err = FillBody(&body, &body_length);
|
||||
if (err != GRPC_ERROR_NONE) {
|
||||
response_cb(metadata_req, err);
|
||||
GRPC_ERROR_UNREF(err);
|
||||
return;
|
||||
}
|
||||
grpc_http_header header = {
|
||||
const_cast<char*>("Content-Type"),
|
||||
const_cast<char*>("application/x-www-form-urlencoded")};
|
||||
grpc_httpcli_request request;
|
||||
memset(&request, 0, sizeof(grpc_httpcli_request));
|
||||
request.host = (char*)sts_url_->authority;
|
||||
request.http.path = (char*)sts_url_->path;
|
||||
request.http.hdr_count = 1;
|
||||
request.http.hdrs = &header;
|
||||
request.handshaker = (strcmp(sts_url_->scheme, "https") == 0)
|
||||
? &grpc_httpcli_ssl
|
||||
: &grpc_httpcli_plaintext;
|
||||
/* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
|
||||
channel. This would allow us to cancel an authentication query when under
|
||||
extreme memory pressure. */
|
||||
grpc_resource_quota* resource_quota =
|
||||
grpc_resource_quota_create("oauth2_credentials_refresh");
|
||||
grpc_httpcli_post(
|
||||
http_context, pollent, resource_quota, &request, body, body_length,
|
||||
deadline,
|
||||
GRPC_CLOSURE_INIT(&http_post_cb_closure_, response_cb, metadata_req,
|
||||
grpc_schedule_on_exec_ctx),
|
||||
&metadata_req->response);
|
||||
grpc_resource_quota_unref_internal(resource_quota);
|
||||
gpr_free(body);
|
||||
}
|
||||
|
||||
grpc_error* FillBody(char** body, size_t* body_length) {
|
||||
*body = nullptr;
|
||||
gpr_strvec body_strvec;
|
||||
gpr_strvec_init(&body_strvec);
|
||||
grpc_slice subject_token = grpc_empty_slice();
|
||||
grpc_slice actor_token = grpc_empty_slice();
|
||||
grpc_error* err = GRPC_ERROR_NONE;
|
||||
|
||||
auto cleanup = [&body, &body_length, &body_strvec, &subject_token,
|
||||
&actor_token, &err]() {
|
||||
if (err == GRPC_ERROR_NONE) {
|
||||
*body = gpr_strvec_flatten(&body_strvec, body_length);
|
||||
} else {
|
||||
gpr_free(*body);
|
||||
}
|
||||
gpr_strvec_destroy(&body_strvec);
|
||||
grpc_slice_unref_internal(subject_token);
|
||||
grpc_slice_unref_internal(actor_token);
|
||||
return err;
|
||||
};
|
||||
|
||||
err = LoadTokenFile(subject_token_path_.get(), &subject_token);
|
||||
if (err != GRPC_ERROR_NONE) return cleanup();
|
||||
gpr_asprintf(
|
||||
body, GRPC_STS_POST_MINIMAL_BODY_FORMAT_STRING,
|
||||
reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(subject_token)),
|
||||
subject_token_type_.get());
|
||||
gpr_strvec_add(&body_strvec, *body);
|
||||
MaybeAddToBody(&body_strvec, "resource", resource_.get());
|
||||
MaybeAddToBody(&body_strvec, "audience", audience_.get());
|
||||
MaybeAddToBody(&body_strvec, "scope", scope_.get());
|
||||
MaybeAddToBody(&body_strvec, "requested_token_type",
|
||||
requested_token_type_.get());
|
||||
if (actor_token_path_ != nullptr) {
|
||||
err = LoadTokenFile(actor_token_path_.get(), &actor_token);
|
||||
if (err != GRPC_ERROR_NONE) return cleanup();
|
||||
MaybeAddToBody(
|
||||
&body_strvec, "actor_token",
|
||||
reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(subject_token)));
|
||||
MaybeAddToBody(&body_strvec, "actor_token_type", actor_token_type_.get());
|
||||
}
|
||||
return cleanup();
|
||||
}
|
||||
|
||||
grpc_uri* sts_url_;
|
||||
grpc_closure http_post_cb_closure_;
|
||||
grpc_core::UniquePtr<char> resource_;
|
||||
grpc_core::UniquePtr<char> audience_;
|
||||
grpc_core::UniquePtr<char> scope_;
|
||||
grpc_core::UniquePtr<char> requested_token_type_;
|
||||
grpc_core::UniquePtr<char> subject_token_path_;
|
||||
grpc_core::UniquePtr<char> subject_token_type_;
|
||||
grpc_core::UniquePtr<char> actor_token_path_;
|
||||
grpc_core::UniquePtr<char> actor_token_type_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
grpc_error* ValidateStsCredentialsOptions(
|
||||
const grpc_sts_credentials_options* options, grpc_uri** sts_url_out) {
|
||||
struct GrpcUriDeleter {
|
||||
void operator()(grpc_uri* uri) { grpc_uri_destroy(uri); }
|
||||
};
|
||||
*sts_url_out = nullptr;
|
||||
InlinedVector<grpc_error*, 3> error_list;
|
||||
UniquePtr<grpc_uri, GrpcUriDeleter> sts_url(
|
||||
options->sts_endpoint_url != nullptr
|
||||
? grpc_uri_parse(options->sts_endpoint_url, false)
|
||||
: nullptr);
|
||||
if (sts_url == nullptr) {
|
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
|
||||
"Invalid or missing STS endpoint URL"));
|
||||
} else {
|
||||
if (strcmp(sts_url->scheme, "https") != 0 &&
|
||||
strcmp(sts_url->scheme, "http") != 0) {
|
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
|
||||
"Invalid URI scheme, must be https to http."));
|
||||
}
|
||||
}
|
||||
if (options->subject_token_path == nullptr ||
|
||||
strlen(options->subject_token_path) == 0) {
|
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
|
||||
"subject_token needs to be specified"));
|
||||
}
|
||||
if (options->subject_token_type == nullptr ||
|
||||
strlen(options->subject_token_type) == 0) {
|
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
|
||||
"subject_token_type needs to be specified"));
|
||||
}
|
||||
if (error_list.empty()) {
|
||||
*sts_url_out = sts_url.release();
|
||||
return GRPC_ERROR_NONE;
|
||||
} else {
|
||||
return GRPC_ERROR_CREATE_FROM_VECTOR("Invalid STS Credentials Options",
|
||||
&error_list);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
grpc_call_credentials* grpc_sts_credentials_create(
|
||||
const grpc_sts_credentials_options* options, void* reserved) {
|
||||
GPR_ASSERT(reserved == nullptr);
|
||||
grpc_uri* sts_url;
|
||||
grpc_error* error =
|
||||
grpc_core::ValidateStsCredentialsOptions(options, &sts_url);
|
||||
if (error != GRPC_ERROR_NONE) {
|
||||
gpr_log(GPR_ERROR, "STS Credentials creation failed. Error: %s.",
|
||||
grpc_error_string(error));
|
||||
GRPC_ERROR_UNREF(error);
|
||||
return nullptr;
|
||||
}
|
||||
return grpc_core::MakeRefCounted<grpc_core::StsTokenFetcherCredentials>(
|
||||
sts_url, options)
|
||||
.release();
|
||||
}
|
||||
|
||||
//
|
||||
// Oauth2 Access Token credentials.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -21,8 +21,15 @@
|
|||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include <grpc/grpc_security.h>
|
||||
#include "src/core/lib/json/json.h"
|
||||
#include "src/core/lib/security/credentials/credentials.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
|
||||
// Constants.
|
||||
#define GRPC_STS_POST_MINIMAL_BODY_FORMAT_STRING \
|
||||
"grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=%" \
|
||||
"s&subject_token_type=%s"
|
||||
|
||||
// auth_refresh_token parsing.
|
||||
typedef struct {
|
||||
|
|
@ -115,6 +122,7 @@ class grpc_google_refresh_token_credentials final
|
|||
|
||||
private:
|
||||
grpc_auth_refresh_token refresh_token_;
|
||||
grpc_closure http_post_cb_closure_;
|
||||
};
|
||||
|
||||
// Access token credentials.
|
||||
|
|
@ -148,4 +156,12 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response(
|
|||
const struct grpc_http_response* response, grpc_mdelem* token_md,
|
||||
grpc_millis* token_lifetime);
|
||||
|
||||
namespace grpc_core {
|
||||
// Exposed for testing only. This function validates the options, ensuring that
|
||||
// the required fields are set, and outputs the parsed URL of the STS token
|
||||
// exchanged service.
|
||||
grpc_error* ValidateStsCredentialsOptions(
|
||||
const grpc_sts_credentials_options* options, grpc_uri** sts_url);
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H */
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <grpc/support/port_platform.h>
|
||||
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/security/util/json_util.h"
|
||||
|
||||
#include <string.h>
|
||||
|
|
@ -26,17 +27,27 @@
|
|||
#include <grpc/support/string_util.h>
|
||||
|
||||
const char* grpc_json_get_string_property(const grpc_json* json,
|
||||
const char* prop_name) {
|
||||
grpc_json* child;
|
||||
const char* prop_name,
|
||||
grpc_error** error) {
|
||||
grpc_json* child = nullptr;
|
||||
if (error != nullptr) *error = GRPC_ERROR_NONE;
|
||||
for (child = json->child; child != nullptr; child = child->next) {
|
||||
if (child->key == nullptr) {
|
||||
gpr_log(GPR_ERROR, "Invalid (null) JSON key encountered");
|
||||
if (error != nullptr) {
|
||||
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
|
||||
"Invalid (null) JSON key encountered");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (strcmp(child->key, prop_name) == 0) break;
|
||||
}
|
||||
if (child == nullptr || child->type != GRPC_JSON_STRING) {
|
||||
gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name);
|
||||
if (error != nullptr) {
|
||||
char* error_msg;
|
||||
gpr_asprintf(&error_msg, "Invalid or missing %s property.", prop_name);
|
||||
*error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg);
|
||||
gpr_free(error_msg);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return child->value;
|
||||
|
|
@ -45,7 +56,10 @@ const char* grpc_json_get_string_property(const grpc_json* json,
|
|||
bool grpc_copy_json_string_property(const grpc_json* json,
|
||||
const char* prop_name,
|
||||
char** copied_value) {
|
||||
const char* prop_value = grpc_json_get_string_property(json, prop_name);
|
||||
grpc_error* error = GRPC_ERROR_NONE;
|
||||
const char* prop_value =
|
||||
grpc_json_get_string_property(json, prop_name, &error);
|
||||
GRPC_LOG_IF_ERROR("Could not copy JSON property", error);
|
||||
if (prop_value == nullptr) return false;
|
||||
*copied_value = gpr_strdup(prop_value);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/json/json.h"
|
||||
|
||||
// Constants.
|
||||
|
|
@ -32,7 +33,8 @@
|
|||
|
||||
// Gets a child property from a json node.
|
||||
const char* grpc_json_get_string_property(const grpc_json* json,
|
||||
const char* prop_name);
|
||||
const char* prop_name,
|
||||
grpc_error** error);
|
||||
|
||||
// Copies the value of the json child property specified by prop_name.
|
||||
// Returns false if the property was not found.
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ grpc_service_account_jwt_access_credentials_create_type grpc_service_account_jwt
|
|||
grpc_google_refresh_token_credentials_create_type grpc_google_refresh_token_credentials_create_import;
|
||||
grpc_access_token_credentials_create_type grpc_access_token_credentials_create_import;
|
||||
grpc_google_iam_credentials_create_type grpc_google_iam_credentials_create_import;
|
||||
grpc_sts_credentials_create_type grpc_sts_credentials_create_import;
|
||||
grpc_metadata_credentials_create_from_plugin_type grpc_metadata_credentials_create_from_plugin_import;
|
||||
grpc_secure_channel_create_type grpc_secure_channel_create_import;
|
||||
grpc_server_credentials_release_type grpc_server_credentials_release_import;
|
||||
|
|
@ -404,6 +405,7 @@ void grpc_rb_load_imports(HMODULE library) {
|
|||
grpc_google_refresh_token_credentials_create_import = (grpc_google_refresh_token_credentials_create_type) GetProcAddress(library, "grpc_google_refresh_token_credentials_create");
|
||||
grpc_access_token_credentials_create_import = (grpc_access_token_credentials_create_type) GetProcAddress(library, "grpc_access_token_credentials_create");
|
||||
grpc_google_iam_credentials_create_import = (grpc_google_iam_credentials_create_type) GetProcAddress(library, "grpc_google_iam_credentials_create");
|
||||
grpc_sts_credentials_create_import = (grpc_sts_credentials_create_type) GetProcAddress(library, "grpc_sts_credentials_create");
|
||||
grpc_metadata_credentials_create_from_plugin_import = (grpc_metadata_credentials_create_from_plugin_type) GetProcAddress(library, "grpc_metadata_credentials_create_from_plugin");
|
||||
grpc_secure_channel_create_import = (grpc_secure_channel_create_type) GetProcAddress(library, "grpc_secure_channel_create");
|
||||
grpc_server_credentials_release_import = (grpc_server_credentials_release_type) GetProcAddress(library, "grpc_server_credentials_release");
|
||||
|
|
|
|||
|
|
@ -377,6 +377,9 @@ extern grpc_access_token_credentials_create_type grpc_access_token_credentials_c
|
|||
typedef grpc_call_credentials*(*grpc_google_iam_credentials_create_type)(const char* authorization_token, const char* authority_selector, void* reserved);
|
||||
extern grpc_google_iam_credentials_create_type grpc_google_iam_credentials_create_import;
|
||||
#define grpc_google_iam_credentials_create grpc_google_iam_credentials_create_import
|
||||
typedef grpc_call_credentials*(*grpc_sts_credentials_create_type)(const grpc_sts_credentials_options* options, void* reserved);
|
||||
extern grpc_sts_credentials_create_type grpc_sts_credentials_create_import;
|
||||
#define grpc_sts_credentials_create grpc_sts_credentials_create_import
|
||||
typedef grpc_call_credentials*(*grpc_metadata_credentials_create_from_plugin_type)(grpc_metadata_credentials_plugin plugin, void* reserved);
|
||||
extern grpc_metadata_credentials_create_from_plugin_type grpc_metadata_credentials_create_from_plugin_import;
|
||||
#define grpc_metadata_credentials_create_from_plugin grpc_metadata_credentials_create_from_plugin_import
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <grpc/grpc_security.h>
|
||||
#include <grpc/slice.h>
|
||||
|
||||
#include <grpc/support/alloc.h>
|
||||
#include <grpc/support/log.h>
|
||||
#include <grpc/support/string_util.h>
|
||||
|
|
@ -34,13 +34,16 @@
|
|||
#include "src/core/lib/gpr/env.h"
|
||||
#include "src/core/lib/gpr/string.h"
|
||||
#include "src/core/lib/gpr/tmpfile.h"
|
||||
#include "src/core/lib/gprpp/host_port.h"
|
||||
#include "src/core/lib/http/httpcli.h"
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/security/credentials/composite/composite_credentials.h"
|
||||
#include "src/core/lib/security/credentials/fake/fake_credentials.h"
|
||||
#include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
|
||||
#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
|
||||
#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
|
||||
#include "src/core/lib/security/transport/auth_filters.h"
|
||||
#include "src/core/lib/uri/uri_parser.h"
|
||||
#include "test/core/util/test_config.h"
|
||||
|
||||
using grpc_core::internal::grpc_flush_cached_google_default_credentials;
|
||||
|
|
@ -99,15 +102,27 @@ static const char valid_oauth2_json_response[] =
|
|||
" \"expires_in\":3599, "
|
||||
" \"token_type\":\"Bearer\"}";
|
||||
|
||||
static const char valid_sts_json_response[] =
|
||||
"{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
|
||||
" \"expires_in\":3599, "
|
||||
" \"issued_token_type\":\"urn:ietf:params:oauth:token-type:access_token\", "
|
||||
" \"token_type\":\"Bearer\"}";
|
||||
|
||||
static const char test_scope[] = "perm1 perm2";
|
||||
|
||||
static const char test_signed_jwt[] =
|
||||
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY0OTRkN2M1YWU2MGRmOTcyNmM4YW"
|
||||
"U0MDcyZTViYTdmZDkwODg2YzcifQ";
|
||||
static const char test_signed_jwt_token_type[] =
|
||||
"urn:ietf:params:oauth:token-type:id_token";
|
||||
static const char test_signed_jwt_path_prefix[] = "test_sign_jwt";
|
||||
|
||||
static const char test_service_url[] = "https://foo.com/foo.v1";
|
||||
static const char other_test_service_url[] = "https://bar.com/bar.v1";
|
||||
|
||||
static const char test_sts_endpoint_url[] =
|
||||
"https://foo.com:5555/v1/token-exchange";
|
||||
|
||||
static const char test_method[] = "ThisIsNotAMethod";
|
||||
|
||||
/* -- Global state flags. -- */
|
||||
|
|
@ -657,11 +672,11 @@ static int refresh_token_httpcli_post_success(
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int refresh_token_httpcli_post_failure(
|
||||
const grpc_httpcli_request* request, const char* body, size_t body_size,
|
||||
grpc_millis deadline, grpc_closure* on_done,
|
||||
grpc_httpcli_response* response) {
|
||||
validate_refresh_token_http_request(request, body, body_size);
|
||||
static int token_httpcli_post_failure(const grpc_httpcli_request* request,
|
||||
const char* body, size_t body_size,
|
||||
grpc_millis deadline,
|
||||
grpc_closure* on_done,
|
||||
grpc_httpcli_response* response) {
|
||||
*response = http_response(403, "Not Authorized.");
|
||||
GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE);
|
||||
return 1;
|
||||
|
|
@ -676,7 +691,7 @@ static void test_refresh_token_creds_success(void) {
|
|||
grpc_call_credentials* creds = grpc_google_refresh_token_credentials_create(
|
||||
test_refresh_token_str, nullptr);
|
||||
|
||||
/* First request: http get should be called. */
|
||||
/* First request: http put should be called. */
|
||||
request_metadata_state* state =
|
||||
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
|
||||
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
|
||||
|
|
@ -707,12 +722,281 @@ static void test_refresh_token_creds_failure(void) {
|
|||
grpc_call_credentials* creds = grpc_google_refresh_token_credentials_create(
|
||||
test_refresh_token_str, nullptr);
|
||||
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
|
||||
refresh_token_httpcli_post_failure);
|
||||
token_httpcli_post_failure);
|
||||
run_request_metadata_test(creds, auth_md_ctx, state);
|
||||
creds->Unref();
|
||||
grpc_httpcli_set_override(nullptr, nullptr);
|
||||
}
|
||||
|
||||
static void test_valid_sts_creds_options(void) {
|
||||
grpc_sts_credentials_options valid_options = {
|
||||
test_sts_endpoint_url, // sts_endpoint_url
|
||||
nullptr, // resource
|
||||
nullptr, // audience
|
||||
nullptr, // scope
|
||||
nullptr, // requested_token_type
|
||||
test_signed_jwt_path_prefix, // subject_token_path
|
||||
test_signed_jwt_token_type, // subject_token_type
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
grpc_uri* sts_url;
|
||||
grpc_error* error =
|
||||
grpc_core::ValidateStsCredentialsOptions(&valid_options, &sts_url);
|
||||
GPR_ASSERT(error == GRPC_ERROR_NONE);
|
||||
GPR_ASSERT(sts_url != nullptr);
|
||||
grpc_core::StringView host;
|
||||
grpc_core::StringView port;
|
||||
GPR_ASSERT(grpc_core::SplitHostPort(sts_url->authority, &host, &port));
|
||||
GPR_ASSERT(host.cmp("foo.com") == 0);
|
||||
GPR_ASSERT(port.cmp("5555") == 0);
|
||||
grpc_uri_destroy(sts_url);
|
||||
}
|
||||
|
||||
static void test_invalid_sts_creds_options(void) {
|
||||
grpc_sts_credentials_options invalid_options = {
|
||||
test_sts_endpoint_url, // sts_endpoint_url
|
||||
nullptr, // resource
|
||||
nullptr, // audience
|
||||
nullptr, // scope
|
||||
nullptr, // requested_token_type
|
||||
nullptr, // subject_token_path (Required)
|
||||
test_signed_jwt_token_type, // subject_token_type
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
grpc_uri* url_should_be_null;
|
||||
grpc_error* error = grpc_core::ValidateStsCredentialsOptions(
|
||||
&invalid_options, &url_should_be_null);
|
||||
GPR_ASSERT(error != GRPC_ERROR_NONE);
|
||||
GRPC_ERROR_UNREF(error);
|
||||
GPR_ASSERT(url_should_be_null == nullptr);
|
||||
|
||||
invalid_options = {
|
||||
test_sts_endpoint_url, // sts_endpoint_url
|
||||
nullptr, // resource
|
||||
nullptr, // audience
|
||||
nullptr, // scope
|
||||
nullptr, // requested_token_type
|
||||
test_signed_jwt_path_prefix, // subject_token_path
|
||||
nullptr, // subject_token_type (Required)
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
error = grpc_core::ValidateStsCredentialsOptions(&invalid_options,
|
||||
&url_should_be_null);
|
||||
GPR_ASSERT(error != GRPC_ERROR_NONE);
|
||||
GRPC_ERROR_UNREF(error);
|
||||
GPR_ASSERT(url_should_be_null == nullptr);
|
||||
|
||||
invalid_options = {
|
||||
nullptr, // sts_endpoint_url (Required)
|
||||
nullptr, // resource
|
||||
nullptr, // audience
|
||||
nullptr, // scope
|
||||
nullptr, // requested_token_type
|
||||
test_signed_jwt_path_prefix, // subject_token_path
|
||||
test_signed_jwt_token_type, // subject_token_type (Required)
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
error = grpc_core::ValidateStsCredentialsOptions(&invalid_options,
|
||||
&url_should_be_null);
|
||||
GPR_ASSERT(error != GRPC_ERROR_NONE);
|
||||
GRPC_ERROR_UNREF(error);
|
||||
GPR_ASSERT(url_should_be_null == nullptr);
|
||||
|
||||
invalid_options = {
|
||||
"not_a_valid_uri", // sts_endpoint_url
|
||||
nullptr, // resource
|
||||
nullptr, // audience
|
||||
nullptr, // scope
|
||||
nullptr, // requested_token_type
|
||||
test_signed_jwt_path_prefix, // subject_token_path
|
||||
test_signed_jwt_token_type, // subject_token_type (Required)
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
error = grpc_core::ValidateStsCredentialsOptions(&invalid_options,
|
||||
&url_should_be_null);
|
||||
GPR_ASSERT(error != GRPC_ERROR_NONE);
|
||||
GRPC_ERROR_UNREF(error);
|
||||
GPR_ASSERT(url_should_be_null == nullptr);
|
||||
|
||||
invalid_options = {
|
||||
"ftp://ftp.is.not.a.valid.scheme/bar", // sts_endpoint_url
|
||||
nullptr, // resource
|
||||
nullptr, // audience
|
||||
nullptr, // scope
|
||||
nullptr, // requested_token_type
|
||||
test_signed_jwt_path_prefix, // subject_token_path
|
||||
test_signed_jwt_token_type, // subject_token_type (Required)
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
error = grpc_core::ValidateStsCredentialsOptions(&invalid_options,
|
||||
&url_should_be_null);
|
||||
GPR_ASSERT(error != GRPC_ERROR_NONE);
|
||||
GRPC_ERROR_UNREF(error);
|
||||
GPR_ASSERT(url_should_be_null == nullptr);
|
||||
}
|
||||
|
||||
static void validate_sts_token_http_request(const grpc_httpcli_request* request,
|
||||
const char* body,
|
||||
size_t body_size) {
|
||||
// Check that the body is constructed properly.
|
||||
GPR_ASSERT(body != nullptr);
|
||||
GPR_ASSERT(body_size != 0);
|
||||
GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
|
||||
char* get_url_equivalent;
|
||||
gpr_asprintf(&get_url_equivalent, "%s?%s", test_sts_endpoint_url, body);
|
||||
grpc_uri* url = grpc_uri_parse(get_url_equivalent, false);
|
||||
GPR_ASSERT(strcmp(grpc_uri_get_query_arg(url, "resource"), "resource") == 0);
|
||||
GPR_ASSERT(strcmp(grpc_uri_get_query_arg(url, "audience"), "audience") == 0);
|
||||
GPR_ASSERT(strcmp(grpc_uri_get_query_arg(url, "scope"), "scope") == 0);
|
||||
GPR_ASSERT(strcmp(grpc_uri_get_query_arg(url, "requested_token_type"),
|
||||
"requested_token_type") == 0);
|
||||
GPR_ASSERT(strcmp(grpc_uri_get_query_arg(url, "subject_token"),
|
||||
test_signed_jwt) == 0);
|
||||
GPR_ASSERT(strcmp(grpc_uri_get_query_arg(url, "subject_token_type"),
|
||||
test_signed_jwt_token_type) == 0);
|
||||
GPR_ASSERT(grpc_uri_get_query_arg(url, "actor_token") == nullptr);
|
||||
GPR_ASSERT(grpc_uri_get_query_arg(url, "actor_token_type") == nullptr);
|
||||
grpc_uri_destroy(url);
|
||||
gpr_free(get_url_equivalent);
|
||||
|
||||
// Check the rest of the request.
|
||||
GPR_ASSERT(strcmp(request->host, "foo.com:5555") == 0);
|
||||
GPR_ASSERT(strcmp(request->http.path, "/v1/token-exchange") == 0);
|
||||
GPR_ASSERT(request->http.hdr_count == 1);
|
||||
GPR_ASSERT(strcmp(request->http.hdrs[0].key, "Content-Type") == 0);
|
||||
GPR_ASSERT(strcmp(request->http.hdrs[0].value,
|
||||
"application/x-www-form-urlencoded") == 0);
|
||||
}
|
||||
|
||||
static int sts_token_httpcli_post_success(const grpc_httpcli_request* request,
|
||||
const char* body, size_t body_size,
|
||||
grpc_millis deadline,
|
||||
grpc_closure* on_done,
|
||||
grpc_httpcli_response* response) {
|
||||
validate_sts_token_http_request(request, body, body_size);
|
||||
*response = http_response(200, valid_sts_json_response);
|
||||
GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char* write_tmp_jwt_file(void) {
|
||||
char* path;
|
||||
FILE* tmp = gpr_tmpfile(test_signed_jwt_path_prefix, &path);
|
||||
GPR_ASSERT(path != nullptr);
|
||||
GPR_ASSERT(tmp != nullptr);
|
||||
size_t jwt_length = strlen(test_signed_jwt);
|
||||
GPR_ASSERT(fwrite(test_signed_jwt, 1, jwt_length, tmp) == jwt_length);
|
||||
fclose(tmp);
|
||||
return path;
|
||||
}
|
||||
|
||||
static void test_sts_creds_success(void) {
|
||||
grpc_core::ExecCtx exec_ctx;
|
||||
expected_md emd[] = {
|
||||
{"authorization", "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"}};
|
||||
grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method,
|
||||
nullptr, nullptr};
|
||||
char* test_signed_jwt_path = write_tmp_jwt_file();
|
||||
grpc_sts_credentials_options valid_options = {
|
||||
test_sts_endpoint_url, // sts_endpoint_url
|
||||
"resource", // resource
|
||||
"audience", // audience
|
||||
"scope", // scope
|
||||
"requested_token_type", // requested_token_type
|
||||
test_signed_jwt_path, // subject_token_path
|
||||
test_signed_jwt_token_type, // subject_token_type
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
grpc_call_credentials* creds =
|
||||
grpc_sts_credentials_create(&valid_options, nullptr);
|
||||
|
||||
/* First request: http put should be called. */
|
||||
request_metadata_state* state =
|
||||
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
|
||||
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
|
||||
sts_token_httpcli_post_success);
|
||||
run_request_metadata_test(creds, auth_md_ctx, state);
|
||||
grpc_core::ExecCtx::Get()->Flush();
|
||||
|
||||
/* Second request: the cached token should be served directly. */
|
||||
state =
|
||||
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
|
||||
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
|
||||
httpcli_post_should_not_be_called);
|
||||
run_request_metadata_test(creds, auth_md_ctx, state);
|
||||
grpc_core::ExecCtx::Get()->Flush();
|
||||
|
||||
creds->Unref();
|
||||
grpc_httpcli_set_override(nullptr, nullptr);
|
||||
gpr_free(test_signed_jwt_path);
|
||||
}
|
||||
|
||||
static void test_sts_creds_load_token_failure(void) {
|
||||
grpc_core::ExecCtx exec_ctx;
|
||||
request_metadata_state* state = make_request_metadata_state(
|
||||
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
|
||||
"Error occurred when fetching oauth2 token."),
|
||||
nullptr, 0);
|
||||
grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method,
|
||||
nullptr, nullptr};
|
||||
char* test_signed_jwt_path = write_tmp_jwt_file();
|
||||
grpc_sts_credentials_options options = {
|
||||
test_sts_endpoint_url, // sts_endpoint_url
|
||||
"resource", // resource
|
||||
"audience", // audience
|
||||
"scope", // scope
|
||||
"requested_token_type", // requested_token_type
|
||||
"invalid_path", // subject_token_path
|
||||
test_signed_jwt_token_type, // subject_token_type
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
grpc_call_credentials* creds = grpc_sts_credentials_create(&options, nullptr);
|
||||
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
|
||||
httpcli_post_should_not_be_called);
|
||||
run_request_metadata_test(creds, auth_md_ctx, state);
|
||||
creds->Unref();
|
||||
grpc_httpcli_set_override(nullptr, nullptr);
|
||||
gpr_free(test_signed_jwt_path);
|
||||
}
|
||||
|
||||
static void test_sts_creds_http_failure(void) {
|
||||
grpc_core::ExecCtx exec_ctx;
|
||||
request_metadata_state* state = make_request_metadata_state(
|
||||
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
|
||||
"Error occurred when fetching oauth2 token."),
|
||||
nullptr, 0);
|
||||
grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method,
|
||||
nullptr, nullptr};
|
||||
char* test_signed_jwt_path = write_tmp_jwt_file();
|
||||
grpc_sts_credentials_options valid_options = {
|
||||
test_sts_endpoint_url, // sts_endpoint_url
|
||||
"resource", // resource
|
||||
"audience", // audience
|
||||
"scope", // scope
|
||||
"requested_token_type", // requested_token_type
|
||||
test_signed_jwt_path, // subject_token_path
|
||||
test_signed_jwt_token_type, // subject_token_type
|
||||
nullptr, // actor_token_path
|
||||
nullptr // actor_token_type
|
||||
};
|
||||
grpc_call_credentials* creds =
|
||||
grpc_sts_credentials_create(&valid_options, nullptr);
|
||||
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
|
||||
token_httpcli_post_failure);
|
||||
run_request_metadata_test(creds, auth_md_ctx, state);
|
||||
creds->Unref();
|
||||
grpc_httpcli_set_override(nullptr, nullptr);
|
||||
gpr_free(test_signed_jwt_path);
|
||||
}
|
||||
|
||||
static void validate_jwt_encode_and_sign_params(
|
||||
const grpc_auth_json_key* json_key, const char* scope,
|
||||
gpr_timespec token_lifetime) {
|
||||
|
|
@ -1288,6 +1572,11 @@ int main(int argc, char** argv) {
|
|||
test_compute_engine_creds_failure();
|
||||
test_refresh_token_creds_success();
|
||||
test_refresh_token_creds_failure();
|
||||
test_valid_sts_creds_options();
|
||||
test_invalid_sts_creds_options();
|
||||
test_sts_creds_success();
|
||||
test_sts_creds_load_token_failure();
|
||||
test_sts_creds_http_failure();
|
||||
test_jwt_creds_lifetime();
|
||||
test_jwt_creds_success();
|
||||
test_jwt_creds_signing_failure();
|
||||
|
|
|
|||
|
|
@ -26,33 +26,82 @@
|
|||
#include <grpc/support/log.h>
|
||||
#include <grpc/support/sync.h>
|
||||
|
||||
#include "src/core/lib/iomgr/error.h"
|
||||
#include "src/core/lib/iomgr/load_file.h"
|
||||
#include "src/core/lib/security/credentials/credentials.h"
|
||||
#include "src/core/lib/security/util/json_util.h"
|
||||
#include "test/core/security/oauth2_utils.h"
|
||||
#include "test/core/util/cmdline.h"
|
||||
|
||||
static grpc_sts_credentials_options sts_options_from_json(grpc_json* json) {
|
||||
grpc_sts_credentials_options options;
|
||||
memset(&options, 0, sizeof(options));
|
||||
grpc_error* error = GRPC_ERROR_NONE;
|
||||
options.sts_endpoint_url =
|
||||
grpc_json_get_string_property(json, "sts_endpoint_url", &error);
|
||||
GRPC_LOG_IF_ERROR("STS credentials parsing", error);
|
||||
options.resource = grpc_json_get_string_property(json, "resource", nullptr);
|
||||
options.audience = grpc_json_get_string_property(json, "audience", nullptr);
|
||||
options.scope = grpc_json_get_string_property(json, "scope", nullptr);
|
||||
options.requested_token_type =
|
||||
grpc_json_get_string_property(json, "requested_token_type", nullptr);
|
||||
options.subject_token_path =
|
||||
grpc_json_get_string_property(json, "subject_token_path", &error);
|
||||
GRPC_LOG_IF_ERROR("STS credentials parsing", error);
|
||||
options.subject_token_type =
|
||||
grpc_json_get_string_property(json, "subject_token_type", &error);
|
||||
GRPC_LOG_IF_ERROR("STS credentials parsing", error);
|
||||
options.actor_token_path =
|
||||
grpc_json_get_string_property(json, "actor_token_path", nullptr);
|
||||
options.actor_token_type =
|
||||
grpc_json_get_string_property(json, "actor_token_type", nullptr);
|
||||
return options;
|
||||
}
|
||||
|
||||
static grpc_call_credentials* create_sts_creds(const char* json_file_path) {
|
||||
grpc_slice sts_options_slice;
|
||||
GPR_ASSERT(GRPC_LOG_IF_ERROR(
|
||||
"load_file", grpc_load_file(json_file_path, 1, &sts_options_slice)));
|
||||
grpc_json* json = grpc_json_parse_string(
|
||||
reinterpret_cast<char*>(GRPC_SLICE_START_PTR(sts_options_slice)));
|
||||
if (json == nullptr) {
|
||||
gpr_log(GPR_ERROR, "Invalid json");
|
||||
return nullptr;
|
||||
}
|
||||
grpc_sts_credentials_options options = sts_options_from_json(json);
|
||||
grpc_call_credentials* result =
|
||||
grpc_sts_credentials_create(&options, nullptr);
|
||||
grpc_json_destroy(json);
|
||||
gpr_slice_unref(sts_options_slice);
|
||||
return result;
|
||||
}
|
||||
|
||||
static grpc_call_credentials* create_refresh_token_creds(
|
||||
const char* json_refresh_token_file_path) {
|
||||
grpc_slice refresh_token;
|
||||
GPR_ASSERT(GRPC_LOG_IF_ERROR(
|
||||
"load_file",
|
||||
grpc_load_file(json_refresh_token_file_path, 1, &refresh_token)));
|
||||
return grpc_google_refresh_token_credentials_create(
|
||||
grpc_call_credentials* result = grpc_google_refresh_token_credentials_create(
|
||||
reinterpret_cast<const char*> GRPC_SLICE_START_PTR(refresh_token),
|
||||
nullptr);
|
||||
gpr_slice_unref(refresh_token);
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
grpc_call_credentials* creds = nullptr;
|
||||
char* json_key_file_path = nullptr;
|
||||
const char* json_sts_options_file_path = nullptr;
|
||||
const char* json_refresh_token_file_path = nullptr;
|
||||
char* token = nullptr;
|
||||
int use_gce = 0;
|
||||
char* scope = nullptr;
|
||||
gpr_cmdline* cl = gpr_cmdline_create("fetch_oauth2");
|
||||
gpr_cmdline_add_string(cl, "json_refresh_token",
|
||||
"File path of the json refresh token.",
|
||||
&json_refresh_token_file_path);
|
||||
gpr_cmdline_add_string(cl, "json_sts_options",
|
||||
"File path of the json sts options.",
|
||||
&json_sts_options_file_path);
|
||||
gpr_cmdline_add_flag(
|
||||
cl, "gce",
|
||||
"Get a token from the GCE metadata server (only works in GCE).",
|
||||
|
|
@ -61,18 +110,20 @@ int main(int argc, char** argv) {
|
|||
|
||||
grpc_init();
|
||||
|
||||
if (json_key_file_path != nullptr &&
|
||||
if (json_sts_options_file_path != nullptr &&
|
||||
json_refresh_token_file_path != nullptr) {
|
||||
gpr_log(GPR_ERROR,
|
||||
"--json_key and --json_refresh_token are mutually exclusive.");
|
||||
gpr_log(
|
||||
GPR_ERROR,
|
||||
"--json_sts_options and --json_refresh_token are mutually exclusive.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (use_gce) {
|
||||
if (json_key_file_path != nullptr || scope != nullptr) {
|
||||
if (json_sts_options_file_path != nullptr ||
|
||||
json_refresh_token_file_path != nullptr) {
|
||||
gpr_log(GPR_INFO,
|
||||
"Ignoring json key and scope to get a token from the GCE "
|
||||
"metadata server.");
|
||||
"Ignoring json refresh token or sts options to get a token from "
|
||||
"the GCE metadata server.");
|
||||
}
|
||||
creds = grpc_google_compute_engine_credentials_create(nullptr);
|
||||
if (creds == nullptr) {
|
||||
|
|
@ -88,8 +139,19 @@ int main(int argc, char** argv) {
|
|||
json_refresh_token_file_path);
|
||||
exit(1);
|
||||
}
|
||||
} else if (json_sts_options_file_path != nullptr) {
|
||||
creds = create_sts_creds(json_sts_options_file_path);
|
||||
if (creds == nullptr) {
|
||||
gpr_log(GPR_ERROR,
|
||||
"Could not create sts creds. %s does probably not contain a "
|
||||
"valid json for sts options.",
|
||||
json_sts_options_file_path);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
gpr_log(GPR_ERROR, "Missing --gce or --json_refresh_token option.");
|
||||
gpr_log(
|
||||
GPR_ERROR,
|
||||
"Missing --gce, --json_sts_options, or --json_refresh_token option.");
|
||||
exit(1);
|
||||
}
|
||||
GPR_ASSERT(creds != nullptr);
|
||||
|
|
|
|||
|
|
@ -63,14 +63,17 @@ static void on_oauth2_response(void* arg, grpc_error* error) {
|
|||
gpr_mu_unlock(request->mu);
|
||||
}
|
||||
|
||||
static void do_nothing(void* unused, grpc_error* error) {}
|
||||
static void destroy_after_shutdown(void* pollset, grpc_error* error) {
|
||||
grpc_pollset_destroy(reinterpret_cast<grpc_pollset*>(pollset));
|
||||
gpr_free(pollset);
|
||||
}
|
||||
|
||||
char* grpc_test_fetch_oauth2_token_with_credentials(
|
||||
grpc_call_credentials* creds) {
|
||||
oauth2_request request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
grpc_core::ExecCtx exec_ctx;
|
||||
grpc_closure do_nothing_closure;
|
||||
grpc_closure destroy_after_shutdown_closure;
|
||||
grpc_auth_metadata_context null_ctx = {"", "", nullptr, nullptr};
|
||||
|
||||
grpc_pollset* pollset =
|
||||
|
|
@ -79,8 +82,8 @@ char* grpc_test_fetch_oauth2_token_with_credentials(
|
|||
request.pops = grpc_polling_entity_create_from_pollset(pollset);
|
||||
request.is_done = false;
|
||||
|
||||
GRPC_CLOSURE_INIT(&do_nothing_closure, do_nothing, nullptr,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
GRPC_CLOSURE_INIT(&destroy_after_shutdown_closure, destroy_after_shutdown,
|
||||
pollset, grpc_schedule_on_exec_ctx);
|
||||
|
||||
GRPC_CLOSURE_INIT(&request.closure, on_oauth2_response, &request,
|
||||
grpc_schedule_on_exec_ctx);
|
||||
|
|
@ -107,8 +110,6 @@ char* grpc_test_fetch_oauth2_token_with_credentials(
|
|||
gpr_mu_unlock(request.mu);
|
||||
|
||||
grpc_pollset_shutdown(grpc_polling_entity_pollset(&request.pops),
|
||||
&do_nothing_closure);
|
||||
|
||||
gpr_free(grpc_polling_entity_pollset(&request.pops));
|
||||
&destroy_after_shutdown_closure);
|
||||
return request.token;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ int main(int argc, char **argv) {
|
|||
printf("%lx", (unsigned long) grpc_google_refresh_token_credentials_create);
|
||||
printf("%lx", (unsigned long) grpc_access_token_credentials_create);
|
||||
printf("%lx", (unsigned long) grpc_google_iam_credentials_create);
|
||||
printf("%lx", (unsigned long) grpc_sts_credentials_create);
|
||||
printf("%lx", (unsigned long) grpc_metadata_credentials_create_from_plugin);
|
||||
printf("%lx", (unsigned long) grpc_secure_channel_create);
|
||||
printf("%lx", (unsigned long) grpc_server_credentials_release);
|
||||
|
|
|
|||
Loading…
Reference in New Issue