|
|
|
@ -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,10 +722,279 @@ 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( |
|
|
|
@ -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(); |
|
|
|
|