diff options
author | jboeuf <jboeuf@google.com> | 2014-12-19 15:44:30 -0800 |
---|---|---|
committer | Jan Tattermusch <jtattermusch@google.com> | 2014-12-29 17:03:04 -0800 |
commit | 1a809c0ebbf77aedf7f6322ef7d6373962c80264 (patch) | |
tree | 40ba18186cf5078fc36d8a5cb99b37e75015b3a1 /test/core/security | |
parent | 3f1af6ee2d5eb9898e9b21270f7fc1f5ee39b37a (diff) |
Adding support for service account credentials.
- Tested end to end with a JSON key I generated for my account using the
fetch_oauth2 binary.
- The same fetch_oauth2 binary can get a token from the GCE metadata service on a VM in cloud.
Change on 2014/12/19 by jboeuf <jboeuf@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=82548689
Diffstat (limited to 'test/core/security')
-rw-r--r-- | test/core/security/credentials_test.c | 392 | ||||
-rw-r--r-- | test/core/security/fetch_oauth2.c | 177 |
2 files changed, 536 insertions, 33 deletions
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c index 7eca8442b4..619f09308e 100644 --- a/test/core/security/credentials_test.c +++ b/test/core/security/credentials_test.c @@ -33,12 +33,16 @@ #include "src/core/security/credentials.h" +#include <string.h> + #include "src/core/httpcli/httpcli.h" +#include "src/core/security/json_token.h" +#include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include <grpc/support/string.h> #include <grpc/support/time.h> #include "test/core/util/test_config.h" - -#include <string.h> +#include <openssl/rsa.h> static const char test_iam_authorization_token[] = "blahblahblhahb"; static const char test_iam_authority_selector[] = "respectmyauthoritah"; @@ -46,30 +50,86 @@ static const char test_oauth2_bearer_token[] = "Bearer blaaslkdjfaslkdfasdsfasf"; static const unsigned char test_root_cert[] = {0xDE, 0xAD, 0xBE, 0xEF}; +/* This JSON key was generated with the GCE console and revoked immediately. + The identifiers have been changed as well. + Maximum size for a string literal is 509 chars in C89, yay! */ +static const char test_json_key_str_part1[] = + "{ \"private_key\": \"-----BEGIN PRIVATE KEY-----" + "\nMIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOEvJsnoHnyHkXcp\n7mJEqg" + "WGjiw71NfXByguekSKho65FxaGbsnSM9SMQAqVk7Q2rG+I0OpsT0LrWQtZ\nyjSeg/" + "rWBQvS4hle4LfijkP3J5BG+" + "IXDMP8RfziNRQsenAXDNPkY4kJCvKux2xdD\nOnVF6N7dL3nTYZg+" + "uQrNsMTz9UxVAgMBAAECgYEAzbLewe1xe9vy+2GoSsfib+28\nDZgSE6Bu/" + "zuFoPrRc6qL9p2SsnV7txrunTyJkkOnPLND9ABAXybRTlcVKP/sGgza\n/" + "8HpCqFYM9V8f34SBWfD4fRFT+n/" + "73cfRUtGXdXpseva2lh8RilIQfPhNZAncenU\ngqXjDvpkypEusgXAykECQQD+"; +static const char test_json_key_str_part2[] = + "53XxNVnxBHsYb+AYEfklR96yVi8HywjVHP34+OQZ\nCslxoHQM8s+" + "dBnjfScLu22JqkPv04xyxmt0QAKm9+vTdAkEA4ib7YvEAn2jXzcCI\nEkoy2L/" + "XydR1GCHoacdfdAwiL2npOdnbvi4ZmdYRPY1LSTO058tQHKVXV7NLeCa3\nAARh2QJBAMKeDAG" + "W303SQv2cZTdbeaLKJbB5drz3eo3j7dDKjrTD9JupixFbzcGw\n8FZi5c8idxiwC36kbAL6HzA" + "ZoX+ofI0CQE6KCzPJTtYNqyShgKAZdJ8hwOcvCZtf\n6z8RJm0+" + "6YBd38lfh5j8mZd7aHFf6I17j5AQY7oPEc47TjJj/" + "5nZ68ECQQDvYuI3\nLyK5fS8g0SYbmPOL9TlcHDOqwG0mrX9qpg5DC2fniXNSrrZ64GTDKdzZY" + "Ap6LI9W\nIqv4vr6y38N79TTC\n-----END PRIVATE KEY-----\n\", "; +static const char test_json_key_str_part3[] = + "\"private_key_id\": \"e6b5137873db8d2ef81e06a47289e6434ec8a165\", " + "\"client_email\": " + "\"777-abaslkan11hlb6nmim3bpspl31ud@developer.gserviceaccount." + "com\", \"client_id\": " + "\"777-abaslkan11hlb6nmim3bpspl31ud.apps.googleusercontent." + "com\", \"type\": \"service_account\" }"; + +static const char valid_oauth2_json_response[] = + "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\"," + " \"expires_in\":3599, " + " \"token_type\":\"Bearer\"}"; + +static const char test_user_data[] = "user data"; + +static const char test_scope[] = "perm1 perm2"; + +static const char test_signed_jwt[] = "signed jwt"; + +static const char expected_service_account_http_body_prefix[] = + "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&" + "assertion="; + +static char *test_json_key_str(void) { + size_t result_len = strlen(test_json_key_str_part1) + + strlen(test_json_key_str_part2) + + strlen(test_json_key_str_part3); + char *result = gpr_malloc(result_len + 1); + char *current = result; + strcpy(result, test_json_key_str_part1); + current += strlen(test_json_key_str_part1); + strcpy(current, test_json_key_str_part2); + current += strlen(test_json_key_str_part2); + strcpy(current, test_json_key_str_part3); + return result; +} + typedef struct { const char *key; const char *value; } expected_md; -static grpc_httpcli_response http_response(int status, char *body) { +static grpc_httpcli_response http_response(int status, const char *body) { grpc_httpcli_response response; memset(&response, 0, sizeof(grpc_httpcli_response)); response.status = status; - response.body = body; + response.body = (char *)body; response.body_length = strlen(body); return response; } -static void test_compute_engine_creds_parsing_ok(void) { +static void test_oauth2_token_fetcher_creds_parsing_ok(void) { grpc_mdctx *ctx = grpc_mdctx_create(); grpc_mdelem *token_elem = NULL; gpr_timespec token_lifetime; grpc_httpcli_response response = - http_response(200, - "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\"," - " \"expires_in\":3599, " - " \"token_type\":\"Bearer\"}"); - GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response( + http_response(200, valid_oauth2_json_response); + GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( &response, ctx, &token_elem, &token_lifetime) == GRPC_CREDENTIALS_OK); GPR_ASSERT(token_lifetime.tv_sec == 3599); @@ -81,33 +141,30 @@ static void test_compute_engine_creds_parsing_ok(void) { grpc_mdctx_orphan(ctx); } -static void test_compute_engine_creds_parsing_bad_http_status(void) { +static void test_oauth2_token_fetcher_creds_parsing_bad_http_status(void) { grpc_mdctx *ctx = grpc_mdctx_create(); grpc_mdelem *token_elem = NULL; gpr_timespec token_lifetime; grpc_httpcli_response response = - http_response(401, - "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\"," - " \"expires_in\":3599, " - " \"token_type\":\"Bearer\"}"); - GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response( + http_response(401, valid_oauth2_json_response); + GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( &response, ctx, &token_elem, &token_lifetime) == GRPC_CREDENTIALS_ERROR); grpc_mdctx_orphan(ctx); } -static void test_compute_engine_creds_parsing_empty_http_body(void) { +static void test_oauth2_token_fetcher_creds_parsing_empty_http_body(void) { grpc_mdctx *ctx = grpc_mdctx_create(); grpc_mdelem *token_elem = NULL; gpr_timespec token_lifetime; grpc_httpcli_response response = http_response(200, ""); - GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response( + GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( &response, ctx, &token_elem, &token_lifetime) == GRPC_CREDENTIALS_ERROR); grpc_mdctx_orphan(ctx); } -static void test_compute_engine_creds_parsing_invalid_json(void) { +static void test_oauth2_token_fetcher_creds_parsing_invalid_json(void) { grpc_mdctx *ctx = grpc_mdctx_create(); grpc_mdelem *token_elem = NULL; gpr_timespec token_lifetime; @@ -116,13 +173,13 @@ static void test_compute_engine_creds_parsing_invalid_json(void) { "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\"," " \"expires_in\":3599, " " \"token_type\":\"Bearer\""); - GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response( + GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( &response, ctx, &token_elem, &token_lifetime) == GRPC_CREDENTIALS_ERROR); grpc_mdctx_orphan(ctx); } -static void test_compute_engine_creds_parsing_missing_token(void) { +static void test_oauth2_token_fetcher_creds_parsing_missing_token(void) { grpc_mdctx *ctx = grpc_mdctx_create(); grpc_mdelem *token_elem = NULL; gpr_timespec token_lifetime; @@ -130,13 +187,13 @@ static void test_compute_engine_creds_parsing_missing_token(void) { "{" " \"expires_in\":3599, " " \"token_type\":\"Bearer\"}"); - GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response( + GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( &response, ctx, &token_elem, &token_lifetime) == GRPC_CREDENTIALS_ERROR); grpc_mdctx_orphan(ctx); } -static void test_compute_engine_creds_parsing_missing_token_type(void) { +static void test_oauth2_token_fetcher_creds_parsing_missing_token_type(void) { grpc_mdctx *ctx = grpc_mdctx_create(); grpc_mdelem *token_elem = NULL; gpr_timespec token_lifetime; @@ -145,13 +202,14 @@ static void test_compute_engine_creds_parsing_missing_token_type(void) { "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\"," " \"expires_in\":3599, " "}"); - GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response( + GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( &response, ctx, &token_elem, &token_lifetime) == GRPC_CREDENTIALS_ERROR); grpc_mdctx_orphan(ctx); } -static void test_compute_engine_creds_parsing_missing_token_lifetime(void) { +static void test_oauth2_token_fetcher_creds_parsing_missing_token_lifetime( + void) { grpc_mdctx *ctx = grpc_mdctx_create(); grpc_mdelem *token_elem = NULL; gpr_timespec token_lifetime; @@ -159,7 +217,7 @@ static void test_compute_engine_creds_parsing_missing_token_lifetime(void) { http_response(200, "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\"," " \"token_type\":\"Bearer\"}"); - GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response( + GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( &response, ctx, &token_elem, &token_lifetime) == GRPC_CREDENTIALS_ERROR); grpc_mdctx_orphan(ctx); @@ -285,17 +343,285 @@ static void test_ssl_oauth2_iam_composite_creds(void) { composite_creds); } +static void on_oauth2_creds_get_metadata_success( + void *user_data, grpc_mdelem **md_elems, size_t num_md, + grpc_credentials_status status) { + GPR_ASSERT(status == GRPC_CREDENTIALS_OK); + GPR_ASSERT(num_md == 1); + GPR_ASSERT( + !strcmp(grpc_mdstr_as_c_string(md_elems[0]->key), "Authorization")); + GPR_ASSERT(!strcmp(grpc_mdstr_as_c_string(md_elems[0]->value), + "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_")); + GPR_ASSERT(user_data != NULL); + GPR_ASSERT(!strcmp((const char *)user_data, test_user_data)); +} + +static void on_oauth2_creds_get_metadata_failure( + void *user_data, grpc_mdelem **md_elems, size_t num_md, + grpc_credentials_status status) { + GPR_ASSERT(status == GRPC_CREDENTIALS_ERROR); + GPR_ASSERT(num_md == 0); + GPR_ASSERT(user_data != NULL); + GPR_ASSERT(!strcmp((const char *)user_data, test_user_data)); +} + +static void validate_compute_engine_http_request( + const grpc_httpcli_request *request) { + GPR_ASSERT(!request->use_ssl); + GPR_ASSERT(!strcmp(request->host, "metadata")); + GPR_ASSERT( + !strcmp(request->path, + "/computeMetadata/v1/instance/service-accounts/default/token")); + GPR_ASSERT(request->hdr_count == 1); + GPR_ASSERT(!strcmp(request->hdrs[0].key, "Metadata-Flavor")); + GPR_ASSERT(!strcmp(request->hdrs[0].value, "Google")); +} + +static int compute_engine_httpcli_get_success_override( + const grpc_httpcli_request *request, gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data) { + grpc_httpcli_response response = + http_response(200, valid_oauth2_json_response); + validate_compute_engine_http_request(request); + on_response(user_data, &response); + return 1; +} + +static int compute_engine_httpcli_get_failure_override( + const grpc_httpcli_request *request, gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data) { + grpc_httpcli_response response = http_response(403, "Not Authorized."); + validate_compute_engine_http_request(request); + on_response(user_data, &response); + return 1; +} + +static int httpcli_post_should_not_be_called( + const grpc_httpcli_request *request, const char *body_bytes, + size_t body_size, gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data) { + GPR_ASSERT("HTTP POST should not be called" == NULL); + return 1; +} + +static int httpcli_get_should_not_be_called( + const grpc_httpcli_request *request, gpr_timespec deadline, + grpc_httpcli_response_cb on_response, void *user_data) { + GPR_ASSERT("HTTP GET should not be called" == NULL); + return 1; +} + +static void test_compute_engine_creds_success(void) { + grpc_credentials *compute_engine_creds = + grpc_compute_engine_credentials_create(); + GPR_ASSERT(grpc_credentials_has_request_metadata(compute_engine_creds)); + GPR_ASSERT(grpc_credentials_has_request_metadata_only(compute_engine_creds)); + + /* First request: http get should be called. */ + grpc_httpcli_set_override(compute_engine_httpcli_get_success_override, + httpcli_post_should_not_be_called); + grpc_credentials_get_request_metadata(compute_engine_creds, + on_oauth2_creds_get_metadata_success, + (void *)test_user_data); + + /* Second request: the cached token should be served directly. */ + grpc_httpcli_set_override(httpcli_get_should_not_be_called, + httpcli_post_should_not_be_called); + grpc_credentials_get_request_metadata(compute_engine_creds, + on_oauth2_creds_get_metadata_success, + (void *)test_user_data); + + grpc_credentials_unref(compute_engine_creds); + grpc_httpcli_set_override(NULL, NULL); +} + +static void test_compute_engine_creds_failure(void) { + grpc_credentials *compute_engine_creds = + grpc_compute_engine_credentials_create(); + grpc_httpcli_set_override(compute_engine_httpcli_get_failure_override, + httpcli_post_should_not_be_called); + GPR_ASSERT(grpc_credentials_has_request_metadata(compute_engine_creds)); + GPR_ASSERT(grpc_credentials_has_request_metadata_only(compute_engine_creds)); + grpc_credentials_get_request_metadata(compute_engine_creds, + on_oauth2_creds_get_metadata_failure, + (void *)test_user_data); + grpc_credentials_unref(compute_engine_creds); + grpc_httpcli_set_override(NULL, NULL); +} + +static void validate_jwt_encode_and_sign_params( + const grpc_auth_json_key *json_key, const char *scope, + gpr_timespec token_lifetime) { + GPR_ASSERT(grpc_auth_json_key_is_valid(json_key)); + GPR_ASSERT(json_key->private_key != NULL); + GPR_ASSERT(RSA_check_key(json_key->private_key)); + GPR_ASSERT(json_key->type != NULL && + !(strcmp(json_key->type, "service_account"))); + GPR_ASSERT(json_key->private_key_id != NULL && + !strcmp(json_key->private_key_id, + "e6b5137873db8d2ef81e06a47289e6434ec8a165")); + GPR_ASSERT(json_key->client_id != NULL && + !strcmp(json_key->client_id, + "777-abaslkan11hlb6nmim3bpspl31ud.apps." + "googleusercontent.com")); + GPR_ASSERT(json_key->client_email != NULL && + !strcmp(json_key->client_email, + "777-abaslkan11hlb6nmim3bpspl31ud@developer." + "gserviceaccount.com")); + GPR_ASSERT(!strcmp(scope, test_scope)); + GPR_ASSERT(!gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime)); +} + +static char *encode_and_sign_jwt_success(const grpc_auth_json_key *json_key, + const char *scope, + gpr_timespec token_lifetime) { + validate_jwt_encode_and_sign_params(json_key, scope, token_lifetime); + return gpr_strdup(test_signed_jwt); +} + +static char *encode_and_sign_jwt_failure(const grpc_auth_json_key *json_key, + const char *scope, + gpr_timespec token_lifetime) { + validate_jwt_encode_and_sign_params(json_key, scope, token_lifetime); + return NULL; +} + +static char *encode_and_sign_jwt_should_not_be_called( + const grpc_auth_json_key *json_key, const char *scope, + gpr_timespec token_lifetime) { + GPR_ASSERT("grpc_jwt_encode_and_sign should not be called" == NULL); +} + +static void validate_service_account_http_request( + const grpc_httpcli_request *request, const char *body, size_t body_size) { + /* The content of the assertion is tested extensively in json_token_test. */ + char *expected_body = NULL; + GPR_ASSERT(body != NULL); + GPR_ASSERT(body_size != 0); + expected_body = gpr_malloc(strlen(expected_service_account_http_body_prefix) + + strlen(test_signed_jwt) + 1); + sprintf(expected_body, "%s%s", expected_service_account_http_body_prefix, + test_signed_jwt); + GPR_ASSERT(strlen(expected_body) == body_size); + GPR_ASSERT(!memcmp(expected_body, body, body_size)); + gpr_free(expected_body); + GPR_ASSERT(request->use_ssl); + GPR_ASSERT(!strcmp(request->host, "www.googleapis.com")); + GPR_ASSERT(!strcmp(request->path, "/oauth2/v3/token")); + GPR_ASSERT(request->hdr_count == 1); + GPR_ASSERT(!strcmp(request->hdrs[0].key, "Content-Type")); + GPR_ASSERT( + !strcmp(request->hdrs[0].value, "application/x-www-form-urlencoded")); +} + +static int service_account_httpcli_post_success( + const grpc_httpcli_request *request, const char *body, size_t body_size, + gpr_timespec deadline, grpc_httpcli_response_cb on_response, + void *user_data) { + grpc_httpcli_response response = + http_response(200, valid_oauth2_json_response); + validate_service_account_http_request(request, body, body_size); + on_response(user_data, &response); + return 1; +} + +static int service_account_httpcli_post_failure( + const grpc_httpcli_request *request, const char *body, size_t body_size, + gpr_timespec deadline, grpc_httpcli_response_cb on_response, + void *user_data) { + grpc_httpcli_response response = http_response(403, "Not Authorized."); + validate_service_account_http_request(request, body, body_size); + on_response(user_data, &response); + return 1; +} + +static void test_service_accounts_creds_success(void) { + char *json_key_string = test_json_key_str(); + grpc_credentials *service_account_creds = + grpc_service_account_credentials_create(json_key_string, test_scope, + grpc_max_auth_token_lifetime); + GPR_ASSERT(grpc_credentials_has_request_metadata(service_account_creds)); + GPR_ASSERT(grpc_credentials_has_request_metadata_only(service_account_creds)); + + /* First request: http get should be called. */ + grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_success); + grpc_httpcli_set_override(httpcli_get_should_not_be_called, + service_account_httpcli_post_success); + grpc_credentials_get_request_metadata(service_account_creds, + on_oauth2_creds_get_metadata_success, + (void *)test_user_data); + + /* Second request: the cached token should be served directly. */ + grpc_jwt_encode_and_sign_set_override( + encode_and_sign_jwt_should_not_be_called); + grpc_httpcli_set_override(httpcli_get_should_not_be_called, + httpcli_post_should_not_be_called); + grpc_credentials_get_request_metadata(service_account_creds, + on_oauth2_creds_get_metadata_success, + (void *)test_user_data); + + gpr_free(json_key_string); + grpc_credentials_unref(service_account_creds); + grpc_jwt_encode_and_sign_set_override(NULL); + grpc_httpcli_set_override(NULL, NULL); +} + +static void test_service_accounts_creds_http_failure(void) { + char *json_key_string = test_json_key_str(); + grpc_credentials *service_account_creds = + grpc_service_account_credentials_create(json_key_string, test_scope, + grpc_max_auth_token_lifetime); + GPR_ASSERT(grpc_credentials_has_request_metadata(service_account_creds)); + GPR_ASSERT(grpc_credentials_has_request_metadata_only(service_account_creds)); + + grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_success); + grpc_httpcli_set_override(httpcli_get_should_not_be_called, + service_account_httpcli_post_failure); + grpc_credentials_get_request_metadata(service_account_creds, + on_oauth2_creds_get_metadata_failure, + (void *)test_user_data); + + gpr_free(json_key_string); + grpc_credentials_unref(service_account_creds); + grpc_httpcli_set_override(NULL, NULL); +} + +static void test_service_accounts_creds_signing_failure(void) { + char *json_key_string = test_json_key_str(); + grpc_credentials *service_account_creds = + grpc_service_account_credentials_create(json_key_string, test_scope, + grpc_max_auth_token_lifetime); + GPR_ASSERT(grpc_credentials_has_request_metadata(service_account_creds)); + GPR_ASSERT(grpc_credentials_has_request_metadata_only(service_account_creds)); + + grpc_jwt_encode_and_sign_set_override(encode_and_sign_jwt_failure); + grpc_httpcli_set_override(httpcli_get_should_not_be_called, + httpcli_post_should_not_be_called); + grpc_credentials_get_request_metadata(service_account_creds, + on_oauth2_creds_get_metadata_failure, + (void *)test_user_data); + + gpr_free(json_key_string); + grpc_credentials_unref(service_account_creds); + grpc_httpcli_set_override(NULL, NULL); +} + int main(int argc, char **argv) { grpc_test_init(argc, argv); - test_compute_engine_creds_parsing_ok(); - test_compute_engine_creds_parsing_bad_http_status(); - test_compute_engine_creds_parsing_empty_http_body(); - test_compute_engine_creds_parsing_invalid_json(); - test_compute_engine_creds_parsing_missing_token(); - test_compute_engine_creds_parsing_missing_token_type(); - test_compute_engine_creds_parsing_missing_token_lifetime(); + test_oauth2_token_fetcher_creds_parsing_ok(); + test_oauth2_token_fetcher_creds_parsing_bad_http_status(); + test_oauth2_token_fetcher_creds_parsing_empty_http_body(); + test_oauth2_token_fetcher_creds_parsing_invalid_json(); + test_oauth2_token_fetcher_creds_parsing_missing_token(); + test_oauth2_token_fetcher_creds_parsing_missing_token_type(); + test_oauth2_token_fetcher_creds_parsing_missing_token_lifetime(); test_iam_creds(); test_ssl_oauth2_composite_creds(); test_ssl_oauth2_iam_composite_creds(); + test_compute_engine_creds_success(); + test_compute_engine_creds_failure(); + test_service_accounts_creds_success(); + test_service_accounts_creds_http_failure(); + test_service_accounts_creds_signing_failure(); return 0; } diff --git a/test/core/security/fetch_oauth2.c b/test/core/security/fetch_oauth2.c new file mode 100644 index 0000000000..c5cc3adfd6 --- /dev/null +++ b/test/core/security/fetch_oauth2.c @@ -0,0 +1,177 @@ +/* + * + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <stdio.h> +#include <string.h> + +#include "src/core/security/credentials.h" +#include <grpc/grpc.h> +#include <grpc/grpc_security.h> +#include <grpc/support/alloc.h> +#include <grpc/support/cmdline.h> +#include <grpc/support/log.h> +#include <grpc/support/slice.h> +#include <grpc/support/sync.h> + +typedef struct { + gpr_cv cv; + gpr_mu mu; + int is_done; +} synchronizer; + +static void on_oauth2_response(void *user_data, grpc_mdelem **md_elems, + size_t num_md, grpc_credentials_status status) { + synchronizer *sync = user_data; + char *token; + gpr_slice token_slice; + if (status == GRPC_CREDENTIALS_ERROR) { + gpr_log(GPR_ERROR, "Fetching token failed."); + } else { + GPR_ASSERT(num_md == 1); + token_slice = md_elems[0]->value->slice; + token = gpr_malloc(GPR_SLICE_LENGTH(token_slice) + 1); + memcpy(token, GPR_SLICE_START_PTR(token_slice), + GPR_SLICE_LENGTH(token_slice)); + token[GPR_SLICE_LENGTH(token_slice)] = '\0'; + printf("Got token: %s.\n", token); + gpr_free(token); + } + gpr_mu_lock(&sync->mu); + sync->is_done = 1; + gpr_mu_unlock(&sync->mu); + gpr_cv_signal(&sync->cv); +} + +static grpc_credentials *create_service_account_creds( + const char *json_key_file_path, const char *scope) { + char json_key[8192]; /* Should be plenty. */ + char *current = json_key; + FILE *json_key_file = fopen(json_key_file_path, "r"); + if (json_key_file == NULL) { + gpr_log(GPR_ERROR, "Invalid path for json key file: %s.", + json_key_file_path); + exit(1); + } + + do { + size_t bytes_read = fread( + current, 1, sizeof(json_key) - (current - json_key), json_key_file); + if (bytes_read == 0) { + if (!feof(json_key_file)) { + gpr_log(GPR_ERROR, "Error occured while reading %s.", + json_key_file_path); + exit(1); + } + break; + } + current += bytes_read; + } while (sizeof(json_key) > (current - json_key)); + + if ((current - json_key) == sizeof(json_key)) { + gpr_log(GPR_ERROR, "Json key file %s exceeds size limit (%d bytes).", + json_key_file_path, (int)sizeof(json_key)); + exit(1); + } + fclose(json_key_file); + + return grpc_service_account_credentials_create(json_key, scope, + grpc_max_auth_token_lifetime); +} + +int main(int argc, char **argv) { + synchronizer sync; + grpc_credentials *creds = NULL; + char *json_key_file_path = NULL; + int use_gce = 0; + char *scope = NULL; + gpr_cmdline *cl = gpr_cmdline_create("fetch_oauth2"); + gpr_cmdline_add_string(cl, "json_key", "File path of the json key.", + &json_key_file_path); + gpr_cmdline_add_string(cl, "scope", "Space delimited permissions.", &scope); + gpr_cmdline_add_flag( + cl, "gce", + "Get a token from the GCE metadata server (only works in GCE).", + &use_gce); + gpr_cmdline_parse(cl, argc, argv); + + grpc_init(); + + if (use_gce) { + if (json_key_file_path != NULL || scope != NULL) { + gpr_log(GPR_INFO, + "Ignoring json key and scope to get a token from the GCE " + "metadata server."); + } + creds = grpc_compute_engine_credentials_create(); + if (creds == NULL) { + gpr_log(GPR_ERROR, "Could not create gce credentials."); + exit(1); + } + } else { + if (json_key_file_path == NULL) { + gpr_log(GPR_ERROR, "missing --json_key option."); + exit(1); + } + if (scope == NULL) { + gpr_log(GPR_ERROR, "Missing --scope option."); + exit(1); + } + + creds = create_service_account_creds(json_key_file_path, scope); + if (creds == NULL) { + gpr_log(GPR_ERROR, + "Could not create service account creds. %s does probably not " + "contain a valid json key.", + json_key_file_path); + exit(1); + } + } + GPR_ASSERT(creds != NULL); + + gpr_mu_init(&sync.mu); + gpr_cv_init(&sync.cv); + sync.is_done = 0; + + grpc_credentials_get_request_metadata(creds, on_oauth2_response, &sync); + + gpr_mu_lock(&sync.mu); + while (!sync.is_done) gpr_cv_wait(&sync.cv, &sync.mu, gpr_inf_future); + gpr_mu_unlock(&sync.mu); + + gpr_mu_destroy(&sync.mu); + gpr_cv_destroy(&sync.cv); + grpc_credentials_release(creds); + gpr_cmdline_destroy(cl); + grpc_shutdown(); + return 0; +} |