Add support for Workforce pool credentials (#27583)

* first commit

* Update external_account_credentials.cc

* Fix CI issues
pull/27728/head
Chuan Ren 3 years ago committed by GitHub
parent 544deed283
commit 0b2598df07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 58
      src/core/lib/security/credentials/external/external_account_credentials.cc
  2. 1
      src/core/lib/security/credentials/external/external_account_credentials.h
  3. 77
      test/core/security/credentials_test.cc

@ -17,9 +17,11 @@
#include "src/core/lib/security/credentials/external/external_account_credentials.h"
#include "absl/strings/match.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/strip.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
@ -59,6 +61,23 @@ std::string UrlEncode(const absl::string_view& s) {
return result;
}
// Expression to match:
// //iam.googleapis.com/locations/[^/]+/workforcePools/[^/]+/providers/.+
bool MatchWorkforcePoolAudience(absl::string_view audience) {
// Match "//iam.googleapis.com/locations/"
if (!absl::ConsumePrefix(&audience, "//iam.googleapis.com")) return false;
if (!absl::ConsumePrefix(&audience, "/locations/")) return false;
// Match "[^/]+/workforcePools/"
std::pair<absl::string_view, absl::string_view> workforce_pools_split_result =
absl::StrSplit(audience, absl::MaxSplits("/workforcePools/", 1));
if (absl::StrContains(workforce_pools_split_result.first, '/')) return false;
// Match "[^/]+/providers/.+"
std::pair<absl::string_view, absl::string_view> providers_split_result =
absl::StrSplit(workforce_pools_split_result.second,
absl::MaxSplits("/providers/", 1));
return !absl::StrContains(providers_split_result.first, '/');
}
} // namespace
RefCountedPtr<ExternalAccountCredentials> ExternalAccountCredentials::Create(
@ -151,6 +170,17 @@ RefCountedPtr<ExternalAccountCredentials> ExternalAccountCredentials::Create(
if (it != json.object_value().end()) {
options.client_secret = it->second.string_value();
}
it = json.object_value().find("workforce_pool_user_project");
if (it != json.object_value().end()) {
if (MatchWorkforcePoolAudience(options.audience)) {
options.workforce_pool_user_project = it->second.string_value();
} else {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"workforce_pool_user_project should not be set for non-workforce "
"pool credentials");
return nullptr;
}
}
RefCountedPtr<ExternalAccountCredentials> creds;
if (options.credential_source.object_value().find("environment_id") !=
options.credential_source.object_value().end()) {
@ -267,25 +297,31 @@ void ExternalAccountCredentials::ExchangeToken(
request.handshaker =
uri->scheme() == "https" ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext;
std::vector<std::string> body_parts;
body_parts.push_back(absl::StrFormat("%s=%s", "audience",
UrlEncode(options_.audience).c_str()));
body_parts.push_back(
absl::StrFormat("audience=%s", UrlEncode(options_.audience).c_str()));
body_parts.push_back(absl::StrFormat(
"%s=%s", "grant_type",
"grant_type=%s",
UrlEncode(EXTERNAL_ACCOUNT_CREDENTIALS_GRANT_TYPE).c_str()));
body_parts.push_back(absl::StrFormat(
"%s=%s", "requested_token_type",
"requested_token_type=%s",
UrlEncode(EXTERNAL_ACCOUNT_CREDENTIALS_REQUESTED_TOKEN_TYPE).c_str()));
body_parts.push_back(absl::StrFormat(
"subject_token_type=%s", UrlEncode(options_.subject_token_type).c_str()));
body_parts.push_back(
absl::StrFormat("%s=%s", "subject_token_type",
UrlEncode(options_.subject_token_type).c_str()));
body_parts.push_back(absl::StrFormat("%s=%s", "subject_token",
UrlEncode(subject_token).c_str()));
absl::StrFormat("subject_token=%s", UrlEncode(subject_token).c_str()));
std::string scope = GOOGLE_CLOUD_PLATFORM_DEFAULT_SCOPE;
if (options_.service_account_impersonation_url.empty()) {
scope = absl::StrJoin(scopes_, " ");
}
body_parts.push_back(
absl::StrFormat("%s=%s", "scope", UrlEncode(scope).c_str()));
body_parts.push_back(absl::StrFormat("scope=%s", UrlEncode(scope).c_str()));
Json::Object addtional_options_json_object;
if (options_.client_id.empty() && options_.client_secret.empty()) {
addtional_options_json_object["userProject"] =
options_.workforce_pool_user_project;
}
Json addtional_options_json(std::move(addtional_options_json_object));
body_parts.push_back(absl::StrFormat(
"options=%s", UrlEncode(addtional_options_json.Dump()).c_str()));
std::string body = absl::StrJoin(body_parts, "&");
grpc_resource_quota* resource_quota =
grpc_resource_quota_create("external_account_credentials");
@ -372,7 +408,7 @@ void ExternalAccountCredentials::ImpersenateServiceAccount() {
request.handshaker =
uri->scheme() == "https" ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext;
std::string scope = absl::StrJoin(scopes_, " ");
std::string body = absl::StrFormat("%s=%s", "scope", scope);
std::string body = absl::StrFormat("scope=%s", scope);
grpc_resource_quota* resource_quota =
grpc_resource_quota_create("external_account_credentials");
grpc_http_response_destroy(&ctx_->response);

@ -46,6 +46,7 @@ class ExternalAccountCredentials
std::string quota_project_id;
std::string client_id;
std::string client_secret;
std::string workforce_pool_user_project;
};
static RefCountedPtr<ExternalAccountCredentials> Create(

@ -2165,8 +2165,8 @@ validate_external_account_creds_token_exchage_request_with_url_encode(
"3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%"
"3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&subject_token_type="
"subject_token_type_!%40%23%24&subject_token=test_subject_token&"
"scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform") ==
0);
"scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&"
"options=%7B%7D") == 0);
// Check the rest of the request.
GPR_ASSERT(strcmp(request->host, "foo.com:5555") == 0);
@ -2371,6 +2371,7 @@ static void test_external_account_creds_success(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
TestExternalAccountCredentials creds(options, {});
/* Check security level. */
@ -2409,6 +2410,7 @@ static void test_external_account_creds_success_with_url_encode(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
TestExternalAccountCredentials creds(options, {});
RequestMetadataState* state =
@ -2439,6 +2441,7 @@ test_external_account_creds_success_with_service_account_impersonation(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
TestExternalAccountCredentials creds(options, {"scope_1", "scope_2"});
/* Check security level. */
@ -2469,6 +2472,7 @@ static void test_external_account_creds_failure_invalid_token_url(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
TestExternalAccountCredentials creds(options, {});
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
@ -2504,6 +2508,7 @@ test_external_account_creds_failure_invalid_service_account_impersonation_url(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
TestExternalAccountCredentials creds(options, {});
grpc_httpcli_set_override(httpcli_get_should_not_be_called,
@ -2540,6 +2545,7 @@ test_external_account_creds_failure_token_exchange_response_missing_access_token
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
TestExternalAccountCredentials creds(options, {});
grpc_httpcli_set_override(
@ -2582,6 +2588,7 @@ static void test_url_external_account_creds_success_format_text(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::UrlExternalAccountCredentials::Create(options, {}, &error);
@ -2620,6 +2627,7 @@ test_url_external_account_creds_success_with_qurey_params_format_text(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::UrlExternalAccountCredentials::Create(options, {}, &error);
@ -2657,6 +2665,7 @@ static void test_url_external_account_creds_success_format_json(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::UrlExternalAccountCredentials::Create(options, {}, &error);
@ -2689,6 +2698,7 @@ test_url_external_account_creds_failure_invalid_credential_source_url(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::UrlExternalAccountCredentials::Create(options, {}, &error);
@ -2725,6 +2735,7 @@ static void test_file_external_account_creds_success_format_text(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::FileExternalAccountCredentials::Create(options, {}, &error);
@ -2775,6 +2786,7 @@ static void test_file_external_account_creds_success_format_json(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::FileExternalAccountCredentials::Create(options, {}, &error);
@ -2811,6 +2823,7 @@ static void test_file_external_account_creds_failure_file_not_found(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::FileExternalAccountCredentials::Create(options, {}, &error);
@ -2861,6 +2874,7 @@ static void test_file_external_account_creds_failure_invalid_json_content(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::FileExternalAccountCredentials::Create(options, {}, &error);
@ -2903,6 +2917,7 @@ static void test_aws_external_account_creds_success(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -2941,6 +2956,7 @@ static void test_aws_external_account_creds_success_path_region_env_keys_url(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -2980,6 +2996,7 @@ test_aws_external_account_creds_success_path_default_region_env_keys_url(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3022,6 +3039,7 @@ test_aws_external_account_creds_success_path_duplicate_region_env_keys_url(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3064,6 +3082,7 @@ static void test_aws_external_account_creds_success_path_region_url_keys_env(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3108,6 +3127,7 @@ static void test_aws_external_account_creds_success_path_region_env_keys_env(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3153,6 +3173,7 @@ test_aws_external_account_creds_success_path_default_region_env_keys_env(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3201,6 +3222,7 @@ test_aws_external_account_creds_success_path_duplicate_region_env_keys_env(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3239,6 +3261,7 @@ static void test_aws_external_account_creds_failure_unmatched_environment_id(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3271,6 +3294,7 @@ static void test_aws_external_account_creds_failure_invalid_region_url(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3312,6 +3336,7 @@ static void test_aws_external_account_creds_failure_invalid_url(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3352,6 +3377,7 @@ static void test_aws_external_account_creds_failure_missing_role_name(void) {
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3395,6 +3421,7 @@ test_aws_external_account_creds_failure_invalid_regional_cred_verification_url(
"quota_project_id", // quota_project_id;
"client_id", // client_id;
"client_secret", // client_secret;
"", // workforce_pool_user_project;
};
auto creds =
grpc_core::AwsExternalAccountCredentials::Create(options, {}, &error);
@ -3502,6 +3529,50 @@ test_external_account_credentials_create_failure_invalid_options_credential_sour
GPR_ASSERT(creds == nullptr);
}
static void test_external_account_credentials_create_success_workforce_pool(
void) {
const char* url_options_string =
"{\"type\":\"external_account\",\"audience\":\"//iam.googleapis.com/"
"locations/location/workforcePools/pool/providers/provider\",\"subject_"
"token_type\":\"subject_token_type\",\"service_account_impersonation_"
"url\":\"service_account_impersonation_url\",\"token_url\":\"https://"
"foo.com:5555/token\",\"token_info_url\":\"https://foo.com:5555/"
"token_info\",\"credential_source\":{\"url\":\"https://foo.com:5555/"
"generate_subject_token_format_json\",\"headers\":{\"Metadata-Flavor\":"
"\"Google\"},\"format\":{\"type\":\"json\",\"subject_token_field_name\":"
"\"access_token\"}},\"quota_project_id\":\"quota_"
"project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
"secret\",\"workforce_pool_user_project\":\"workforce_pool_user_"
"project\"}";
const char* url_scopes_string = "scope1,scope2";
grpc_call_credentials* url_creds = grpc_external_account_credentials_create(
url_options_string, url_scopes_string);
GPR_ASSERT(url_creds != nullptr);
url_creds->Unref();
}
static void
test_external_account_credentials_create_failure_invalid_workforce_pool_audience(
void) {
const char* url_options_string =
"{\"type\":\"external_account\",\"audience\":\"invalid_workforce_pool_"
"audience\",\"subject_"
"token_type\":\"subject_token_type\",\"service_account_impersonation_"
"url\":\"service_account_impersonation_url\",\"token_url\":\"https://"
"foo.com:5555/token\",\"token_info_url\":\"https://foo.com:5555/"
"token_info\",\"credential_source\":{\"url\":\"https://foo.com:5555/"
"generate_subject_token_format_json\",\"headers\":{\"Metadata-Flavor\":"
"\"Google\"},\"format\":{\"type\":\"json\",\"subject_token_field_name\":"
"\"access_token\"}},\"quota_project_id\":\"quota_"
"project_id\",\"client_id\":\"client_id\",\"client_secret\":\"client_"
"secret\",\"workforce_pool_user_project\":\"workforce_pool_user_"
"project\"}";
const char* url_scopes_string = "scope1,scope2";
grpc_call_credentials* url_creds = grpc_external_account_credentials_create(
url_options_string, url_scopes_string);
GPR_ASSERT(url_creds == nullptr);
}
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(argc, argv);
grpc_init();
@ -3581,6 +3652,8 @@ int main(int argc, char** argv) {
test_external_account_credentials_create_failure_invalid_json_format();
test_external_account_credentials_create_failure_invalid_options_format();
test_external_account_credentials_create_failure_invalid_options_credential_source();
test_external_account_credentials_create_success_workforce_pool();
test_external_account_credentials_create_failure_invalid_workforce_pool_audience();
grpc_shutdown();
return 0;
}

Loading…
Cancel
Save