/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include "shared_credentials_test_definitions.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_relative_uri, "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"); AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_full_uri, "AWS_CONTAINER_CREDENTIALS_FULL_URI"); AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_token_file, "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"); AWS_STATIC_STRING_FROM_LITERAL(s_ecs_creds_env_token, "AWS_CONTAINER_AUTHORIZATION_TOKEN"); struct aws_mock_ecs_tester { struct aws_allocator *allocator; struct aws_string *request_path_and_query; struct aws_string *request_authorization_header; struct aws_string *selected_host; struct aws_array_list response_data_callbacks; bool is_connection_acquire_successful; bool is_request_successful; struct aws_mutex lock; struct aws_condition_variable signal; struct aws_credentials *credentials; bool has_received_credentials_callback; bool has_received_shutdown_callback; bool selected_tls; uint32_t selected_port; int error_code; struct aws_event_loop_group *el_group; struct aws_host_resolver *host_resolver; struct aws_client_bootstrap *bootstrap; }; static struct aws_mock_ecs_tester s_tester; static void s_on_shutdown_complete(void *user_data) { (void)user_data; aws_mutex_lock(&s_tester.lock); s_tester.has_received_shutdown_callback = true; aws_mutex_unlock(&s_tester.lock); aws_condition_variable_notify_one(&s_tester.signal); } static bool s_has_tester_received_shutdown_callback(void *user_data) { (void)user_data; return s_tester.has_received_shutdown_callback; } static void s_aws_wait_for_provider_shutdown_callback(void) { aws_mutex_lock(&s_tester.lock); aws_condition_variable_wait_pred(&s_tester.signal, &s_tester.lock, s_has_tester_received_shutdown_callback, NULL); aws_mutex_unlock(&s_tester.lock); } static struct aws_http_connection_manager *s_aws_http_connection_manager_new_mock( struct aws_allocator *allocator, const struct aws_http_connection_manager_options *options) { (void)allocator; (void)options; aws_mutex_lock(&s_tester.lock); s_tester.selected_host = aws_string_new_from_cursor(allocator, &options->host); s_tester.selected_port = options->port; s_tester.selected_tls = options->tls_connection_options != NULL; aws_mutex_unlock(&s_tester.lock); return (struct aws_http_connection_manager *)1; } static void s_aws_http_connection_manager_release_mock(struct aws_http_connection_manager *manager) { (void)manager; s_on_shutdown_complete(NULL); } static void s_aws_http_connection_manager_acquire_connection_mock( struct aws_http_connection_manager *manager, aws_http_connection_manager_on_connection_setup_fn *callback, void *user_data) { (void)manager; (void)callback; (void)user_data; if (s_tester.is_connection_acquire_successful) { callback((struct aws_http_connection *)1, AWS_OP_SUCCESS, user_data); } else { aws_raise_error(AWS_ERROR_HTTP_UNKNOWN); callback(NULL, AWS_OP_ERR, user_data); } } static int s_aws_http_connection_manager_release_connection_mock( struct aws_http_connection_manager *manager, struct aws_http_connection *connection) { (void)manager; (void)connection; return AWS_OP_SUCCESS; } static void s_invoke_mock_request_callbacks( const struct aws_http_make_request_options *options, struct aws_array_list *data_callbacks, bool is_request_successful) { size_t data_callback_count = aws_array_list_length(data_callbacks); struct aws_http_header headers[1]; AWS_ZERO_ARRAY(headers); headers[0].name = aws_byte_cursor_from_c_str("some-header"); headers[0].value = aws_byte_cursor_from_c_str("value"); options->on_response_headers( (struct aws_http_stream *)1, AWS_HTTP_HEADER_BLOCK_MAIN, headers, 1, options->user_data); if (options->on_response_header_block_done) { options->on_response_header_block_done( (struct aws_http_stream *)1, data_callback_count > 0, options->user_data); } for (size_t i = 0; i < data_callback_count; ++i) { struct aws_byte_cursor data_callback_cursor; if (aws_array_list_get_at(data_callbacks, &data_callback_cursor, i)) { continue; } options->on_response_body((struct aws_http_stream *)1, &data_callback_cursor, options->user_data); } options->on_complete( (struct aws_http_stream *)1, is_request_successful ? AWS_ERROR_SUCCESS : AWS_ERROR_HTTP_UNKNOWN, options->user_data); } static struct aws_http_stream *s_aws_http_connection_make_request_mock( struct aws_http_connection *client_connection, const struct aws_http_make_request_options *options) { (void)client_connection; (void)options; struct aws_byte_cursor path; AWS_ZERO_STRUCT(path); aws_http_message_get_request_path(options->request, &path); s_tester.request_path_and_query = aws_string_new_from_cursor(s_tester.allocator, &path); struct aws_byte_cursor authorization_header_value; AWS_ZERO_STRUCT(authorization_header_value); if (aws_http_headers_get( aws_http_message_get_headers(options->request), aws_byte_cursor_from_c_str("Authorization"), &authorization_header_value) == AWS_OP_SUCCESS) { s_tester.request_authorization_header = aws_string_new_from_cursor(s_tester.allocator, &authorization_header_value); } s_invoke_mock_request_callbacks(options, &s_tester.response_data_callbacks, s_tester.is_request_successful); return (struct aws_http_stream *)1; } static int s_aws_http_stream_activate_mock(struct aws_http_stream *stream) { (void)stream; return AWS_OP_SUCCESS; } static int s_aws_http_stream_get_incoming_response_status_mock( const struct aws_http_stream *stream, int *out_status_code) { (void)stream; *out_status_code = AWS_HTTP_STATUS_CODE_200_OK; return AWS_OP_SUCCESS; } static void s_aws_http_stream_release_mock(struct aws_http_stream *stream) { (void)stream; } static void s_aws_http_connection_close_mock(struct aws_http_connection *connection) { (void)connection; } static struct aws_auth_http_system_vtable s_mock_function_table = { .aws_http_connection_manager_new = s_aws_http_connection_manager_new_mock, .aws_http_connection_manager_release = s_aws_http_connection_manager_release_mock, .aws_http_connection_manager_acquire_connection = s_aws_http_connection_manager_acquire_connection_mock, .aws_http_connection_manager_release_connection = s_aws_http_connection_manager_release_connection_mock, .aws_http_connection_make_request = s_aws_http_connection_make_request_mock, .aws_http_stream_activate = s_aws_http_stream_activate_mock, .aws_http_stream_get_incoming_response_status = s_aws_http_stream_get_incoming_response_status_mock, .aws_http_stream_release = s_aws_http_stream_release_mock, .aws_http_connection_close = s_aws_http_connection_close_mock}; static int s_aws_ecs_tester_init(struct aws_allocator *allocator) { aws_auth_library_init(allocator); s_tester.allocator = allocator; if (aws_array_list_init_dynamic(&s_tester.response_data_callbacks, allocator, 10, sizeof(struct aws_byte_cursor))) { return AWS_OP_ERR; } if (aws_mutex_init(&s_tester.lock)) { return AWS_OP_ERR; } if (aws_condition_variable_init(&s_tester.signal)) { return AWS_OP_ERR; } /* default to everything successful */ s_tester.is_connection_acquire_successful = true; s_tester.is_request_successful = true; s_tester.el_group = aws_event_loop_group_new_default(allocator, 1, NULL); struct aws_host_resolver_default_options resolver_options = { .el_group = s_tester.el_group, .max_entries = 8, }; s_tester.host_resolver = aws_host_resolver_new_default(allocator, &resolver_options); struct aws_client_bootstrap_options bootstrap_options = { .event_loop_group = s_tester.el_group, .host_resolver = s_tester.host_resolver, }; s_tester.bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); /* ensure pre-existing environment doesn't interfere with tests */ aws_unset_environment_value(s_ecs_creds_env_relative_uri); aws_unset_environment_value(s_ecs_creds_env_full_uri); aws_unset_environment_value(s_ecs_creds_env_token_file); aws_unset_environment_value(s_ecs_creds_env_token); return AWS_OP_SUCCESS; } static void s_aws_ecs_tester_reset(void) { aws_array_list_clean_up(&s_tester.response_data_callbacks); aws_string_destroy(s_tester.request_path_and_query); aws_string_destroy(s_tester.request_authorization_header); aws_string_destroy(s_tester.selected_host); aws_condition_variable_clean_up(&s_tester.signal); aws_mutex_clean_up(&s_tester.lock); aws_credentials_release(s_tester.credentials); aws_client_bootstrap_release(s_tester.bootstrap); aws_host_resolver_release(s_tester.host_resolver); aws_event_loop_group_release(s_tester.el_group); AWS_ZERO_STRUCT(s_tester); } static void s_aws_ecs_tester_cleanup(void) { s_aws_ecs_tester_reset(); aws_auth_library_clean_up(); } static bool s_has_tester_received_credentials_callback(void *user_data) { (void)user_data; return s_tester.has_received_credentials_callback; } static void s_aws_wait_for_credentials_result(void) { aws_mutex_lock(&s_tester.lock); aws_condition_variable_wait_pred( &s_tester.signal, &s_tester.lock, s_has_tester_received_credentials_callback, NULL); aws_mutex_unlock(&s_tester.lock); } static void s_get_credentials_callback(struct aws_credentials *credentials, int error_code, void *user_data) { (void)user_data; aws_mutex_lock(&s_tester.lock); s_tester.has_received_credentials_callback = true; s_tester.error_code = error_code; s_tester.credentials = credentials; aws_credentials_acquire(credentials); aws_condition_variable_notify_one(&s_tester.signal); aws_mutex_unlock(&s_tester.lock); } static int s_credentials_provider_ecs_new_destroy(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_ecs_tester_init(allocator); struct aws_credentials_provider_ecs_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); /* Because we mock the http connection manager, we never get a callback back from it */ aws_mem_release(provider->allocator, provider); s_aws_ecs_tester_cleanup(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_new_destroy, s_credentials_provider_ecs_new_destroy); static int s_credentials_provider_ecs_connect_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_ecs_tester_init(allocator); s_tester.is_connection_acquire_successful = false; struct aws_credentials_provider_ecs_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials == NULL); ASSERT_UINT_EQUALS(80, s_tester.selected_port); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); /* Because we mock the http connection manager, we never get a callback back from it */ aws_mem_release(provider->allocator, provider); s_aws_ecs_tester_cleanup(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_connect_failure, s_credentials_provider_ecs_connect_failure); static int s_credentials_provider_ecs_request_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_ecs_tester_init(allocator); s_tester.is_request_successful = false; struct aws_credentials_provider_ecs_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_STR_EQUALS("/path/to/resource/?a=b&c=d", aws_string_c_str(s_tester.request_path_and_query)); ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials == NULL); ASSERT_UINT_EQUALS(80, s_tester.selected_port); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); /* Because we mock the http connection manager, we never get a callback back from it */ aws_mem_release(provider->allocator, provider); s_aws_ecs_tester_cleanup(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_request_failure, s_credentials_provider_ecs_request_failure); AWS_STATIC_STRING_FROM_LITERAL(s_bad_document_response, "{\"NotTheExpectedDocumentFormat\":\"Error\"}"); static int s_credentials_provider_ecs_bad_document_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_ecs_tester_init(allocator); struct aws_byte_cursor bad_document_cursor = aws_byte_cursor_from_string(s_bad_document_response); aws_array_list_push_back(&s_tester.response_data_callbacks, &bad_document_cursor); struct aws_credentials_provider_ecs_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), .port = 555, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_STR_EQUALS("/path/to/resource/?a=b&c=d", aws_string_c_str(s_tester.request_path_and_query)); ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials == NULL); ASSERT_UINT_EQUALS(555, s_tester.selected_port); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); /* Because we mock the http connection manager, we never get a callback back from it */ aws_mem_release(provider->allocator, provider); s_aws_ecs_tester_cleanup(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_bad_document_failure, s_credentials_provider_ecs_bad_document_failure); AWS_STATIC_STRING_FROM_LITERAL( s_good_response, "{\"AccessKeyId\":\"SuccessfulAccessKey\", \n \"SecretAccessKey\":\"SuccessfulSecret\", \n " "\"Token\":\"TokenSuccess\", \n \"Expiration\":\"2020-02-25T06:03:31Z\"}"); AWS_STATIC_STRING_FROM_LITERAL(s_good_access_key_id, "SuccessfulAccessKey"); AWS_STATIC_STRING_FROM_LITERAL(s_good_secret_access_key, "SuccessfulSecret"); AWS_STATIC_STRING_FROM_LITERAL(s_good_session_token, "TokenSuccess"); AWS_STATIC_STRING_FROM_LITERAL(s_good_response_expiration, "2020-02-25T06:03:31Z"); /* Check that expected URI and Authorization token were used to make request. * URI must be super explicit, specifying scheme and port. */ static int s_check_ecs_tester_request_uri_and_authorization(const char *expected_uri_cstr, const char *expected_token) { struct aws_byte_cursor expected_uri_cursor = aws_byte_cursor_from_c_str(expected_uri_cstr); struct aws_uri expected_uri; ASSERT_SUCCESS(aws_uri_init_parse(&expected_uri, s_tester.allocator, &expected_uri_cursor)); ASSERT_TRUE(aws_uri_scheme(&expected_uri)->len != 0); ASSERT_TRUE(aws_uri_port(&expected_uri) != 0); ASSERT_CURSOR_VALUE_STRING_EQUALS(*aws_uri_host_name(&expected_uri), s_tester.selected_host); ASSERT_CURSOR_VALUE_STRING_EQUALS(*aws_uri_path_and_query(&expected_uri), s_tester.request_path_and_query); ASSERT_INT_EQUALS( aws_byte_cursor_eq_c_str_ignore_case(aws_uri_scheme(&expected_uri), "https"), s_tester.selected_tls); ASSERT_UINT_EQUALS(aws_uri_port(&expected_uri), s_tester.selected_port); if (expected_token != NULL) { ASSERT_STR_EQUALS(expected_token, aws_string_c_str(s_tester.request_authorization_header)); } else { ASSERT_NULL(s_tester.request_authorization_header); } aws_uri_clean_up(&expected_uri); return 0; } static int s_do_ecs_success_test( struct aws_allocator *allocator, struct aws_credentials_provider_ecs_options *options, const char *expected_uri, const char *expected_token) { struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_SUCCESS(s_check_ecs_tester_request_uri_and_authorization(expected_uri, expected_token)); ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials != NULL); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_access_key_id(s_tester.credentials), s_good_access_key_id); ASSERT_CURSOR_VALUE_STRING_EQUALS( aws_credentials_get_secret_access_key(s_tester.credentials), s_good_secret_access_key); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_session_token(s_tester.credentials), s_good_session_token); struct aws_date_time expiration; struct aws_byte_cursor date_cursor = aws_byte_cursor_from_string(s_good_response_expiration); aws_date_time_init_from_str_cursor(&expiration, &date_cursor, AWS_DATE_FORMAT_ISO_8601); ASSERT_TRUE( aws_credentials_get_expiration_timepoint_seconds(s_tester.credentials) == (uint64_t)expiration.timestamp); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); /* Because we mock the http connection manager, we never get a callback back from it */ aws_mem_release(provider->allocator, provider); return AWS_OP_SUCCESS; } static int s_do_ecs_env_success_test( struct aws_allocator *allocator, struct aws_credentials_provider_ecs_environment_options *options, const char *relative_uri, const char *full_uri, const char *auth_token, const char *auth_token_file_content, const char *expected_uri, const char *expected_token) { struct aws_string *relative_uri_str = NULL; if (relative_uri != NULL) { relative_uri_str = aws_string_new_from_c_str(allocator, relative_uri); ASSERT_SUCCESS(aws_set_environment_value(s_ecs_creds_env_relative_uri, relative_uri_str)); } struct aws_string *full_uri_str = NULL; if (full_uri != NULL) { full_uri_str = aws_string_new_from_c_str(allocator, full_uri); ASSERT_SUCCESS(aws_set_environment_value(s_ecs_creds_env_full_uri, full_uri_str)); } struct aws_string *auth_token_str = NULL; if (auth_token != NULL) { auth_token_str = aws_string_new_from_c_str(allocator, auth_token); ASSERT_SUCCESS(aws_set_environment_value(s_ecs_creds_env_token, auth_token_str)); } struct aws_string *auth_token_file_contents_str = NULL; struct aws_string *auth_token_file_path = NULL; if (auth_token_file_content != NULL) { auth_token_file_contents_str = aws_string_new_from_c_str(allocator, auth_token_file_content); auth_token_file_path = aws_create_process_unique_file_name(allocator); ASSERT_NOT_NULL(auth_token_file_path); ASSERT_TRUE(aws_create_profile_file(auth_token_file_path, auth_token_file_contents_str) == AWS_OP_SUCCESS); ASSERT_SUCCESS(aws_set_environment_value(s_ecs_creds_env_token_file, auth_token_file_path)); } struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs_from_environment(allocator, options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_SUCCESS(s_check_ecs_tester_request_uri_and_authorization(expected_uri, expected_token)); ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials != NULL); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_access_key_id(s_tester.credentials), s_good_access_key_id); ASSERT_CURSOR_VALUE_STRING_EQUALS( aws_credentials_get_secret_access_key(s_tester.credentials), s_good_secret_access_key); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_session_token(s_tester.credentials), s_good_session_token); struct aws_date_time expiration; struct aws_byte_cursor date_cursor = aws_byte_cursor_from_string(s_good_response_expiration); aws_date_time_init_from_str_cursor(&expiration, &date_cursor, AWS_DATE_FORMAT_ISO_8601); ASSERT_TRUE( aws_credentials_get_expiration_timepoint_seconds(s_tester.credentials) == (uint64_t)expiration.timestamp); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); /* Because we mock the http connection manager, we never get a callback back from it */ aws_mem_release(provider->allocator, provider); if (auth_token_file_path != NULL) { aws_file_delete(auth_token_file_path); } aws_string_destroy(relative_uri_str); aws_string_destroy(full_uri_str); aws_string_destroy(auth_token_str); aws_string_destroy(auth_token_file_contents_str); aws_string_destroy(auth_token_file_path); return AWS_OP_SUCCESS; } static int s_credentials_provider_ecs_basic_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_ecs_tester_init(allocator); struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); aws_array_list_push_back(&s_tester.response_data_callbacks, &good_response_cursor); struct aws_credentials_provider_ecs_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), }; ASSERT_SUCCESS(s_do_ecs_success_test( allocator, &options, "http://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d" /*expected_uri*/, "test-token-1234-abcd" /*expected_token*/)); s_aws_ecs_tester_cleanup(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_basic_success, s_credentials_provider_ecs_basic_success); static int s_credentials_provider_ecs_basic_success_token_file(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_ecs_tester_init(allocator); struct aws_string *auth_token = aws_string_new_from_c_str(allocator, "test-token-1234-abcd"); struct aws_string *token_file_path = aws_create_process_unique_file_name(allocator); ASSERT_NOT_NULL(token_file_path); ASSERT_TRUE(aws_create_profile_file(token_file_path, auth_token) == AWS_OP_SUCCESS); /* test that static auth token is not preferred over file token */ struct aws_string *bad_auth_token = aws_string_new_from_c_str(allocator, "badtoken"); struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); aws_array_list_push_back(&s_tester.response_data_callbacks, &good_response_cursor); struct aws_credentials_provider_ecs_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_string(auth_token), .auth_token_file_path = aws_byte_cursor_from_string(token_file_path), }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_SUCCESS(s_check_ecs_tester_request_uri_and_authorization( "http://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d", aws_string_c_str(auth_token))); aws_string_destroy(s_tester.request_path_and_query); aws_string_destroy(s_tester.request_authorization_header); aws_credentials_release(s_tester.credentials); aws_mutex_unlock(&s_tester.lock); /* update the file with updated token */ struct aws_string *auth_token2 = aws_string_new_from_c_str(allocator, "test-token2-4321-qwer"); ASSERT_TRUE(aws_create_profile_file(token_file_path, auth_token2) == AWS_OP_SUCCESS); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_SUCCESS(s_check_ecs_tester_request_uri_and_authorization( "http://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d", aws_string_c_str(auth_token2))); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); /* Because we mock the http connection manager, we never get a callback back from it */ aws_mem_release(provider->allocator, provider); s_aws_ecs_tester_cleanup(); aws_file_delete(token_file_path); aws_string_destroy(auth_token); aws_string_destroy(auth_token2); aws_string_destroy(token_file_path); aws_string_destroy(bad_auth_token); return 0; } AWS_TEST_CASE(credentials_provider_ecs_basic_success_token_file, s_credentials_provider_ecs_basic_success_token_file); static int s_credentials_provider_ecs_basic_success_uri_env(struct aws_allocator *allocator, void *ctx) { (void)ctx; aws_auth_library_init(allocator); const struct test_case { const char *relative_uri; const char *full_uri; const char *expected_uri; const char *auth_token; const char *auth_token_file_content; const char *expected_auth_token; } test_cases[] = { /* simple full uri*/ { .full_uri = "http://127.0.0.1/credentials", .expected_uri = "http://127.0.0.1:80/credentials", }, /* explicit port */ { .full_uri = "http://127.0.0.1:8080/credentials", .expected_uri = "http://127.0.0.1:8080/credentials", }, /* https */ { .full_uri = "https://www.xxx123321testmocknonexsitingawsservice.com/credentials", .expected_uri = "https://www.xxx123321testmocknonexsitingawsservice.com:443/credentials", }, /* path and query */ { .full_uri = "http://127.0.0.1/path/to/resource/?a=b&c=d", .expected_uri = "http://127.0.0.1:80/path/to/resource/?a=b&c=d", }, /* relative URI */ { .relative_uri = "/path/to/resource/?a=b&c=d", .expected_uri = "http://169.254.170.2:80/path/to/resource/?a=b&c=d", }, /* relative URI takes priority, when both RELATIVE and FULL are set */ { .relative_uri = "/from-relative-uri", .full_uri = "http://127.0.0.1/from-full-uri", .expected_uri = "http://169.254.170.2:80/from-relative-uri", }, /* auth token is properly set */ { .full_uri = "http://127.0.0.1:8080/credentials", .expected_uri = "http://127.0.0.1:8080/credentials", .auth_token = "testToken", .expected_auth_token = "testToken", }, /* auth_token is respected */ { .full_uri = "http://127.0.0.1:8080/credentials", .expected_uri = "http://127.0.0.1:8080/credentials", .auth_token = "testToken", .expected_auth_token = "testToken", }, /* auth_token_file_path is respected */ { .full_uri = "http://127.0.0.1:8080/credentials", .expected_uri = "http://127.0.0.1:8080/credentials", .auth_token_file_content = "testToken", .expected_auth_token = "testToken", }, /* auth_token_file_path is preferred */ { .full_uri = "http://127.0.0.1:8080/credentials", .expected_uri = "http://127.0.0.1:8080/credentials", .auth_token = "BadToken", .auth_token_file_content = "testToken", .expected_auth_token = "testToken", }, }; /* Provide tls_ctx, in case FULL_URI scheme is "https://" */ struct aws_tls_ctx_options tls_options; aws_tls_ctx_options_init_default_client(&tls_options, allocator); struct aws_tls_ctx *tls_ctx = aws_tls_client_ctx_new(allocator, &tls_options); ASSERT_NOT_NULL(tls_ctx); for (size_t case_idx = 0; case_idx < AWS_ARRAY_SIZE(test_cases); ++case_idx) { struct test_case case_i = test_cases[case_idx]; printf( "CASE[%zu]: AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=%s AWS_CONTAINER_CREDENTIALS_FULL_URI=%s\n, " "AWS_CONTAINER_AUTHORIZATION_TOKEN=%s\n, auth_token_file_content=%s\n", case_idx, case_i.relative_uri ? case_i.relative_uri : "", case_i.full_uri ? case_i.full_uri : "", case_i.auth_token ? case_i.auth_token : "", case_i.auth_token_file_content ? case_i.auth_token_file_content : ""); /* This unsets previous env vars */ ASSERT_SUCCESS(s_aws_ecs_tester_init(allocator)); struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); aws_array_list_push_back(&s_tester.response_data_callbacks, &good_response_cursor); struct aws_credentials_provider_ecs_environment_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .tls_ctx = tls_ctx, }; ASSERT_SUCCESS(s_do_ecs_env_success_test( allocator, &options, case_i.relative_uri, case_i.full_uri, case_i.auth_token, case_i.auth_token_file_content, case_i.expected_uri, case_i.expected_auth_token)); s_aws_ecs_tester_reset(); } aws_tls_ctx_release(tls_ctx); aws_tls_ctx_options_clean_up(&tls_options); s_aws_ecs_tester_cleanup(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_basic_success_uri_env, s_credentials_provider_ecs_basic_success_uri_env); static int s_credentials_provider_ecs_no_auth_token_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_ecs_tester_init(allocator); struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); aws_array_list_push_back(&s_tester.response_data_callbacks, &good_response_cursor); struct aws_credentials_provider_ecs_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), }; ASSERT_SUCCESS(s_do_ecs_success_test( allocator, &options, "http://www.xxx123321testmocknonexsitingawsservice.com:80/path/to/resource/?a=b&c=d" /*expected_uri*/, NULL /*expected_token*/)); s_aws_ecs_tester_cleanup(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_no_auth_token_success, s_credentials_provider_ecs_no_auth_token_success); AWS_STATIC_STRING_FROM_LITERAL(s_good_response_first_part, "{\"AccessKeyId\":\"SuccessfulAccessKey\", \n \"Secret"); AWS_STATIC_STRING_FROM_LITERAL(s_good_response_second_part, "AccessKey\":\"SuccessfulSecret\", \n \"Token\":\"Token"); AWS_STATIC_STRING_FROM_LITERAL(s_good_response_third_part, "Success\", \n \"Expiration\":\"2020-02-25T06:03:31Z\"}"); static int s_credentials_provider_ecs_success_multi_part_doc(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_ecs_tester_init(allocator); struct aws_byte_cursor good_response_cursor1 = aws_byte_cursor_from_string(s_good_response_first_part); struct aws_byte_cursor good_response_cursor2 = aws_byte_cursor_from_string(s_good_response_second_part); struct aws_byte_cursor good_response_cursor3 = aws_byte_cursor_from_string(s_good_response_third_part); aws_array_list_push_back(&s_tester.response_data_callbacks, &good_response_cursor1); aws_array_list_push_back(&s_tester.response_data_callbacks, &good_response_cursor2); aws_array_list_push_back(&s_tester.response_data_callbacks, &good_response_cursor3); struct aws_credentials_provider_ecs_options options = { .bootstrap = s_tester.bootstrap, .function_table = &s_mock_function_table, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_STR_EQUALS("/path/to/resource/?a=b&c=d", aws_string_c_str(s_tester.request_path_and_query)); ASSERT_TRUE(s_tester.has_received_credentials_callback == true); ASSERT_TRUE(s_tester.credentials != NULL); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_access_key_id(s_tester.credentials), s_good_access_key_id); ASSERT_CURSOR_VALUE_STRING_EQUALS( aws_credentials_get_secret_access_key(s_tester.credentials), s_good_secret_access_key); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_session_token(s_tester.credentials), s_good_session_token); struct aws_date_time expiration; struct aws_byte_cursor date_cursor = aws_byte_cursor_from_string(s_good_response_expiration); aws_date_time_init_from_str_cursor(&expiration, &date_cursor, AWS_DATE_FORMAT_ISO_8601); ASSERT_TRUE( aws_credentials_get_expiration_timepoint_seconds(s_tester.credentials) == (uint64_t)expiration.timestamp); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); /* Because we mock the http connection manager, we never get a callback back from it */ aws_mem_release(provider->allocator, provider); s_aws_ecs_tester_cleanup(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_success_multi_part_doc, s_credentials_provider_ecs_success_multi_part_doc); static int s_credentials_provider_ecs_real_new_destroy(struct aws_allocator *allocator, void *ctx) { (void)ctx; aws_auth_library_init(allocator); s_aws_ecs_tester_init(allocator); struct aws_event_loop_group *el_group = aws_event_loop_group_new_default(allocator, 1, NULL); struct aws_host_resolver_default_options resolver_options = { .el_group = el_group, .max_entries = 8, }; struct aws_host_resolver *resolver = aws_host_resolver_new_default(allocator, &resolver_options); struct aws_client_bootstrap_options bootstrap_options = { .event_loop_group = el_group, .host_resolver = resolver, }; struct aws_client_bootstrap *bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); struct aws_credentials_provider_ecs_options options = { .bootstrap = bootstrap, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); aws_client_bootstrap_release(bootstrap); aws_host_resolver_release(resolver); aws_event_loop_group_release(el_group); s_aws_ecs_tester_cleanup(); aws_auth_library_clean_up(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_real_new_destroy, s_credentials_provider_ecs_real_new_destroy); static int s_credentials_provider_ecs_real_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; aws_auth_library_init(allocator); s_aws_ecs_tester_init(allocator); struct aws_event_loop_group *el_group = aws_event_loop_group_new_default(allocator, 1, NULL); struct aws_host_resolver_default_options resolver_options = { .el_group = el_group, .max_entries = 8, }; struct aws_host_resolver *resolver = aws_host_resolver_new_default(allocator, &resolver_options); struct aws_client_bootstrap_options bootstrap_options = { .event_loop_group = el_group, .host_resolver = resolver, }; struct aws_client_bootstrap *bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); struct aws_credentials_provider_ecs_options options = { .bootstrap = bootstrap, .shutdown_options = { .shutdown_callback = s_on_shutdown_complete, .shutdown_user_data = NULL, }, .host = aws_byte_cursor_from_c_str("www.xxx123321testmocknonexsitingawsservice.com"), .path_and_query = aws_byte_cursor_from_c_str("/path/to/resource/?a=b&c=d"), .auth_token = aws_byte_cursor_from_c_str("test-token-1234-abcd"), }; struct aws_credentials_provider *provider = aws_credentials_provider_new_ecs(allocator, &options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); s_aws_wait_for_credentials_result(); aws_mutex_lock(&s_tester.lock); ASSERT_TRUE(s_tester.credentials != NULL); aws_mutex_unlock(&s_tester.lock); aws_credentials_provider_release(provider); s_aws_wait_for_provider_shutdown_callback(); s_aws_ecs_tester_cleanup(); aws_client_bootstrap_release(bootstrap); aws_host_resolver_release(resolver); aws_event_loop_group_release(el_group); aws_auth_library_clean_up(); return 0; } AWS_TEST_CASE(credentials_provider_ecs_real_success, s_credentials_provider_ecs_real_success);