From 73daee840347bd2d6044c163b6da26a0e2a98cba Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 1 Mar 2023 16:29:18 -0800 Subject: [PATCH 01/93] maybe --- source/credentials.c | 157 +++++++++++++++++++++++++++++++------------ 1 file changed, 113 insertions(+), 44 deletions(-) diff --git a/source/credentials.c b/source/credentials.c index f838c3e1..2ec792c0 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -9,6 +9,29 @@ #include #include +struct aws_ecc_identity { + struct aws_string *access_key_id; + struct aws_string *session_token; + struct aws_ecc_key_pair *ecc_key; +}; + +struct aws_credentials_identity { + struct aws_string *access_key_id; + struct aws_string *secret_access_key; + struct aws_string *session_token; +}; + +struct aws_token_identity { + struct aws_string *token; +}; + +enum IdentityType { + CREDENTIALS_IDENTITY, + TOKEN_IDENTITY, + ANONYMOUS_IDENTITY, + ECC_IDENTITY, +}; + /* * A structure that wraps the public/private data needed to sign an authenticated AWS request */ @@ -16,11 +39,6 @@ struct aws_credentials { struct aws_allocator *allocator; struct aws_atomic_var ref_count; - - struct aws_string *access_key_id; - struct aws_string *secret_access_key; - struct aws_string *session_token; - /* * A timepoint, in seconds since epoch, at which the credentials should no longer be used because they * will have expired. @@ -51,7 +69,12 @@ struct aws_credentials { */ uint64_t expiration_timepoint_seconds; - struct aws_ecc_key_pair *ecc_key; + enum IdentityType type; + union { + struct aws_credentials_identity credentials; + struct aws_token_identity token; + struct aws_ecc_identity ecc_identity; + } identity; }; /* @@ -83,23 +106,23 @@ struct aws_credentials *aws_credentials_new( credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); - - credentials->access_key_id = + credentials->type = CREDENTIALS_IDENTITY; + credentials->identity.credentials.access_key_id = aws_string_new_from_array(allocator, access_key_id_cursor.ptr, access_key_id_cursor.len); - if (credentials->access_key_id == NULL) { + if (credentials->identity.credentials.access_key_id == NULL) { goto error; } - credentials->secret_access_key = + credentials->identity.credentials.secret_access_key = aws_string_new_from_array(allocator, secret_access_key_cursor.ptr, secret_access_key_cursor.len); - if (credentials->secret_access_key == NULL) { + if (credentials->identity.credentials.secret_access_key == NULL) { goto error; } if (session_token_cursor.ptr != NULL && session_token_cursor.len > 0) { - credentials->session_token = + credentials->identity.credentials.session_token = aws_string_new_from_array(allocator, session_token_cursor.ptr, session_token_cursor.len); - if (credentials->session_token == NULL) { + if (credentials->identity.credentials.session_token == NULL) { goto error; } } @@ -120,6 +143,7 @@ struct aws_credentials *aws_credentials_new_anonymous(struct aws_allocator *allo struct aws_credentials *credentials = aws_mem_calloc(allocator, 1, sizeof(struct aws_credentials)); credentials->allocator = allocator; + credentials->type = ANONYMOUS_IDENTITY; aws_atomic_init_int(&credentials->ref_count, 1); credentials->expiration_timepoint_seconds = UINT64_MAX; @@ -131,21 +155,35 @@ static void s_aws_credentials_destroy(struct aws_credentials *credentials) { if (credentials == NULL) { return; } - - if (credentials->access_key_id != NULL) { - aws_string_destroy(credentials->access_key_id); - } - - if (credentials->secret_access_key != NULL) { - aws_string_destroy_secure(credentials->secret_access_key); - } - - if (credentials->session_token != NULL) { - aws_string_destroy_secure(credentials->session_token); + switch (credentials->type) { + case CREDENTIALS_IDENTITY: + if (credentials->identity.credentials.access_key_id != NULL) { + aws_string_destroy(credentials->identity.credentials.access_key_id); + } + + if (credentials->identity.credentials.secret_access_key != NULL) { + aws_string_destroy_secure(credentials->identity.credentials.secret_access_key); + } + + if (credentials->identity.credentials.session_token != NULL) { + aws_string_destroy_secure(credentials->identity.credentials.session_token); + } + break; + case ECC_IDENTITY: + if (credentials->identity.ecc_identity.access_key_id != NULL) { + + aws_string_destroy(credentials->identity.ecc_identity.access_key_id); + } + if (credentials->identity.ecc_identity.session_token != NULL) { + aws_string_destroy_secure(credentials->identity.ecc_identity.session_token); + } + aws_ecc_key_pair_release(credentials->identity.ecc_identity.ecc_key); + break; + + default: + break; } - aws_ecc_key_pair_release(credentials->ecc_key); - aws_mem_release(credentials->allocator, credentials); } @@ -174,26 +212,51 @@ static struct aws_byte_cursor s_empty_token_cursor = { }; struct aws_byte_cursor aws_credentials_get_access_key_id(const struct aws_credentials *credentials) { - if (credentials->access_key_id == NULL) { - return s_empty_token_cursor; + switch (credentials->type) { + case CREDENTIALS_IDENTITY: + if (credentials->identity.credentials.access_key_id != NULL) { + return aws_byte_cursor_from_string(credentials->identity.credentials.access_key_id); + } + break; + case ECC_IDENTITY: + if (credentials->identity.ecc_identity.access_key_id != NULL) { + return aws_byte_cursor_from_string(credentials->identity.ecc_identity.access_key_id); + } + break; + default: + break; } - - return aws_byte_cursor_from_string(credentials->access_key_id); + return s_empty_token_cursor; } struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_credentials *credentials) { - if (credentials->secret_access_key == NULL) { - return s_empty_token_cursor; + switch (credentials->type) { + case CREDENTIALS_IDENTITY: + if (credentials->identity.credentials.secret_access_key != NULL) { + return aws_byte_cursor_from_string(credentials->identity.credentials.secret_access_key); + } + break; + default: + break; } - - return aws_byte_cursor_from_string(credentials->secret_access_key); + return s_empty_token_cursor; } struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_credentials *credentials) { - if (credentials->session_token != NULL) { - return aws_byte_cursor_from_string(credentials->session_token); + switch (credentials->type) { + case CREDENTIALS_IDENTITY: + if (credentials->identity.credentials.session_token != NULL) { + return aws_byte_cursor_from_string(credentials->identity.credentials.session_token); + } + break; + case ECC_IDENTITY: + if (credentials->identity.ecc_identity.session_token != NULL) { + return aws_byte_cursor_from_string(credentials->identity.ecc_identity.session_token); + } + break; + default: + break; } - return s_empty_token_cursor; } @@ -202,12 +265,15 @@ uint64_t aws_credentials_get_expiration_timepoint_seconds(const struct aws_crede } struct aws_ecc_key_pair *aws_credentials_get_ecc_key_pair(const struct aws_credentials *credentials) { - return credentials->ecc_key; + if (credentials->type == ECC_IDENTITY) { + return credentials->identity.ecc_identity.ecc_key; + } + return NULL; } bool aws_credentials_is_anonymous(const struct aws_credentials *credentials) { AWS_PRECONDITION(credentials); - return credentials->access_key_id == NULL && credentials->secret_access_key == NULL; + return credentials->type == ANONYMOUS_IDENTITY; } struct aws_credentials *aws_credentials_new_from_string( @@ -250,16 +316,19 @@ struct aws_credentials *aws_credentials_new_ecc( credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds; aws_atomic_init_int(&credentials->ref_count, 1); aws_ecc_key_pair_acquire(ecc_key); - credentials->ecc_key = ecc_key; + credentials->type = ECC_IDENTITY; + credentials->identity.ecc_identity.ecc_key = ecc_key; - credentials->access_key_id = aws_string_new_from_array(allocator, access_key_id.ptr, access_key_id.len); - if (credentials->access_key_id == NULL) { + credentials->identity.ecc_identity.access_key_id = + aws_string_new_from_array(allocator, access_key_id.ptr, access_key_id.len); + if (credentials->identity.ecc_identity.access_key_id == NULL) { goto on_error; } if (session_token.ptr != NULL && session_token.len > 0) { - credentials->session_token = aws_string_new_from_array(allocator, session_token.ptr, session_token.len); - if (credentials->session_token == NULL) { + credentials->identity.ecc_identity.session_token = + aws_string_new_from_array(allocator, session_token.ptr, session_token.len); + if (credentials->identity.ecc_identity.session_token == NULL) { goto on_error; } } From d01bb050ab3b28733414ec930f92ba3267deef54 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 1 Mar 2023 16:47:28 -0800 Subject: [PATCH 02/93] cleanup --- source/credentials.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/source/credentials.c b/source/credentials.c index 2ec792c0..b3809e65 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -107,22 +107,23 @@ struct aws_credentials *aws_credentials_new( credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); credentials->type = CREDENTIALS_IDENTITY; - credentials->identity.credentials.access_key_id = + struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials; + credentials_identity->access_key_id = aws_string_new_from_array(allocator, access_key_id_cursor.ptr, access_key_id_cursor.len); - if (credentials->identity.credentials.access_key_id == NULL) { + if (credentials_identity->access_key_id == NULL) { goto error; } - credentials->identity.credentials.secret_access_key = + credentials_identity->secret_access_key = aws_string_new_from_array(allocator, secret_access_key_cursor.ptr, secret_access_key_cursor.len); - if (credentials->identity.credentials.secret_access_key == NULL) { + if (credentials_identity->secret_access_key == NULL) { goto error; } if (session_token_cursor.ptr != NULL && session_token_cursor.len > 0) { - credentials->identity.credentials.session_token = + credentials_identity->session_token = aws_string_new_from_array(allocator, session_token_cursor.ptr, session_token_cursor.len); - if (credentials->identity.credentials.session_token == NULL) { + if (credentials_identity->session_token == NULL) { goto error; } } @@ -157,26 +158,13 @@ static void s_aws_credentials_destroy(struct aws_credentials *credentials) { } switch (credentials->type) { case CREDENTIALS_IDENTITY: - if (credentials->identity.credentials.access_key_id != NULL) { - aws_string_destroy(credentials->identity.credentials.access_key_id); - } - - if (credentials->identity.credentials.secret_access_key != NULL) { - aws_string_destroy_secure(credentials->identity.credentials.secret_access_key); - } - - if (credentials->identity.credentials.session_token != NULL) { - aws_string_destroy_secure(credentials->identity.credentials.session_token); - } + aws_string_destroy(credentials->identity.credentials.access_key_id); + aws_string_destroy_secure(credentials->identity.credentials.secret_access_key); + aws_string_destroy_secure(credentials->identity.credentials.session_token); break; case ECC_IDENTITY: - if (credentials->identity.ecc_identity.access_key_id != NULL) { - - aws_string_destroy(credentials->identity.ecc_identity.access_key_id); - } - if (credentials->identity.ecc_identity.session_token != NULL) { - aws_string_destroy_secure(credentials->identity.ecc_identity.session_token); - } + aws_string_destroy(credentials->identity.ecc_identity.access_key_id); + aws_string_destroy_secure(credentials->identity.ecc_identity.session_token); aws_ecc_key_pair_release(credentials->identity.ecc_identity.ecc_key); break; From bea4bf050201151e7d01b9d8eceb780386f993e8 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 2 Mar 2023 12:41:39 -0800 Subject: [PATCH 03/93] basic impl for parsing region and start url --- include/aws/auth/credentials.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index cc1e2657..19de68d0 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -525,6 +525,24 @@ struct aws_credentials_provider_cognito_options { struct aws_auth_http_system_vtable *function_table; }; +/** + * Configuration options for a provider that sources credentials from the aws profile and credentials files + * (by default ~/.aws/profile and ~/.aws/credentials) + */ +struct aws_token_provider_profile_options { + struct aws_credentials_provider_shutdown_options shutdown_options; + + /* + * Override of what profile to use to source credentials from ('default' by default) + */ + struct aws_byte_cursor profile_name_override; + + /* + * Override path to the profile config file (~/.aws/config by default) + */ + struct aws_byte_cursor config_file_name_override; +}; + AWS_EXTERN_C_BEGIN /* @@ -980,6 +998,21 @@ struct aws_credentials_provider *aws_credentials_provider_new_chain_default( struct aws_allocator *allocator, const struct aws_credentials_provider_chain_default_options *options); +/** + * Creates a provider that sources credentials from key-value profiles loaded from the aws credentials + * file ("~/.aws/credentials" by default) and the aws config file ("~/.aws/config" by + * default) + * + * @param allocator memory allocator to use for all memory allocation + * @param options provider-specific configuration options + * + * @return the newly-constructed credentials provider, or NULL if an error occurred. + */ +AWS_AUTH_API +struct aws_credentials_provider *aws_token_provider_new_profile( + struct aws_allocator *allocator, + const struct aws_token_provider_profile_options *options); + AWS_AUTH_API extern const struct aws_auth_http_system_vtable *g_aws_credentials_provider_http_function_table; AWS_EXTERN_C_END From 559370ec21f27d9e084c8ed3c5d6a31a197d0328 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 2 Mar 2023 12:51:52 -0800 Subject: [PATCH 04/93] rename sso_token_provider --- include/aws/auth/credentials.h | 6 +- source/sso_token_provider_profile.c | 270 ++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 source/sso_token_provider_profile.c diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 19de68d0..3d6d06a6 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -529,7 +529,7 @@ struct aws_credentials_provider_cognito_options { * Configuration options for a provider that sources credentials from the aws profile and credentials files * (by default ~/.aws/profile and ~/.aws/credentials) */ -struct aws_token_provider_profile_options { +struct aws_sso_token_provider_profile_options { struct aws_credentials_provider_shutdown_options shutdown_options; /* @@ -1009,9 +1009,9 @@ struct aws_credentials_provider *aws_credentials_provider_new_chain_default( * @return the newly-constructed credentials provider, or NULL if an error occurred. */ AWS_AUTH_API -struct aws_credentials_provider *aws_token_provider_new_profile( +struct aws_credentials_provider *aws_sso_token_provider_new_profile( struct aws_allocator *allocator, - const struct aws_token_provider_profile_options *options); + const struct aws_sso_token_provider_profile_options *options); AWS_AUTH_API extern const struct aws_auth_http_system_vtable *g_aws_credentials_provider_http_function_table; diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c new file mode 100644 index 00000000..254186af --- /dev/null +++ b/source/sso_token_provider_profile.c @@ -0,0 +1,270 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +/* allow non-constant declared initializers. */ +# pragma warning(disable : 4204) +#endif + +/* + * Profile provider implementation + */ +struct aws_token_provider_profile_impl { + struct aws_string *sso_region; + struct aws_string *sso_start_url; + struct aws_string *token_file_path; +}; + +static int s_token_provider_profile_get_token_async( + struct aws_credentials_provider *provider, + aws_on_get_credentials_callback_fn callback, + void *user_data) { + // TODO: + return AWS_OP_ERR; +} + +static void s_token_provider_profile_destroy(struct aws_credentials_provider *provider) { + struct aws_token_provider_profile_impl *impl = provider->impl; + if (impl == NULL) { + return; + } + + aws_string_destroy(impl->sso_region); + aws_string_destroy(impl->sso_start_url); + aws_string_destroy(impl->token_file_path); + + aws_credentials_provider_invoke_shutdown_callback(provider); + aws_mem_release(provider->allocator, provider); +} + +static struct aws_credentials_provider_vtable s_aws_token_provider_profile_vtable = { + .get_credentials = s_token_provider_profile_get_token_async, + .destroy = s_token_provider_profile_destroy, +}; + +AWS_STRING_FROM_LITERAL(s_sso_session_name, "sso_session"); +AWS_STRING_FROM_LITERAL(s_sso_account_id_name, "sso_account_id"); +AWS_STRING_FROM_LITERAL(s_sso_role_name_name, "sso_role_name"); +AWS_STRING_FROM_LITERAL(s_sso_region_name, "sso_region"); +AWS_STRING_FROM_LITERAL(s_sso_start_url_name, "sso_start_url"); + +struct token_provider_profile_parameters { + struct aws_string *sso_region; + struct aws_string *sso_start_url; +}; + +static int s_token_provider_profile_parameters_init( + struct aws_allocator *allocator, + struct token_provider_profile_parameters *parameters, + const struct aws_profile *profile) { + const struct aws_profile_property *sso_region_property = aws_profile_get_property(profile, s_sso_region_name); + const struct aws_profile_property *sso_start_url_property = aws_profile_get_property(profile, s_sso_start_url_name); + + if (!sso_region_property) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_region in profile"); + return AWS_OP_ERR; + } + + if (!sso_start_url_property) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_start_url in profile"); + return AWS_OP_ERR; + } + + parameters->sso_region = aws_string_new_from_string(allocator, sso_region_property); + parameters->sso_start_url = aws_string_new_from_string(allocator, sso_start_url_property); + return AWS_OP_SUCCESS; +} + +static int s_token_provider_profile_parameters_sso_session_init( + struct aws_allocator *allocator, + struct token_provider_profile_parameters *parameters, + const struct aws_profile_collection *profile_collection, + const struct aws_profile *profile, + struct aws_string *sso_session_name) { + + const struct aws_profile *session_profile = + aws_profile_collection_get_sso_session(profile_collection, sso_session_name); + if (!session_profile) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find an sso-session"); + return AWS_OP_ERR; + } + + const struct aws_profile_property *sso_region_property = + aws_profile_get_property(session_profile, s_sso_region_name); + const struct aws_profile_property *sso_start_url_property = + aws_profile_get_property(session_profile, s_sso_start_url_name); + + if (!sso_region_property) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_region in sso-session"); + return AWS_OP_ERR; + } + + if (!sso_start_url_property) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token parser failed to find sso_start_url in sso-session"); + return AWS_OP_ERR; + } + + struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); + struct aws_string *sso_start_url = aws_profile_property_get_value(sso_start_url_property); + + /* Verify if sso_region & start_url are the same in profile section if they exist */ + const struct aws_profile_property *profile_sso_region_property = + aws_profile_get_property(profile, s_sso_region_name); + const struct aws_profile_property *profile_sso_start_url_property = + aws_profile_get_property(profile, s_sso_start_url_name); + + if (profile_sso_region_property && + aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_region"); + return AWS_OP_ERR; + } + + if (profile_sso_start_url_property && + aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_start_url"); + return AWS_OP_ERR; + } + + parameters->sso_region = aws_string_new_from_string(allocator, sso_region); + parameters->sso_start_url = aws_string_new_from_string(allocator, sso_start_url); + return AWS_OP_SUCCESS; +} +static void s_token_provider_profile_parameters_destroy( + struct aws_allocator *allocator, + struct token_provider_profile_parameters *parameters) { + + aws_string_destroy(parameters->sso_region); + aws_string_destroy(parameters->sso_start_url); + aws_mem_release(allocator, parameters); +} + +static struct token_provider_profile_parameters *s_token_provider_profile_parameters_new( + struct aws_allocator *allocator, + struct aws_byte_cursor profile_name_override, + struct aws_byte_cursor config_file_name_override) { + struct aws_profile_collection *config_profiles = NULL; + struct aws_string *config_file_path = NULL; + struct aws_string *profile_name = NULL; + bool success = false; + + struct token_provider_profile_parameters *parameters = + aws_mem_calloc(allocator, 1, sizeof(struct token_provider_profile_parameters)); + config_file_path = aws_get_config_file_path(allocator, &config_file_name_override); + + if (!config_file_path) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed resolve config file path"); + goto cleanup; + } + + profile_name = aws_get_profile_name(allocator, &profile_name_override); + if (!profile_name) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to resolve profile name"); + goto cleanup; + } + config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG); + + if (!config_profiles) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token parser could not load or parse" + " a config file."); + goto cleanup; + } + + const struct aws_profile *profile = aws_profile_collection_get_profile(config_profiles, profile_name); + + if (!profile) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token provider could not load" + " a profile at %s.", + aws_string_c_str(profile_name)); + goto cleanup; + } + + const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session_name); + if (sso_session_property) { + if (s_token_provider_profile_parameters_sso_session_init( + allocator, + parameters, + config_profiles, + profile, + aws_profile_property_get_value(sso_session_property))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token provider could not load a valid sso profile and session at %s", + aws_string_c_str(profile_name)); + goto cleanup; + } + } else if (s_token_provider_profile_parameters_init(allocator, parameters, profile)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token provider could not load a valid sso profile at %s", + aws_string_c_str(profile_name)); + goto cleanup; + } + + success = true; + +cleanup: + aws_string_destroy(config_file_path); + aws_string_destroy(profile_name); + aws_profile_collection_release(config_profiles); + if (!success) { + s_token_provider_profile_parameters_destroy(allocator, parameters); + parameters = NULL; + } + return parameters; +} + +struct aws_credentials_provider *aws_sso_token_provider_new_profile( + struct aws_allocator *allocator, + const struct aws_sso_token_provider_profile_options *options) { + + const struct token_provider_profile_parameters *parameters = s_token_provider_profile_parameters_new( + allocator, options->config_file_name_override, options->config_file_name_override); + if (!parameters) { + return NULL; + } + struct aws_credentials_provider *provider = NULL; + struct aws_token_provider_profile_impl *impl = NULL; + + aws_mem_acquire_many( + allocator, + 2, + &provider, + sizeof(struct aws_credentials_provider), + &impl, + sizeof(struct aws_token_provider_profile_impl)); + AWS_ZERO_STRUCT(*provider); + AWS_ZERO_STRUCT(*impl); + aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_profile_vtable, impl); + impl->sso_region = aws_string_new_from_string(allocator, parameters->sso_region); + impl->sso_start_url = aws_string_new_from_string(allocator, parameters->sso_start_url); + provider->shutdown_options = options->shutdown_options; + + s_token_provider_profile_parameters_destroy(allocator, parameters); + return provider; +on_error: + aws_credentials_provider_destroy(provider); + s_token_provider_profile_parameters_destroy(allocator, parameters); + return NULL; +} From bb0302dd64d51aa2fb8ec995ee28e8c827b2d684 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 2 Mar 2023 14:09:56 -0800 Subject: [PATCH 05/93] test infra --- tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3b631ecd..132530e9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -102,6 +102,8 @@ if (AWS_HAS_CI_ENVIRONMENT) add_net_test_case(credentials_provider_cognito_success_unauthenticated) endif() +add_net_test_case(sso_token_provider_profile_default_test) + add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) add_test_case(imds_client_token_request_failure) From ec164b8a92161bc2c5dbe91cf1a50cdfd4fc99f6 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 2 Mar 2023 14:19:00 -0800 Subject: [PATCH 06/93] adds invalid tests --- tests/soo_token_provider_profile_tests.c | 112 +++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 tests/soo_token_provider_profile_tests.c diff --git a/tests/soo_token_provider_profile_tests.c b/tests/soo_token_provider_profile_tests.c new file mode 100644 index 00000000..615e5bdd --- /dev/null +++ b/tests/soo_token_provider_profile_tests.c @@ -0,0 +1,112 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "shared_credentials_test_definitions.h" + +struct sso_session_profile_example { + const char *name; + struct aws_byte_cursor text; +}; + +static int s_sso_token_provider_profile_default_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + static struct sso_session_profile_example s_invalid_profile_examples[] = { + { + .name = "No sso-session", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( + "[profile default]\naws_access_key_id=fake_access_key\naws_secret_access_key=fake_secret_key\n"), + }, + { + .name = "No sso-region", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_start_url=url\n"), + }, + { + .name = "No sso_start_url", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_region=us-east-1\n"), + }, + { + .name = "Only sso_session", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\n"), + }, + { + .name = "No sso_session sso_start_url", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( + "[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\n[sso-session dev]\nsso_region=us-east-1"), + }, + { + .name = "No sso_session region", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( + "[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\n[sso-session dev]\nsso_start_url=url"), + }, + { + .name = "sso_session with profile region and sso_start_url", + .text = + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\nsso_region=us-east-" + "1\nsso_start_url=url\n[sso-session dev]\nsso_start_url=url"), + }, + { + .name = "sso_session with different profile region", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( + "[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\nsso_region=us-west-" + "1\nsso_start_url=url\n[sso-session dev]\nsso_region=us-east-1\nsso_start_url=url"), + }, + { + .name = "sso_session with different profile start url", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( + "[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\nsso_region=us-east-" + "1\nsso_start_url=url\n[sso-session dev]\nsso_region=us-east-1\nsso_start_url=url2"), + }, + }; + + aws_unset_environment_value(s_default_profile_env_variable_name); + aws_unset_environment_value(s_default_config_path_env_variable_name); + aws_unset_environment_value(s_default_credentials_path_env_variable_name); + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + struct aws_sso_token_provider_profile_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + }; + + for (size_t i = 0; i < AWS_ARRAY_SIZE(s_invalid_profile_examples); ++i) { + printf("invalid example [%zu]: %s\n", i, s_invalid_profile_examples[i].name); + struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_invalid_profile_examples[i].text); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); + ASSERT_NULL(aws_sso_token_provider_new_profile(allocator, &options)); + aws_string_destroy(config_contents); + } + + aws_string_destroy(config_file_str); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_profile_default_test, s_sso_token_provider_profile_default_test); From 3348681d315651b32ce4de8c0a17b5c5ce27f6eb Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 2 Mar 2023 15:29:56 -0800 Subject: [PATCH 07/93] fix valid test --- source/sso_token_provider_profile.c | 7 ++-- tests/CMakeLists.txt | 3 +- tests/soo_token_provider_profile_tests.c | 42 ++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index 254186af..68da5092 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -82,8 +82,9 @@ static int s_token_provider_profile_parameters_init( return AWS_OP_ERR; } - parameters->sso_region = aws_string_new_from_string(allocator, sso_region_property); - parameters->sso_start_url = aws_string_new_from_string(allocator, sso_start_url_property); + parameters->sso_region = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_region_property)); + parameters->sso_start_url = + aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_start_url_property)); return AWS_OP_SUCCESS; } @@ -240,7 +241,7 @@ struct aws_credentials_provider *aws_sso_token_provider_new_profile( const struct aws_sso_token_provider_profile_options *options) { const struct token_provider_profile_parameters *parameters = s_token_provider_profile_parameters_new( - allocator, options->config_file_name_override, options->config_file_name_override); + allocator, options->profile_name_override, options->config_file_name_override); if (!parameters) { return NULL; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 132530e9..8a73baa0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -102,7 +102,8 @@ if (AWS_HAS_CI_ENVIRONMENT) add_net_test_case(credentials_provider_cognito_success_unauthenticated) endif() -add_net_test_case(sso_token_provider_profile_default_test) +add_net_test_case(sso_token_provider_profile_invalid_profile_test) +add_net_test_case(sso_token_provider_profile_valid_profile_test) add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) diff --git a/tests/soo_token_provider_profile_tests.c b/tests/soo_token_provider_profile_tests.c index 615e5bdd..c930e553 100644 --- a/tests/soo_token_provider_profile_tests.c +++ b/tests/soo_token_provider_profile_tests.c @@ -22,7 +22,7 @@ struct sso_session_profile_example { struct aws_byte_cursor text; }; -static int s_sso_token_provider_profile_default_test(struct aws_allocator *allocator, void *ctx) { +static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocator *allocator, void *ctx) { (void)ctx; static struct sso_session_profile_example s_invalid_profile_examples[] = { @@ -109,4 +109,42 @@ static int s_sso_token_provider_profile_default_test(struct aws_allocator *alloc return AWS_OP_SUCCESS; } -AWS_TEST_CASE(sso_token_provider_profile_default_test, s_sso_token_provider_profile_default_test); +AWS_TEST_CASE(sso_token_provider_profile_invalid_profile_test, s_sso_token_provider_profile_invalid_profile_test); + +static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + static struct sso_session_profile_example s_valid_profile_examples[] = { + { + .name = "Profile", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_region=us-east-" + "1\nsso_start_url=url"), + }, + }; + + aws_unset_environment_value(s_default_profile_env_variable_name); + aws_unset_environment_value(s_default_config_path_env_variable_name); + aws_unset_environment_value(s_default_credentials_path_env_variable_name); + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + struct aws_sso_token_provider_profile_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .profile_name_override = NULL, + .shutdown_options = NULL, + }; + + for (size_t i = 0; i < AWS_ARRAY_SIZE(s_valid_profile_examples); ++i) { + printf("valid example [%zu]: %s\n", i, s_valid_profile_examples[i].name); + struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_valid_profile_examples[i].text); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); + struct aws_credentials_provider *provider = aws_sso_token_provider_new_profile(allocator, &options); + ASSERT_NOT_NULL(provider); + aws_credentials_provider_release(provider); + aws_string_destroy(config_contents); + } + + aws_string_destroy(config_file_str); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_profile_valid_profile_test, s_sso_token_provider_profile_valid_profile_test); From 3aaaec27b4ac553fd7515ab5c4c6d65b4d4d89ca Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 2 Mar 2023 15:42:30 -0800 Subject: [PATCH 08/93] more tests --- source/sso_token_provider_profile.c | 21 ++++++++-------- tests/soo_token_provider_profile_tests.c | 31 +++++++++++++++++++++++- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index 68da5092..6db3c5b4 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -93,7 +93,7 @@ static int s_token_provider_profile_parameters_sso_session_init( struct token_provider_profile_parameters *parameters, const struct aws_profile_collection *profile_collection, const struct aws_profile *profile, - struct aws_string *sso_session_name) { + const struct aws_string *sso_session_name) { const struct aws_profile *session_profile = aws_profile_collection_get_sso_session(profile_collection, sso_session_name); @@ -120,8 +120,8 @@ static int s_token_provider_profile_parameters_sso_session_init( return AWS_OP_ERR; } - struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); - struct aws_string *sso_start_url = aws_profile_property_get_value(sso_start_url_property); + const struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); + const struct aws_string *sso_start_url = aws_profile_property_get_value(sso_start_url_property); /* Verify if sso_region & start_url are the same in profile section if they exist */ const struct aws_profile_property *profile_sso_region_property = @@ -130,14 +130,14 @@ static int s_token_provider_profile_parameters_sso_session_init( aws_profile_get_property(profile, s_sso_start_url_name); if (profile_sso_region_property && - aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { + !aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_region"); return AWS_OP_ERR; } if (profile_sso_start_url_property && - aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) { + !aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_start_url"); return AWS_OP_ERR; @@ -240,7 +240,7 @@ struct aws_credentials_provider *aws_sso_token_provider_new_profile( struct aws_allocator *allocator, const struct aws_sso_token_provider_profile_options *options) { - const struct token_provider_profile_parameters *parameters = s_token_provider_profile_parameters_new( + struct token_provider_profile_parameters *parameters = s_token_provider_profile_parameters_new( allocator, options->profile_name_override, options->config_file_name_override); if (!parameters) { return NULL; @@ -264,8 +264,9 @@ struct aws_credentials_provider *aws_sso_token_provider_new_profile( s_token_provider_profile_parameters_destroy(allocator, parameters); return provider; -on_error: - aws_credentials_provider_destroy(provider); - s_token_provider_profile_parameters_destroy(allocator, parameters); - return NULL; + + // on_error: + // aws_credentials_provider_destroy(provider); + // s_token_provider_profile_parameters_destroy(allocator, parameters); + // return NULL; } diff --git a/tests/soo_token_provider_profile_tests.c b/tests/soo_token_provider_profile_tests.c index c930e553..bbca749e 100644 --- a/tests/soo_token_provider_profile_tests.c +++ b/tests/soo_token_provider_profile_tests.c @@ -116,11 +116,40 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator static struct sso_session_profile_example s_valid_profile_examples[] = { { - .name = "Profile", + .name = "profile", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_region=us-east-" "1\nsso_start_url=url"), }, + { + .name = "sso_session", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( + "[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\n[sso-session dev]\nsso_region=us-east-" + "1\nsso_start_url=url"), + }, + { + .name = "sso_session with profile region", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_region=us-east-1\nsso_" + "session=dev\n[sso-session dev]\nsso_region=us-east-" + "1\nsso_start_url=url"), + }, + { + .name = "sso_session with start url", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_start_url=url\nsso_" + "session=dev\n[sso-session dev]\nsso_region=us-east-" + "1\nsso_start_url=url"), + }, + { + .name = "sso_session with region and start url", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( + "[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_region=us-east-1\nsso_start_url=url\nsso_" + "session=dev\n[sso-session dev]\nsso_region=us-east-" + "1\nsso_start_url=url"), + }, }; aws_unset_environment_value(s_default_profile_env_variable_name); From 3c50ecc56bdf407b1824dd873449b80e697091d7 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sat, 4 Mar 2023 18:26:59 -0800 Subject: [PATCH 09/93] token file parsing initial --- builder.json | 16 +++++++++--- include/aws/auth/auth.h | 2 ++ source/sso_token_provider_profile.c | 39 +++++++++++++++++++++++------ tests/CMakeLists.txt | 4 +++ 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/builder.json b/builder.json index f256c7c2..6bff5e69 100644 --- a/builder.json +++ b/builder.json @@ -7,12 +7,20 @@ } }, "upstream": [ - { "name": "aws-c-http" }, - { "name": "aws-c-cal" }, - { "name": "aws-c-sdkutils" } + { + "name": "aws-c-http" + }, + { + "name": "aws-c-cal" + }, + { + "name": "aws-c-sdkutils" + } ], "downstream": [ - { "name": "aws-c-s3" } + { + "name": "aws-c-s3" + } ], "+cmake_args": [ "-DAWS_HAS_CI_ENVIRONMENT=ON" diff --git a/include/aws/auth/auth.h b/include/aws/auth/auth.h index 4411d8ff..a9683a11 100644 --- a/include/aws/auth/auth.h +++ b/include/aws/auth/auth.h @@ -43,6 +43,8 @@ enum aws_auth_errors { AWS_AUTH_SIGV4A_SIGNATURE_VALIDATION_FAILURE, AWS_AUTH_CREDENTIALS_PROVIDER_COGNITO_SOURCE_FAILURE, AWS_AUTH_CREDENTIALS_PROVIDER_DELEGATE_FAILURE, + AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE, + AWS_AUTH_SSO_TOKEN_INVALID, AWS_AUTH_ERROR_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_AUTH_PACKAGE_ID) }; diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index 6db3c5b4..4689fc51 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -7,10 +7,10 @@ #include #include +#include #include #include #include - #ifdef _MSC_VER /* allow non-constant declared initializers. */ # pragma warning(disable : 4204) @@ -29,6 +29,11 @@ static int s_token_provider_profile_get_token_async( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, void *user_data) { + (void)provider; + (void)callback; + (void)user_data; + struct aws_token_provider_profile_impl *impl = provider->impl; + // TODO: return AWS_OP_ERR; } @@ -61,6 +66,7 @@ AWS_STRING_FROM_LITERAL(s_sso_start_url_name, "sso_start_url"); struct token_provider_profile_parameters { struct aws_string *sso_region; struct aws_string *sso_start_url; + struct aws_string *token_path; }; static int s_token_provider_profile_parameters_init( @@ -73,18 +79,25 @@ static int s_token_provider_profile_parameters_init( if (!sso_region_property) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_region in profile"); - return AWS_OP_ERR; + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } if (!sso_start_url_property) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_start_url in profile"); - return AWS_OP_ERR; + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } parameters->sso_region = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_region_property)); parameters->sso_start_url = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_start_url_property)); + parameters->token_path = construct_token_path(allocator, parameters->sso_start_url); + if (!parameters->token_path) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to construct token path in profile"); + return AWS_OP_ERR; + } + return AWS_OP_SUCCESS; } @@ -99,7 +112,7 @@ static int s_token_provider_profile_parameters_sso_session_init( aws_profile_collection_get_sso_session(profile_collection, sso_session_name); if (!session_profile) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find an sso-session"); - return AWS_OP_ERR; + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } const struct aws_profile_property *sso_region_property = @@ -110,14 +123,14 @@ static int s_token_provider_profile_parameters_sso_session_init( if (!sso_region_property) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_region in sso-session"); - return AWS_OP_ERR; + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } if (!sso_start_url_property) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_start_url in sso-session"); - return AWS_OP_ERR; + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } const struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); @@ -133,26 +146,35 @@ static int s_token_provider_profile_parameters_sso_session_init( !aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_region"); - return AWS_OP_ERR; + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } if (profile_sso_start_url_property && !aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_start_url"); - return AWS_OP_ERR; + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } parameters->sso_region = aws_string_new_from_string(allocator, sso_region); parameters->sso_start_url = aws_string_new_from_string(allocator, sso_start_url); + parameters->token_path = construct_token_path(allocator, sso_session_name); + if (!parameters->token_path) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token parser failed to construct token path in sso-session"); + return AWS_OP_ERR; + } return AWS_OP_SUCCESS; } + static void s_token_provider_profile_parameters_destroy( struct aws_allocator *allocator, struct token_provider_profile_parameters *parameters) { aws_string_destroy(parameters->sso_region); aws_string_destroy(parameters->sso_start_url); + aws_string_destroy(parameters->token_path); aws_mem_release(allocator, parameters); } @@ -260,6 +282,7 @@ struct aws_credentials_provider *aws_sso_token_provider_new_profile( aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_profile_vtable, impl); impl->sso_region = aws_string_new_from_string(allocator, parameters->sso_region); impl->sso_start_url = aws_string_new_from_string(allocator, parameters->sso_start_url); + impl->token_file_path = aws_string_new_from_string(allocator, parameters->sso_start_url); provider->shutdown_options = options->shutdown_options; s_token_provider_profile_parameters_destroy(allocator, parameters); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8a73baa0..263d9646 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -105,6 +105,10 @@ endif() add_net_test_case(sso_token_provider_profile_invalid_profile_test) add_net_test_case(sso_token_provider_profile_valid_profile_test) +add_net_test_case(parse_token_location_url_test) +add_net_test_case(parse_token_location_session_test) +add_net_test_case(parse_sso_token_valid) + add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) add_test_case(imds_client_token_request_failure) From ff5836ee1ffd9841639129eabcf40e0c6230a33c Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sat, 4 Mar 2023 19:45:35 -0800 Subject: [PATCH 10/93] test date --- include/aws/auth/private/sso_token_utils.h | 32 +++++ source/sso_token_utils.c | 155 +++++++++++++++++++++ tests/token_util_tests.c | 63 +++++++++ 3 files changed, 250 insertions(+) create mode 100644 include/aws/auth/private/sso_token_utils.h create mode 100644 source/sso_token_utils.c create mode 100644 tests/token_util_tests.c diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h new file mode 100644 index 00000000..63e6ae74 --- /dev/null +++ b/include/aws/auth/private/sso_token_utils.h @@ -0,0 +1,32 @@ +#ifndef AWS_AUTH_TOKEN_PRIVATE_H +#define AWS_AUTH_TOKEN_PRIVATE_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include + +struct aws_sso_token { + struct aws_string *token; + struct aws_date_time expiration; +}; + +AWS_EXTERN_C_BEGIN + +/* Construct token path which ~/.aws/sso/cache/.json */ +AWS_AUTH_API +struct aws_string *construct_token_path(struct aws_allocator *allocator, const struct aws_string *input); + +AWS_AUTH_API +void aws_sso_token_destroy(struct aws_allocator *allocator, struct aws_sso_token *token); + +AWS_AUTH_API +struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocator, const struct aws_string *file_path); + +AWS_EXTERN_C_END + +#endif /* AWS_AUTH_CREDENTIALS_PRIVATE_H */ diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c new file mode 100644 index 00000000..7b8e46c2 --- /dev/null +++ b/source/sso_token_utils.c @@ -0,0 +1,155 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(disable : 4232) +#endif /* _MSC_VER */ + +AWS_STATIC_STRING_FROM_LITERAL(s_sso_cache_directory, "/.aws/sso/cache/"); + +struct aws_string *construct_token_path(struct aws_allocator *allocator, const struct aws_string *input) { + AWS_PRECONDITION(input); + + struct aws_string *token_path_str = NULL; + + struct aws_string *home_directory = aws_get_home_directory(allocator); + if (!home_directory) { + return NULL; + } + + struct aws_byte_cursor home_dir_cursor = aws_byte_cursor_from_string(home_directory); + struct aws_byte_cursor cache_dir_cursor = aws_byte_cursor_from_string(s_sso_cache_directory); + struct aws_byte_cursor input_cursor = aws_byte_cursor_from_string(input); + struct aws_byte_cursor json_cursor = aws_byte_cursor_from_c_str(".json"); + const size_t hex_sha_length = AWS_SHA1_LEN * 2; + size_t path_length = home_dir_cursor.len + cache_dir_cursor.len + hex_sha_length + json_cursor.len; + + struct aws_byte_buf token_path_buf; + AWS_ZERO_STRUCT(token_path_buf); + struct aws_byte_buf sha1_buf; + AWS_ZERO_STRUCT(sha1_buf); + + aws_byte_buf_init(&token_path_buf, allocator, path_length); + /* append home directory */ + aws_byte_buf_append_dynamic(&token_path_buf, &home_dir_cursor); + /* append sso cache directory */ + aws_byte_buf_append_dynamic(&token_path_buf, &cache_dir_cursor); + + /* append hex encoded sha1 of input */ + aws_byte_buf_init(&sha1_buf, allocator, AWS_SHA1_LEN); + if (aws_sha1_compute(allocator, &input_cursor, &sha1_buf, 0)) { + goto cleanup; + } + struct aws_byte_cursor sha1_cursor = aws_byte_cursor_from_buf(&sha1_buf); + if (aws_hex_encode_append_dynamic(&sha1_cursor, &token_path_buf)) { + goto cleanup; + } + + /* append .json */ + aws_byte_buf_append_dynamic(&token_path_buf, &json_cursor); + + // Use platform-specific directory separator. + const char local_platform_separator = aws_get_platform_directory_separator(); + for (size_t i = 0; i < token_path_buf.len; ++i) { + if (aws_is_any_directory_separator((char)token_path_buf.buffer[i])) { + ((char *)token_path_buf.buffer)[i] = local_platform_separator; + } + } + + token_path_str = aws_string_new_from_buf(allocator, &token_path_buf); + +cleanup: + aws_byte_buf_clean_up(&token_path_buf); + aws_byte_buf_clean_up(&sha1_buf); + return token_path_str; +} + +void aws_sso_token_destroy(struct aws_allocator *allocator, struct aws_sso_token *token) { + aws_string_destroy(token->token); + aws_mem_release(allocator, token); +} + +struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocator, const struct aws_string *file_path) { + AWS_PRECONDITION(allocator); + AWS_PRECONDITION(file_path); + + bool success = false; + + struct aws_sso_token *token = aws_mem_calloc(allocator, 1, sizeof(struct aws_sso_token)); + struct aws_byte_buf file_contents_buf; + AWS_ZERO_STRUCT(file_contents_buf); + struct aws_json_value *document_root = NULL; + + if (aws_byte_buf_init_from_file(&file_contents_buf, allocator, aws_string_c_str(file_path))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso token: failed to load token file %s", aws_string_c_str(file_path)); + goto cleanup; + } + + struct aws_byte_cursor document_cursor = aws_byte_cursor_from_buf(&file_contents_buf); + document_root = aws_json_value_new_from_string(allocator, document_cursor); + if (document_root == NULL) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso token: failed to parse access token file %s", + aws_string_c_str(file_path)); + goto cleanup; + } + + struct aws_byte_cursor access_token_cursor; + struct aws_json_value *access_token = + aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str("accessToken")); + if (!aws_json_value_is_string(access_token) || aws_json_value_get_string(access_token, &access_token_cursor)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso token: failed to parse accessToken from %s", + aws_string_c_str(file_path)); + aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); + goto cleanup; + } + + struct aws_byte_cursor expires_at_cursor; + struct aws_json_value *expires_at = + aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str("expiresAt")); + if (!aws_json_value_is_string(expires_at) || aws_json_value_get_string(expires_at, &expires_at_cursor)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso token: failed to parse expiresAt from %s", + aws_string_c_str(file_path)); + aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); + goto cleanup; + } + struct aws_date_time expiration; + if (aws_date_time_init_from_str_cursor(&expiration, &expires_at_cursor, AWS_DATE_FORMAT_ISO_8601)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso token: expiresAt '" PRInSTR "' in %s is not a valid ISO-8601 date string", + AWS_BYTE_CURSOR_PRI(expires_at_cursor), + aws_string_c_str(file_path)); + aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); + goto cleanup; + } + printf("%s", access_token_cursor.ptr); + token->token = aws_string_new_from_cursor(allocator, &access_token_cursor); + token->expiration = expiration; + + success = true; + +cleanup: + aws_json_value_destroy(document_root); + aws_byte_buf_clean_up(&file_contents_buf); + if (!success) { + aws_string_destroy(token->token); + aws_mem_release(allocator, token); + token = NULL; + } + return token; +} diff --git a/tests/token_util_tests.c b/tests/token_util_tests.c new file mode 100644 index 00000000..f2a9e89d --- /dev/null +++ b/tests/token_util_tests.c @@ -0,0 +1,63 @@ +/** + * 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 + +static int s_parse_token_location_url_test(struct aws_allocator *allocator, void *ctx) { + struct aws_string *start_url = aws_string_new_from_c_str(allocator, "https://d-92671207e4.awsapps.com/start"); + struct aws_string *token_path = construct_token_path(allocator, start_url); + struct aws_string *expected_token_path = + aws_string_new_from_c_str(allocator, "13f9d35043871d073ab260e020f0ffde092cb14b.json"); + + // TODO: mock home + ASSERT_TRUE(aws_string_eq(token_path, expected_token_path)); + + aws_string_destroy(start_url); + aws_string_destroy(token_path); + aws_string_destroy(expected_token_path); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(parse_token_location_url_test, s_parse_token_location_url_test); + +static int s_parse_token_location_session_test(struct aws_allocator *allocator, void *ctx) { + struct aws_string *session = aws_string_new_from_c_str(allocator, "admin"); + struct aws_string *token_path = construct_token_path(allocator, session); + struct aws_string *expected_token_path = + aws_string_new_from_c_str(allocator, "d033e22ae348aeb5660fc2140aec35850c4da997.json"); + // TODO: mock home + ASSERT_TRUE(aws_string_eq(token_path, expected_token_path)); + + aws_string_destroy(session); + aws_string_destroy(token_path); + aws_string_destroy(expected_token_path); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(parse_token_location_session_test, s_parse_token_location_session_test); + +AWS_STATIC_STRING_FROM_LITERAL( + s_valid_token_json, + "{\"accessToken\": \"string\",\"expiresAt\": \"2019-11-14T04:05:45Z\",\"refreshToken\": \"string\",\"clientId\": " + "\"ABCDEFG323242423121312312312312312\",\"clientSecret\": \"ABCDE123\",\"registrationExpiresAt\": " + "\"2022-03-06T19:53:17Z\",\"region\": \"us-west-2\",\"startUrl\": \"https://d-abc123.awsapps.com/start\"}"); +static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { + + aws_auth_library_init(allocator); + + struct aws_string *file_path = aws_create_process_unique_file_name(allocator); + ASSERT_TRUE(aws_create_profile_file(file_path, s_valid_token_json) == AWS_OP_SUCCESS); + struct aws_sso_token *token = aws_sso_token_new_from_file(allocator, file_path); + ASSERT_TRUE(aws_string_eq_c_str(token->token, "string")); + ASSERT_INT_EQUALS(aws_date_time_as_epoch_secs(&token->expiration), 1573704345); + aws_string_destroy(file_path); + aws_sso_token_destroy(allocator, token); + aws_auth_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(parse_sso_token_valid, s_parse_sso_token_valid); From 085f79121ad999c6c6a5c00dfcb8a737f88ff50b Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sat, 4 Mar 2023 20:14:32 -0800 Subject: [PATCH 11/93] adds fetch token logic --- include/aws/auth/credentials.h | 6 ++++ source/credentials.c | 19 +++++++++++ source/sso_token_provider_profile.c | 32 ++++++++++++++++++- ...en_util_tests.c => sso_token_util_tests.c} | 0 4 files changed, 56 insertions(+), 1 deletion(-) rename tests/{token_util_tests.c => sso_token_util_tests.c} (100%) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 3d6d06a6..100d44d0 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -634,6 +634,12 @@ struct aws_credentials *aws_credentials_new_ecc_from_aws_credentials( struct aws_allocator *allocator, const struct aws_credentials *credentials); +AWS_AUTH_API +struct aws_credentials *aws_credentials_new_token( + struct aws_allocator *allocator, + struct aws_byte_cursor token, + uint64_t expiration_timepoint_in_seconds); + /** * Add a reference to some credentials * diff --git a/source/credentials.c b/source/credentials.c index b3809e65..f98582f7 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -352,6 +352,25 @@ struct aws_credentials *aws_credentials_new_ecc_from_aws_credentials( return ecc_credentials; } +struct aws_credentials *aws_credentials_new_token( + struct aws_allocator *allocator, + struct aws_byte_cursor token, + uint64_t expiration_timepoint_in_seconds) { + if (token.ptr == NULL || token.len == 0) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + struct aws_credentials *credentials = aws_mem_calloc(allocator, 1, sizeof(struct aws_credentials)); + + credentials->allocator = allocator; + aws_atomic_init_int(&credentials->ref_count, 1); + credentials->type = TOKEN_IDENTITY; + struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials; + credentials_identity->access_key_id = aws_string_new_from_array(allocator, token.ptr, token.len); + credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds; + return credentials; +} + /* * global credentials provider APIs */ diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index 4689fc51..e03fe950 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -33,8 +33,38 @@ static int s_token_provider_profile_get_token_async( (void)callback; (void)user_data; struct aws_token_provider_profile_impl *impl = provider->impl; + struct aws_sso_token *sso_token = NULL; + struct aws_credentials *credentials = NULL; + sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); + if (!sso_token) { + goto on_error; + } + + /* Check token expiration. TODO: Refresh token if it is within refresh window */ + struct aws_date_time now; + aws_date_time_init_now(&now); + if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: cached token is expired."); + aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); + goto on_error; + } - // TODO: + credentials = aws_credentials_new_token( + provider->allocator, + aws_byte_cursor_from_string(sso_token->token), + aws_date_time_as_epoch_secs(&sso_token->expiration)); + if (!credentials) { + goto on_error; + } + + callback(credentials, AWS_OP_SUCCESS, user_data); + return AWS_OP_SUCCESS; +on_error: + if (sso_token) { + aws_sso_token_destroy(provider->allocator, sso_token); + } + aws_credentials_release(credentials); + callback(credentials, aws_last_error(), user_data); return AWS_OP_ERR; } diff --git a/tests/token_util_tests.c b/tests/sso_token_util_tests.c similarity index 100% rename from tests/token_util_tests.c rename to tests/sso_token_util_tests.c From 819a39e8d96d31698dd1c751d36cf9cbac224b4f Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 6 Mar 2023 08:58:03 -0800 Subject: [PATCH 12/93] split into two different token providers --- include/aws/auth/credentials.h | 19 ++ source/sso_token_provider_profile.c | 96 +----- source/sso_token_provider_sso_session.c | 286 ++++++++++++++++++ source/sso_token_utils.c | 1 + tests/CMakeLists.txt | 2 + ...ile_tests.c => soo_token_provider_tests.c} | 137 ++++++--- 6 files changed, 415 insertions(+), 126 deletions(-) create mode 100644 source/sso_token_provider_sso_session.c rename tests/{soo_token_provider_profile_tests.c => soo_token_provider_tests.c} (61%) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 100d44d0..6e1232ae 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -543,6 +543,20 @@ struct aws_sso_token_provider_profile_options { struct aws_byte_cursor config_file_name_override; }; +struct aws_sso_token_provider_sso_session_options { + struct aws_credentials_provider_shutdown_options shutdown_options; + + /* + * Override of what profile to use to source credentials from ('default' by default) + */ + struct aws_byte_cursor profile_name_override; + + /* + * Override path to the profile config file (~/.aws/config by default) + */ + struct aws_byte_cursor config_file_name_override; +}; + AWS_EXTERN_C_BEGIN /* @@ -1019,6 +1033,11 @@ struct aws_credentials_provider *aws_sso_token_provider_new_profile( struct aws_allocator *allocator, const struct aws_sso_token_provider_profile_options *options); +AWS_AUTH_API +struct aws_credentials_provider *aws_sso_token_provider_new_sso_session( + struct aws_allocator *allocator, + const struct aws_sso_token_provider_sso_session_options *options); + AWS_AUTH_API extern const struct aws_auth_http_system_vtable *g_aws_credentials_provider_http_function_table; AWS_EXTERN_C_END diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index e03fe950..fa5c9c7a 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -87,11 +87,8 @@ static struct aws_credentials_provider_vtable s_aws_token_provider_profile_vtabl .destroy = s_token_provider_profile_destroy, }; -AWS_STRING_FROM_LITERAL(s_sso_session_name, "sso_session"); -AWS_STRING_FROM_LITERAL(s_sso_account_id_name, "sso_account_id"); -AWS_STRING_FROM_LITERAL(s_sso_role_name_name, "sso_role_name"); -AWS_STRING_FROM_LITERAL(s_sso_region_name, "sso_region"); -AWS_STRING_FROM_LITERAL(s_sso_start_url_name, "sso_start_url"); +AWS_STRING_FROM_LITERAL(s_profile_sso_region_name, "sso_region"); +AWS_STRING_FROM_LITERAL(s_profile_sso_start_url_name, "sso_start_url"); struct token_provider_profile_parameters { struct aws_string *sso_region; @@ -103,8 +100,10 @@ static int s_token_provider_profile_parameters_init( struct aws_allocator *allocator, struct token_provider_profile_parameters *parameters, const struct aws_profile *profile) { - const struct aws_profile_property *sso_region_property = aws_profile_get_property(profile, s_sso_region_name); - const struct aws_profile_property *sso_start_url_property = aws_profile_get_property(profile, s_sso_start_url_name); + const struct aws_profile_property *sso_region_property = + aws_profile_get_property(profile, s_profile_sso_region_name); + const struct aws_profile_property *sso_start_url_property = + aws_profile_get_property(profile, s_profile_sso_start_url_name); if (!sso_region_property) { AWS_LOGF_ERROR( @@ -131,73 +130,6 @@ static int s_token_provider_profile_parameters_init( return AWS_OP_SUCCESS; } -static int s_token_provider_profile_parameters_sso_session_init( - struct aws_allocator *allocator, - struct token_provider_profile_parameters *parameters, - const struct aws_profile_collection *profile_collection, - const struct aws_profile *profile, - const struct aws_string *sso_session_name) { - - const struct aws_profile *session_profile = - aws_profile_collection_get_sso_session(profile_collection, sso_session_name); - if (!session_profile) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find an sso-session"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - } - - const struct aws_profile_property *sso_region_property = - aws_profile_get_property(session_profile, s_sso_region_name); - const struct aws_profile_property *sso_start_url_property = - aws_profile_get_property(session_profile, s_sso_start_url_name); - - if (!sso_region_property) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_region in sso-session"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - } - - if (!sso_start_url_property) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token parser failed to find sso_start_url in sso-session"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - } - - const struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); - const struct aws_string *sso_start_url = aws_profile_property_get_value(sso_start_url_property); - - /* Verify if sso_region & start_url are the same in profile section if they exist */ - const struct aws_profile_property *profile_sso_region_property = - aws_profile_get_property(profile, s_sso_region_name); - const struct aws_profile_property *profile_sso_start_url_property = - aws_profile_get_property(profile, s_sso_start_url_name); - - if (profile_sso_region_property && - !aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_region"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - } - - if (profile_sso_start_url_property && - !aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_start_url"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - } - - parameters->sso_region = aws_string_new_from_string(allocator, sso_region); - parameters->sso_start_url = aws_string_new_from_string(allocator, sso_start_url); - parameters->token_path = construct_token_path(allocator, sso_session_name); - if (!parameters->token_path) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token parser failed to construct token path in sso-session"); - return AWS_OP_ERR; - } - return AWS_OP_SUCCESS; -} - static void s_token_provider_profile_parameters_destroy( struct aws_allocator *allocator, struct token_provider_profile_parameters *parameters) { @@ -253,21 +185,7 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame goto cleanup; } - const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session_name); - if (sso_session_property) { - if (s_token_provider_profile_parameters_sso_session_init( - allocator, - parameters, - config_profiles, - profile, - aws_profile_property_get_value(sso_session_property))) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token provider could not load a valid sso profile and session at %s", - aws_string_c_str(profile_name)); - goto cleanup; - } - } else if (s_token_provider_profile_parameters_init(allocator, parameters, profile)) { + if (s_token_provider_profile_parameters_init(allocator, parameters, profile)) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token provider could not load a valid sso profile at %s", diff --git a/source/sso_token_provider_sso_session.c b/source/sso_token_provider_sso_session.c new file mode 100644 index 00000000..9bddb8be --- /dev/null +++ b/source/sso_token_provider_sso_session.c @@ -0,0 +1,286 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include +#include +#include +#ifdef _MSC_VER +/* allow non-constant declared initializers. */ +# pragma warning(disable : 4204) +#endif + +/* + * Profile provider implementation + */ +struct aws_token_provider_sso_session_impl { + struct aws_string *sso_region; + struct aws_string *sso_start_url; + struct aws_string *token_file_path; +}; + +static int s_token_provider_sso_session_get_token_async( + struct aws_credentials_provider *provider, + aws_on_get_credentials_callback_fn callback, + void *user_data) { + (void)provider; + (void)callback; + (void)user_data; + struct aws_token_provider_sso_session_impl *impl = provider->impl; + struct aws_sso_token *sso_token = NULL; + struct aws_credentials *credentials = NULL; + sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); + if (!sso_token) { + goto on_error; + } + + /* Check token expiration. TODO: Refresh token if it is within refresh window */ + struct aws_date_time now; + aws_date_time_init_now(&now); + if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: cached token is expired."); + aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); + goto on_error; + } + + credentials = aws_credentials_new_token( + provider->allocator, + aws_byte_cursor_from_string(sso_token->token), + aws_date_time_as_epoch_secs(&sso_token->expiration)); + if (!credentials) { + goto on_error; + } + + callback(credentials, AWS_OP_SUCCESS, user_data); + return AWS_OP_SUCCESS; +on_error: + if (sso_token) { + aws_sso_token_destroy(provider->allocator, sso_token); + } + aws_credentials_release(credentials); + callback(credentials, aws_last_error(), user_data); + return AWS_OP_ERR; +} + +static void s_token_provider_sso_session_destroy(struct aws_credentials_provider *provider) { + struct aws_token_provider_sso_session_impl *impl = provider->impl; + if (impl == NULL) { + return; + } + + aws_string_destroy(impl->sso_region); + aws_string_destroy(impl->sso_start_url); + aws_string_destroy(impl->token_file_path); + + aws_credentials_provider_invoke_shutdown_callback(provider); + aws_mem_release(provider->allocator, provider); +} + +static struct aws_credentials_provider_vtable s_aws_token_provider_sso_session_vtable = { + .get_credentials = s_token_provider_sso_session_get_token_async, + .destroy = s_token_provider_sso_session_destroy, +}; + +AWS_STRING_FROM_LITERAL(s_sso_session_name, "sso_session"); +AWS_STRING_FROM_LITERAL(s_sso_region_name, "sso_region"); +AWS_STRING_FROM_LITERAL(s_sso_start_url_name, "sso_start_url"); + +struct token_provider_sso_session_parameters { + struct aws_string *sso_region; + struct aws_string *sso_start_url; + struct aws_string *token_path; +}; + +static int s_token_provider_sso_session_parameters_sso_session_init( + struct aws_allocator *allocator, + struct token_provider_sso_session_parameters *parameters, + const struct aws_profile_collection *profile_collection, + const struct aws_profile *profile, + const struct aws_string *sso_session_name) { + + const struct aws_profile *session_profile = + aws_profile_collection_get_sso_session(profile_collection, sso_session_name); + if (!session_profile) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find an sso-session"); + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + } + + const struct aws_profile_property *sso_region_property = + aws_profile_get_property(session_profile, s_sso_region_name); + const struct aws_profile_property *sso_start_url_property = + aws_profile_get_property(session_profile, s_sso_start_url_name); + + if (!sso_region_property) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_region in sso-session"); + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + } + + if (!sso_start_url_property) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token parser failed to find sso_start_url in sso-session"); + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + } + + const struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); + const struct aws_string *sso_start_url = aws_profile_property_get_value(sso_start_url_property); + + /* Verify if sso_region & start_url are the same in profile section if they exist */ + const struct aws_profile_property *profile_sso_region_property = + aws_profile_get_property(profile, s_sso_region_name); + const struct aws_profile_property *profile_sso_start_url_property = + aws_profile_get_property(profile, s_sso_start_url_name); + + if (profile_sso_region_property && + !aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_region"); + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + } + + if (profile_sso_start_url_property && + !aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_start_url"); + return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + } + + parameters->sso_region = aws_string_new_from_string(allocator, sso_region); + parameters->sso_start_url = aws_string_new_from_string(allocator, sso_start_url); + parameters->token_path = construct_token_path(allocator, sso_session_name); + if (!parameters->token_path) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token parser failed to construct token path in sso-session"); + return AWS_OP_ERR; + } + return AWS_OP_SUCCESS; +} + +static void s_token_provider_sso_session_parameters_destroy( + struct aws_allocator *allocator, + struct token_provider_sso_session_parameters *parameters) { + + aws_string_destroy(parameters->sso_region); + aws_string_destroy(parameters->sso_start_url); + aws_string_destroy(parameters->token_path); + aws_mem_release(allocator, parameters); +} + +static struct token_provider_sso_session_parameters *s_token_provider_sso_session_parameters_new( + struct aws_allocator *allocator, + struct aws_byte_cursor profile_name_override, + struct aws_byte_cursor config_file_name_override) { + struct aws_profile_collection *config_profiles = NULL; + struct aws_string *config_file_path = NULL; + struct aws_string *profile_name = NULL; + bool success = false; + + struct token_provider_sso_session_parameters *parameters = + aws_mem_calloc(allocator, 1, sizeof(struct token_provider_sso_session_parameters)); + config_file_path = aws_get_config_file_path(allocator, &config_file_name_override); + + if (!config_file_path) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed resolve config file path"); + goto cleanup; + } + + profile_name = aws_get_profile_name(allocator, &profile_name_override); + if (!profile_name) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to resolve profile name"); + goto cleanup; + } + config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG); + + if (!config_profiles) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token parser could not load or parse" + " a config file."); + goto cleanup; + } + + const struct aws_profile *profile = aws_profile_collection_get_profile(config_profiles, profile_name); + + if (!profile) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token provider could not load" + " a profile at %s.", + aws_string_c_str(profile_name)); + goto cleanup; + } + + const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session_name); + if (sso_session_property) { + if (s_token_provider_sso_session_parameters_sso_session_init( + allocator, + parameters, + config_profiles, + profile, + aws_profile_property_get_value(sso_session_property))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "static: Profile token provider could not load a valid sso profile and session at %s", + aws_string_c_str(profile_name)); + goto cleanup; + } + } else { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso-session-token-provider: Could not find an sso-session at profile %s", + aws_string_c_str(profile_name)); + goto cleanup; + } + + success = true; + +cleanup: + aws_string_destroy(config_file_path); + aws_string_destroy(profile_name); + aws_profile_collection_release(config_profiles); + if (!success) { + s_token_provider_sso_session_parameters_destroy(allocator, parameters); + parameters = NULL; + } + return parameters; +} + +struct aws_credentials_provider *aws_sso_token_provider_new_sso_session( + struct aws_allocator *allocator, + const struct aws_sso_token_provider_sso_session_options *options) { + + struct token_provider_sso_session_parameters *parameters = s_token_provider_sso_session_parameters_new( + allocator, options->profile_name_override, options->config_file_name_override); + if (!parameters) { + return NULL; + } + struct aws_credentials_provider *provider = NULL; + struct aws_token_provider_sso_session_impl *impl = NULL; + + aws_mem_acquire_many( + allocator, + 2, + &provider, + sizeof(struct aws_credentials_provider), + &impl, + sizeof(struct aws_token_provider_sso_session_impl)); + AWS_ZERO_STRUCT(*provider); + AWS_ZERO_STRUCT(*impl); + aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_sso_session_vtable, impl); + impl->sso_region = aws_string_new_from_string(allocator, parameters->sso_region); + impl->sso_start_url = aws_string_new_from_string(allocator, parameters->sso_start_url); + impl->token_file_path = aws_string_new_from_string(allocator, parameters->sso_start_url); + provider->shutdown_options = options->shutdown_options; + + s_token_provider_sso_session_parameters_destroy(allocator, parameters); + return provider; +} diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index 7b8e46c2..772a64b0 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -69,6 +69,7 @@ struct aws_string *construct_token_path(struct aws_allocator *allocator, const s cleanup: aws_byte_buf_clean_up(&token_path_buf); aws_byte_buf_clean_up(&sha1_buf); + aws_string_destroy(home_directory); return token_path_str; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 263d9646..d4f0d87b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -104,6 +104,8 @@ endif() add_net_test_case(sso_token_provider_profile_invalid_profile_test) add_net_test_case(sso_token_provider_profile_valid_profile_test) +add_net_test_case(sso_token_provider_sso_session_invalid_profile_test) +add_net_test_case(sso_token_provider_sso_session_valid_profile_test) add_net_test_case(parse_token_location_url_test) add_net_test_case(parse_token_location_session_test) diff --git a/tests/soo_token_provider_profile_tests.c b/tests/soo_token_provider_tests.c similarity index 61% rename from tests/soo_token_provider_profile_tests.c rename to tests/soo_token_provider_tests.c index bbca749e..d52aa03f 100644 --- a/tests/soo_token_provider_profile_tests.c +++ b/tests/soo_token_provider_tests.c @@ -27,7 +27,7 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato static struct sso_session_profile_example s_invalid_profile_examples[] = { { - .name = "No sso-session", + .name = "No config", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( "[profile default]\naws_access_key_id=fake_access_key\naws_secret_access_key=fake_secret_key\n"), }, @@ -44,32 +44,91 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato "access_key=fake_secret_key\nsso_region=us-east-1\n"), }, { - .name = "Only sso_session", + .name = "only sso_session", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " "default]\naws_access_key_id=fake_access_key\naws_secret_" - "access_key=fake_secret_key\nsso_session=dev\n"), + "access_key=fake_secret_key\nsso_session=dev\n[sso-session " + "dev]\nsso_start_url=url\nsso_region=us-east-1"), }, + }; + + aws_unset_environment_value(s_default_profile_env_variable_name); + aws_unset_environment_value(s_default_config_path_env_variable_name); + aws_unset_environment_value(s_default_credentials_path_env_variable_name); + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + struct aws_sso_token_provider_profile_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + }; + + for (size_t i = 0; i < AWS_ARRAY_SIZE(s_invalid_profile_examples); ++i) { + printf("invalid example [%zu]: %s\n", i, s_invalid_profile_examples[i].name); + struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_invalid_profile_examples[i].text); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); + ASSERT_NULL(aws_sso_token_provider_new_profile(allocator, &options)); + aws_string_destroy(config_contents); + } + + aws_string_destroy(config_file_str); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_profile_invalid_profile_test, s_sso_token_provider_profile_invalid_profile_test); + +static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + static struct sso_session_profile_example s_valid_profile_examples[] = { { - .name = "No sso_session sso_start_url", - .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( - "[profile " - "default]\naws_access_key_id=fake_access_key\naws_secret_" - "access_key=fake_secret_key\nsso_session=dev\n[sso-session dev]\nsso_region=us-east-1"), + .name = "profile", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_region=us-east-" + "1\nsso_start_url=url"), }, { - .name = "No sso_session region", + .name = "with sso_session", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( - "[profile " - "default]\naws_access_key_id=fake_access_key\naws_secret_" - "access_key=fake_secret_key\nsso_session=dev\n[sso-session dev]\nsso_start_url=url"), + "[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_region=us-east-1\nsso_start_url=url\nsso_" + "session=dev\n[sso-session dev]\nsso_region=us-east-" + "1\nsso_start_url=url"), }, + }; + + aws_unset_environment_value(s_default_profile_env_variable_name); + aws_unset_environment_value(s_default_config_path_env_variable_name); + aws_unset_environment_value(s_default_credentials_path_env_variable_name); + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + struct aws_sso_token_provider_profile_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .profile_name_override = NULL, + .shutdown_options = NULL, + }; + + for (size_t i = 0; i < AWS_ARRAY_SIZE(s_valid_profile_examples); ++i) { + printf("valid example [%zu]: %s\n", i, s_valid_profile_examples[i].name); + struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_valid_profile_examples[i].text); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); + struct aws_credentials_provider *provider = aws_sso_token_provider_new_profile(allocator, &options); + ASSERT_NOT_NULL(provider); + aws_credentials_provider_release(provider); + aws_string_destroy(config_contents); + } + + aws_string_destroy(config_file_str); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_profile_valid_profile_test, s_sso_token_provider_profile_valid_profile_test); + +static int s_sso_token_provider_sso_session_invalid_profile_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + static struct sso_session_profile_example s_invalid_profile_examples[] = { { - .name = "sso_session with profile region and sso_start_url", - .text = - AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " - "default]\naws_access_key_id=fake_access_key\naws_secret_" - "access_key=fake_secret_key\nsso_session=dev\nsso_region=us-east-" - "1\nsso_start_url=url\n[sso-session dev]\nsso_start_url=url"), + .name = "no sso-session", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_region=us-east-" + "1\nsso_start_url=url"), }, { .name = "sso_session with different profile region", @@ -87,13 +146,21 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato "access_key=fake_secret_key\nsso_session=dev\nsso_region=us-east-" "1\nsso_start_url=url\n[sso-session dev]\nsso_region=us-east-1\nsso_start_url=url2"), }, + { + .name = "different sso_session name", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( + "[default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_region=us-east-1\nsso_start_url=url\nsso_" + "session=dev\n[sso-session dev2]\nsso_region=us-east-" + "1\nsso_start_url=url"), + }, }; aws_unset_environment_value(s_default_profile_env_variable_name); aws_unset_environment_value(s_default_config_path_env_variable_name); aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); - struct aws_sso_token_provider_profile_options options = { + struct aws_sso_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), }; @@ -101,7 +168,7 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato printf("invalid example [%zu]: %s\n", i, s_invalid_profile_examples[i].name); struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_invalid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); - ASSERT_NULL(aws_sso_token_provider_new_profile(allocator, &options)); + ASSERT_NULL(aws_sso_token_provider_new_sso_session(allocator, &options)); aws_string_destroy(config_contents); } @@ -109,41 +176,37 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato return AWS_OP_SUCCESS; } -AWS_TEST_CASE(sso_token_provider_profile_invalid_profile_test, s_sso_token_provider_profile_invalid_profile_test); +AWS_TEST_CASE( + sso_token_provider_sso_session_invalid_profile_test, + s_sso_token_provider_sso_session_invalid_profile_test); -static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator *allocator, void *ctx) { +static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_allocator *allocator, void *ctx) { (void)ctx; static struct sso_session_profile_example s_valid_profile_examples[] = { { - .name = "profile", + .name = "sso-session", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" - "access_key=fake_secret_key\nsso_region=us-east-" + "access_key=fake_secret_key\nsso_" + "session=dev\n[sso-session dev]\nsso_region=us-east-" "1\nsso_start_url=url"), }, { - .name = "sso_session", - .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( - "[default]\naws_access_key_id=fake_access_key\naws_secret_" - "access_key=fake_secret_key\nsso_session=dev\n[sso-session dev]\nsso_region=us-east-" - "1\nsso_start_url=url"), - }, - { - .name = "sso_session with profile region", + .name = "with nsso_region", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_region=us-east-1\nsso_" "session=dev\n[sso-session dev]\nsso_region=us-east-" "1\nsso_start_url=url"), }, { - .name = "sso_session with start url", + .name = "with nsso_start_url", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_start_url=url\nsso_" "session=dev\n[sso-session dev]\nsso_region=us-east-" "1\nsso_start_url=url"), }, { - .name = "sso_session with region and start url", + .name = "with profile", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( "[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_region=us-east-1\nsso_start_url=url\nsso_" @@ -156,7 +219,7 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator aws_unset_environment_value(s_default_config_path_env_variable_name); aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); - struct aws_sso_token_provider_profile_options options = { + struct aws_sso_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), .profile_name_override = NULL, .shutdown_options = NULL, @@ -166,7 +229,7 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator printf("valid example [%zu]: %s\n", i, s_valid_profile_examples[i].name); struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_valid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); - struct aws_credentials_provider *provider = aws_sso_token_provider_new_profile(allocator, &options); + struct aws_credentials_provider *provider = aws_sso_token_provider_new_sso_session(allocator, &options); ASSERT_NOT_NULL(provider); aws_credentials_provider_release(provider); aws_string_destroy(config_contents); @@ -176,4 +239,4 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator return AWS_OP_SUCCESS; } -AWS_TEST_CASE(sso_token_provider_profile_valid_profile_test, s_sso_token_provider_profile_valid_profile_test); +AWS_TEST_CASE(sso_token_provider_sso_session_valid_profile_test, s_sso_token_provider_sso_session_valid_profile_test); From d606b79654feecbc776abac5b3ad6f447f5f33bc Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 6 Mar 2023 10:10:05 -0800 Subject: [PATCH 13/93] cleanup --- source/sso_token_provider_profile.c | 43 +++++++++++++++---------- source/sso_token_provider_sso_session.c | 13 +++++--- source/sso_token_utils.c | 4 +++ 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index fa5c9c7a..1f7dcace 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -17,7 +17,7 @@ #endif /* - * Profile provider implementation + * sso-token profile provider implementation */ struct aws_token_provider_profile_impl { struct aws_string *sso_region; @@ -29,22 +29,23 @@ static int s_token_provider_profile_get_token_async( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, void *user_data) { - (void)provider; - (void)callback; - (void)user_data; struct aws_token_provider_profile_impl *impl = provider->impl; + struct aws_sso_token *sso_token = NULL; struct aws_credentials *credentials = NULL; + sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: unable to read file."); + aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto on_error; } - /* Check token expiration. TODO: Refresh token if it is within refresh window */ + /* check token expiration. */ struct aws_date_time now; aws_date_time_init_now(&now); if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: cached token is expired."); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: cached token is expired."); aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto on_error; } @@ -59,11 +60,11 @@ static int s_token_provider_profile_get_token_async( callback(credentials, AWS_OP_SUCCESS, user_data); return AWS_OP_SUCCESS; + on_error: - if (sso_token) { - aws_sso_token_destroy(provider->allocator, sso_token); - } + aws_sso_token_destroy(provider->allocator, sso_token); aws_credentials_release(credentials); + // TODO: Should I check last error and set it to source failure? callback(credentials, aws_last_error(), user_data); return AWS_OP_ERR; } @@ -107,13 +108,14 @@ static int s_token_provider_profile_parameters_init( if (!sso_region_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_region in profile"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: Profile token parser failed to find sso_region in profile"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } if (!sso_start_url_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_start_url in profile"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso-profile: Profile token parser failed to find sso_start_url in profile"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } @@ -123,7 +125,8 @@ static int s_token_provider_profile_parameters_init( parameters->token_path = construct_token_path(allocator, parameters->sso_start_url); if (!parameters->token_path) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to construct token path in profile"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso-profile: Profile token parser failed to construct token path in profile"); return AWS_OP_ERR; } @@ -155,13 +158,16 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame if (!config_file_path) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed resolve config file path"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: Profile token parser failed resolve config file path"); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } profile_name = aws_get_profile_name(allocator, &profile_name_override); if (!profile_name) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to resolve profile name"); + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: Profile token parser failed to resolve profile name"); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG); @@ -169,8 +175,9 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame if (!config_profiles) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token parser could not load or parse" + "sso-profile: Profile token parser could not load or parse" " a config file."); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } @@ -179,17 +186,19 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame if (!profile) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token provider could not load" + "sso-profile: Profile token provider could not load" " a profile at %s.", aws_string_c_str(profile_name)); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } if (s_token_provider_profile_parameters_init(allocator, parameters, profile)) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token provider could not load a valid sso profile at %s", + "sso-profile: Profile token provider could not load a valid sso profile at %s", aws_string_c_str(profile_name)); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } diff --git a/source/sso_token_provider_sso_session.c b/source/sso_token_provider_sso_session.c index 9bddb8be..181156d8 100644 --- a/source/sso_token_provider_sso_session.c +++ b/source/sso_token_provider_sso_session.c @@ -29,22 +29,24 @@ static int s_token_provider_sso_session_get_token_async( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, void *user_data) { - (void)provider; - (void)callback; - (void)user_data; + struct aws_token_provider_sso_session_impl *impl = provider->impl; struct aws_sso_token *sso_token = NULL; struct aws_credentials *credentials = NULL; + sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: unable to read file."); + aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto on_error; } - /* Check token expiration. TODO: Refresh token if it is within refresh window */ + /* TODO: Refresh token if it is within refresh window */ + /* check token expiration. */ struct aws_date_time now; aws_date_time_init_now(&now); if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: cached token is expired."); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: cached token is expired."); aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto on_error; } @@ -59,6 +61,7 @@ static int s_token_provider_sso_session_get_token_async( callback(credentials, AWS_OP_SUCCESS, user_data); return AWS_OP_SUCCESS; + on_error: if (sso_token) { aws_sso_token_destroy(provider->allocator, sso_token); diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index 772a64b0..3628dd4a 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -74,6 +74,10 @@ struct aws_string *construct_token_path(struct aws_allocator *allocator, const s } void aws_sso_token_destroy(struct aws_allocator *allocator, struct aws_sso_token *token) { + if (token == NULL) { + return; + } + aws_string_destroy(token->token); aws_mem_release(allocator, token); } From f566454c1066ca26aeda4e23d16200a999627ce0 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 6 Mar 2023 10:16:05 -0800 Subject: [PATCH 14/93] cleanup --- include/aws/auth/private/sso_token_utils.h | 2 +- source/sso_token_provider_profile.c | 7 +---- source/sso_token_provider_sso_session.c | 34 +++++++++++----------- source/sso_token_utils.c | 2 +- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index 63e6ae74..9e1dace3 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -19,7 +19,7 @@ AWS_EXTERN_C_BEGIN /* Construct token path which ~/.aws/sso/cache/.json */ AWS_AUTH_API -struct aws_string *construct_token_path(struct aws_allocator *allocator, const struct aws_string *input); +struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, const struct aws_string *input); AWS_AUTH_API void aws_sso_token_destroy(struct aws_allocator *allocator, struct aws_sso_token *token); diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index 1f7dcace..5ec405e2 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -122,7 +122,7 @@ static int s_token_provider_profile_parameters_init( parameters->sso_region = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_region_property)); parameters->sso_start_url = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_start_url_property)); - parameters->token_path = construct_token_path(allocator, parameters->sso_start_url); + parameters->token_path = aws_construct_token_path(allocator, parameters->sso_start_url); if (!parameters->token_path) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, @@ -244,9 +244,4 @@ struct aws_credentials_provider *aws_sso_token_provider_new_profile( s_token_provider_profile_parameters_destroy(allocator, parameters); return provider; - - // on_error: - // aws_credentials_provider_destroy(provider); - // s_token_provider_profile_parameters_destroy(allocator, parameters); - // return NULL; } diff --git a/source/sso_token_provider_sso_session.c b/source/sso_token_provider_sso_session.c index 181156d8..8dcbc223 100644 --- a/source/sso_token_provider_sso_session.c +++ b/source/sso_token_provider_sso_session.c @@ -17,7 +17,7 @@ #endif /* - * Profile provider implementation + * sso-session token provider implementation */ struct aws_token_provider_sso_session_impl { struct aws_string *sso_region; @@ -110,7 +110,7 @@ static int s_token_provider_sso_session_parameters_sso_session_init( const struct aws_profile *session_profile = aws_profile_collection_get_sso_session(profile_collection, sso_session_name); if (!session_profile) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find an sso-session"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find an sso-session"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } @@ -121,21 +121,20 @@ static int s_token_provider_sso_session_parameters_sso_session_init( if (!sso_region_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to find sso_region in sso-session"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find sso_region in sso-session"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } if (!sso_start_url_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token parser failed to find sso_start_url in sso-session"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find sso_start_url in sso-session"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } const struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); const struct aws_string *sso_start_url = aws_profile_property_get_value(sso_start_url_property); - /* Verify if sso_region & start_url are the same in profile section if they exist */ + /* Verify sso_region & start_url are the same in profile section if they exist */ const struct aws_profile_property *profile_sso_region_property = aws_profile_get_property(profile, s_sso_region_name); const struct aws_profile_property *profile_sso_start_url_property = @@ -144,24 +143,25 @@ static int s_token_provider_sso_session_parameters_sso_session_init( if (profile_sso_region_property && !aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_region"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: profile & sso-session have different value for sso_region"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } if (profile_sso_start_url_property && !aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: profile & sso-session have different value for sso_start_url"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso-session: profile & sso-session have different value for sso_start_url"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } parameters->sso_region = aws_string_new_from_string(allocator, sso_region); parameters->sso_start_url = aws_string_new_from_string(allocator, sso_start_url); - parameters->token_path = construct_token_path(allocator, sso_session_name); + parameters->token_path = aws_construct_token_path(allocator, sso_session_name); if (!parameters->token_path) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token parser failed to construct token path in sso-session"); + "sso-session: token parser failed to construct token path in sso-session"); return AWS_OP_ERR; } return AWS_OP_SUCCESS; @@ -191,14 +191,13 @@ static struct token_provider_sso_session_parameters *s_token_provider_sso_sessio config_file_path = aws_get_config_file_path(allocator, &config_file_name_override); if (!config_file_path) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed resolve config file path"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token provider failed resolve config file path"); goto cleanup; } profile_name = aws_get_profile_name(allocator, &profile_name_override); if (!profile_name) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "static: Profile token parser failed to resolve profile name"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token provider failed to resolve profile name"); goto cleanup; } config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG); @@ -206,7 +205,7 @@ static struct token_provider_sso_session_parameters *s_token_provider_sso_sessio if (!config_profiles) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token parser could not load or parse" + "sso-session: token provider could not load or parse" " a config file."); goto cleanup; } @@ -216,7 +215,7 @@ static struct token_provider_sso_session_parameters *s_token_provider_sso_sessio if (!profile) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token provider could not load" + "sso-session: token provider could not load" " a profile at %s.", aws_string_c_str(profile_name)); goto cleanup; @@ -232,15 +231,16 @@ static struct token_provider_sso_session_parameters *s_token_provider_sso_sessio aws_profile_property_get_value(sso_session_property))) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "static: Profile token provider could not load a valid sso profile and session at %s", + "sso-session: token provider could not load a valid sso profile and session at %s", aws_string_c_str(profile_name)); goto cleanup; } } else { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-session-token-provider: Could not find an sso-session at profile %s", + "sso-session: token provider could not find an sso-session at profile %s", aws_string_c_str(profile_name)); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index 3628dd4a..4c2608be 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -15,7 +15,7 @@ AWS_STATIC_STRING_FROM_LITERAL(s_sso_cache_directory, "/.aws/sso/cache/"); -struct aws_string *construct_token_path(struct aws_allocator *allocator, const struct aws_string *input) { +struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, const struct aws_string *input) { AWS_PRECONDITION(input); struct aws_string *token_path_str = NULL; From 93825052a37edd8a3395b6520158c4e93ee38ffa Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 6 Mar 2023 11:46:16 -0800 Subject: [PATCH 15/93] initial sso provider --- include/aws/auth/auth.h | 1 + include/aws/auth/credentials.h | 33 ++ include/aws/auth/private/credentials_utils.h | 8 + source/credentials_provider_sso.c | 303 +++++++++++++++++++ source/credentials_utils.c | 41 +++ source/sso_token_provider_profile.c | 22 +- tests/CMakeLists.txt | 2 + tests/credentials_provider_sso_tests.c | 33 ++ tests/soo_token_provider_tests.c | 4 - tests/sso_token_util_tests.c | 24 +- 10 files changed, 442 insertions(+), 29 deletions(-) create mode 100644 source/credentials_provider_sso.c create mode 100644 tests/credentials_provider_sso_tests.c diff --git a/include/aws/auth/auth.h b/include/aws/auth/auth.h index a9683a11..90a94756 100644 --- a/include/aws/auth/auth.h +++ b/include/aws/auth/auth.h @@ -45,6 +45,7 @@ enum aws_auth_errors { AWS_AUTH_CREDENTIALS_PROVIDER_DELEGATE_FAILURE, AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE, AWS_AUTH_SSO_TOKEN_INVALID, + AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE, AWS_AUTH_ERROR_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_AUTH_PACKAGE_ID) }; diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 6e1232ae..a0711244 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -340,6 +340,34 @@ struct aws_credentials_provider_sts_web_identity_options { struct aws_auth_http_system_vtable *function_table; }; +struct aws_credentials_provider_sso_options { + struct aws_credentials_provider_shutdown_options shutdown_options; + + /* + * Override of what profile to use to source credentials from ('default' by default) + */ + struct aws_byte_cursor profile_name_override; + + /* + * Override path to the profile config file (~/.aws/config by default) + */ + struct aws_byte_cursor config_file_name_override; + + /* + * Connection bootstrap to use for any network connections made while sourcing credentials + */ + struct aws_client_bootstrap *bootstrap; + + /* + * Client TLS context to use when querying STS web identity provider. + * Required. + */ + struct aws_tls_ctx *tls_ctx; + + /* For mocking the http layer in tests, leave NULL otherwise */ + struct aws_auth_http_system_vtable *function_table; +}; + /** * Configuration options for the STS credentials provider */ @@ -941,6 +969,11 @@ struct aws_credentials_provider *aws_credentials_provider_new_sts_web_identity( struct aws_allocator *allocator, const struct aws_credentials_provider_sts_web_identity_options *options); +AWS_AUTH_API +struct aws_credentials_provider *aws_credentials_provider_new_sso( + struct aws_allocator *allocator, + const struct aws_credentials_provider_sso_options *options); + /* * Creates a provider that sources credentials from running an external command or process * diff --git a/include/aws/auth/private/credentials_utils.h b/include/aws/auth/private/credentials_utils.h index 7bba2433..99c3e5ce 100644 --- a/include/aws/auth/private/credentials_utils.h +++ b/include/aws/auth/private/credentials_utils.h @@ -160,6 +160,14 @@ struct aws_credentials *aws_parse_credentials_from_json_document( AWS_AUTH_API enum aws_retry_error_type aws_credentials_provider_compute_retry_error_type(int response_code, int error_code); +/* + * Loads an aws config profile collection + */ +AWS_AUTH_API +struct aws_profile_collection *aws_load_config( + struct aws_allocator *allocator, + struct aws_byte_cursor config_file_name_override); + AWS_EXTERN_C_END #endif /* AWS_AUTH_CREDENTIALS_PRIVATE_H */ diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c new file mode 100644 index 00000000..db04da02 --- /dev/null +++ b/source/credentials_provider_sso.c @@ -0,0 +1,303 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(disable : 4204) +# pragma warning(disable : 4232) +#endif /* _MSC_VER */ + +static int s_construct_endpoint( + struct aws_allocator *allocator, + struct aws_byte_buf *endpoint, + const struct aws_string *region) { + // TODO: confirm logic + if (!allocator || !endpoint || !region) { + return AWS_ERROR_INVALID_ARGUMENT; + } + aws_byte_buf_clean_up(endpoint); + struct aws_byte_cursor sso_prefix = aws_byte_cursor_from_c_str("portal.sso."); + struct aws_byte_cursor region_cursor = aws_byte_cursor_from_string(region); + struct aws_byte_cursor amazonaws_cursor = aws_byte_cursor_from_c_str(".amazonaws.com"); + struct aws_byte_cursor cn_cursor = aws_byte_cursor_from_c_str(".cn"); + + if (aws_byte_buf_init_copy_from_cursor(endpoint, allocator, sso_prefix)) { + goto on_error; + } + + if (aws_byte_buf_append(endpoint, ®ion_cursor) || aws_byte_buf_append(endpoint, &amazonaws_cursor)) { + goto on_error; + } + + if (aws_string_eq_c_str_ignore_case(region, "cn-north-1") || + aws_string_eq_c_str_ignore_case(region, "cn-northwest-1")) { + if (aws_byte_buf_append_dynamic(endpoint, &cn_cursor)) { + goto on_error; + } + } + return AWS_OP_SUCCESS; + +on_error: + aws_byte_buf_clean_up(endpoint); + return AWS_OP_ERR; +} + +AWS_STATIC_STRING_FROM_LITERAL(s_sso_account_id, "sso_account_id"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_region, "sso_region"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_role_name, "sso_role_name"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_session, "sso_session"); + +struct sso_parameters { + struct aws_allocator *allocator; + struct aws_byte_buf endpoint; + struct aws_string *sso_account_id; + /* region is actually used to construct endpoint */ + struct aws_string *sso_role_name; + struct aws_credentials_provider *token_provider; +}; + +static void s_parameters_destroy(struct sso_parameters *parameters) { + if (!parameters) { + return; + } + aws_byte_buf_clean_up(¶meters->endpoint); + aws_string_destroy(parameters->sso_account_id); + aws_string_destroy(parameters->sso_role_name); + aws_credentials_provider_release(parameters->token_provider); + aws_mem_release(parameters->allocator, parameters); +} + +static struct sso_parameters *s_parameters_new( + struct aws_allocator *allocator, + const struct aws_credentials_provider_sso_options *options) { + + struct sso_parameters *parameters = aws_mem_calloc(allocator, 1, sizeof(struct sso_parameters)); + parameters->allocator = allocator; + + struct aws_profile_collection *config_profile = NULL; + struct aws_string *profile_name = NULL; + bool success = false; + + profile_name = aws_get_profile_name(allocator, &options->profile_name_override); + if (!profile_name) { + AWS_LOGF_DEBUG(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: failed to resolve profile name"); + goto on_finish; + } + + config_profile = aws_load_config(allocator, options->config_file_name_override); + if (!config_profile) { + goto on_finish; + } + + const struct aws_profile *profile = aws_profile_collection_get_profile(config_profile, profile_name); + if (!profile) { + AWS_LOGF_DEBUG( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: failed to load \"%s\" profile", aws_string_c_str(profile_name)); + goto on_finish; + } + + const struct aws_profile_property *sso_account_id = aws_profile_get_property(profile, s_sso_account_id); + const struct aws_profile_property *sso_role_name = aws_profile_get_property(profile, s_sso_role_name); + const struct aws_profile_property *sso_region = aws_profile_get_property(profile, s_sso_region); + + if (!sso_account_id) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_account_id is missing"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + if (!sso_role_name) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_role_name is missing"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + if (!sso_region) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_region is missing"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + + const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session); + if (sso_session_property) { + struct aws_sso_token_provider_sso_session_options token_provider_options; + AWS_ZERO_STRUCT(options); + token_provider_options.config_file_name_override = options->config_file_name_override; + token_provider_options.profile_name_override = options->profile_name_override; + + parameters->token_provider = aws_sso_token_provider_new_sso_session(allocator, &token_provider_options); + if (!parameters->token_provider) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a sso token provider"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + } else { + struct aws_sso_token_provider_profile_options token_provider_options; + AWS_ZERO_STRUCT(options); + token_provider_options.config_file_name_override = options->config_file_name_override; + token_provider_options.profile_name_override = options->profile_name_override; + + parameters->token_provider = aws_sso_token_provider_new_profile(allocator, &token_provider_options); + if (!parameters->token_provider) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a profile token provider"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + } + + parameters->sso_account_id = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_account_id)); + parameters->sso_role_name = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_role_name)); + /* determine endpoint */ + if (s_construct_endpoint(allocator, ¶meters->endpoint, parameters->sso_region)) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to construct sso endpoint"); + goto on_finish; + } + AWS_LOGF_DEBUG( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Successfully loaded all required parameters for sso credentials provider."); + success = true; + +on_finish: + + if (!success) { + s_parameters_destroy(parameters); + parameters = NULL; + } + aws_string_destroy(profile_name); + aws_profile_collection_release(config_profile); + + return parameters; +} + +struct aws_credentials_provider *aws_credentials_provider_new_sso( + struct aws_allocator *allocator, + const struct aws_credentials_provider_sso_options *options) { + + struct sso_parameters *parameters = s_parameters_new(allocator, options); + if (!parameters) { + return NULL; + } + + // struct aws_tls_connection_options tls_connection_options; + // AWS_ZERO_STRUCT(tls_connection_options); + + // struct aws_credentials_provider *provider = NULL; + // struct aws_credentials_provider_sts_web_identity_impl *impl = NULL; + + // aws_mem_acquire_many( + // allocator, + // 2, + // &provider, + // sizeof(struct aws_credentials_provider), + // &impl, + // sizeof(struct aws_credentials_provider_sts_web_identity_impl)); + + // if (!provider) { + // goto on_error; + // } + + // AWS_ZERO_STRUCT(*provider); + // AWS_ZERO_STRUCT(*impl); + + // aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_sts_web_identity_vtable, + // impl); + + // if (!options->tls_ctx) { + // AWS_LOGF_ERROR( + // AWS_LS_AUTH_CREDENTIALS_PROVIDER, + // "a TLS context must be provided to the STS web identity credentials provider"); + // aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + // return NULL; + // } + + // aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx); + // struct aws_byte_cursor host = aws_byte_cursor_from_buf(¶meters->endpoint); + // if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) { + // AWS_LOGF_ERROR( + // AWS_LS_AUTH_CREDENTIALS_PROVIDER, + // "(id=%p): failed to create a tls connection options with error %s", + // (void *)provider, + // aws_error_str(aws_last_error())); + // goto on_error; + // } + + // struct aws_socket_options socket_options; + // AWS_ZERO_STRUCT(socket_options); + // socket_options.type = AWS_SOCKET_STREAM; + // socket_options.domain = AWS_SOCKET_IPV4; + // socket_options.connect_timeout_ms = (uint32_t)aws_timestamp_convert( + // STS_WEB_IDENTITY_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL); + + // struct aws_http_connection_manager_options manager_options; + // AWS_ZERO_STRUCT(manager_options); + // manager_options.bootstrap = options->bootstrap; + // manager_options.initial_window_size = STS_WEB_IDENTITY_RESPONSE_SIZE_LIMIT; + // manager_options.socket_options = &socket_options; + // manager_options.host = host; + // manager_options.port = 443; + // manager_options.max_connections = 2; + // manager_options.shutdown_complete_callback = s_on_connection_manager_shutdown; + // manager_options.shutdown_complete_user_data = provider; + // manager_options.tls_connection_options = &tls_connection_options; + + // impl->function_table = options->function_table; + // if (impl->function_table == NULL) { + // impl->function_table = g_aws_credentials_provider_http_function_table; + // } + + // impl->connection_manager = impl->function_table->aws_http_connection_manager_new(allocator, + // &manager_options); if (impl->connection_manager == NULL) { + // goto on_error; + // } + + // impl->role_arn = aws_string_new_from_array(allocator, parameters->role_arn.buffer, parameters->role_arn.len); + // if (impl->role_arn == NULL) { + // goto on_error; + // } + + // impl->role_session_name = + // aws_string_new_from_array(allocator, parameters->role_session_name.buffer, + // parameters->role_session_name.len); + // if (impl->role_session_name == NULL) { + // goto on_error; + // } + + // impl->token_file_path = + // aws_string_new_from_array(allocator, parameters->token_file_path.buffer, + // parameters->token_file_path.len); + // if (impl->token_file_path == NULL) { + // goto on_error; + // } + + // provider->shutdown_options = options->shutdown_options; + // s_parameters_destroy(parameters); + // aws_tls_connection_options_clean_up(&tls_connection_options); + // return provider; + + // on_error: + + // aws_credentials_provider_destroy(provider); + // s_parameters_destroy(parameters); + // aws_tls_connection_options_clean_up(&tls_connection_options); + return NULL; +} diff --git a/source/credentials_utils.c b/source/credentials_utils.c index 2cb61d65..1bf0462b 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ +#include #include #include @@ -292,3 +293,43 @@ enum aws_retry_error_type aws_credentials_provider_compute_retry_error_type(int return error_type; } + +struct aws_profile_collection *aws_load_config( + struct aws_allocator *allocator, + struct aws_byte_cursor config_file_name_override) { + + struct aws_profile_collection *config_profiles = NULL; + struct aws_string *config_file_path = NULL; + + config_file_path = aws_get_config_file_path(allocator, &config_file_name_override); + if (!config_file_path) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "Failed to resolve config file path: %s", + aws_error_str(aws_last_error())); + goto on_error; + } + + config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG); + if (config_profiles != NULL) { + AWS_LOGF_DEBUG( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "Successfully built config profile collection from file at (%s)", + aws_string_c_str(config_file_path)); + } else { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "Failed to build config profile collection from file at (%s) : %s", + aws_string_c_str(config_file_path), + aws_error_str(aws_last_error())); + goto on_error; + } + + aws_string_destroy(config_file_path); + return config_profiles; + +on_error: + aws_string_destroy(config_file_path); + aws_profile_collection_destroy(config_profiles); + return NULL; +} diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index 5ec405e2..defe181e 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -64,7 +64,6 @@ static int s_token_provider_profile_get_token_async( on_error: aws_sso_token_destroy(provider->allocator, sso_token); aws_credentials_release(credentials); - // TODO: Should I check last error and set it to source failure? callback(credentials, aws_last_error(), user_data); return AWS_OP_ERR; } @@ -108,14 +107,13 @@ static int s_token_provider_profile_parameters_init( if (!sso_region_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: Profile token parser failed to find sso_region in profile"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to find sso_region in profile"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } if (!sso_start_url_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-profile: Profile token parser failed to find sso_start_url in profile"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to find sso_start_url in profile"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); } @@ -125,8 +123,7 @@ static int s_token_provider_profile_parameters_init( parameters->token_path = aws_construct_token_path(allocator, parameters->sso_start_url); if (!parameters->token_path) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-profile: Profile token parser failed to construct token path in profile"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to construct token path in profile"); return AWS_OP_ERR; } @@ -147,6 +144,7 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame struct aws_allocator *allocator, struct aws_byte_cursor profile_name_override, struct aws_byte_cursor config_file_name_override) { + struct aws_profile_collection *config_profiles = NULL; struct aws_string *config_file_path = NULL; struct aws_string *profile_name = NULL; @@ -157,16 +155,14 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame config_file_path = aws_get_config_file_path(allocator, &config_file_name_override); if (!config_file_path) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: Profile token parser failed resolve config file path"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed resolve config file path"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } profile_name = aws_get_profile_name(allocator, &profile_name_override); if (!profile_name) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: Profile token parser failed to resolve profile name"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to resolve profile name"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } @@ -175,7 +171,7 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame if (!config_profiles) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-profile: Profile token parser could not load or parse" + "sso-profile: token parser could not load or parse" " a config file."); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; @@ -186,7 +182,7 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame if (!profile) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-profile: Profile token provider could not load" + "sso-profile: token provider could not load" " a profile at %s.", aws_string_c_str(profile_name)); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); @@ -196,7 +192,7 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame if (s_token_provider_profile_parameters_init(allocator, parameters, profile)) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-profile: Profile token provider could not load a valid sso profile at %s", + "sso-profile: token provider could not load a valid sso profile at %s", aws_string_c_str(profile_name)); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d4f0d87b..c75bec70 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -111,6 +111,8 @@ add_net_test_case(parse_token_location_url_test) add_net_test_case(parse_token_location_session_test) add_net_test_case(parse_sso_token_valid) +add_net_test_case(credentials_provider_sso_new_destroy) + add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) add_test_case(imds_client_token_request_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c new file mode 100644 index 00000000..5d3bcf3f --- /dev/null +++ b/tests/credentials_provider_sso_tests.c @@ -0,0 +1,33 @@ +/** + * 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 + +static int s_credentials_provider_sso_new_destroy(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + struct aws_sso_token_provider_sso_session_options options; + AWS_ZERO_STRUCT(options); + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_new_destroy, s_credentials_provider_sso_new_destroy); diff --git a/tests/soo_token_provider_tests.c b/tests/soo_token_provider_tests.c index d52aa03f..2a2aecc8 100644 --- a/tests/soo_token_provider_tests.c +++ b/tests/soo_token_provider_tests.c @@ -100,8 +100,6 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_sso_token_provider_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), - .profile_name_override = NULL, - .shutdown_options = NULL, }; for (size_t i = 0; i < AWS_ARRAY_SIZE(s_valid_profile_examples); ++i) { @@ -221,8 +219,6 @@ static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_alloca struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_sso_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), - .profile_name_override = NULL, - .shutdown_options = NULL, }; for (size_t i = 0; i < AWS_ARRAY_SIZE(s_valid_profile_examples); ++i) { diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index f2a9e89d..b17ae29b 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -12,31 +12,31 @@ static int s_parse_token_location_url_test(struct aws_allocator *allocator, void *ctx) { struct aws_string *start_url = aws_string_new_from_c_str(allocator, "https://d-92671207e4.awsapps.com/start"); - struct aws_string *token_path = construct_token_path(allocator, start_url); - struct aws_string *expected_token_path = - aws_string_new_from_c_str(allocator, "13f9d35043871d073ab260e020f0ffde092cb14b.json"); + struct aws_string *token_path = aws_construct_token_path(allocator, start_url); - // TODO: mock home - ASSERT_TRUE(aws_string_eq(token_path, expected_token_path)); + struct aws_byte_cursor token_cursor = aws_byte_cursor_from_string(token_path); + struct aws_byte_cursor expected_token_cursor = + aws_byte_cursor_from_c_str("/.aws/sso/cache/13f9d35043871d073ab260e020f0ffde092cb14b.json"); + struct aws_byte_cursor find_cursor; + ASSERT_SUCCESS(aws_byte_cursor_find_exact(&token_cursor, &expected_token_cursor, &find_cursor)); aws_string_destroy(start_url); aws_string_destroy(token_path); - aws_string_destroy(expected_token_path); return AWS_OP_SUCCESS; } AWS_TEST_CASE(parse_token_location_url_test, s_parse_token_location_url_test); static int s_parse_token_location_session_test(struct aws_allocator *allocator, void *ctx) { struct aws_string *session = aws_string_new_from_c_str(allocator, "admin"); - struct aws_string *token_path = construct_token_path(allocator, session); - struct aws_string *expected_token_path = - aws_string_new_from_c_str(allocator, "d033e22ae348aeb5660fc2140aec35850c4da997.json"); - // TODO: mock home - ASSERT_TRUE(aws_string_eq(token_path, expected_token_path)); + struct aws_string *token_path = aws_construct_token_path(allocator, session); + struct aws_byte_cursor token_cursor = aws_byte_cursor_from_string(token_path); + struct aws_byte_cursor expected_token_cursor = + aws_byte_cursor_from_c_str("/.aws/sso/cache/d033e22ae348aeb5660fc2140aec35850c4da997.json"); + struct aws_byte_cursor find_cursor; + ASSERT_SUCCESS(aws_byte_cursor_find_exact(&token_cursor, &expected_token_cursor, &find_cursor)); aws_string_destroy(session); aws_string_destroy(token_path); - aws_string_destroy(expected_token_path); return AWS_OP_SUCCESS; } AWS_TEST_CASE(parse_token_location_session_test, s_parse_token_location_session_test); From 20d5a3a1c723a246c6e86def9e871bf6b92904f2 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Tue, 7 Mar 2023 09:49:22 -0800 Subject: [PATCH 16/93] in progress --- source/credentials_provider_sso.c | 332 +++++++++++++++++++++--------- 1 file changed, 229 insertions(+), 103 deletions(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index db04da02..5bf3b8d5 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -30,6 +30,155 @@ # pragma warning(disable : 4232) #endif /* _MSC_VER */ +#define SSO_RESPONSE_SIZE_INITIAL 2048 +#define SSO_RESPONSE_SIZE_LIMIT 10000 +#define SSO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS 2 +#define SSO_CREDS_DEFAULT_DURATION_SECONDS 900 +#define SSO_MAX_ATTEMPTS 3 + +struct aws_credentials_provider_sso_impl { + struct aws_http_connection_manager *connection_manager; + const struct aws_auth_http_system_vtable *function_table; + struct aws_string *endpoint; + struct aws_string *sso_account_id; + struct aws_string *sso_role_name; + struct aws_credentials_provider *token_provider; +}; + +/** + * sso_user_data - scratch data for each outstanding SSO query. + */ +struct sso_user_data { + /* immutable post-creation */ + struct aws_allocator *allocator; + struct aws_credentials_provider *provider; + aws_on_get_credentials_callback_fn *original_callback; + void *original_user_data; + + /* mutable */ + struct aws_http_connection *connection; + struct aws_http_message *request; + struct aws_byte_buf response; + struct aws_retry_token *retry_token; + + /* URI path and query string. */ + struct aws_byte_buf path_and_query; + + /* Track last HTTP response status and last error code. */ + int status_code; + int error_code; +}; + +static void s_user_data_destroy(struct sso_user_data *user_data) { + if (user_data == NULL) { + return; + } + + s_user_data_reset_request_and_response(user_data); + + if (user_data->connection) { + struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + + impl->function_table->aws_http_connection_manager_release_connection( + impl->connection_manager, user_data->connection); + } + + aws_byte_buf_clean_up(&user_data->response); + aws_retry_token_release(user_data->retry_token); + + aws_byte_buf_clean_up(&user_data->path_and_query); + + aws_credentials_provider_release(user_data->provider); + aws_mem_release(user_data->allocator, user_data); +} + +static struct sso_user_data *s_user_data_new( + struct aws_credentials_provider *provider, + aws_on_get_credentials_callback_fn callback, + void *user_data) { + struct aws_credentials_provider_sso_impl *impl = provider->impl; + + struct sso_user_data *wrapped_user_data = aws_mem_calloc(provider->allocator, 1, sizeof(struct sso_user_data)); + wrapped_user_data->allocator = provider->allocator; + wrapped_user_data->provider = aws_credentials_provider_acquire(provider); + wrapped_user_data->original_user_data = user_data; + wrapped_user_data->original_callback = callback; + + struct aws_byte_cursor account_id_cursor = aws_byte_cursor_from_string(impl->sso_account_id); + struct aws_byte_cursor role_name_cursor = aws_byte_cursor_from_string(impl->sso_role_name); + struct aws_byte_cursor path_cursor = aws_byte_cursor_from_c_str("/federation/credentials?account_id="); + struct aws_byte_cursor role_name_param_cursor = aws_byte_cursor_from_c_str("&role_name="); + + if (aws_byte_buf_init_copy_from_cursor(&wrapped_user_data->path_and_query, provider->allocator, path_cursor) || + aws_byte_buf_append_encoding_uri_param(&wrapped_user_data->path_and_query, &account_id_cursor) || + aws_byte_buf_append_dynamic(&wrapped_user_data->path_and_query, &role_name_param_cursor) || + aws_byte_buf_append_encoding_uri_param(&wrapped_user_data->path_and_query, &role_name_cursor)) { + goto on_error; + } + + if (aws_byte_buf_init(&wrapped_user_data->response, provider->allocator, SSO_RESPONSE_SIZE_INITIAL)) { + goto on_error; + } + + return wrapped_user_data; + +on_error: + s_user_data_destroy(wrapped_user_data); + + return NULL; +} + +static int s_credentials_provider_sso_get_credentials_async( + struct aws_credentials_provider *provider, + aws_on_get_credentials_callback_fn callback, + void *user_data) { + + struct aws_credentials_provider_sso_impl *impl = provider->impl; + + struct sso_user_data *wrapped_user_data = s_user_data_new(provider, callback, user_data); + if (wrapped_user_data == NULL) { + callback(NULL, aws_last_error(), user_data); + return AWS_OP_ERR; + } + + return AWS_OP_ERR; +} + +static void s_on_connection_manager_shutdown(void *user_data) { + struct aws_credentials_provider *provider = user_data; + + aws_credentials_provider_invoke_shutdown_callback(provider); + aws_mem_release(provider->allocator, provider); +} +static void s_credentials_provider_sso_destroy(struct aws_credentials_provider *provider) { + + struct aws_credentials_provider_sso_impl *impl = provider->impl; + if (impl == NULL) { + return; + } + aws_string_destroy(impl->endpoint); + aws_string_destroy(impl->sso_account_id); + aws_string_destroy(impl->sso_role_name); + aws_credentials_provider_release(impl->token_provider); + + /* aws_http_connection_manager_release will eventually leads to call of s_on_connection_manager_shutdown, + * which will do memory release for provider and impl. So We should be freeing impl + * related memory first, then call aws_http_connection_manager_release. + */ + if (impl->connection_manager) { + impl->function_table->aws_http_connection_manager_release(impl->connection_manager); + } else { + /* If provider setup failed halfway through, connection_manager might not exist. + * In this case invoke shutdown completion callback directly to finish cleanup */ + s_on_connection_manager_shutdown(provider); + } +} + +static struct aws_credentials_provider_vtable s_aws_credentials_provider_sso_vtable = { + .get_credentials = s_credentials_provider_sso_get_credentials_async, + .destroy = s_credentials_provider_sso_destroy, +}; + static int s_construct_endpoint( struct aws_allocator *allocator, struct aws_byte_buf *endpoint, @@ -74,7 +223,6 @@ struct sso_parameters { struct aws_allocator *allocator; struct aws_byte_buf endpoint; struct aws_string *sso_account_id; - /* region is actually used to construct endpoint */ struct aws_string *sso_role_name; struct aws_credentials_provider *token_provider; }; @@ -169,7 +317,7 @@ static struct sso_parameters *s_parameters_new( parameters->sso_account_id = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_account_id)); parameters->sso_role_name = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_role_name)); /* determine endpoint */ - if (s_construct_endpoint(allocator, ¶meters->endpoint, parameters->sso_region)) { + if (s_construct_endpoint(allocator, ¶meters->endpoint, aws_profile_property_get_value(sso_region))) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to construct sso endpoint"); goto on_finish; } @@ -198,106 +346,84 @@ struct aws_credentials_provider *aws_credentials_provider_new_sso( return NULL; } - // struct aws_tls_connection_options tls_connection_options; - // AWS_ZERO_STRUCT(tls_connection_options); - - // struct aws_credentials_provider *provider = NULL; - // struct aws_credentials_provider_sts_web_identity_impl *impl = NULL; - - // aws_mem_acquire_many( - // allocator, - // 2, - // &provider, - // sizeof(struct aws_credentials_provider), - // &impl, - // sizeof(struct aws_credentials_provider_sts_web_identity_impl)); - - // if (!provider) { - // goto on_error; - // } - - // AWS_ZERO_STRUCT(*provider); - // AWS_ZERO_STRUCT(*impl); - - // aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_sts_web_identity_vtable, - // impl); - - // if (!options->tls_ctx) { - // AWS_LOGF_ERROR( - // AWS_LS_AUTH_CREDENTIALS_PROVIDER, - // "a TLS context must be provided to the STS web identity credentials provider"); - // aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - // return NULL; - // } - - // aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx); - // struct aws_byte_cursor host = aws_byte_cursor_from_buf(¶meters->endpoint); - // if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) { - // AWS_LOGF_ERROR( - // AWS_LS_AUTH_CREDENTIALS_PROVIDER, - // "(id=%p): failed to create a tls connection options with error %s", - // (void *)provider, - // aws_error_str(aws_last_error())); - // goto on_error; - // } - - // struct aws_socket_options socket_options; - // AWS_ZERO_STRUCT(socket_options); - // socket_options.type = AWS_SOCKET_STREAM; - // socket_options.domain = AWS_SOCKET_IPV4; - // socket_options.connect_timeout_ms = (uint32_t)aws_timestamp_convert( - // STS_WEB_IDENTITY_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL); - - // struct aws_http_connection_manager_options manager_options; - // AWS_ZERO_STRUCT(manager_options); - // manager_options.bootstrap = options->bootstrap; - // manager_options.initial_window_size = STS_WEB_IDENTITY_RESPONSE_SIZE_LIMIT; - // manager_options.socket_options = &socket_options; - // manager_options.host = host; - // manager_options.port = 443; - // manager_options.max_connections = 2; - // manager_options.shutdown_complete_callback = s_on_connection_manager_shutdown; - // manager_options.shutdown_complete_user_data = provider; - // manager_options.tls_connection_options = &tls_connection_options; - - // impl->function_table = options->function_table; - // if (impl->function_table == NULL) { - // impl->function_table = g_aws_credentials_provider_http_function_table; - // } - - // impl->connection_manager = impl->function_table->aws_http_connection_manager_new(allocator, - // &manager_options); if (impl->connection_manager == NULL) { - // goto on_error; - // } - - // impl->role_arn = aws_string_new_from_array(allocator, parameters->role_arn.buffer, parameters->role_arn.len); - // if (impl->role_arn == NULL) { - // goto on_error; - // } - - // impl->role_session_name = - // aws_string_new_from_array(allocator, parameters->role_session_name.buffer, - // parameters->role_session_name.len); - // if (impl->role_session_name == NULL) { - // goto on_error; - // } - - // impl->token_file_path = - // aws_string_new_from_array(allocator, parameters->token_file_path.buffer, - // parameters->token_file_path.len); - // if (impl->token_file_path == NULL) { - // goto on_error; - // } - - // provider->shutdown_options = options->shutdown_options; - // s_parameters_destroy(parameters); - // aws_tls_connection_options_clean_up(&tls_connection_options); - // return provider; - - // on_error: - - // aws_credentials_provider_destroy(provider); - // s_parameters_destroy(parameters); - // aws_tls_connection_options_clean_up(&tls_connection_options); + struct aws_credentials_provider *provider = NULL; + struct aws_credentials_provider_sso_impl *impl = NULL; + struct aws_tls_connection_options tls_connection_options; + + aws_mem_acquire_many( + allocator, + 2, + &provider, + sizeof(struct aws_credentials_provider), + &impl, + sizeof(struct aws_credentials_provider_sso_impl)); + + AWS_ZERO_STRUCT(*provider); + AWS_ZERO_STRUCT(*impl); + AWS_ZERO_STRUCT(tls_connection_options); + + aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_sso_vtable, impl); + + if (!options->tls_ctx) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "a TLS context must be provided to the SSO credentials provider"); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx); + struct aws_byte_cursor host = aws_byte_cursor_from_buf(¶meters->endpoint); + if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to create a tls connection options with error %s", + (void *)provider, + aws_error_str(aws_last_error())); + goto on_error; + } + + struct aws_socket_options socket_options; + AWS_ZERO_STRUCT(socket_options); + socket_options.type = AWS_SOCKET_STREAM; + socket_options.domain = AWS_SOCKET_IPV4; + socket_options.connect_timeout_ms = (uint32_t)aws_timestamp_convert( + SSO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL); + + struct aws_http_connection_manager_options manager_options; + AWS_ZERO_STRUCT(manager_options); + manager_options.bootstrap = options->bootstrap; + manager_options.initial_window_size = SSO_RESPONSE_SIZE_LIMIT; + manager_options.socket_options = &socket_options; + manager_options.host = host; + manager_options.port = 443; + manager_options.max_connections = 2; + manager_options.shutdown_complete_callback = s_on_connection_manager_shutdown; + manager_options.shutdown_complete_user_data = provider; + manager_options.tls_connection_options = &tls_connection_options; + + impl->function_table = options->function_table; + if (impl->function_table == NULL) { + impl->function_table = g_aws_credentials_provider_http_function_table; + } + + impl->connection_manager = impl->function_table->aws_http_connection_manager_new(allocator, &manager_options); + if (impl->connection_manager == NULL) { + goto on_error; + } + + impl->token_provider = aws_credentials_provider_acquire(parameters->token_provider); + impl->endpoint = aws_string_new_from_buf(allocator, ¶meters->endpoint); + impl->sso_account_id = aws_string_new_from_string(allocator, parameters->sso_account_id); + impl->sso_role_name = aws_string_new_from_string(allocator, parameters->sso_role_name); + + provider->shutdown_options = options->shutdown_options; + s_parameters_destroy(parameters); + aws_tls_connection_options_clean_up(&tls_connection_options); + return provider; + +on_error: + aws_credentials_provider_destroy(provider); + s_parameters_destroy(parameters); + aws_tls_connection_options_clean_up(&tls_connection_options); return NULL; } From 0247b0e43c9782151a0d7931fad8fe86c3d3ec10 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 8 Mar 2023 11:40:36 -0800 Subject: [PATCH 17/93] working temp test --- include/aws/auth/credentials.h | 9 + include/aws/auth/private/credentials_utils.h | 1 + source/credentials.c | 13 + source/credentials_provider_sso.c | 424 +++++++++++++++++- .../credentials_provider_sts_web_identity.c | 1 + source/credentials_utils.c | 13 +- source/sso_token_provider_profile.c | 2 +- source/sso_token_provider_sso_session.c | 2 +- tests/credentials_provider_sso_tests.c | 48 +- 9 files changed, 498 insertions(+), 15 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index a0711244..3713e84a 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -725,6 +725,15 @@ struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_cr AWS_AUTH_API struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_credentials *credentials); +/** + * Get the AWS token from a set of credentials + * + * @param credentials credentials to get the session token from + * @return a byte cursor to the session token or an empty byte cursor if there is no session token + */ +AWS_AUTH_API +struct aws_byte_cursor aws_credentials_get_token(const struct aws_credentials *credentials); + /** * Get the expiration timepoint (in seconds since epoch) associated with a set of credentials * diff --git a/include/aws/auth/private/credentials_utils.h b/include/aws/auth/private/credentials_utils.h index 99c3e5ce..46a7ff5c 100644 --- a/include/aws/auth/private/credentials_utils.h +++ b/include/aws/auth/private/credentials_utils.h @@ -80,6 +80,7 @@ struct aws_parse_credentials_from_json_doc_options { const char *secret_access_key_name; const char *token_name; const char *expiration_name; + const char *top_level_object_name; enum aws_parse_credentials_expiration_format expiration_format; bool token_required; bool expiration_required; diff --git a/source/credentials.c b/source/credentials.c index f98582f7..79fdeaa5 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -248,6 +248,19 @@ struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_creden return s_empty_token_cursor; } +struct aws_byte_cursor aws_credentials_get_token(const struct aws_credentials *credentials) { + switch (credentials->type) { + case TOKEN_IDENTITY: + if (credentials->identity.token.token != NULL) { + return aws_byte_cursor_from_string(credentials->identity.token.token); + } + break; + default: + break; + } + return s_empty_token_cursor; +} + uint64_t aws_credentials_get_expiration_timepoint_seconds(const struct aws_credentials *credentials) { return credentials->expiration_timepoint_seconds; } diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 5bf3b8d5..2aca454c 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -17,8 +17,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -35,6 +37,7 @@ #define SSO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS 2 #define SSO_CREDS_DEFAULT_DURATION_SECONDS 900 #define SSO_MAX_ATTEMPTS 3 +#define SSO_RETRY_TIMEOUT_MS 100 struct aws_credentials_provider_sso_impl { struct aws_http_connection_manager *connection_manager; @@ -43,6 +46,7 @@ struct aws_credentials_provider_sso_impl { struct aws_string *sso_account_id; struct aws_string *sso_role_name; struct aws_credentials_provider *token_provider; + struct aws_retry_strategy *retry_strategy; }; /** @@ -60,15 +64,25 @@ struct sso_user_data { struct aws_http_message *request; struct aws_byte_buf response; struct aws_retry_token *retry_token; - - /* URI path and query string. */ struct aws_byte_buf path_and_query; + struct aws_string *token; - /* Track last HTTP response status and last error code. */ int status_code; int error_code; + int attempt_count; }; +/* called in between retries. */ +static void s_user_data_reset_request_and_response(struct sso_user_data *user_data) { + aws_http_message_destroy(user_data->request); + user_data->request = NULL; + + aws_byte_buf_reset(&user_data->response, true /*zero out*/); + + user_data->status_code = 0; + user_data->error_code = AWS_OP_SUCCESS; +} + static void s_user_data_destroy(struct sso_user_data *user_data) { if (user_data == NULL) { return; @@ -84,11 +98,9 @@ static void s_user_data_destroy(struct sso_user_data *user_data) { } aws_byte_buf_clean_up(&user_data->response); - aws_retry_token_release(user_data->retry_token); - aws_byte_buf_clean_up(&user_data->path_and_query); - aws_credentials_provider_release(user_data->provider); + aws_retry_token_release(user_data->retry_token); aws_mem_release(user_data->allocator, user_data); } @@ -128,6 +140,363 @@ static struct sso_user_data *s_user_data_new( return NULL; } +/* + * No matter the result, this always gets called assuming that user_data is successfully allocated + */ +static void s_finalize_get_credentials_query(struct sso_user_data *user_data) { + /* Try to build credentials from whatever, if anything, was in the result */ + struct aws_credentials *credentials = NULL; + if (user_data->status_code == AWS_HTTP_STATUS_CODE_200_OK) { + struct aws_parse_credentials_from_json_doc_options parse_options = { + .access_key_id_name = "accessKeyId", + .secret_access_key_name = "secretAccessKey", + .token_name = "sessionToken", + .expiration_name = "expiration", + .top_level_object_name = "roleCredentials", + .token_required = true, + .expiration_required = true, + .expiration_format = AWS_PCEF_NUMBER_UNIX_EPOCH, + }; + + credentials = aws_parse_credentials_from_json_document( + user_data->allocator, (const char *)user_data->response.buffer, &parse_options); + } + + if (credentials != NULL) { + AWS_LOGF_INFO( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p)SSO credentials provider successfully queried credentials", + (void *)user_data->provider); + } else { + AWS_LOGF_WARN( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) SSO credentials provider failed to query credentials", + (void *)user_data->provider); + + if (user_data->error_code == AWS_ERROR_SUCCESS) { + user_data->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE; + } + } + + /* pass the credentials back */ + user_data->original_callback(credentials, user_data->error_code, user_data->original_user_data); + + /* clean up */ + s_user_data_destroy(user_data); + aws_credentials_release(credentials); +} +static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *wrapped_user_data); + +static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_code, void *data) { + struct sso_user_data *user_data = data; + user_data->error_code = error_code; + if (user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK) { + user_data->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_HTTP_STATUS_FAILURE; + } + + struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + struct aws_http_connection *connection = impl->function_table->aws_http_stream_get_connection(stream); + impl->function_table->aws_http_stream_release(stream); + impl->function_table->aws_http_connection_manager_release_connection(impl->connection_manager, connection); + + /* + * On anything other than a 200, if we can retry the request based on + * error response, retry it, otherwise, call the finalize function. + */ + if (user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK || error_code != AWS_OP_SUCCESS) { + enum aws_retry_error_type error_type = + aws_credentials_provider_compute_retry_error_type(user_data->status_code, error_code); + + /* don't retry client errors at all. */ + if (error_type != AWS_RETRY_ERROR_TYPE_CLIENT_ERROR) { + + /* clear data used by the previous attempt. */ + s_user_data_reset_request_and_response(user_data); + + if (aws_retry_strategy_schedule_retry(user_data->retry_token, error_type, s_on_retry_ready, user_data) == + AWS_OP_SUCCESS) { + return; + } + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to schedule retry: %s", + (void *)user_data->provider, + aws_error_str(aws_last_error())); + } + } else { + if (aws_retry_token_record_success(user_data->retry_token)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to register operation success: %s", + (void *)user_data->provider, + aws_error_str(aws_last_error())); + } + } + + s_finalize_get_credentials_query(user_data); +} + +static int s_on_incoming_body_fn( + struct aws_http_stream *stream, + const struct aws_byte_cursor *body, + void *wrapped_user_data) { + + (void)stream; + + struct sso_user_data *user_data = wrapped_user_data; + struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + + AWS_LOGF_TRACE( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) SSO credentials provider received %zu response bytes", + (void *)user_data->provider, + body->len); + + if (body->len + user_data->response.len > SSO_RESPONSE_SIZE_LIMIT) { + impl->function_table->aws_http_connection_close(user_data->connection); + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) SSO credentials provider query response exceeded maximum allowed length", + (void *)user_data->provider); + + return aws_raise_error(AWS_ERROR_SHORT_BUFFER); + } + + if (aws_byte_buf_append_dynamic(&user_data->response, body)) { + impl->function_table->aws_http_connection_close(user_data->connection); + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) SSO credentials provider query error appending response: %s", + (void *)user_data->provider, + aws_error_str(aws_last_error())); + + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; +} + +static int s_on_incoming_headers_fn( + struct aws_http_stream *stream, + enum aws_http_header_block header_block, + const struct aws_http_header *header_array, + size_t num_headers, + void *wrapped_user_data) { + + (void)header_array; + (void)num_headers; + + if (header_block != AWS_HTTP_HEADER_BLOCK_MAIN) { + return AWS_OP_SUCCESS; + } + + struct sso_user_data *user_data = wrapped_user_data; + if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN && user_data->status_code == 0) { + struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + if (impl->function_table->aws_http_stream_get_incoming_response_status(stream, &user_data->status_code)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) SSO credentials provider failed to get http status code: %s", + (void *)user_data->provider, + aws_error_str(aws_last_error())); + + return AWS_OP_ERR; + } + AWS_LOGF_DEBUG( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) SSO credentials provider query received http status code %d", + (void *)user_data->provider, + user_data->status_code); + } + + return AWS_OP_SUCCESS; +} + +/* Request headers. */ +AWS_STATIC_STRING_FROM_LITERAL(s_sso_token_header, "x-amz-sso_bearer_token"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header, "User-Agent"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header_value, "CRTAuthSSOCredentialsProvider"); + +static void s_query_credentials(struct sso_user_data *user_data) { + AWS_FATAL_ASSERT(user_data->connection); + struct aws_http_stream *stream = NULL; + struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + + /* "Clear" the result */ + s_user_data_reset_request_and_response(user_data); + + user_data->request = aws_http_message_new_request(user_data->allocator); + if (user_data->request == NULL) { + goto on_error; + } + + struct aws_http_header auth_header = { + .name = aws_byte_cursor_from_string(s_sso_token_header), + .value = aws_byte_cursor_from_string(user_data->token), + }; + struct aws_http_header host_header = { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Host"), + .value = aws_byte_cursor_from_string(impl->endpoint), + }; + struct aws_http_header user_agent_header = { + .name = aws_byte_cursor_from_string(s_sso_user_agent_header), + .value = aws_byte_cursor_from_string(s_sso_user_agent_header_value), + }; + + if (aws_http_message_add_header(user_data->request, auth_header) || + aws_http_message_add_header(user_data->request, host_header) || + aws_http_message_add_header(user_data->request, user_agent_header)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p)sso credentials provider failed to add http headers %s", + (void *)user_data->provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + if (aws_http_message_set_request_method(user_data->request, aws_http_method_get)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p)sso credentials provider failed to set request method %s", + (void *)user_data->provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + if (aws_http_message_set_request_path(user_data->request, aws_byte_cursor_from_buf(&user_data->path_and_query))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p)sso credentials provider failed to set request path %s", + (void *)user_data->provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + struct aws_http_make_request_options request_options = { + .self_size = sizeof(request_options), + .on_response_headers = s_on_incoming_headers_fn, + .on_response_header_block_done = NULL, + .on_response_body = s_on_incoming_body_fn, + .on_complete = s_on_stream_complete_fn, + .user_data = user_data, + .request = user_data->request, + }; + + stream = impl->function_table->aws_http_connection_make_request(user_data->connection, &request_options); + if (!stream) { + goto on_error; + } + + if (impl->function_table->aws_http_stream_activate(stream)) { + goto on_error; + } + + return; +on_error: + impl->function_table->aws_http_stream_release(stream); + s_finalize_get_credentials_query(user_data); +} +static void s_on_get_token_callback(struct aws_credentials *credentials, int error_code, void *user_data) { + struct sso_user_data *sso_user_data = user_data; + + if (error_code) { + AWS_LOGF_WARN( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: SSO provider failed to acquire a token, error code %d(%s)", + (void *)sso_user_data->provider, + error_code, + aws_error_str(error_code)); + + s_finalize_get_credentials_query(user_data); + return; + } + + struct aws_byte_cursor token = aws_credentials_get_token(credentials); + if (token.len == 0 || token.ptr == NULL) { + AWS_LOGF_WARN( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: SSO provider failed to acquire a token, error code %d(%s)", + (void *)sso_user_data->provider, + error_code, + aws_error_str(error_code)); + + s_finalize_get_credentials_query(user_data); + return; + } + sso_user_data->token = aws_string_new_from_cursor(sso_user_data->allocator, &token); + s_query_credentials(sso_user_data); +} +static void s_on_acquire_connection(struct aws_http_connection *connection, int error_code, void *data) { + struct sso_user_data *user_data = data; + + if (error_code) { + AWS_LOGF_WARN( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: SSO provider failed to acquire a connection, error code %d(%s)", + (void *)user_data->provider, + error_code, + aws_error_str(error_code)); + + s_finalize_get_credentials_query(user_data); + return; + } + + user_data->connection = connection; + + struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + if (aws_credentials_provider_get_credentials(impl->token_provider, s_on_get_token_callback, user_data)) { + AWS_LOGF_WARN( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: failed get a token, error code %d(%s)", + (void *)user_data->provider, + error_code, + aws_error_str(error_code)); + + s_finalize_get_credentials_query(user_data); + return; + } +} + +/* called for each retry. */ +static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *wrapped_user_data) { + (void)token; + struct sso_user_data *user_data = wrapped_user_data; + struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + + if (error_code) { + user_data->error_code = error_code; + s_finalize_get_credentials_query(user_data); + return; + } + + impl->function_table->aws_http_connection_manager_acquire_connection( + impl->connection_manager, s_on_acquire_connection, user_data); +} + +static void s_on_retry_token_acquired( + struct aws_retry_strategy *strategy, + int error_code, + struct aws_retry_token *token, + void *user_data) { + struct sso_user_data *provider_user_data = user_data; + (void)strategy; + + if (error_code) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to acquire retry token: %s", + (void *)provider_user_data->provider, + aws_error_debug_str(error_code)); + s_finalize_get_credentials_query(provider_user_data); + return; + } + /* success */ + provider_user_data->retry_token = token; + struct aws_credentials_provider_sso_impl *impl = provider_user_data->provider->impl; + impl->function_table->aws_http_connection_manager_acquire_connection( + impl->connection_manager, s_on_acquire_connection, user_data); +} + static int s_credentials_provider_sso_get_credentials_async( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, @@ -137,10 +506,23 @@ static int s_credentials_provider_sso_get_credentials_async( struct sso_user_data *wrapped_user_data = s_user_data_new(provider, callback, user_data); if (wrapped_user_data == NULL) { - callback(NULL, aws_last_error(), user_data); - return AWS_OP_ERR; + goto on_error; } + if (aws_retry_strategy_acquire_retry_token( + impl->retry_strategy, NULL, s_on_retry_token_acquired, wrapped_user_data, SSO_RETRY_TIMEOUT_MS)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to acquire retry token: %s", + (void *)provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + return AWS_OP_SUCCESS; +on_error: + s_user_data_destroy(wrapped_user_data); + callback(NULL, aws_last_error(), user_data); return AWS_OP_ERR; } @@ -159,6 +541,7 @@ static void s_credentials_provider_sso_destroy(struct aws_credentials_provider * aws_string_destroy(impl->endpoint); aws_string_destroy(impl->sso_account_id); aws_string_destroy(impl->sso_role_name); + aws_retry_strategy_release(impl->retry_strategy); aws_credentials_provider_release(impl->token_provider); /* aws_http_connection_manager_release will eventually leads to call of s_on_connection_manager_shutdown, @@ -197,7 +580,8 @@ static int s_construct_endpoint( goto on_error; } - if (aws_byte_buf_append(endpoint, ®ion_cursor) || aws_byte_buf_append(endpoint, &amazonaws_cursor)) { + if (aws_byte_buf_append_dynamic(endpoint, ®ion_cursor) || + aws_byte_buf_append_dynamic(endpoint, &amazonaws_cursor)) { goto on_error; } @@ -290,7 +674,7 @@ static struct sso_parameters *s_parameters_new( const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session); if (sso_session_property) { struct aws_sso_token_provider_sso_session_options token_provider_options; - AWS_ZERO_STRUCT(options); + AWS_ZERO_STRUCT(token_provider_options); token_provider_options.config_file_name_override = options->config_file_name_override; token_provider_options.profile_name_override = options->profile_name_override; @@ -302,7 +686,7 @@ static struct sso_parameters *s_parameters_new( } } else { struct aws_sso_token_provider_profile_options token_provider_options; - AWS_ZERO_STRUCT(options); + AWS_ZERO_STRUCT(token_provider_options); token_provider_options.config_file_name_override = options->config_file_name_override; token_provider_options.profile_name_override = options->profile_name_override; @@ -417,6 +801,24 @@ struct aws_credentials_provider *aws_credentials_provider_new_sso( impl->sso_role_name = aws_string_new_from_string(allocator, parameters->sso_role_name); provider->shutdown_options = options->shutdown_options; + + struct aws_standard_retry_options retry_options = { + .backoff_retry_options = + { + .el_group = options->bootstrap->event_loop_group, + .max_retries = SSO_MAX_ATTEMPTS, + }, + }; + impl->retry_strategy = aws_retry_strategy_new_standard(allocator, &retry_options); + if (!impl->retry_strategy) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to create a retry strategy with error %s", + (void *)provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + s_parameters_destroy(parameters); aws_tls_connection_options_clean_up(&tls_connection_options); return provider; diff --git a/source/credentials_provider_sts_web_identity.c b/source/credentials_provider_sts_web_identity.c index c3e46971..e86f0a48 100644 --- a/source/credentials_provider_sts_web_identity.c +++ b/source/credentials_provider_sts_web_identity.c @@ -751,6 +751,7 @@ static int s_credentials_provider_sts_web_identity_get_credentials_async( error: s_user_data_destroy(wrapped_user_data); + callback(NULL, aws_last_error(), user_data); return AWS_OP_ERR; } diff --git a/source/credentials_utils.c b/source/credentials_utils.c index 1bf0462b..d3a582ea 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -261,7 +261,18 @@ struct aws_credentials *aws_parse_credentials_from_json_document( AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse document as Json document."); return NULL; } - struct aws_credentials *credentials = aws_parse_credentials_from_aws_json_object(allocator, document_root, options); + + struct aws_json_value *top_level_object = NULL; + if (options->top_level_object_name) { + top_level_object = aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str("roleCredentials")); + if (!top_level_object) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse document as Json document."); + } + } + + struct aws_credentials *credentials = aws_parse_credentials_from_aws_json_object( + allocator, top_level_object ? top_level_object : document_root, options); + aws_json_value_destroy(document_root); return credentials; } diff --git a/source/sso_token_provider_profile.c b/source/sso_token_provider_profile.c index defe181e..e60e72ff 100644 --- a/source/sso_token_provider_profile.c +++ b/source/sso_token_provider_profile.c @@ -235,7 +235,7 @@ struct aws_credentials_provider *aws_sso_token_provider_new_profile( aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_profile_vtable, impl); impl->sso_region = aws_string_new_from_string(allocator, parameters->sso_region); impl->sso_start_url = aws_string_new_from_string(allocator, parameters->sso_start_url); - impl->token_file_path = aws_string_new_from_string(allocator, parameters->sso_start_url); + impl->token_file_path = aws_string_new_from_string(allocator, parameters->token_path); provider->shutdown_options = options->shutdown_options; s_token_provider_profile_parameters_destroy(allocator, parameters); diff --git a/source/sso_token_provider_sso_session.c b/source/sso_token_provider_sso_session.c index 8dcbc223..95c1cad7 100644 --- a/source/sso_token_provider_sso_session.c +++ b/source/sso_token_provider_sso_session.c @@ -108,7 +108,7 @@ static int s_token_provider_sso_session_parameters_sso_session_init( const struct aws_string *sso_session_name) { const struct aws_profile *session_profile = - aws_profile_collection_get_sso_session(profile_collection, sso_session_name); + aws_profile_collection_get_section(profile_collection, sso_session_name, AWS_PROFILE_SECTION_TYPE_SSO_SESSION); if (!session_profile) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find an sso-session"); return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 5d3bcf3f..39d2d974 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -18,16 +18,62 @@ #include #include #include +#include #include #include #include #include +static bool s_has_tester_received_credentials_callback(void *user_data) { + (void)user_data; + + return false; +} +static void s_get_credentials_callback(struct aws_credentials *credentials, int error_code, void *user_data) { + (void)user_data; + (void)credentials; + printf("get credentials callback, %d", error_code); +} + static int s_credentials_provider_sso_new_destroy(struct aws_allocator *allocator, void *ctx) { (void)ctx; - struct aws_sso_token_provider_sso_session_options options; + aws_auth_library_init(allocator); + struct aws_credentials_provider_sso_options options; AWS_ZERO_STRUCT(options); + options.profile_name_override = aws_byte_cursor_from_c_str("AdministratorAccess-069542832437"); + + struct aws_tls_ctx_options tls_options; + aws_tls_ctx_options_init_default_client(&tls_options, allocator); + options.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_options); + + struct aws_event_loop_group *el_group = aws_event_loop_group_new_default(allocator, 0, 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, + }; + options.bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); + struct aws_mutex lock; + struct aws_condition_variable signal; + if (aws_mutex_init(&lock)) { + return AWS_OP_ERR; + } + + if (aws_condition_variable_init(&signal)) { + return AWS_OP_ERR; + } + + aws_condition_variable_wait_pred(&signal, &lock, s_has_tester_received_credentials_callback, NULL); + return 0; } AWS_TEST_CASE(credentials_provider_sso_new_destroy, s_credentials_provider_sso_new_destroy); From eef60383f74b46c2800505cebe2acd1e6fa3f75c Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 8 Mar 2023 12:08:51 -0800 Subject: [PATCH 18/93] rename to token_provider --- include/aws/auth/credentials.h | 12 ++++++------ source/credentials_provider_sso.c | 8 ++++---- tests/soo_token_provider_tests.c | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 3713e84a..0dc5b6be 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -557,7 +557,7 @@ struct aws_credentials_provider_cognito_options { * Configuration options for a provider that sources credentials from the aws profile and credentials files * (by default ~/.aws/profile and ~/.aws/credentials) */ -struct aws_sso_token_provider_profile_options { +struct aws_token_provider_profile_options { struct aws_credentials_provider_shutdown_options shutdown_options; /* @@ -571,7 +571,7 @@ struct aws_sso_token_provider_profile_options { struct aws_byte_cursor config_file_name_override; }; -struct aws_sso_token_provider_sso_session_options { +struct aws_token_provider_sso_session_options { struct aws_credentials_provider_shutdown_options shutdown_options; /* @@ -1071,14 +1071,14 @@ struct aws_credentials_provider *aws_credentials_provider_new_chain_default( * @return the newly-constructed credentials provider, or NULL if an error occurred. */ AWS_AUTH_API -struct aws_credentials_provider *aws_sso_token_provider_new_profile( +struct aws_credentials_provider *aws_token_provider_new_profile( struct aws_allocator *allocator, - const struct aws_sso_token_provider_profile_options *options); + const struct aws_token_provider_profile_options *options); AWS_AUTH_API -struct aws_credentials_provider *aws_sso_token_provider_new_sso_session( +struct aws_credentials_provider *aws_token_provider_new_sso_session( struct aws_allocator *allocator, - const struct aws_sso_token_provider_sso_session_options *options); + const struct aws_token_provider_sso_session_options *options); AWS_AUTH_API extern const struct aws_auth_http_system_vtable *g_aws_credentials_provider_http_function_table; diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 2aca454c..4386aa13 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -673,24 +673,24 @@ static struct sso_parameters *s_parameters_new( const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session); if (sso_session_property) { - struct aws_sso_token_provider_sso_session_options token_provider_options; + struct aws_token_provider_sso_session_options token_provider_options; AWS_ZERO_STRUCT(token_provider_options); token_provider_options.config_file_name_override = options->config_file_name_override; token_provider_options.profile_name_override = options->profile_name_override; - parameters->token_provider = aws_sso_token_provider_new_sso_session(allocator, &token_provider_options); + parameters->token_provider = aws_token_provider_new_sso_session(allocator, &token_provider_options); if (!parameters->token_provider) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a sso token provider"); aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); goto on_finish; } } else { - struct aws_sso_token_provider_profile_options token_provider_options; + struct aws_token_provider_profile_options token_provider_options; AWS_ZERO_STRUCT(token_provider_options); token_provider_options.config_file_name_override = options->config_file_name_override; token_provider_options.profile_name_override = options->profile_name_override; - parameters->token_provider = aws_sso_token_provider_new_profile(allocator, &token_provider_options); + parameters->token_provider = aws_token_provider_new_profile(allocator, &token_provider_options); if (!parameters->token_provider) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a profile token provider"); aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); diff --git a/tests/soo_token_provider_tests.c b/tests/soo_token_provider_tests.c index 2a2aecc8..cb466d0a 100644 --- a/tests/soo_token_provider_tests.c +++ b/tests/soo_token_provider_tests.c @@ -56,7 +56,7 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato aws_unset_environment_value(s_default_config_path_env_variable_name); aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); - struct aws_sso_token_provider_profile_options options = { + struct aws_token_provider_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), }; @@ -64,7 +64,7 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato printf("invalid example [%zu]: %s\n", i, s_invalid_profile_examples[i].name); struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_invalid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); - ASSERT_NULL(aws_sso_token_provider_new_profile(allocator, &options)); + ASSERT_NULL(aws_token_provider_new_profile(allocator, &options)); aws_string_destroy(config_contents); } @@ -98,7 +98,7 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator aws_unset_environment_value(s_default_config_path_env_variable_name); aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); - struct aws_sso_token_provider_profile_options options = { + struct aws_token_provider_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), }; @@ -106,7 +106,7 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator printf("valid example [%zu]: %s\n", i, s_valid_profile_examples[i].name); struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_valid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); - struct aws_credentials_provider *provider = aws_sso_token_provider_new_profile(allocator, &options); + struct aws_credentials_provider *provider = aws_token_provider_new_profile(allocator, &options); ASSERT_NOT_NULL(provider); aws_credentials_provider_release(provider); aws_string_destroy(config_contents); @@ -158,7 +158,7 @@ static int s_sso_token_provider_sso_session_invalid_profile_test(struct aws_allo aws_unset_environment_value(s_default_config_path_env_variable_name); aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); - struct aws_sso_token_provider_sso_session_options options = { + struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), }; @@ -166,7 +166,7 @@ static int s_sso_token_provider_sso_session_invalid_profile_test(struct aws_allo printf("invalid example [%zu]: %s\n", i, s_invalid_profile_examples[i].name); struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_invalid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); - ASSERT_NULL(aws_sso_token_provider_new_sso_session(allocator, &options)); + ASSERT_NULL(aws_token_provider_new_sso_session(allocator, &options)); aws_string_destroy(config_contents); } @@ -217,7 +217,7 @@ static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_alloca aws_unset_environment_value(s_default_config_path_env_variable_name); aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); - struct aws_sso_token_provider_sso_session_options options = { + struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), }; @@ -225,7 +225,7 @@ static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_alloca printf("valid example [%zu]: %s\n", i, s_valid_profile_examples[i].name); struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_valid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); - struct aws_credentials_provider *provider = aws_sso_token_provider_new_sso_session(allocator, &options); + struct aws_credentials_provider *provider = aws_token_provider_new_sso_session(allocator, &options); ASSERT_NOT_NULL(provider); aws_credentials_provider_release(provider); aws_string_destroy(config_contents); From e79186acd22578284d01e1bc5d45f6352435d4f4 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 8 Mar 2023 12:14:58 -0800 Subject: [PATCH 19/93] rename files --- ...oken_provider_profile.c => token_provider_sso_profile.c} | 4 ++-- ..._provider_sso_session.c => token_provider_sso_session.c} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename source/{sso_token_provider_profile.c => token_provider_sso_profile.c} (98%) rename source/{sso_token_provider_sso_session.c => token_provider_sso_session.c} (98%) diff --git a/source/sso_token_provider_profile.c b/source/token_provider_sso_profile.c similarity index 98% rename from source/sso_token_provider_profile.c rename to source/token_provider_sso_profile.c index e60e72ff..890ea48b 100644 --- a/source/sso_token_provider_profile.c +++ b/source/token_provider_sso_profile.c @@ -211,9 +211,9 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame return parameters; } -struct aws_credentials_provider *aws_sso_token_provider_new_profile( +struct aws_credentials_provider *aws_token_provider_new_profile( struct aws_allocator *allocator, - const struct aws_sso_token_provider_profile_options *options) { + const struct aws_token_provider_profile_options *options) { struct token_provider_profile_parameters *parameters = s_token_provider_profile_parameters_new( allocator, options->profile_name_override, options->config_file_name_override); diff --git a/source/sso_token_provider_sso_session.c b/source/token_provider_sso_session.c similarity index 98% rename from source/sso_token_provider_sso_session.c rename to source/token_provider_sso_session.c index 95c1cad7..16516940 100644 --- a/source/sso_token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -41,7 +41,7 @@ static int s_token_provider_sso_session_get_token_async( goto on_error; } - /* TODO: Refresh token if it is within refresh window */ + /* TODO: Refresh token if it is within refresh window and refreshable */ /* check token expiration. */ struct aws_date_time now; aws_date_time_init_now(&now); @@ -257,9 +257,9 @@ static struct token_provider_sso_session_parameters *s_token_provider_sso_sessio return parameters; } -struct aws_credentials_provider *aws_sso_token_provider_new_sso_session( +struct aws_credentials_provider *aws_token_provider_new_sso_session( struct aws_allocator *allocator, - const struct aws_sso_token_provider_sso_session_options *options) { + const struct aws_token_provider_sso_session_options *options) { struct token_provider_sso_session_parameters *parameters = s_token_provider_sso_session_parameters_new( allocator, options->profile_name_override, options->config_file_name_override); From e3944d24460607a6141d45021df8d8a6d9b11df7 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 8 Mar 2023 13:03:34 -0800 Subject: [PATCH 20/93] fix memory leaks --- source/credentials.c | 4 ++- source/credentials_provider_sso.c | 1 + source/sso_token_utils.c | 1 - source/token_provider_sso_profile.c | 18 +++++++------ tests/credentials_provider_sso_tests.c | 26 ++++++++++++++----- ...der_tests.c => token_provider_sso_tests.c} | 0 6 files changed, 34 insertions(+), 16 deletions(-) rename tests/{soo_token_provider_tests.c => token_provider_sso_tests.c} (100%) diff --git a/source/credentials.c b/source/credentials.c index 79fdeaa5..026c170d 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -167,7 +167,9 @@ static void s_aws_credentials_destroy(struct aws_credentials *credentials) { aws_string_destroy_secure(credentials->identity.ecc_identity.session_token); aws_ecc_key_pair_release(credentials->identity.ecc_identity.ecc_key); break; - + case TOKEN_IDENTITY: + aws_string_destroy(credentials->identity.token.token); + break; default: break; } diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 4386aa13..bd408809 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -101,6 +101,7 @@ static void s_user_data_destroy(struct sso_user_data *user_data) { aws_byte_buf_clean_up(&user_data->path_and_query); aws_credentials_provider_release(user_data->provider); aws_retry_token_release(user_data->retry_token); + aws_string_destroy(user_data->token); aws_mem_release(user_data->allocator, user_data); } diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index 4c2608be..2b3e40bb 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -142,7 +142,6 @@ struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocato aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto cleanup; } - printf("%s", access_token_cursor.ptr); token->token = aws_string_new_from_cursor(allocator, &access_token_cursor); token->expiration = expiration; diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 890ea48b..fe5672fb 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -33,12 +33,12 @@ static int s_token_provider_profile_get_token_async( struct aws_sso_token *sso_token = NULL; struct aws_credentials *credentials = NULL; - + bool success = false; sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: unable to read file."); aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); - goto on_error; + goto done; } /* check token expiration. */ @@ -47,7 +47,7 @@ static int s_token_provider_profile_get_token_async( if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: cached token is expired."); aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); - goto on_error; + goto done; } credentials = aws_credentials_new_token( @@ -55,17 +55,19 @@ static int s_token_provider_profile_get_token_async( aws_byte_cursor_from_string(sso_token->token), aws_date_time_as_epoch_secs(&sso_token->expiration)); if (!credentials) { - goto on_error; + goto done; } callback(credentials, AWS_OP_SUCCESS, user_data); - return AWS_OP_SUCCESS; + success = true; -on_error: +done: aws_sso_token_destroy(provider->allocator, sso_token); aws_credentials_release(credentials); - callback(credentials, aws_last_error(), user_data); - return AWS_OP_ERR; + if (!success) { + callback(NULL, aws_last_error(), user_data); + } + return success ? AWS_OP_SUCCESS : AWS_OP_ERR; } static void s_token_provider_profile_destroy(struct aws_credentials_provider *provider) { diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 39d2d974..79117589 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -24,15 +24,22 @@ #include #include +static bool received_callback = false; +static struct aws_mutex lock; +static struct aws_condition_variable tester_signal; static bool s_has_tester_received_credentials_callback(void *user_data) { (void)user_data; - return false; + return received_callback; } static void s_get_credentials_callback(struct aws_credentials *credentials, int error_code, void *user_data) { (void)user_data; (void)credentials; - printf("get credentials callback, %d", error_code); + printf("credentials callback, %d", error_code); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "waahm7 callback %d", error_code); + + received_callback = true; + aws_condition_variable_notify_one(&tester_signal); } static int s_credentials_provider_sso_new_destroy(struct aws_allocator *allocator, void *ctx) { @@ -62,18 +69,25 @@ static int s_credentials_provider_sso_new_destroy(struct aws_allocator *allocato struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); - struct aws_mutex lock; - struct aws_condition_variable signal; + if (aws_mutex_init(&lock)) { return AWS_OP_ERR; } - if (aws_condition_variable_init(&signal)) { + if (aws_condition_variable_init(&tester_signal)) { return AWS_OP_ERR; } - aws_condition_variable_wait_pred(&signal, &lock, s_has_tester_received_credentials_callback, NULL); + aws_condition_variable_wait_pred(&tester_signal, &lock, s_has_tester_received_credentials_callback, NULL); + + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "waahm7 releasing"); + aws_credentials_provider_release(provider); + aws_event_loop_group_release(el_group); + aws_host_resolver_release(resolver); + aws_client_bootstrap_release(options.bootstrap); + aws_tls_ctx_release(options.tls_ctx); + aws_auth_library_clean_up(); return 0; } AWS_TEST_CASE(credentials_provider_sso_new_destroy, s_credentials_provider_sso_new_destroy); diff --git a/tests/soo_token_provider_tests.c b/tests/token_provider_sso_tests.c similarity index 100% rename from tests/soo_token_provider_tests.c rename to tests/token_provider_sso_tests.c From fc8fb5a824c3f0f8280b1a640247e6a2081ef773 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 8 Mar 2023 13:15:41 -0800 Subject: [PATCH 21/93] fix get token async --- include/aws/auth/auth.h | 2 ++ source/token_provider_sso_profile.c | 6 +++--- source/token_provider_sso_session.c | 28 ++++++++++++++-------------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/include/aws/auth/auth.h b/include/aws/auth/auth.h index 90a94756..0ba3a5e5 100644 --- a/include/aws/auth/auth.h +++ b/include/aws/auth/auth.h @@ -45,7 +45,9 @@ enum aws_auth_errors { AWS_AUTH_CREDENTIALS_PROVIDER_DELEGATE_FAILURE, AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE, AWS_AUTH_SSO_TOKEN_INVALID, + AWS_AUTH_SSO_TOKEN_EXPIRED, AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE, + // TODO: add descriptions AWS_AUTH_ERROR_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_AUTH_PACKAGE_ID) }; diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index fe5672fb..fe8d0ccb 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -36,7 +36,7 @@ static int s_token_provider_profile_get_token_async( bool success = false; sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: unable to read file."); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto done; } @@ -45,8 +45,8 @@ static int s_token_provider_profile_get_token_async( struct aws_date_time now; aws_date_time_init_now(&now); if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: cached token is expired."); - aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", provider); + aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 16516940..0c1ac8ad 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -33,12 +33,13 @@ static int s_token_provider_sso_session_get_token_async( struct aws_token_provider_sso_session_impl *impl = provider->impl; struct aws_sso_token *sso_token = NULL; struct aws_credentials *credentials = NULL; + bool success = false; sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: unable to read file."); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); - goto on_error; + goto done; } /* TODO: Refresh token if it is within refresh window and refreshable */ @@ -46,9 +47,9 @@ static int s_token_provider_sso_session_get_token_async( struct aws_date_time now; aws_date_time_init_now(&now); if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: cached token is expired."); - aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); - goto on_error; + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", provider); + aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); + goto done; } credentials = aws_credentials_new_token( @@ -56,19 +57,18 @@ static int s_token_provider_sso_session_get_token_async( aws_byte_cursor_from_string(sso_token->token), aws_date_time_as_epoch_secs(&sso_token->expiration)); if (!credentials) { - goto on_error; + goto done; } - callback(credentials, AWS_OP_SUCCESS, user_data); - return AWS_OP_SUCCESS; + success = true; -on_error: - if (sso_token) { - aws_sso_token_destroy(provider->allocator, sso_token); - } +done: + aws_sso_token_destroy(provider->allocator, sso_token); aws_credentials_release(credentials); - callback(credentials, aws_last_error(), user_data); - return AWS_OP_ERR; + if (!success) { + callback(NULL, aws_last_error(), user_data); + } + return success ? AWS_OP_SUCCESS : AWS_OP_ERR; } static void s_token_provider_sso_session_destroy(struct aws_credentials_provider *provider) { From b962d5c7928b35d330b3db10b718e196b4b5a449 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 8 Mar 2023 15:21:00 -0800 Subject: [PATCH 22/93] refactor sso token profile --- include/aws/auth/credentials.h | 15 ++-- source/credentials_provider_sso.c | 24 ++++-- source/sso_token_utils.c | 6 +- source/token_provider_sso_profile.c | 114 +++++----------------------- source/token_provider_sso_session.c | 4 +- tests/token_provider_sso_tests.c | 8 +- 6 files changed, 51 insertions(+), 120 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 0dc5b6be..5acf1734 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -554,10 +554,10 @@ struct aws_credentials_provider_cognito_options { }; /** - * Configuration options for a provider that sources credentials from the aws profile and credentials files - * (by default ~/.aws/profile and ~/.aws/credentials) + * Configuration options for a provider that sources sso token information from the aws profile (by default + * ~/.aws/profile) and token from ~/.aws/sso/cache/.json. */ -struct aws_token_provider_profile_options { +struct aws_token_provider_sso_profile_options { struct aws_credentials_provider_shutdown_options shutdown_options; /* @@ -1061,9 +1061,8 @@ struct aws_credentials_provider *aws_credentials_provider_new_chain_default( const struct aws_credentials_provider_chain_default_options *options); /** - * Creates a provider that sources credentials from key-value profiles loaded from the aws credentials - * file ("~/.aws/credentials" by default) and the aws config file ("~/.aws/config" by - * default) + * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws + * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json * * @param allocator memory allocator to use for all memory allocation * @param options provider-specific configuration options @@ -1071,9 +1070,9 @@ struct aws_credentials_provider *aws_credentials_provider_new_chain_default( * @return the newly-constructed credentials provider, or NULL if an error occurred. */ AWS_AUTH_API -struct aws_credentials_provider *aws_token_provider_new_profile( +struct aws_credentials_provider *aws_token_provider_new_sso_profile( struct aws_allocator *allocator, - const struct aws_token_provider_profile_options *options); + const struct aws_token_provider_sso_profile_options *options); AWS_AUTH_API struct aws_credentials_provider *aws_token_provider_new_sso_session( diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index bd408809..9d0216a9 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -654,7 +654,7 @@ static struct sso_parameters *s_parameters_new( const struct aws_profile_property *sso_account_id = aws_profile_get_property(profile, s_sso_account_id); const struct aws_profile_property *sso_role_name = aws_profile_get_property(profile, s_sso_role_name); - const struct aws_profile_property *sso_region = aws_profile_get_property(profile, s_sso_region); + const struct aws_profile_property *sso_region = NULL; if (!sso_account_id) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_account_id is missing"); @@ -666,11 +666,6 @@ static struct sso_parameters *s_parameters_new( aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); goto on_finish; } - if (!sso_region) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_region is missing"); - aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); - goto on_finish; - } const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session); if (sso_session_property) { @@ -685,18 +680,31 @@ static struct sso_parameters *s_parameters_new( aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); goto on_finish; } + sso_region = aws_profile_get_property( + aws_profile_collection_get_section( + config_profile, + aws_profile_property_get_value(sso_session_property), + AWS_PROFILE_SECTION_TYPE_SSO_SESSION), + s_sso_region); } else { - struct aws_token_provider_profile_options token_provider_options; + struct aws_token_provider_sso_profile_options token_provider_options; AWS_ZERO_STRUCT(token_provider_options); token_provider_options.config_file_name_override = options->config_file_name_override; token_provider_options.profile_name_override = options->profile_name_override; - parameters->token_provider = aws_token_provider_new_profile(allocator, &token_provider_options); + parameters->token_provider = aws_token_provider_new_sso_profile(allocator, &token_provider_options); if (!parameters->token_provider) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a profile token provider"); aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); goto on_finish; } + sso_region = aws_profile_get_property(profile, s_sso_region); + } + + if (!sso_region) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_region is missing"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; } parameters->sso_account_id = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_account_id)); diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index 2b3e40bb..f206af4f 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -29,17 +29,14 @@ struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, con struct aws_byte_cursor cache_dir_cursor = aws_byte_cursor_from_string(s_sso_cache_directory); struct aws_byte_cursor input_cursor = aws_byte_cursor_from_string(input); struct aws_byte_cursor json_cursor = aws_byte_cursor_from_c_str(".json"); - const size_t hex_sha_length = AWS_SHA1_LEN * 2; - size_t path_length = home_dir_cursor.len + cache_dir_cursor.len + hex_sha_length + json_cursor.len; struct aws_byte_buf token_path_buf; AWS_ZERO_STRUCT(token_path_buf); struct aws_byte_buf sha1_buf; AWS_ZERO_STRUCT(sha1_buf); - aws_byte_buf_init(&token_path_buf, allocator, path_length); /* append home directory */ - aws_byte_buf_append_dynamic(&token_path_buf, &home_dir_cursor); + aws_byte_buf_init_copy_from_cursor(&token_path_buf, allocator, home_dir_cursor); /* append sso cache directory */ aws_byte_buf_append_dynamic(&token_path_buf, &cache_dir_cursor); @@ -106,6 +103,7 @@ struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocato AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso token: failed to parse access token file %s", aws_string_c_str(file_path)); + aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto cleanup; } diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index fe8d0ccb..38ed66e6 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -20,8 +20,6 @@ * sso-token profile provider implementation */ struct aws_token_provider_profile_impl { - struct aws_string *sso_region; - struct aws_string *sso_start_url; struct aws_string *token_file_path; }; @@ -36,8 +34,7 @@ static int s_token_provider_profile_get_token_async( bool success = false; sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", provider); - aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", (void *)provider); goto done; } @@ -45,7 +42,7 @@ static int s_token_provider_profile_get_token_async( struct aws_date_time now; aws_date_time_init_now(&now); if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", provider); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } @@ -76,8 +73,6 @@ static void s_token_provider_profile_destroy(struct aws_credentials_provider *pr return; } - aws_string_destroy(impl->sso_region); - aws_string_destroy(impl->sso_start_url); aws_string_destroy(impl->token_file_path); aws_credentials_provider_invoke_shutdown_callback(provider); @@ -89,78 +84,16 @@ static struct aws_credentials_provider_vtable s_aws_token_provider_profile_vtabl .destroy = s_token_provider_profile_destroy, }; -AWS_STRING_FROM_LITERAL(s_profile_sso_region_name, "sso_region"); AWS_STRING_FROM_LITERAL(s_profile_sso_start_url_name, "sso_start_url"); -struct token_provider_profile_parameters { - struct aws_string *sso_region; - struct aws_string *sso_start_url; - struct aws_string *token_path; -}; - -static int s_token_provider_profile_parameters_init( - struct aws_allocator *allocator, - struct token_provider_profile_parameters *parameters, - const struct aws_profile *profile) { - const struct aws_profile_property *sso_region_property = - aws_profile_get_property(profile, s_profile_sso_region_name); - const struct aws_profile_property *sso_start_url_property = - aws_profile_get_property(profile, s_profile_sso_start_url_name); - - if (!sso_region_property) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to find sso_region in profile"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - } - - if (!sso_start_url_property) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to find sso_start_url in profile"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - } - - parameters->sso_region = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_region_property)); - parameters->sso_start_url = - aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_start_url_property)); - parameters->token_path = aws_construct_token_path(allocator, parameters->sso_start_url); - if (!parameters->token_path) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to construct token path in profile"); - return AWS_OP_ERR; - } - - return AWS_OP_SUCCESS; -} - -static void s_token_provider_profile_parameters_destroy( - struct aws_allocator *allocator, - struct token_provider_profile_parameters *parameters) { - - aws_string_destroy(parameters->sso_region); - aws_string_destroy(parameters->sso_start_url); - aws_string_destroy(parameters->token_path); - aws_mem_release(allocator, parameters); -} - -static struct token_provider_profile_parameters *s_token_provider_profile_parameters_new( +static struct aws_string *s_construct_profile_token_path( struct aws_allocator *allocator, struct aws_byte_cursor profile_name_override, struct aws_byte_cursor config_file_name_override) { struct aws_profile_collection *config_profiles = NULL; - struct aws_string *config_file_path = NULL; struct aws_string *profile_name = NULL; - bool success = false; - - struct token_provider_profile_parameters *parameters = - aws_mem_calloc(allocator, 1, sizeof(struct token_provider_profile_parameters)); - config_file_path = aws_get_config_file_path(allocator, &config_file_name_override); - - if (!config_file_path) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed resolve config file path"); - aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - goto cleanup; - } + struct aws_string *token_path = NULL; profile_name = aws_get_profile_name(allocator, &profile_name_override); if (!profile_name) { @@ -168,7 +101,7 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } - config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG); + config_profiles = aws_load_config(allocator, config_file_name_override); if (!config_profiles) { AWS_LOGF_ERROR( @@ -191,37 +124,32 @@ static struct token_provider_profile_parameters *s_token_provider_profile_parame goto cleanup; } - if (s_token_provider_profile_parameters_init(allocator, parameters, profile)) { + const struct aws_profile_property *sso_start_url_property = + aws_profile_get_property(profile, s_profile_sso_start_url_name); + + if (!sso_start_url_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-profile: token provider could not load a valid sso profile at %s", - aws_string_c_str(profile_name)); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to find sso_start_url in profile"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } - success = true; + token_path = aws_construct_token_path(allocator, aws_profile_property_get_value(sso_start_url_property)); + if (!token_path) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to construct token path in profile"); + } cleanup: - aws_string_destroy(config_file_path); aws_string_destroy(profile_name); aws_profile_collection_release(config_profiles); - if (!success) { - s_token_provider_profile_parameters_destroy(allocator, parameters); - parameters = NULL; - } - return parameters; + return token_path; } -struct aws_credentials_provider *aws_token_provider_new_profile( +struct aws_credentials_provider *aws_token_provider_new_sso_profile( struct aws_allocator *allocator, - const struct aws_token_provider_profile_options *options) { + const struct aws_token_provider_sso_profile_options *options) { - struct token_provider_profile_parameters *parameters = s_token_provider_profile_parameters_new( - allocator, options->profile_name_override, options->config_file_name_override); - if (!parameters) { - return NULL; - } struct aws_credentials_provider *provider = NULL; struct aws_token_provider_profile_impl *impl = NULL; @@ -235,11 +163,9 @@ struct aws_credentials_provider *aws_token_provider_new_profile( AWS_ZERO_STRUCT(*provider); AWS_ZERO_STRUCT(*impl); aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_profile_vtable, impl); - impl->sso_region = aws_string_new_from_string(allocator, parameters->sso_region); - impl->sso_start_url = aws_string_new_from_string(allocator, parameters->sso_start_url); - impl->token_file_path = aws_string_new_from_string(allocator, parameters->token_path); + impl->token_file_path = + s_construct_profile_token_path(allocator, options->profile_name_override, options->config_file_name_override); provider->shutdown_options = options->shutdown_options; - s_token_provider_profile_parameters_destroy(allocator, parameters); return provider; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 0c1ac8ad..e35daebe 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -37,7 +37,7 @@ static int s_token_provider_sso_session_get_token_async( sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", provider); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto done; } @@ -47,7 +47,7 @@ static int s_token_provider_sso_session_get_token_async( struct aws_date_time now; aws_date_time_init_now(&now); if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", provider); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index cb466d0a..bfd1487c 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -56,7 +56,7 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato aws_unset_environment_value(s_default_config_path_env_variable_name); aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); - struct aws_token_provider_profile_options options = { + struct aws_token_provider_sso_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), }; @@ -64,7 +64,7 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato printf("invalid example [%zu]: %s\n", i, s_invalid_profile_examples[i].name); struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_invalid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); - ASSERT_NULL(aws_token_provider_new_profile(allocator, &options)); + ASSERT_NULL(aws_token_provider_new_sso_profile(allocator, &options)); aws_string_destroy(config_contents); } @@ -98,7 +98,7 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator aws_unset_environment_value(s_default_config_path_env_variable_name); aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); - struct aws_token_provider_profile_options options = { + struct aws_token_provider_sso_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), }; @@ -106,7 +106,7 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator printf("valid example [%zu]: %s\n", i, s_valid_profile_examples[i].name); struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_valid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); - struct aws_credentials_provider *provider = aws_token_provider_new_profile(allocator, &options); + struct aws_credentials_provider *provider = aws_token_provider_new_sso_profile(allocator, &options); ASSERT_NOT_NULL(provider); aws_credentials_provider_release(provider); aws_string_destroy(config_contents); From 5d4ebf1a266240a7507cfd88631ae3834754aed2 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 10 Mar 2023 20:25:16 -0800 Subject: [PATCH 23/93] refactor --- include/aws/auth/credentials.h | 11 ++ include/aws/auth/private/sso_token_utils.h | 4 +- source/credentials_provider_sso.c | 210 +++++++++++---------- source/sso_token_utils.c | 25 ++- source/token_provider_sso_profile.c | 12 +- source/token_provider_sso_session.c | 178 ++++++----------- tests/credentials_provider_sso_tests.c | 1 + tests/sso_token_util_tests.c | 2 +- 8 files changed, 203 insertions(+), 240 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 5acf1734..f5cd928c 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -583,6 +583,17 @@ struct aws_token_provider_sso_session_options { * Override path to the profile config file (~/.aws/config by default) */ struct aws_byte_cursor config_file_name_override; + + /* + * Connection bootstrap to use for any network connections made while sourcing credentials + */ + struct aws_client_bootstrap *bootstrap; + + /* + * Client TLS context to use when querying STS web identity provider. + * Required. + */ + struct aws_tls_ctx *tls_ctx; }; AWS_EXTERN_C_BEGIN diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index 9e1dace3..c0a04c86 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -11,6 +11,8 @@ #include struct aws_sso_token { + struct aws_allocator *allocator; + struct aws_string *token; struct aws_date_time expiration; }; @@ -22,7 +24,7 @@ AWS_AUTH_API struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, const struct aws_string *input); AWS_AUTH_API -void aws_sso_token_destroy(struct aws_allocator *allocator, struct aws_sso_token *token); +void aws_sso_token_destroy(struct aws_sso_token *token); AWS_AUTH_API struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocator, const struct aws_string *file_path); diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 9d0216a9..71eef395 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -62,7 +62,7 @@ struct sso_user_data { /* mutable */ struct aws_http_connection *connection; struct aws_http_message *request; - struct aws_byte_buf response; + struct aws_byte_buf payload; struct aws_retry_token *retry_token; struct aws_byte_buf path_and_query; struct aws_string *token; @@ -74,11 +74,11 @@ struct sso_user_data { /* called in between retries. */ static void s_user_data_reset_request_and_response(struct sso_user_data *user_data) { - aws_http_message_destroy(user_data->request); + aws_http_message_release(user_data->request); user_data->request = NULL; - aws_byte_buf_reset(&user_data->response, true /*zero out*/); - + aws_byte_buf_reset(&user_data->payload, true /* zero out */); + aws_string_destroy(user_data->token); user_data->status_code = 0; user_data->error_code = AWS_OP_SUCCESS; } @@ -97,11 +97,10 @@ static void s_user_data_destroy(struct sso_user_data *user_data) { impl->connection_manager, user_data->connection); } - aws_byte_buf_clean_up(&user_data->response); + aws_byte_buf_clean_up(&user_data->payload); aws_byte_buf_clean_up(&user_data->path_and_query); aws_credentials_provider_release(user_data->provider); aws_retry_token_release(user_data->retry_token); - aws_string_destroy(user_data->token); aws_mem_release(user_data->allocator, user_data); } @@ -129,7 +128,7 @@ static struct sso_user_data *s_user_data_new( goto on_error; } - if (aws_byte_buf_init(&wrapped_user_data->response, provider->allocator, SSO_RESPONSE_SIZE_INITIAL)) { + if (aws_byte_buf_init(&wrapped_user_data->payload, provider->allocator, SSO_RESPONSE_SIZE_INITIAL)) { goto on_error; } @@ -160,19 +159,15 @@ static void s_finalize_get_credentials_query(struct sso_user_data *user_data) { }; credentials = aws_parse_credentials_from_json_document( - user_data->allocator, (const char *)user_data->response.buffer, &parse_options); + user_data->allocator, (const char *)user_data->payload.buffer, &parse_options); } - if (credentials != NULL) { + if (credentials) { AWS_LOGF_INFO( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p)SSO credentials provider successfully queried credentials", - (void *)user_data->provider); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) successfully queried credentials", (void *)user_data->provider); } else { AWS_LOGF_WARN( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) SSO credentials provider failed to query credentials", - (void *)user_data->provider); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) failed to query credentials", (void *)user_data->provider); if (user_data->error_code == AWS_ERROR_SUCCESS) { user_data->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE; @@ -190,8 +185,10 @@ static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_code, void *data) { struct sso_user_data *user_data = data; + + /* set error code */ user_data->error_code = error_code; - if (user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK) { + if (!error_code && user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK) { user_data->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_HTTP_STATUS_FAILURE; } @@ -204,7 +201,7 @@ static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_co * On anything other than a 200, if we can retry the request based on * error response, retry it, otherwise, call the finalize function. */ - if (user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK || error_code != AWS_OP_SUCCESS) { + if (user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK || error_code) { enum aws_retry_error_type error_type = aws_credentials_provider_compute_retry_error_type(user_data->status_code, error_code); @@ -224,51 +221,46 @@ static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_co (void *)user_data->provider, aws_error_str(aws_last_error())); } - } else { - if (aws_retry_token_record_success(user_data->retry_token)) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): failed to register operation success: %s", - (void *)user_data->provider, - aws_error_str(aws_last_error())); - } + } else if (aws_retry_token_record_success(user_data->retry_token)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to register operation success: %s", + (void *)user_data->provider, + aws_error_str(aws_last_error())); } s_finalize_get_credentials_query(user_data); } -static int s_on_incoming_body_fn( - struct aws_http_stream *stream, - const struct aws_byte_cursor *body, - void *wrapped_user_data) { +static int s_on_incoming_body_fn(struct aws_http_stream *stream, const struct aws_byte_cursor *body, void *user_data) { (void)stream; - struct sso_user_data *user_data = wrapped_user_data; - struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + struct sso_user_data *sso_user_data = user_data; + struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; AWS_LOGF_TRACE( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) SSO credentials provider received %zu response bytes", - (void *)user_data->provider, + "(id=%p) received %zu response bytes", + (void *)sso_user_data->provider, body->len); - if (body->len + user_data->response.len > SSO_RESPONSE_SIZE_LIMIT) { - impl->function_table->aws_http_connection_close(user_data->connection); + if (body->len + sso_user_data->payload.len > SSO_RESPONSE_SIZE_LIMIT) { + impl->function_table->aws_http_connection_close(sso_user_data->connection); AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) SSO credentials provider query response exceeded maximum allowed length", - (void *)user_data->provider); + "(id=%p) response exceeded maximum allowed length", + (void *)sso_user_data->provider); return aws_raise_error(AWS_ERROR_SHORT_BUFFER); } - if (aws_byte_buf_append_dynamic(&user_data->response, body)) { - impl->function_table->aws_http_connection_close(user_data->connection); + if (aws_byte_buf_append_dynamic(&sso_user_data->payload, body)) { + impl->function_table->aws_http_connection_close(sso_user_data->connection); AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) SSO credentials provider query error appending response: %s", - (void *)user_data->provider, + "(id=%p) error appending payload: %s", + (void *)sso_user_data->provider, aws_error_str(aws_last_error())); return AWS_OP_ERR; @@ -282,7 +274,7 @@ static int s_on_incoming_headers_fn( enum aws_http_header_block header_block, const struct aws_http_header *header_array, size_t num_headers, - void *wrapped_user_data) { + void *user_data) { (void)header_array; (void)num_headers; @@ -291,23 +283,23 @@ static int s_on_incoming_headers_fn( return AWS_OP_SUCCESS; } - struct sso_user_data *user_data = wrapped_user_data; - if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN && user_data->status_code == 0) { - struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; - if (impl->function_table->aws_http_stream_get_incoming_response_status(stream, &user_data->status_code)) { + struct sso_user_data *sso_user_data = user_data; + if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN && sso_user_data->status_code == 0) { + struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; + if (impl->function_table->aws_http_stream_get_incoming_response_status(stream, &sso_user_data->status_code)) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) SSO credentials provider failed to get http status code: %s", - (void *)user_data->provider, + "(id=%p) failed to get http status code: %s", + (void *)sso_user_data->provider, aws_error_str(aws_last_error())); return AWS_OP_ERR; } AWS_LOGF_DEBUG( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) SSO credentials provider query received http status code %d", - (void *)user_data->provider, - user_data->status_code); + "(id=%p) received http status code %d", + (void *)sso_user_data->provider, + sso_user_data->status_code); } return AWS_OP_SUCCESS; @@ -323,9 +315,6 @@ static void s_query_credentials(struct sso_user_data *user_data) { struct aws_http_stream *stream = NULL; struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; - /* "Clear" the result */ - s_user_data_reset_request_and_response(user_data); - user_data->request = aws_http_message_new_request(user_data->allocator); if (user_data->request == NULL) { goto on_error; @@ -349,7 +338,7 @@ static void s_query_credentials(struct sso_user_data *user_data) { aws_http_message_add_header(user_data->request, user_agent_header)) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p)sso credentials provider failed to add http headers %s", + "(id=%p) failed to add http headers with error: %s", (void *)user_data->provider, aws_error_debug_str(aws_last_error())); goto on_error; @@ -358,7 +347,7 @@ static void s_query_credentials(struct sso_user_data *user_data) { if (aws_http_message_set_request_method(user_data->request, aws_http_method_get)) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p)sso credentials provider failed to set request method %s", + "(id=%p) failed to set request method with error: %s", (void *)user_data->provider, aws_error_debug_str(aws_last_error())); goto on_error; @@ -367,7 +356,7 @@ static void s_query_credentials(struct sso_user_data *user_data) { if (aws_http_message_set_request_path(user_data->request, aws_byte_cursor_from_buf(&user_data->path_and_query))) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p)sso credentials provider failed to set request path %s", + "(id=%p) failed to set request path with error: %s", (void *)user_data->provider, aws_error_debug_str(aws_last_error())); goto on_error; @@ -385,29 +374,41 @@ static void s_query_credentials(struct sso_user_data *user_data) { stream = impl->function_table->aws_http_connection_make_request(user_data->connection, &request_options); if (!stream) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) failed to make request with error: %s", + (void *)user_data->provider, + aws_error_debug_str(aws_last_error())); goto on_error; } if (impl->function_table->aws_http_stream_activate(stream)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) failed to activate the stream with error: %s", + (void *)user_data->provider, + aws_error_debug_str(aws_last_error())); goto on_error; } return; + on_error: impl->function_table->aws_http_stream_release(stream); s_finalize_get_credentials_query(user_data); } + static void s_on_get_token_callback(struct aws_credentials *credentials, int error_code, void *user_data) { struct sso_user_data *sso_user_data = user_data; if (error_code) { AWS_LOGF_WARN( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: SSO provider failed to acquire a token, error code %d(%s)", + "id=%p: failed to acquire a token, error code %d(%s)", (void *)sso_user_data->provider, error_code, aws_error_str(error_code)); - + sso_user_data->error_code = error_code; s_finalize_get_credentials_query(user_data); return; } @@ -416,62 +417,73 @@ static void s_on_get_token_callback(struct aws_credentials *credentials, int err if (token.len == 0 || token.ptr == NULL) { AWS_LOGF_WARN( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: SSO provider failed to acquire a token, error code %d(%s)", + "id=%p: token is empty with error code %d(%s)", (void *)sso_user_data->provider, error_code, aws_error_str(error_code)); + sso_user_data->error_code = error_code; s_finalize_get_credentials_query(user_data); return; } + + /* clear the result from previous attempt */ + s_user_data_reset_request_and_response(sso_user_data); + sso_user_data->token = aws_string_new_from_cursor(sso_user_data->allocator, &token); s_query_credentials(sso_user_data); } -static void s_on_acquire_connection(struct aws_http_connection *connection, int error_code, void *data) { - struct sso_user_data *user_data = data; + +static void s_on_acquire_connection(struct aws_http_connection *connection, int error_code, void *user_data) { + struct sso_user_data *sso_user_data = user_data; if (error_code) { AWS_LOGF_WARN( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: SSO provider failed to acquire a connection, error code %d(%s)", - (void *)user_data->provider, + "id=%p: failed to acquire a connection, error code %d(%s)", + (void *)sso_user_data->provider, error_code, aws_error_str(error_code)); - + sso_user_data->error_code = error_code; s_finalize_get_credentials_query(user_data); return; } - user_data->connection = connection; + sso_user_data->connection = connection; - struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; if (aws_credentials_provider_get_credentials(impl->token_provider, s_on_get_token_callback, user_data)) { AWS_LOGF_WARN( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: failed get a token, error code %d(%s)", - (void *)user_data->provider, + "id=%p: failed to get a token, error code %d(%s)", + (void *)sso_user_data->provider, error_code, aws_error_str(error_code)); - s_finalize_get_credentials_query(user_data); - return; + sso_user_data->error_code = error_code; + s_finalize_get_credentials_query(sso_user_data); } } /* called for each retry. */ -static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *wrapped_user_data) { +static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *user_data) { (void)token; - struct sso_user_data *user_data = wrapped_user_data; - struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; + struct sso_user_data *sso_user_data = user_data; + struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; if (error_code) { - user_data->error_code = error_code; - s_finalize_get_credentials_query(user_data); + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to acquire retry token: %s", + (void *)sso_user_data->provider, + aws_error_debug_str(error_code)); + sso_user_data->error_code = error_code; + s_finalize_get_credentials_query(sso_user_data); return; } impl->function_table->aws_http_connection_manager_acquire_connection( - impl->connection_manager, s_on_acquire_connection, user_data); + impl->connection_manager, s_on_acquire_connection, sso_user_data); } static void s_on_retry_token_acquired( @@ -479,21 +491,23 @@ static void s_on_retry_token_acquired( int error_code, struct aws_retry_token *token, void *user_data) { - struct sso_user_data *provider_user_data = user_data; + struct sso_user_data *sso_user_data = user_data; (void)strategy; if (error_code) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): failed to acquire retry token: %s", - (void *)provider_user_data->provider, + (void *)sso_user_data->provider, aws_error_debug_str(error_code)); - s_finalize_get_credentials_query(provider_user_data); + sso_user_data->error_code = error_code; + s_finalize_get_credentials_query(sso_user_data); return; } + /* success */ - provider_user_data->retry_token = token; - struct aws_credentials_provider_sso_impl *impl = provider_user_data->provider->impl; + sso_user_data->retry_token = token; + struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; impl->function_table->aws_http_connection_manager_acquire_connection( impl->connection_manager, s_on_acquire_connection, user_data); } @@ -521,6 +535,7 @@ static int s_credentials_provider_sso_get_credentials_async( } return AWS_OP_SUCCESS; + on_error: s_user_data_destroy(wrapped_user_data); callback(NULL, aws_last_error(), user_data); @@ -533,6 +548,7 @@ static void s_on_connection_manager_shutdown(void *user_data) { aws_credentials_provider_invoke_shutdown_callback(provider); aws_mem_release(provider->allocator, provider); } + static void s_credentials_provider_sso_destroy(struct aws_credentials_provider *provider) { struct aws_credentials_provider_sso_impl *impl = provider->impl; @@ -577,11 +593,8 @@ static int s_construct_endpoint( struct aws_byte_cursor amazonaws_cursor = aws_byte_cursor_from_c_str(".amazonaws.com"); struct aws_byte_cursor cn_cursor = aws_byte_cursor_from_c_str(".cn"); - if (aws_byte_buf_init_copy_from_cursor(endpoint, allocator, sso_prefix)) { - goto on_error; - } - - if (aws_byte_buf_append_dynamic(endpoint, ®ion_cursor) || + if (aws_byte_buf_init_copy_from_cursor(endpoint, allocator, sso_prefix) || + aws_byte_buf_append_dynamic(endpoint, ®ion_cursor) || aws_byte_buf_append_dynamic(endpoint, &amazonaws_cursor)) { goto on_error; } @@ -668,12 +681,12 @@ static struct sso_parameters *s_parameters_new( } const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session); + /* read the approriate config based on sso_session property is available or not */ if (sso_session_property) { - struct aws_token_provider_sso_session_options token_provider_options; - AWS_ZERO_STRUCT(token_provider_options); - token_provider_options.config_file_name_override = options->config_file_name_override; - token_provider_options.profile_name_override = options->profile_name_override; - + struct aws_token_provider_sso_session_options token_provider_options = { + .config_file_name_override = options->config_file_name_override, + .profile_name_override = options->profile_name_override, + }; parameters->token_provider = aws_token_provider_new_sso_session(allocator, &token_provider_options); if (!parameters->token_provider) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a sso token provider"); @@ -687,10 +700,10 @@ static struct sso_parameters *s_parameters_new( AWS_PROFILE_SECTION_TYPE_SSO_SESSION), s_sso_region); } else { - struct aws_token_provider_sso_profile_options token_provider_options; - AWS_ZERO_STRUCT(token_provider_options); - token_provider_options.config_file_name_override = options->config_file_name_override; - token_provider_options.profile_name_override = options->profile_name_override; + struct aws_token_provider_sso_profile_options token_provider_options = { + .config_file_name_override = options->config_file_name_override, + .profile_name_override = options->profile_name_override, + }; parameters->token_provider = aws_token_provider_new_sso_profile(allocator, &token_provider_options); if (!parameters->token_provider) { @@ -758,10 +771,9 @@ struct aws_credentials_provider *aws_credentials_provider_new_sso( aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_sso_vtable, impl); if (!options->tls_ctx) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "a TLS context must be provided to the SSO credentials provider"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): a TLS context must be provided", (void *)provider); aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - return NULL; + goto on_error; } aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx); diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index f206af4f..d1b52e9d 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -36,13 +36,17 @@ struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, con AWS_ZERO_STRUCT(sha1_buf); /* append home directory */ - aws_byte_buf_init_copy_from_cursor(&token_path_buf, allocator, home_dir_cursor); + if (aws_byte_buf_init_copy_from_cursor(&token_path_buf, allocator, home_dir_cursor)) { + goto cleanup; + } /* append sso cache directory */ - aws_byte_buf_append_dynamic(&token_path_buf, &cache_dir_cursor); + if (aws_byte_buf_append_dynamic(&token_path_buf, &cache_dir_cursor)) { + goto cleanup; + } /* append hex encoded sha1 of input */ - aws_byte_buf_init(&sha1_buf, allocator, AWS_SHA1_LEN); - if (aws_sha1_compute(allocator, &input_cursor, &sha1_buf, 0)) { + if (aws_byte_buf_init(&sha1_buf, allocator, AWS_SHA1_LEN) || + aws_sha1_compute(allocator, &input_cursor, &sha1_buf, 0)) { goto cleanup; } struct aws_byte_cursor sha1_cursor = aws_byte_cursor_from_buf(&sha1_buf); @@ -51,7 +55,9 @@ struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, con } /* append .json */ - aws_byte_buf_append_dynamic(&token_path_buf, &json_cursor); + if (aws_byte_buf_append_dynamic(&token_path_buf, &json_cursor)) { + goto cleanup; + } // Use platform-specific directory separator. const char local_platform_separator = aws_get_platform_directory_separator(); @@ -70,13 +76,13 @@ struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, con return token_path_str; } -void aws_sso_token_destroy(struct aws_allocator *allocator, struct aws_sso_token *token) { - if (token == NULL) { +void aws_sso_token_destroy(struct aws_sso_token *sso_token) { + if (sso_token == NULL) { return; } - aws_string_destroy(token->token); - aws_mem_release(allocator, token); + aws_string_destroy(sso_token->token); + aws_mem_release(sso_token->allocator, sso_token); } struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocator, const struct aws_string *file_path) { @@ -86,6 +92,7 @@ struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocato bool success = false; struct aws_sso_token *token = aws_mem_calloc(allocator, 1, sizeof(struct aws_sso_token)); + token->allocator = allocator; struct aws_byte_buf file_contents_buf; AWS_ZERO_STRUCT(file_contents_buf); struct aws_json_value *document_root = NULL; diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 38ed66e6..6b54109e 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -59,7 +59,7 @@ static int s_token_provider_profile_get_token_async( success = true; done: - aws_sso_token_destroy(provider->allocator, sso_token); + aws_sso_token_destroy(sso_token); aws_credentials_release(credentials); if (!success) { callback(NULL, aws_last_error(), user_data); @@ -91,7 +91,7 @@ static struct aws_string *s_construct_profile_token_path( struct aws_byte_cursor profile_name_override, struct aws_byte_cursor config_file_name_override) { - struct aws_profile_collection *config_profiles = NULL; + struct aws_profile_collection *config_collection = NULL; struct aws_string *profile_name = NULL; struct aws_string *token_path = NULL; @@ -101,9 +101,9 @@ static struct aws_string *s_construct_profile_token_path( aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } - config_profiles = aws_load_config(allocator, config_file_name_override); + config_collection = aws_load_config(allocator, config_file_name_override); - if (!config_profiles) { + if (!config_collection) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser could not load or parse" @@ -112,7 +112,7 @@ static struct aws_string *s_construct_profile_token_path( goto cleanup; } - const struct aws_profile *profile = aws_profile_collection_get_profile(config_profiles, profile_name); + const struct aws_profile *profile = aws_profile_collection_get_profile(config_collection, profile_name); if (!profile) { AWS_LOGF_ERROR( @@ -142,7 +142,7 @@ static struct aws_string *s_construct_profile_token_path( cleanup: aws_string_destroy(profile_name); - aws_profile_collection_release(config_profiles); + aws_profile_collection_release(config_collection); return token_path; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index e35daebe..3e02f7e3 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -20,8 +20,6 @@ * sso-session token provider implementation */ struct aws_token_provider_sso_session_impl { - struct aws_string *sso_region; - struct aws_string *sso_start_url; struct aws_string *token_file_path; }; @@ -38,7 +36,6 @@ static int s_token_provider_sso_session_get_token_async( sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", (void *)provider); - aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto done; } @@ -63,7 +60,7 @@ static int s_token_provider_sso_session_get_token_async( success = true; done: - aws_sso_token_destroy(provider->allocator, sso_token); + aws_sso_token_destroy(sso_token); aws_credentials_release(credentials); if (!success) { callback(NULL, aws_last_error(), user_data); @@ -77,8 +74,6 @@ static void s_token_provider_sso_session_destroy(struct aws_credentials_provider return; } - aws_string_destroy(impl->sso_region); - aws_string_destroy(impl->sso_start_url); aws_string_destroy(impl->token_file_path); aws_credentials_provider_invoke_shutdown_callback(provider); @@ -94,24 +89,48 @@ AWS_STRING_FROM_LITERAL(s_sso_session_name, "sso_session"); AWS_STRING_FROM_LITERAL(s_sso_region_name, "sso_region"); AWS_STRING_FROM_LITERAL(s_sso_start_url_name, "sso_start_url"); -struct token_provider_sso_session_parameters { - struct aws_string *sso_region; - struct aws_string *sso_start_url; - struct aws_string *token_path; -}; - -static int s_token_provider_sso_session_parameters_sso_session_init( +static struct aws_string *s_verify_config_and_construct_token_path( struct aws_allocator *allocator, - struct token_provider_sso_session_parameters *parameters, - const struct aws_profile_collection *profile_collection, - const struct aws_profile *profile, - const struct aws_string *sso_session_name) { + struct aws_byte_cursor profile_name_override, + struct aws_byte_cursor config_file_name_override) { + struct aws_profile_collection *config_collection = NULL; + struct aws_string *profile_name = NULL; + struct aws_string *token_path = NULL; + + profile_name = aws_get_profile_name(allocator, &profile_name_override); + if (!profile_name) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token provider failed to resolve profile name"); + goto cleanup; + } + config_collection = aws_load_config(allocator, config_file_name_override); + const struct aws_profile *profile = aws_profile_collection_get_profile(config_collection, profile_name); + + if (!profile) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso-session: token provider could not load" + " a profile at %s.", + aws_string_c_str(profile_name)); + goto cleanup; + } + + const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session_name); + if (!sso_session_property) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "sso-session: token provider could not find an sso-session at profile %s", + aws_string_c_str(profile_name)); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + goto cleanup; + } + const struct aws_string *sso_session_name = aws_profile_property_get_value(sso_session_property); const struct aws_profile *session_profile = - aws_profile_collection_get_section(profile_collection, sso_session_name, AWS_PROFILE_SECTION_TYPE_SSO_SESSION); + aws_profile_collection_get_section(config_collection, sso_session_name, AWS_PROFILE_SECTION_TYPE_SSO_SESSION); if (!session_profile) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find an sso-session"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + goto cleanup; } const struct aws_profile_property *sso_region_property = @@ -122,13 +141,15 @@ static int s_token_provider_sso_session_parameters_sso_session_init( if (!sso_region_property) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find sso_region in sso-session"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + goto cleanup; } if (!sso_start_url_property) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find sso_start_url in sso-session"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + goto cleanup; } const struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); @@ -144,7 +165,8 @@ static int s_token_provider_sso_session_parameters_sso_session_init( !aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: profile & sso-session have different value for sso_region"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); + goto cleanup; } if (profile_sso_start_url_property && @@ -152,118 +174,29 @@ static int s_token_provider_sso_session_parameters_sso_session_init( AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: profile & sso-session have different value for sso_start_url"); - return aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); - } - - parameters->sso_region = aws_string_new_from_string(allocator, sso_region); - parameters->sso_start_url = aws_string_new_from_string(allocator, sso_start_url); - parameters->token_path = aws_construct_token_path(allocator, sso_session_name); - if (!parameters->token_path) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-session: token parser failed to construct token path in sso-session"); - return AWS_OP_ERR; - } - return AWS_OP_SUCCESS; -} - -static void s_token_provider_sso_session_parameters_destroy( - struct aws_allocator *allocator, - struct token_provider_sso_session_parameters *parameters) { - - aws_string_destroy(parameters->sso_region); - aws_string_destroy(parameters->sso_start_url); - aws_string_destroy(parameters->token_path); - aws_mem_release(allocator, parameters); -} - -static struct token_provider_sso_session_parameters *s_token_provider_sso_session_parameters_new( - struct aws_allocator *allocator, - struct aws_byte_cursor profile_name_override, - struct aws_byte_cursor config_file_name_override) { - struct aws_profile_collection *config_profiles = NULL; - struct aws_string *config_file_path = NULL; - struct aws_string *profile_name = NULL; - bool success = false; - - struct token_provider_sso_session_parameters *parameters = - aws_mem_calloc(allocator, 1, sizeof(struct token_provider_sso_session_parameters)); - config_file_path = aws_get_config_file_path(allocator, &config_file_name_override); - - if (!config_file_path) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token provider failed resolve config file path"); - goto cleanup; - } - - profile_name = aws_get_profile_name(allocator, &profile_name_override); - if (!profile_name) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token provider failed to resolve profile name"); - goto cleanup; - } - config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG); - - if (!config_profiles) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-session: token provider could not load or parse" - " a config file."); - goto cleanup; - } - - const struct aws_profile *profile = aws_profile_collection_get_profile(config_profiles, profile_name); - - if (!profile) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-session: token provider could not load" - " a profile at %s.", - aws_string_c_str(profile_name)); - goto cleanup; - } - - const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session_name); - if (sso_session_property) { - if (s_token_provider_sso_session_parameters_sso_session_init( - allocator, - parameters, - config_profiles, - profile, - aws_profile_property_get_value(sso_session_property))) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-session: token provider could not load a valid sso profile and session at %s", - aws_string_c_str(profile_name)); - goto cleanup; - } - } else { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-session: token provider could not find an sso-session at profile %s", - aws_string_c_str(profile_name)); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } - success = true; + token_path = aws_construct_token_path(allocator, sso_session_name); cleanup: - aws_string_destroy(config_file_path); aws_string_destroy(profile_name); - aws_profile_collection_release(config_profiles); - if (!success) { - s_token_provider_sso_session_parameters_destroy(allocator, parameters); - parameters = NULL; - } - return parameters; + aws_profile_collection_release(config_collection); + return token_path; } struct aws_credentials_provider *aws_token_provider_new_sso_session( struct aws_allocator *allocator, const struct aws_token_provider_sso_session_options *options) { - struct token_provider_sso_session_parameters *parameters = s_token_provider_sso_session_parameters_new( + /* Currently, they are not used but they will be required when we implement the refresh token functionality. */ + AWS_ASSERT(options->bootstrap); + AWS_ASSERT(options->tls_ctx); + + struct aws_string *token_path = s_verify_config_and_construct_token_path( allocator, options->profile_name_override, options->config_file_name_override); - if (!parameters) { + if (!token_path) { return NULL; } struct aws_credentials_provider *provider = NULL; @@ -279,11 +212,8 @@ struct aws_credentials_provider *aws_token_provider_new_sso_session( AWS_ZERO_STRUCT(*provider); AWS_ZERO_STRUCT(*impl); aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_sso_session_vtable, impl); - impl->sso_region = aws_string_new_from_string(allocator, parameters->sso_region); - impl->sso_start_url = aws_string_new_from_string(allocator, parameters->sso_start_url); - impl->token_file_path = aws_string_new_from_string(allocator, parameters->sso_start_url); + impl->token_file_path = aws_string_new_from_string(allocator, token_path); provider->shutdown_options = options->shutdown_options; - s_token_provider_sso_session_parameters_destroy(allocator, parameters); return provider; } diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 79117589..4dbaf525 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -39,6 +39,7 @@ static void s_get_credentials_callback(struct aws_credentials *credentials, int AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "waahm7 callback %d", error_code); received_callback = true; + AWS_FATAL_ASSERT(credentials != NULL); aws_condition_variable_notify_one(&tester_signal); } diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index b17ae29b..69f593a9 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -56,7 +56,7 @@ static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { ASSERT_TRUE(aws_string_eq_c_str(token->token, "string")); ASSERT_INT_EQUALS(aws_date_time_as_epoch_secs(&token->expiration), 1573704345); aws_string_destroy(file_path); - aws_sso_token_destroy(allocator, token); + aws_sso_token_destroy(token); aws_auth_library_clean_up(); return AWS_OP_SUCCESS; } From 01db30c805e980b4db761fdc1d47800c4cc34aaf Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sat, 11 Mar 2023 13:13:33 -0800 Subject: [PATCH 24/93] first working test with mock in utils --- source/credentials_provider_sso.c | 7 + tests/CMakeLists.txt | 1 + tests/credentials_provider_sso_tests.c | 80 +++++++ tests/credentials_provider_utils.c | 289 +++++++++++++++++++++++++ tests/credentials_provider_utils.h | 71 +++++- 5 files changed, 447 insertions(+), 1 deletion(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 71eef395..4e7d6d14 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -776,6 +776,13 @@ struct aws_credentials_provider *aws_credentials_provider_new_sso( goto on_error; } + if (!options->bootstrap) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): a bootstrap instance must be provided", (void *)provider); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + goto on_error; + } + aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx); struct aws_byte_cursor host = aws_byte_cursor_from_buf(¶meters->endpoint); if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c75bec70..3f150bce 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -112,6 +112,7 @@ add_net_test_case(parse_token_location_session_test) add_net_test_case(parse_sso_token_valid) add_net_test_case(credentials_provider_sso_new_destroy) +add_net_test_case(credentials_provider_sso_new_destroy_from_profile_config) add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 4dbaf525..f776d04d 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -5,7 +5,9 @@ #include +#include "credentials_provider_utils.h" #include "shared_credentials_test_definitions.h" + #include #include #include @@ -92,3 +94,81 @@ static int s_credentials_provider_sso_new_destroy(struct aws_allocator *allocato return 0; } AWS_TEST_CASE(credentials_provider_sso_new_destroy, s_credentials_provider_sso_new_destroy); + +AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile, "sso"); +static int s_aws_credentials_provider_sso_test_init_config_profile( + struct aws_allocator *allocator, + const struct aws_string *config_contents) { + + struct aws_string *config_file_path_str = aws_create_process_unique_file_name(allocator); + ASSERT_TRUE(config_file_path_str != NULL); + ASSERT_TRUE(aws_create_profile_file(config_file_path_str, config_contents) == AWS_OP_SUCCESS); + + ASSERT_TRUE( + aws_set_environment_value(s_default_config_path_env_variable_name, config_file_path_str) == AWS_OP_SUCCESS); + + ASSERT_TRUE(aws_set_environment_value(s_default_profile_env_variable_name, s_sso_profile) == AWS_OP_SUCCESS); + + aws_string_destroy(config_file_path_str); + return AWS_OP_SUCCESS; +} + +AWS_STATIC_STRING_FROM_LITERAL( + s_sso_profile_config_contents, + "[profile sso]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n" + "sso_account_id = 123\n" + "sso_role_name = roleName\n"); + +AWS_STATIC_STRING_FROM_LITERAL( + s_sso_session_config_contents, + "[profile sso]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n" + "sso_account_id = 123\n" + "sso_role_name = roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"); + +static int s_credentials_provider_sso_new_destroy_from_profile_config(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_profile_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE( + credentials_provider_sso_new_destroy_from_profile_config, + s_credentials_provider_sso_new_destroy_from_profile_config); diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index b663477d..d5ae4232 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -4,13 +4,18 @@ */ #include "credentials_provider_utils.h" +#include #include #include #include #include +#include +#include #include #include +#include +#include #include @@ -81,6 +86,22 @@ void aws_wait_on_credentials_callback(struct aws_get_credentials_test_callback_r /* * Mock provider */ + +struct aws_auth_http_system_vtable aws_credentials_provider_http_mock_function_table = { + .aws_http_connection_manager_new = aws_credentials_provider_http_mock_connection_manager_new, + .aws_http_connection_manager_release = aws_credentials_provider_http_mock_connection_manager_release, + .aws_http_connection_manager_acquire_connection = + aws_credentials_provider_http_mock_connection_manager_acquire_connection, + .aws_http_connection_manager_release_connection = + aws_credentials_provider_http_mock_connection_manager_release_connection, + .aws_http_connection_make_request = aws_credentials_provider_http_mock_make_request, + .aws_http_stream_activate = aws_credentials_provider_http_mock_stream_activate, + .aws_http_stream_get_connection = aws_credentials_provider_http_mock_stream_get_connection, + .aws_http_stream_get_incoming_response_status = + aws_credentials_provider_http_mock_stream_get_incoming_response_status, + .aws_http_stream_release = aws_credentials_provider_http_mock_stream_release, + .aws_http_connection_close = aws_credentials_provider_http_mock_connection_close}; + struct aws_credentials_provider_mock_impl { struct aws_array_list results; size_t next_result; @@ -465,3 +486,271 @@ struct aws_credentials_provider *aws_credentials_provider_new_null( return provider; } + +/* + * Mocked Functions for Tests + */ +struct aws_credentials_provider_http_mock_tester credentials_provider_http_mock_tester; + +int aws_credentials_provider_http_mock_tester_init(struct aws_allocator *allocator) { + aws_auth_library_init(allocator); + + AWS_ZERO_STRUCT(credentials_provider_http_mock_tester); + + struct aws_tls_ctx_options tls_options; + aws_tls_ctx_options_init_default_client(&tls_options, allocator); + credentials_provider_http_mock_tester.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_options); + ASSERT_NOT_NULL(credentials_provider_http_mock_tester.tls_ctx); + + credentials_provider_http_mock_tester.el_group = aws_event_loop_group_new_default(allocator, 0, NULL); + + struct aws_host_resolver_default_options resolver_options = { + .el_group = credentials_provider_http_mock_tester.el_group, + .max_entries = 8, + }; + credentials_provider_http_mock_tester.resolver = aws_host_resolver_new_default(allocator, &resolver_options); + + struct aws_client_bootstrap_options bootstrap_options = { + .event_loop_group = credentials_provider_http_mock_tester.el_group, + .host_resolver = credentials_provider_http_mock_tester.resolver, + }; + credentials_provider_http_mock_tester.bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + + if (aws_array_list_init_dynamic( + &credentials_provider_http_mock_tester.response_data_callbacks, + allocator, + 10, + sizeof(struct aws_byte_cursor))) { + return AWS_OP_ERR; + } + + if (aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256)) { + return AWS_OP_ERR; + } + + if (aws_mutex_init(&credentials_provider_http_mock_tester.lock)) { + return AWS_OP_ERR; + } + + if (aws_condition_variable_init(&credentials_provider_http_mock_tester.signal)) { + return AWS_OP_ERR; + } + + /* default to everything successful */ + credentials_provider_http_mock_tester.is_connection_acquire_successful = true; + credentials_provider_http_mock_tester.is_request_successful = true; + + return AWS_OP_SUCCESS; +} + +void aws_credentials_provider_http_mock_tester_cleanup(void) { + aws_tls_ctx_release(credentials_provider_http_mock_tester.tls_ctx); + aws_client_bootstrap_release(credentials_provider_http_mock_tester.bootstrap); + aws_host_resolver_release(credentials_provider_http_mock_tester.resolver); + aws_event_loop_group_release(credentials_provider_http_mock_tester.el_group); + aws_array_list_clean_up(&credentials_provider_http_mock_tester.response_data_callbacks); + aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); + aws_condition_variable_clean_up(&credentials_provider_http_mock_tester.signal); + aws_mutex_clean_up(&credentials_provider_http_mock_tester.lock); + aws_credentials_release(credentials_provider_http_mock_tester.credentials); + aws_auth_library_clean_up(); +} + +void aws_credentials_provider_http_mock_on_shutdown_complete(void *user_data) { + (void)user_data; + aws_mutex_lock(&credentials_provider_http_mock_tester.lock); + credentials_provider_http_mock_tester.has_received_shutdown_callback = true; + aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); + + aws_condition_variable_notify_one(&credentials_provider_http_mock_tester.signal); +} + +bool aws_credentials_provider_http_mock_has_received_shutdown_callback(void *user_data) { + (void)user_data; + + return credentials_provider_http_mock_tester.has_received_shutdown_callback; +} + +void aws_credentials_provider_http_mock_wait_for_shutdown_callback() { + aws_mutex_lock(&credentials_provider_http_mock_tester.lock); + aws_condition_variable_wait_pred( + &credentials_provider_http_mock_tester.signal, + &credentials_provider_http_mock_tester.lock, + aws_credentials_provider_http_mock_has_received_shutdown_callback, + NULL); + aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); +} + +struct mock_connection_manager { + struct aws_allocator *allocator; + aws_http_connection_manager_shutdown_complete_fn *shutdown_complete_callback; + void *shutdown_complete_user_data; +}; + +struct aws_http_connection_manager *aws_credentials_provider_http_mock_connection_manager_new( + struct aws_allocator *allocator, + const struct aws_http_connection_manager_options *options) { + + struct mock_connection_manager *mock_manager = aws_mem_calloc(allocator, 1, sizeof(struct mock_connection_manager)); + mock_manager->allocator = allocator; + mock_manager->shutdown_complete_callback = options->shutdown_complete_callback; + mock_manager->shutdown_complete_user_data = options->shutdown_complete_user_data; + return (struct aws_http_connection_manager *)mock_manager; +} + +void aws_credentials_provider_http_mock_connection_manager_release(struct aws_http_connection_manager *manager) { + struct mock_connection_manager *mock_manager = (struct mock_connection_manager *)manager; + mock_manager->shutdown_complete_callback(mock_manager->shutdown_complete_user_data); + aws_mem_release(mock_manager->allocator, mock_manager); +} + +void aws_credentials_provider_http_mock_connection_manager_acquire_connection( + 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 (credentials_provider_http_mock_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); + } +} + +int aws_credentials_provider_http_mock_connection_manager_release_connection( + struct aws_http_connection_manager *manager, + struct aws_http_connection *connection) { + + (void)manager; + (void)connection; + + return AWS_OP_SUCCESS; +} + +void aws_credentials_provider_http_mock_invoke_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); +} + +struct aws_http_stream *aws_credentials_provider_http_mock_make_request( + 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); + struct aws_input_stream *body_stream = aws_http_message_get_body_stream(options->request); + struct aws_allocator *allocator = credentials_provider_http_mock_tester.request_body.allocator; + aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); + aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256); + aws_input_stream_read(body_stream, &credentials_provider_http_mock_tester.request_body); + aws_credentials_provider_http_mock_invoke_request_callbacks( + options, + &credentials_provider_http_mock_tester.response_data_callbacks, + credentials_provider_http_mock_tester.is_request_successful); + + credentials_provider_http_mock_tester.attempts++; + return (struct aws_http_stream *)1; +} + +int aws_credentials_provider_http_mock_stream_activate(struct aws_http_stream *stream) { + (void)stream; + return AWS_OP_SUCCESS; +} + +int aws_credentials_provider_http_mock_stream_get_incoming_response_status( + const struct aws_http_stream *stream, + int *out_status_code) { + (void)stream; + + if (credentials_provider_http_mock_tester.response_code) { + *out_status_code = credentials_provider_http_mock_tester.response_code; + } else { + *out_status_code = AWS_HTTP_STATUS_CODE_200_OK; + } + + return AWS_OP_SUCCESS; +} + +void aws_credentials_provider_http_mock_stream_release(struct aws_http_stream *stream) { + (void)stream; +} + +void aws_credentials_provider_http_mock_connection_close(struct aws_http_connection *connection) { + (void)connection; +} + +struct aws_http_connection *aws_credentials_provider_http_mock_stream_get_connection( + const struct aws_http_stream *stream) { + (void)stream; + return (struct aws_http_connection *)1; +} + +bool aws_credentials_provider_http_mock_has_received_credentials_callback(void *user_data) { + (void)user_data; + + return credentials_provider_http_mock_tester.has_received_credentials_callback; +} + +void aws_credentials_provider_http_mock_wait_for_credentials_result(void) { + aws_mutex_lock(&credentials_provider_http_mock_tester.lock); + aws_condition_variable_wait_pred( + &credentials_provider_http_mock_tester.signal, + &credentials_provider_http_mock_tester.lock, + aws_credentials_provider_http_mock_has_received_credentials_callback, + NULL); + aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); +} + +void aws_credentials_provider_http_mock_get_credentials_callback( + struct aws_credentials *credentials, + int error_code, + void *user_data) { + (void)user_data; + + aws_mutex_lock(&credentials_provider_http_mock_tester.lock); + credentials_provider_http_mock_tester.has_received_credentials_callback = true; + credentials_provider_http_mock_tester.credentials = credentials; + credentials_provider_http_mock_tester.error_code = error_code; + if (credentials != NULL) { + aws_credentials_acquire(credentials); + } + aws_condition_variable_notify_one(&credentials_provider_http_mock_tester.signal); + aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); +} diff --git a/tests/credentials_provider_utils.h b/tests/credentials_provider_utils.h index 960e858e..a515b4ae 100644 --- a/tests/credentials_provider_utils.h +++ b/tests/credentials_provider_utils.h @@ -7,9 +7,11 @@ */ #include - +#include #include #include +#include +#include struct aws_credentials; struct aws_credentials_provider; @@ -91,4 +93,71 @@ struct aws_credentials_provider *aws_credentials_provider_new_null( struct aws_allocator *allocator, struct aws_credentials_provider_shutdown_options *shutdown_options); +/** + * Mocked Function + */ +struct aws_credentials_provider_http_mock_tester { + struct aws_tls_ctx *tls_ctx; + struct aws_event_loop_group *el_group; + struct aws_host_resolver *resolver; + struct aws_client_bootstrap *bootstrap; + + struct aws_byte_buf request_body; + + 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; + + int attempts; + int response_code; + int error_code; +}; + +extern struct aws_credentials_provider_http_mock_tester credentials_provider_http_mock_tester; +int aws_credentials_provider_http_mock_tester_init(struct aws_allocator *allocator); +void aws_credentials_provider_http_mock_tester_cleanup(void); +void aws_credentials_provider_http_mock_on_shutdown_complete(void *user_data); +bool aws_credentials_provider_http_mock_has_received_shutdown_callback(void *user_data); +void aws_credentials_provider_http_mock_wait_for_shutdown_callback(void); +struct aws_http_connection_manager *aws_credentials_provider_http_mock_connection_manager_new( + struct aws_allocator *allocator, + const struct aws_http_connection_manager_options *options); +void aws_credentials_provider_http_mock_connection_manager_release(struct aws_http_connection_manager *manager); +void aws_credentials_provider_http_mock_connection_manager_acquire_connection( + struct aws_http_connection_manager *manager, + aws_http_connection_manager_on_connection_setup_fn *callback, + void *user_data); +int aws_credentials_provider_http_mock_connection_manager_release_connection( + struct aws_http_connection_manager *manager, + struct aws_http_connection *connection); +void aws_credentials_provider_http_mock_invoke_request_callbacks( + const struct aws_http_make_request_options *options, + struct aws_array_list *data_callbacks, + bool is_request_successful); +struct aws_http_stream *aws_credentials_provider_http_mock_make_request( + struct aws_http_connection *client_connection, + const struct aws_http_make_request_options *options); +int aws_credentials_provider_http_mock_stream_activate(struct aws_http_stream *stream); +int aws_credentials_provider_http_mock_stream_get_incoming_response_status( + const struct aws_http_stream *stream, + int *out_status_code); +void aws_credentials_provider_http_mock_stream_release(struct aws_http_stream *stream); +void aws_credentials_provider_http_mock_connection_close(struct aws_http_connection *connection); +struct aws_http_connection *aws_credentials_provider_http_mock_stream_get_connection( + const struct aws_http_stream *stream); +bool aws_credentials_provider_http_mock_has_received_credentials_callback(void *user_data); +void aws_credentials_provider_http_mock_wait_for_credentials_result(void); +void aws_credentials_provider_http_mock_get_credentials_callback( + struct aws_credentials *credentials, + int error_code, + void *user_data); +extern struct aws_auth_http_system_vtable aws_credentials_provider_http_mock_function_table; + #endif /* AWS_AUTH_CREDENTIALS_PROVIDER_MOCK_H */ From 2d4271ec09f57fb7bf463a23d4783d7b4d7b64a5 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sat, 11 Mar 2023 13:31:29 -0800 Subject: [PATCH 25/93] basic sanity tests --- source/credentials_provider_sso.c | 2 + source/token_provider_sso_session.c | 1 + tests/CMakeLists.txt | 2 + tests/credentials_provider_sso_tests.c | 74 ++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 4e7d6d14..44dd0194 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -686,6 +686,8 @@ static struct sso_parameters *s_parameters_new( struct aws_token_provider_sso_session_options token_provider_options = { .config_file_name_override = options->config_file_name_override, .profile_name_override = options->profile_name_override, + .bootstrap = options->bootstrap, + .tls_ctx = options->tls_ctx, }; parameters->token_provider = aws_token_provider_new_sso_session(allocator, &token_provider_options); if (!parameters->token_provider) { diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 3e02f7e3..1e410e48 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -215,5 +215,6 @@ struct aws_credentials_provider *aws_token_provider_new_sso_session( impl->token_file_path = aws_string_new_from_string(allocator, token_path); provider->shutdown_options = options->shutdown_options; + aws_string_destroy(token_path); return provider; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3f150bce..40d140be 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -113,6 +113,8 @@ add_net_test_case(parse_sso_token_valid) add_net_test_case(credentials_provider_sso_new_destroy) add_net_test_case(credentials_provider_sso_new_destroy_from_profile_config) +add_net_test_case(credentials_provider_sso_new_destroy_from_sso_session_config) +add_net_test_case(credentials_provider_sso_failed_without_config) add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index f776d04d..e0974752 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -112,6 +112,7 @@ static int s_aws_credentials_provider_sso_test_init_config_profile( aws_string_destroy(config_file_path_str); return AWS_OP_SUCCESS; } +// TODO: add config tests AWS_STATIC_STRING_FROM_LITERAL( s_sso_profile_config_contents, @@ -161,6 +162,8 @@ static int s_credentials_provider_sso_new_destroy_from_profile_config(struct aws }; struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + aws_credentials_provider_release(provider); aws_credentials_provider_http_mock_wait_for_shutdown_callback(); @@ -172,3 +175,74 @@ static int s_credentials_provider_sso_new_destroy_from_profile_config(struct aws AWS_TEST_CASE( credentials_provider_sso_new_destroy_from_profile_config, s_credentials_provider_sso_new_destroy_from_profile_config); + +static int s_credentials_provider_sso_new_destroy_from_sso_session_config(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE( + credentials_provider_sso_new_destroy_from_sso_session_config, + s_credentials_provider_sso_new_destroy_from_sso_session_config); + +static int s_credentials_provider_sso_failed_without_config(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + struct aws_string *empty_content = aws_string_new_from_c_str(allocator, ""); + ASSERT_TRUE(empty_content != NULL); + s_aws_credentials_provider_sso_test_init_config_profile(allocator, empty_content); + aws_string_destroy(empty_content); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NULL(provider); + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_failed_without_config, s_credentials_provider_sso_failed_without_config); From f2876e373a2641b05f6f24a9b62ee52076996238 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sat, 11 Mar 2023 13:50:08 -0800 Subject: [PATCH 26/93] verify_credentials --- source/credentials_provider_sso.c | 1 + tests/credentials_provider_sso_tests.c | 40 ++++++++++++++++++++++++++ tests/credentials_provider_utils.c | 12 ++++++++ tests/credentials_provider_utils.h | 1 + 4 files changed, 54 insertions(+) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 44dd0194..a3999935 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -157,6 +157,7 @@ static void s_finalize_get_credentials_query(struct sso_user_data *user_data) { .expiration_required = true, .expiration_format = AWS_PCEF_NUMBER_UNIX_EPOCH, }; + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "waahm7:%s", user_data->payload.buffer); credentials = aws_parse_credentials_from_json_document( user_data->allocator, (const char *)user_data->payload.buffer, &parse_options); diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index e0974752..30de5729 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -246,3 +246,43 @@ static int s_credentials_provider_sso_failed_without_config(struct aws_allocator return 0; } AWS_TEST_CASE(credentials_provider_sso_failed_without_config, s_credentials_provider_sso_failed_without_config); + +AWS_STATIC_STRING_FROM_LITERAL( + s_expected_sso_request_path, + "/federation/credentials?account_id=123&role_name=roleName"); + +AWS_STATIC_STRING_FROM_LITERAL( + s_good_response, + "{\"roleCredentials\": {\"accessKeyId\": \"SuccessfulAccessKey\",\"secretAccessKey\": " + "\"SuccessfulSecret\",\"sessionToken\": \"SuccessfulToken\",\"expiration\": 1678574216000}}"); +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, "SuccessfulToken"); +AWS_STATIC_STRING_FROM_LITERAL(s_good_response_expiration, "2020-02-25T06:03:31Z"); + +static int s_verify_credentials(bool request_made, bool got_credentials, int expected_attempts) { + + if (request_made) { + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_byte_cursor_from_buf(&credentials_provider_http_mock_tester.request_path), s_expected_sso_request_path); + } + + ASSERT_TRUE(credentials_provider_http_mock_tester.has_received_credentials_callback); + + if (got_credentials) { + ASSERT_TRUE(credentials_provider_http_mock_tester.credentials != NULL); + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_credentials_get_access_key_id(credentials_provider_http_mock_tester.credentials), s_good_access_key_id); + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_credentials_get_secret_access_key(credentials_provider_http_mock_tester.credentials), + s_good_secret_access_key); + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_credentials_get_session_token(credentials_provider_http_mock_tester.credentials), s_good_session_token); + } else { + ASSERT_TRUE(credentials_provider_http_mock_tester.credentials == NULL); + } + + ASSERT_TRUE(credentials_provider_http_mock_tester.attempts == expected_attempts); + + return AWS_OP_SUCCESS; +} diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index d5ae4232..4325066f 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -524,6 +524,9 @@ int aws_credentials_provider_http_mock_tester_init(struct aws_allocator *allocat return AWS_OP_ERR; } + if (aws_byte_buf_init(&credentials_provider_http_mock_tester.request_path, allocator, 256)) { + return AWS_OP_ERR; + } if (aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256)) { return AWS_OP_ERR; } @@ -549,6 +552,7 @@ void aws_credentials_provider_http_mock_tester_cleanup(void) { aws_host_resolver_release(credentials_provider_http_mock_tester.resolver); aws_event_loop_group_release(credentials_provider_http_mock_tester.el_group); aws_array_list_clean_up(&credentials_provider_http_mock_tester.response_data_callbacks); + aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_path); aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); aws_condition_variable_clean_up(&credentials_provider_http_mock_tester.signal); aws_mutex_clean_up(&credentials_provider_http_mock_tester.lock); @@ -680,6 +684,14 @@ struct aws_http_stream *aws_credentials_provider_http_mock_make_request( aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256); aws_input_stream_read(body_stream, &credentials_provider_http_mock_tester.request_body); + + aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_path); + + struct aws_byte_cursor request_path_cursor; + aws_http_message_get_request_path(options->request, &request_path_cursor); + aws_byte_buf_init_copy_from_cursor( + &credentials_provider_http_mock_tester.request_body, allocator, request_path_cursor); + aws_credentials_provider_http_mock_invoke_request_callbacks( options, &credentials_provider_http_mock_tester.response_data_callbacks, diff --git a/tests/credentials_provider_utils.h b/tests/credentials_provider_utils.h index a515b4ae..0ee05087 100644 --- a/tests/credentials_provider_utils.h +++ b/tests/credentials_provider_utils.h @@ -102,6 +102,7 @@ struct aws_credentials_provider_http_mock_tester { struct aws_host_resolver *resolver; struct aws_client_bootstrap *bootstrap; + struct aws_byte_buf request_path; struct aws_byte_buf request_body; struct aws_array_list response_data_callbacks; From 963978ed5ee6a75f0f71529c92c07ccb608fd4b5 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sat, 11 Mar 2023 15:30:57 -0800 Subject: [PATCH 27/93] sso token failure test --- source/credentials_provider_sso.c | 2 +- source/token_provider_sso_profile.c | 2 +- source/token_provider_sso_session.c | 2 +- tests/CMakeLists.txt | 2 + tests/credentials_provider_sso_tests.c | 96 ++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 3 deletions(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index a3999935..55cf00d6 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -410,7 +410,7 @@ static void s_on_get_token_callback(struct aws_credentials *credentials, int err error_code, aws_error_str(error_code)); sso_user_data->error_code = error_code; - s_finalize_get_credentials_query(user_data); + s_finalize_get_credentials_query(sso_user_data); return; } diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 6b54109e..d046b683 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -64,7 +64,7 @@ static int s_token_provider_profile_get_token_async( if (!success) { callback(NULL, aws_last_error(), user_data); } - return success ? AWS_OP_SUCCESS : AWS_OP_ERR; + return AWS_OP_SUCCESS; } static void s_token_provider_profile_destroy(struct aws_credentials_provider *provider) { diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 1e410e48..956cd142 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -65,7 +65,7 @@ static int s_token_provider_sso_session_get_token_async( if (!success) { callback(NULL, aws_last_error(), user_data); } - return success ? AWS_OP_SUCCESS : AWS_OP_ERR; + return AWS_OP_SUCCESS; } static void s_token_provider_sso_session_destroy(struct aws_credentials_provider *provider) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 40d140be..5f73058a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -115,6 +115,8 @@ add_net_test_case(credentials_provider_sso_new_destroy) add_net_test_case(credentials_provider_sso_new_destroy_from_profile_config) add_net_test_case(credentials_provider_sso_new_destroy_from_sso_session_config) add_net_test_case(credentials_provider_sso_failed_without_config) +add_net_test_case(credentials_provider_sso_connect_failure) +add_net_test_case(credentials_provider_sso_token_failure) add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 30de5729..b594116f 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -286,3 +286,99 @@ static int s_verify_credentials(bool request_made, bool got_credentials, int exp return AWS_OP_SUCCESS; } + +static int s_credentials_provider_sso_connect_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_connection_acquire_successful = false; + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_connect_failure, s_credentials_provider_sso_connect_failure); + +static int s_credentials_provider_sso_token_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_request_successful = false; + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_token_failure, s_credentials_provider_sso_token_failure); From 86ef49b9e3f0a2bb6a9081aefd49baeb084d2105 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sat, 11 Mar 2023 22:44:22 -0800 Subject: [PATCH 28/93] more tests --- source/credentials_provider_sso.c | 17 +- tests/CMakeLists.txt | 5 + tests/credentials_provider_sso_tests.c | 364 ++++++++++++++++++++++++- tests/credentials_provider_sts_tests.c | 2 +- tests/credentials_provider_utils.c | 19 +- tests/credentials_provider_utils.h | 1 + 6 files changed, 389 insertions(+), 19 deletions(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 55cf00d6..efb119f7 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -209,9 +209,6 @@ static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_co /* don't retry client errors at all. */ if (error_type != AWS_RETRY_ERROR_TYPE_CLIENT_ERROR) { - /* clear data used by the previous attempt. */ - s_user_data_reset_request_and_response(user_data); - if (aws_retry_strategy_schedule_retry(user_data->retry_token, error_type, s_on_retry_ready, user_data) == AWS_OP_SUCCESS) { return; @@ -427,7 +424,8 @@ static void s_on_get_token_callback(struct aws_credentials *credentials, int err s_finalize_get_credentials_query(user_data); return; } - + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): successfully accquired a token", (void *)sso_user_data->provider); /* clear the result from previous attempt */ s_user_data_reset_request_and_response(sso_user_data); @@ -449,7 +447,10 @@ static void s_on_acquire_connection(struct aws_http_connection *connection, int s_finalize_get_credentials_query(user_data); return; } - + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): successfully accquired a connection", + (void *)sso_user_data->provider); sso_user_data->connection = connection; struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; @@ -482,7 +483,10 @@ static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void s_finalize_get_credentials_query(sso_user_data); return; } - + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): successfully acquired a retry token", + (void *)sso_user_data->provider); impl->function_table->aws_http_connection_manager_acquire_connection( impl->connection_manager, s_on_acquire_connection, sso_user_data); } @@ -506,7 +510,6 @@ static void s_on_retry_token_acquired( return; } - /* success */ sso_user_data->retry_token = token; struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; impl->function_table->aws_http_connection_manager_acquire_connection( diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5f73058a..c5d744d5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -117,6 +117,11 @@ add_net_test_case(credentials_provider_sso_new_destroy_from_sso_session_config) add_net_test_case(credentials_provider_sso_failed_without_config) add_net_test_case(credentials_provider_sso_connect_failure) add_net_test_case(credentials_provider_sso_token_failure) +add_net_test_case(credentials_provider_sso_request_failure) +add_net_test_case(credentials_provider_sso_bad_response) +add_net_test_case(credentials_provider_sso_retryable_error) +add_net_test_case(credentials_provider_sso_basic_success) +add_net_test_case(credentials_provider_sso_basic_success_profile) add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index b594116f..48702c2f 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -114,6 +115,8 @@ static int s_aws_credentials_provider_sso_test_init_config_profile( } // TODO: add config tests +/* start_url should be same in `s_sso_profile_start_url` and `s_sso_profile_config_contents` */ +AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile_start_url, "https://d-123.awsapps.com/start"); AWS_STATIC_STRING_FROM_LITERAL( s_sso_profile_config_contents, "[profile sso]\n" @@ -121,7 +124,8 @@ AWS_STATIC_STRING_FROM_LITERAL( "sso_region = us-west-2\n" "sso_account_id = 123\n" "sso_role_name = roleName\n"); - +/* session name should be same in both `s_sso_session_name` and `s_sso_session_config_contents`*/ +AWS_STATIC_STRING_FROM_LITERAL(s_sso_session_name, "session"); AWS_STATIC_STRING_FROM_LITERAL( s_sso_session_config_contents, "[profile sso]\n" @@ -282,7 +286,7 @@ static int s_verify_credentials(bool request_made, bool got_credentials, int exp ASSERT_TRUE(credentials_provider_http_mock_tester.credentials == NULL); } - ASSERT_TRUE(credentials_provider_http_mock_tester.attempts == expected_attempts); + ASSERT_INT_EQUALS(credentials_provider_http_mock_tester.attempts, expected_attempts); return AWS_OP_SUCCESS; } @@ -382,3 +386,359 @@ static int s_credentials_provider_sso_token_failure(struct aws_allocator *alloca return 0; } AWS_TEST_CASE(credentials_provider_sso_token_failure, s_credentials_provider_sso_token_failure); + +/** + * Create the directory components of @path: + * - if @path ends in a path separator, create every directory component; + * - else, stop at the last path separator (parent directory of @path). + */ +static int s_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path) { + const char local_platform_separator = aws_get_platform_directory_separator(); + + /* Create directory components and ensure use of platform separator at the same time. */ + for (size_t i = 0; i < path->len; ++i) { + if (aws_is_any_directory_separator((char)path->bytes[i])) { + ((char *)path->bytes)[i] = local_platform_separator; + + struct aws_string *segment = aws_string_new_from_array(allocator, path->bytes, i); + int rc = aws_directory_create(segment); + aws_string_destroy(segment); + + if (rc != AWS_OP_SUCCESS) { + return rc; + } + } + } + return AWS_OP_SUCCESS; +} + +AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); +AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); + +AWS_STATIC_STRING_FROM_LITERAL( + s_valid_sso_token, + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2030-03-12T05:35:19Z\"}"); + +static int s_credentials_provider_sso_request_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_request_successful = false; + + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 4 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_request_failure, s_credentials_provider_sso_request_failure); + +AWS_STATIC_STRING_FROM_LITERAL(s_bad_json_response, "{ \"accessKey\": \"bad\"}"); +static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + struct aws_byte_cursor bad_json_cursor = aws_byte_cursor_from_string(s_bad_json_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &bad_json_cursor); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_bad_response, s_credentials_provider_sso_bad_response); + +static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR; + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + struct aws_byte_cursor bad_json_cursor = aws_byte_cursor_from_string(s_bad_json_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &bad_json_cursor); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 4 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_retryable_error, s_credentials_provider_sso_retryable_error); + +static int s_credentials_provider_sso_basic_success(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + /* set the response */ + struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_basic_success, s_credentials_provider_sso_basic_success); + +static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_profile_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + /* set the response */ + struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_basic_success_profile, s_credentials_provider_sso_basic_success_profile); diff --git a/tests/credentials_provider_sts_tests.c b/tests/credentials_provider_sts_tests.c index f1ab19a3..9fa6412a 100644 --- a/tests/credentials_provider_sts_tests.c +++ b/tests/credentials_provider_sts_tests.c @@ -448,7 +448,7 @@ static int s_credentials_provider_sts_direct_config_succeeds_after_retry_fn( s_tester.mock_body = aws_byte_buf_from_c_str(success_creds_doc); s_tester.mock_response_code = 200; s_tester.mock_failure_code = 429; - s_tester.fail_operations = 2; + s_tester.fail_operations = 20; struct aws_credentials_provider *sts_provider = aws_credentials_provider_new_sts(allocator, &options); diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index 4325066f..c50237cc 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -683,26 +683,27 @@ struct aws_http_stream *aws_credentials_provider_http_mock_make_request( struct aws_allocator *allocator = credentials_provider_http_mock_tester.request_body.allocator; aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256); - aws_input_stream_read(body_stream, &credentials_provider_http_mock_tester.request_body); - + if (body_stream) { + aws_input_stream_read(body_stream, &credentials_provider_http_mock_tester.request_body); + } aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_path); struct aws_byte_cursor request_path_cursor; aws_http_message_get_request_path(options->request, &request_path_cursor); aws_byte_buf_init_copy_from_cursor( - &credentials_provider_http_mock_tester.request_body, allocator, request_path_cursor); - - aws_credentials_provider_http_mock_invoke_request_callbacks( - options, - &credentials_provider_http_mock_tester.response_data_callbacks, - credentials_provider_http_mock_tester.is_request_successful); - + &credentials_provider_http_mock_tester.request_path, allocator, request_path_cursor); credentials_provider_http_mock_tester.attempts++; + credentials_provider_http_mock_tester.request_options = *options; + return (struct aws_http_stream *)1; } int aws_credentials_provider_http_mock_stream_activate(struct aws_http_stream *stream) { (void)stream; + aws_credentials_provider_http_mock_invoke_request_callbacks( + &credentials_provider_http_mock_tester.request_options, + &credentials_provider_http_mock_tester.response_data_callbacks, + credentials_provider_http_mock_tester.is_request_successful); return AWS_OP_SUCCESS; } diff --git a/tests/credentials_provider_utils.h b/tests/credentials_provider_utils.h index 0ee05087..51d85125 100644 --- a/tests/credentials_provider_utils.h +++ b/tests/credentials_provider_utils.h @@ -104,6 +104,7 @@ struct aws_credentials_provider_http_mock_tester { struct aws_byte_buf request_path; struct aws_byte_buf request_body; + struct aws_http_make_request_options request_options; struct aws_array_list response_data_callbacks; bool is_connection_acquire_successful; From 672f88959bcd9ae59695ca3d76394d0346df80a8 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sun, 12 Mar 2023 12:04:18 -0700 Subject: [PATCH 29/93] fix expiration --- include/aws/auth/private/credentials_utils.h | 1 + source/credentials_provider_sso.c | 3 +-- source/credentials_utils.c | 15 +++++++++++++++ tests/CMakeLists.txt | 2 +- tests/credentials_provider_sso_tests.c | 10 ++++++---- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/include/aws/auth/private/credentials_utils.h b/include/aws/auth/private/credentials_utils.h index 46a7ff5c..590082cc 100644 --- a/include/aws/auth/private/credentials_utils.h +++ b/include/aws/auth/private/credentials_utils.h @@ -73,6 +73,7 @@ struct aws_auth_http_system_vtable { enum aws_parse_credentials_expiration_format { AWS_PCEF_STRING_ISO_8601_DATE, AWS_PCEF_NUMBER_UNIX_EPOCH, + AWS_PCEF_NUMBER_UNIX_EPOCH_MS, }; struct aws_parse_credentials_from_json_doc_options { diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index efb119f7..9292f4d3 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -155,9 +155,8 @@ static void s_finalize_get_credentials_query(struct sso_user_data *user_data) { .top_level_object_name = "roleCredentials", .token_required = true, .expiration_required = true, - .expiration_format = AWS_PCEF_NUMBER_UNIX_EPOCH, + .expiration_format = AWS_PCEF_NUMBER_UNIX_EPOCH_MS, }; - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "waahm7:%s", user_data->payload.buffer); credentials = aws_parse_credentials_from_json_document( user_data->allocator, (const char *)user_data->payload.buffer, &parse_options); diff --git a/source/credentials_utils.c b/source/credentials_utils.c index d3a582ea..a23fe184 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -128,6 +129,20 @@ static bool s_parse_expiration_value_from_json_object( return true; } + case AWS_PCEF_NUMBER_UNIX_EPOCH_MS: { + double expiration_value_ms = 0; + if (aws_json_value_get_number(value, &expiration_value_ms)) { + AWS_LOGF_INFO( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "Unabled to extract credentials Expiration field from Json document."); + return false; + } + + *expiration_timepoint_in_seconds = + (uint64_t)aws_timestamp_convert(expiration_value_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_SECS, NULL); + return true; + } + default: return false; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c5d744d5..2a3792a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -114,7 +114,7 @@ add_net_test_case(parse_sso_token_valid) add_net_test_case(credentials_provider_sso_new_destroy) add_net_test_case(credentials_provider_sso_new_destroy_from_profile_config) add_net_test_case(credentials_provider_sso_new_destroy_from_sso_session_config) -add_net_test_case(credentials_provider_sso_failed_without_config) +add_net_test_case(credentials_provider_sso_failed_invalid_config) add_net_test_case(credentials_provider_sso_connect_failure) add_net_test_case(credentials_provider_sso_token_failure) add_net_test_case(credentials_provider_sso_request_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 48702c2f..573af3c5 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -222,7 +222,7 @@ AWS_TEST_CASE( credentials_provider_sso_new_destroy_from_sso_session_config, s_credentials_provider_sso_new_destroy_from_sso_session_config); -static int s_credentials_provider_sso_failed_without_config(struct aws_allocator *allocator, void *ctx) { +static int s_credentials_provider_sso_failed_invalid_config(struct aws_allocator *allocator, void *ctx) { (void)ctx; aws_credentials_provider_http_mock_tester_init(allocator); @@ -249,7 +249,7 @@ static int s_credentials_provider_sso_failed_without_config(struct aws_allocator return 0; } -AWS_TEST_CASE(credentials_provider_sso_failed_without_config, s_credentials_provider_sso_failed_without_config); +AWS_TEST_CASE(credentials_provider_sso_failed_invalid_config, s_credentials_provider_sso_failed_invalid_config); AWS_STATIC_STRING_FROM_LITERAL( s_expected_sso_request_path, @@ -262,8 +262,7 @@ AWS_STATIC_STRING_FROM_LITERAL( 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, "SuccessfulToken"); -AWS_STATIC_STRING_FROM_LITERAL(s_good_response_expiration, "2020-02-25T06:03:31Z"); - +static int s_good_response_expiration = 1678574216; static int s_verify_credentials(bool request_made, bool got_credentials, int expected_attempts) { if (request_made) { @@ -282,6 +281,9 @@ static int s_verify_credentials(bool request_made, bool got_credentials, int exp s_good_secret_access_key); ASSERT_CURSOR_VALUE_STRING_EQUALS( aws_credentials_get_session_token(credentials_provider_http_mock_tester.credentials), s_good_session_token); + ASSERT_INT_EQUALS( + aws_credentials_get_expiration_timepoint_seconds(credentials_provider_http_mock_tester.credentials), + s_good_response_expiration); } else { ASSERT_TRUE(credentials_provider_http_mock_tester.credentials == NULL); } From 46d8a266bfb0cf0a198ff6e92ff00c8dde482f8d Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sun, 12 Mar 2023 12:39:08 -0700 Subject: [PATCH 30/93] adds invalid config examples --- source/token_provider_sso_profile.c | 10 ++- tests/credentials_provider_sso_tests.c | 108 +++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 10 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index d046b683..038cb957 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -149,7 +149,11 @@ static struct aws_string *s_construct_profile_token_path( struct aws_credentials_provider *aws_token_provider_new_sso_profile( struct aws_allocator *allocator, const struct aws_token_provider_sso_profile_options *options) { - + struct aws_string *token_path = + s_construct_profile_token_path(allocator, options->profile_name_override, options->config_file_name_override); + if (!token_path) { + return NULL; + } struct aws_credentials_provider *provider = NULL; struct aws_token_provider_profile_impl *impl = NULL; @@ -163,9 +167,9 @@ struct aws_credentials_provider *aws_token_provider_new_sso_profile( AWS_ZERO_STRUCT(*provider); AWS_ZERO_STRUCT(*impl); aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_profile_vtable, impl); - impl->token_file_path = - s_construct_profile_token_path(allocator, options->profile_name_override, options->config_file_name_override); + impl->token_file_path = aws_string_new_from_string(allocator, token_path); provider->shutdown_options = options->shutdown_options; + aws_string_destroy(token_path); return provider; } diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 573af3c5..047eae9b 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -225,11 +225,99 @@ AWS_TEST_CASE( static int s_credentials_provider_sso_failed_invalid_config(struct aws_allocator *allocator, void *ctx) { (void)ctx; + const struct { + const char *name; + const char *text; + } invalid_config_examples[] = { + {"empty", ""}, + + {"profile without any sso config", "[profile sso]\naccessKey=access"}, + + {"profile without role_name", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n"}, + + {"profile without account_id", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_role_name=roleName\n"}, + + {"profile without region", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n"}, + + {"profile without start_url", + "[profile sso]\n" + "accessKey=access\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n"}, + + {"profile with invalid session", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n"}, + + {"session without start_url", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_region = us-west-2\n"}, + + {"session without region", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n"}, + + {"session with different region", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-east-1\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"}, + + {"session with different start-url", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-321.awsapps.com/start\n" + "sso_region = us-west-2\n"}, + }; + aws_credentials_provider_http_mock_tester_init(allocator); - struct aws_string *empty_content = aws_string_new_from_c_str(allocator, ""); - ASSERT_TRUE(empty_content != NULL); - s_aws_credentials_provider_sso_test_init_config_profile(allocator, empty_content); - aws_string_destroy(empty_content); struct aws_credentials_provider_sso_options options = { .bootstrap = credentials_provider_http_mock_tester.bootstrap, @@ -241,9 +329,15 @@ static int s_credentials_provider_sso_failed_invalid_config(struct aws_allocator .shutdown_user_data = NULL, }, }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NULL(provider); + for (int i = 0; i < AWS_ARRAY_SIZE(invalid_config_examples); i++) { + printf("invalid config example [%d]: %s\n", i, invalid_config_examples[i].name); + struct aws_string *content = aws_string_new_from_c_str(allocator, invalid_config_examples[i].text); + ASSERT_TRUE(content != NULL); + s_aws_credentials_provider_sso_test_init_config_profile(allocator, content); + aws_string_destroy(content); + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NULL(provider); + } aws_credentials_provider_http_mock_tester_cleanup(); From 828cf2c406dbde5550f8005f32efbe4859523220 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sun, 12 Mar 2023 13:20:32 -0700 Subject: [PATCH 31/93] cleanup tests --- tests/CMakeLists.txt | 4 +- tests/credentials_provider_sso_tests.c | 227 ++++++++----------------- tests/credentials_provider_sts_tests.c | 2 +- tests/token_provider_sso_tests.c | 27 +-- 4 files changed, 81 insertions(+), 179 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2a3792a3..4bfb450c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -111,10 +111,8 @@ add_net_test_case(parse_token_location_url_test) add_net_test_case(parse_token_location_session_test) add_net_test_case(parse_sso_token_valid) -add_net_test_case(credentials_provider_sso_new_destroy) -add_net_test_case(credentials_provider_sso_new_destroy_from_profile_config) -add_net_test_case(credentials_provider_sso_new_destroy_from_sso_session_config) add_net_test_case(credentials_provider_sso_failed_invalid_config) +add_net_test_case(credentials_provider_sso_create_destroy_valid_config) add_net_test_case(credentials_provider_sso_connect_failure) add_net_test_case(credentials_provider_sso_token_failure) add_net_test_case(credentials_provider_sso_request_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 047eae9b..616eeec3 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -27,75 +27,6 @@ #include #include -static bool received_callback = false; -static struct aws_mutex lock; -static struct aws_condition_variable tester_signal; -static bool s_has_tester_received_credentials_callback(void *user_data) { - (void)user_data; - - return received_callback; -} -static void s_get_credentials_callback(struct aws_credentials *credentials, int error_code, void *user_data) { - (void)user_data; - (void)credentials; - printf("credentials callback, %d", error_code); - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "waahm7 callback %d", error_code); - - received_callback = true; - AWS_FATAL_ASSERT(credentials != NULL); - aws_condition_variable_notify_one(&tester_signal); -} - -static int s_credentials_provider_sso_new_destroy(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - aws_auth_library_init(allocator); - struct aws_credentials_provider_sso_options options; - AWS_ZERO_STRUCT(options); - options.profile_name_override = aws_byte_cursor_from_c_str("AdministratorAccess-069542832437"); - - struct aws_tls_ctx_options tls_options; - aws_tls_ctx_options_init_default_client(&tls_options, allocator); - options.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_options); - - struct aws_event_loop_group *el_group = aws_event_loop_group_new_default(allocator, 0, 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, - }; - options.bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - aws_credentials_provider_get_credentials(provider, s_get_credentials_callback, NULL); - - if (aws_mutex_init(&lock)) { - return AWS_OP_ERR; - } - - if (aws_condition_variable_init(&tester_signal)) { - return AWS_OP_ERR; - } - - aws_condition_variable_wait_pred(&tester_signal, &lock, s_has_tester_received_credentials_callback, NULL); - - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "waahm7 releasing"); - - aws_credentials_provider_release(provider); - aws_event_loop_group_release(el_group); - aws_host_resolver_release(resolver); - aws_client_bootstrap_release(options.bootstrap); - aws_tls_ctx_release(options.tls_ctx); - aws_auth_library_clean_up(); - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_new_destroy, s_credentials_provider_sso_new_destroy); - AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile, "sso"); static int s_aws_credentials_provider_sso_test_init_config_profile( struct aws_allocator *allocator, @@ -138,90 +69,6 @@ AWS_STATIC_STRING_FROM_LITERAL( "sso_start_url = https://d-123.awsapps.com/start\n" "sso_region = us-west-2\n"); -static int s_credentials_provider_sso_new_destroy_from_profile_config(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_profile_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - return 0; -} -AWS_TEST_CASE( - credentials_provider_sso_new_destroy_from_profile_config, - s_credentials_provider_sso_new_destroy_from_profile_config); - -static int s_credentials_provider_sso_new_destroy_from_sso_session_config(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - return 0; -} -AWS_TEST_CASE( - credentials_provider_sso_new_destroy_from_sso_session_config, - s_credentials_provider_sso_new_destroy_from_sso_session_config); - static int s_credentials_provider_sso_failed_invalid_config(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -345,6 +192,77 @@ static int s_credentials_provider_sso_failed_invalid_config(struct aws_allocator } AWS_TEST_CASE(credentials_provider_sso_failed_invalid_config, s_credentials_provider_sso_failed_invalid_config); +static int s_credentials_provider_sso_create_destroy_valid_config(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + const struct { + const char *name; + const char *text; + } valid_config_examples[] = { + + {"profile", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_account_id=123\n" + "sso_region=us-west-2\n" + "sso_role_name=roleName\n"}, + + {"session", + "[profile sso]\n" + "accessKey=access\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"}, + + {"session with profile", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"}, + + }; + + aws_credentials_provider_http_mock_tester_init(allocator); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + for (int i = 0; i < AWS_ARRAY_SIZE(valid_config_examples); i++) { + printf("valid config example [%d]: %s\n", i, valid_config_examples[i].name); + struct aws_string *content = aws_string_new_from_c_str(allocator, valid_config_examples[i].text); + ASSERT_TRUE(content != NULL); + s_aws_credentials_provider_sso_test_init_config_profile(allocator, content); + aws_string_destroy(content); + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + aws_credentials_provider_release(provider); + } + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE( + credentials_provider_sso_create_destroy_valid_config, + s_credentials_provider_sso_create_destroy_valid_config); + AWS_STATIC_STRING_FROM_LITERAL( s_expected_sso_request_path, "/federation/credentials?account_id=123&role_name=roleName"); @@ -520,6 +438,7 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo aws_credentials_provider_http_mock_tester_init(allocator); credentials_provider_http_mock_tester.is_request_successful = false; + credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_400_BAD_REQUEST; struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); /* redirect $HOME */ @@ -562,7 +481,7 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo aws_credentials_provider_http_mock_wait_for_credentials_result(); - ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 4 /*expected attempts*/)); + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 1 /*expected attempts*/)); aws_credentials_provider_release(provider); diff --git a/tests/credentials_provider_sts_tests.c b/tests/credentials_provider_sts_tests.c index 9fa6412a..f1ab19a3 100644 --- a/tests/credentials_provider_sts_tests.c +++ b/tests/credentials_provider_sts_tests.c @@ -448,7 +448,7 @@ static int s_credentials_provider_sts_direct_config_succeeds_after_retry_fn( s_tester.mock_body = aws_byte_buf_from_c_str(success_creds_doc); s_tester.mock_response_code = 200; s_tester.mock_failure_code = 429; - s_tester.fail_operations = 20; + s_tester.fail_operations = 2; struct aws_credentials_provider *sts_provider = aws_credentials_provider_new_sts(allocator, &options); diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index bfd1487c..cefe82fe 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -25,18 +25,12 @@ struct sso_session_profile_example { static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocator *allocator, void *ctx) { (void)ctx; - static struct sso_session_profile_example s_invalid_profile_examples[] = { + const struct sso_session_profile_example invalid_profile_examples[] = { { .name = "No config", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( "[profile default]\naws_access_key_id=fake_access_key\naws_secret_access_key=fake_secret_key\n"), }, - { - .name = "No sso-region", - .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " - "default]\naws_access_key_id=fake_access_key\naws_secret_" - "access_key=fake_secret_key\nsso_start_url=url\n"), - }, { .name = "No sso_start_url", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " @@ -52,17 +46,14 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato }, }; - aws_unset_environment_value(s_default_profile_env_variable_name); - aws_unset_environment_value(s_default_config_path_env_variable_name); - aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_token_provider_sso_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), }; - for (size_t i = 0; i < AWS_ARRAY_SIZE(s_invalid_profile_examples); ++i) { - printf("invalid example [%zu]: %s\n", i, s_invalid_profile_examples[i].name); - struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_invalid_profile_examples[i].text); + for (size_t i = 0; i < AWS_ARRAY_SIZE(invalid_profile_examples); ++i) { + printf("invalid example [%zu]: %s\n", i, invalid_profile_examples[i].name); + struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &invalid_profile_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); ASSERT_NULL(aws_token_provider_new_sso_profile(allocator, &options)); aws_string_destroy(config_contents); @@ -154,9 +145,6 @@ static int s_sso_token_provider_sso_session_invalid_profile_test(struct aws_allo }, }; - aws_unset_environment_value(s_default_profile_env_variable_name); - aws_unset_environment_value(s_default_config_path_env_variable_name); - aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), @@ -190,14 +178,14 @@ static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_alloca "1\nsso_start_url=url"), }, { - .name = "with nsso_region", + .name = "with sso_region", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_region=us-east-1\nsso_" "session=dev\n[sso-session dev]\nsso_region=us-east-" "1\nsso_start_url=url"), }, { - .name = "with nsso_start_url", + .name = "with sso_start_url", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_start_url=url\nsso_" "session=dev\n[sso-session dev]\nsso_region=us-east-" @@ -213,9 +201,6 @@ static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_alloca }, }; - aws_unset_environment_value(s_default_profile_env_variable_name); - aws_unset_environment_value(s_default_config_path_env_variable_name); - aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), From 65688b0af780e1264362352ee955679838767588 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sun, 12 Mar 2023 13:36:35 -0700 Subject: [PATCH 32/93] fix token provider tests --- tests/CMakeLists.txt | 4 +- tests/credentials_provider_utils.c | 6 +- tests/token_provider_sso_tests.c | 89 +++++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4bfb450c..f95ab37f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -104,8 +104,8 @@ endif() add_net_test_case(sso_token_provider_profile_invalid_profile_test) add_net_test_case(sso_token_provider_profile_valid_profile_test) -add_net_test_case(sso_token_provider_sso_session_invalid_profile_test) -add_net_test_case(sso_token_provider_sso_session_valid_profile_test) +add_net_test_case(sso_token_provider_sso_session_invalid_config_test) +add_net_test_case(sso_token_provider_sso_session_valid_config_test) add_net_test_case(parse_token_location_url_test) add_net_test_case(parse_token_location_session_test) diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index c50237cc..02685bfd 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -497,9 +497,9 @@ int aws_credentials_provider_http_mock_tester_init(struct aws_allocator *allocat AWS_ZERO_STRUCT(credentials_provider_http_mock_tester); - struct aws_tls_ctx_options tls_options; - aws_tls_ctx_options_init_default_client(&tls_options, allocator); - credentials_provider_http_mock_tester.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_options); + struct aws_tls_ctx_options tls_ctx_options; + aws_tls_ctx_options_init_default_client(&tls_ctx_options, allocator); + credentials_provider_http_mock_tester.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_options); ASSERT_NOT_NULL(credentials_provider_http_mock_tester.tls_ctx); credentials_provider_http_mock_tester.el_group = aws_event_loop_group_new_default(allocator, 0, NULL); diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index cefe82fe..61cc69dc 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #include @@ -81,13 +84,10 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator "[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_region=us-east-1\nsso_start_url=url\nsso_" "session=dev\n[sso-session dev]\nsso_region=us-east-" - "1\nsso_start_url=url"), + "1\nsso_start_url=url2"), }, }; - aws_unset_environment_value(s_default_profile_env_variable_name); - aws_unset_environment_value(s_default_config_path_env_variable_name); - aws_unset_environment_value(s_default_credentials_path_env_variable_name); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_token_provider_sso_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), @@ -109,10 +109,64 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator AWS_TEST_CASE(sso_token_provider_profile_valid_profile_test, s_sso_token_provider_profile_valid_profile_test); -static int s_sso_token_provider_sso_session_invalid_profile_test(struct aws_allocator *allocator, void *ctx) { - (void)ctx; +static struct aws_mock_token_provider_sso_tester { + struct aws_tls_ctx *tls_ctx; + struct aws_event_loop_group *el_group; + struct aws_host_resolver *resolver; + struct aws_client_bootstrap *bootstrap; + + struct aws_mutex lock; + struct aws_condition_variable signal; + struct aws_credentials *credentials; + bool has_received_credentials_callback; + bool has_received_shutdown_callback; + int error_code; + +} s_tester; + +static int s_aws_mock_token_provider_sso_tester_init(struct aws_allocator *allocator) { + aws_auth_library_init(allocator); + + AWS_ZERO_STRUCT(s_tester); + + struct aws_tls_ctx_options tls_ctx_options; + aws_tls_ctx_options_init_default_client(&tls_ctx_options, allocator); + s_tester.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_options); + ASSERT_NOT_NULL(s_tester.tls_ctx); + + s_tester.el_group = aws_event_loop_group_new_default(allocator, 0, NULL); + + struct aws_host_resolver_default_options resolver_options = { + .el_group = s_tester.el_group, + .max_entries = 8, + }; + s_tester.resolver = aws_host_resolver_new_default(allocator, &resolver_options); - static struct sso_session_profile_example s_invalid_profile_examples[] = { + struct aws_client_bootstrap_options bootstrap_options = { + .event_loop_group = s_tester.el_group, + .host_resolver = s_tester.resolver, + }; + s_tester.bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + + return AWS_OP_SUCCESS; +} + +void s_aws_mock_token_provider_sso_tester_cleanup(void) { + aws_tls_ctx_release(s_tester.tls_ctx); + aws_client_bootstrap_release(s_tester.bootstrap); + aws_host_resolver_release(s_tester.resolver); + aws_event_loop_group_release(s_tester.el_group); + + aws_condition_variable_clean_up(&s_tester.signal); + aws_mutex_clean_up(&s_tester.lock); + aws_credentials_release(s_tester.credentials); + aws_auth_library_clean_up(); +} + +static int s_sso_token_provider_sso_session_invalid_config_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + s_aws_mock_token_provider_sso_tester_init(allocator); + const struct sso_session_profile_example invalid_config_examples[] = { { .name = "no sso-session", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" @@ -148,26 +202,28 @@ static int s_sso_token_provider_sso_session_invalid_profile_test(struct aws_allo struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .tls_ctx = s_tester.tls_ctx, + .bootstrap = s_tester.bootstrap, }; - for (size_t i = 0; i < AWS_ARRAY_SIZE(s_invalid_profile_examples); ++i) { - printf("invalid example [%zu]: %s\n", i, s_invalid_profile_examples[i].name); - struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &s_invalid_profile_examples[i].text); + for (size_t i = 0; i < AWS_ARRAY_SIZE(invalid_config_examples); ++i) { + printf("invalid example [%zu]: %s\n", i, invalid_config_examples[i].name); + struct aws_string *config_contents = aws_string_new_from_cursor(allocator, &invalid_config_examples[i].text); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, config_contents)); ASSERT_NULL(aws_token_provider_new_sso_session(allocator, &options)); aws_string_destroy(config_contents); } aws_string_destroy(config_file_str); + s_aws_mock_token_provider_sso_tester_cleanup(); return AWS_OP_SUCCESS; } -AWS_TEST_CASE( - sso_token_provider_sso_session_invalid_profile_test, - s_sso_token_provider_sso_session_invalid_profile_test); +AWS_TEST_CASE(sso_token_provider_sso_session_invalid_config_test, s_sso_token_provider_sso_session_invalid_config_test); -static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_allocator *allocator, void *ctx) { +static int s_sso_token_provider_sso_session_valid_config_test(struct aws_allocator *allocator, void *ctx) { (void)ctx; + s_aws_mock_token_provider_sso_tester_init(allocator); static struct sso_session_profile_example s_valid_profile_examples[] = { { @@ -204,6 +260,8 @@ static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_alloca struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .tls_ctx = s_tester.tls_ctx, + .bootstrap = s_tester.bootstrap, }; for (size_t i = 0; i < AWS_ARRAY_SIZE(s_valid_profile_examples); ++i) { @@ -217,7 +275,8 @@ static int s_sso_token_provider_sso_session_valid_profile_test(struct aws_alloca } aws_string_destroy(config_file_str); + s_aws_mock_token_provider_sso_tester_cleanup(); return AWS_OP_SUCCESS; } -AWS_TEST_CASE(sso_token_provider_sso_session_valid_profile_test, s_sso_token_provider_sso_session_valid_profile_test); +AWS_TEST_CASE(sso_token_provider_sso_session_valid_config_test, s_sso_token_provider_sso_session_valid_config_test); From 66657d96b01d831fe40b1f3bab3a84b7d8d1bbeb Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sun, 12 Mar 2023 16:07:27 -0700 Subject: [PATCH 33/93] adds token provider tests --- tests/CMakeLists.txt | 4 + tests/credentials_provider_sso_tests.c | 39 +--- tests/shared_credentials_test_definitions.h | 25 +++ tests/token_provider_sso_tests.c | 224 ++++++++++++++++++++ 4 files changed, 259 insertions(+), 33 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f95ab37f..56c9fc97 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -106,6 +106,10 @@ add_net_test_case(sso_token_provider_profile_invalid_profile_test) add_net_test_case(sso_token_provider_profile_valid_profile_test) add_net_test_case(sso_token_provider_sso_session_invalid_config_test) add_net_test_case(sso_token_provider_sso_session_valid_config_test) +add_net_test_case(sso_token_provider_sso_session_basic_success) +add_net_test_case(sso_token_provider_sso_session_expired_token) +add_net_test_case(sso_token_provider_profile_basic_success) +add_net_test_case(sso_token_provider_profile_expired_token) add_net_test_case(parse_token_location_url_test) add_net_test_case(parse_token_location_session_test) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 616eeec3..f7e1f5ce 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -44,7 +44,6 @@ static int s_aws_credentials_provider_sso_test_init_config_profile( aws_string_destroy(config_file_path_str); return AWS_OP_SUCCESS; } -// TODO: add config tests /* start_url should be same in `s_sso_profile_start_url` and `s_sso_profile_config_contents` */ AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile_start_url, "https://d-123.awsapps.com/start"); @@ -401,37 +400,12 @@ static int s_credentials_provider_sso_token_failure(struct aws_allocator *alloca } AWS_TEST_CASE(credentials_provider_sso_token_failure, s_credentials_provider_sso_token_failure); -/** - * Create the directory components of @path: - * - if @path ends in a path separator, create every directory component; - * - else, stop at the last path separator (parent directory of @path). - */ -static int s_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path) { - const char local_platform_separator = aws_get_platform_directory_separator(); - - /* Create directory components and ensure use of platform separator at the same time. */ - for (size_t i = 0; i < path->len; ++i) { - if (aws_is_any_directory_separator((char)path->bytes[i])) { - ((char *)path->bytes)[i] = local_platform_separator; - - struct aws_string *segment = aws_string_new_from_array(allocator, path->bytes, i); - int rc = aws_directory_create(segment); - aws_string_destroy(segment); - - if (rc != AWS_OP_SUCCESS) { - return rc; - } - } - } - return AWS_OP_SUCCESS; -} - AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); AWS_STATIC_STRING_FROM_LITERAL( s_valid_sso_token, - "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2030-03-12T05:35:19Z\"}"); + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2099-03-12T05:35:19Z\"}"); static int s_credentials_provider_sso_request_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -447,8 +421,7 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo /* create token file */ struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - - ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; @@ -511,7 +484,7 @@ static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocat struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; @@ -576,7 +549,7 @@ static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allo struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; @@ -640,7 +613,7 @@ static int s_credentials_provider_sso_basic_success(struct aws_allocator *alloca struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; @@ -705,7 +678,7 @@ static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; diff --git a/tests/shared_credentials_test_definitions.h b/tests/shared_credentials_test_definitions.h index 8662e429..688ce69f 100644 --- a/tests/shared_credentials_test_definitions.h +++ b/tests/shared_credentials_test_definitions.h @@ -66,6 +66,31 @@ static int aws_create_profile_file(const struct aws_string *file_name, const str return AWS_OP_SUCCESS; } +/** + * Create the directory components of @path: + * - if @path ends in a path separator, create every directory component; + * - else, stop at the last path separator (parent directory of @path). + */ +static int s_aws_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path) { + const char local_platform_separator = aws_get_platform_directory_separator(); + + /* Create directory components and ensure use of platform separator at the same time. */ + for (size_t i = 0; i < path->len; ++i) { + if (aws_is_any_directory_separator((char)path->bytes[i])) { + ((char *)path->bytes)[i] = local_platform_separator; + + struct aws_string *segment = aws_string_new_from_array(allocator, path->bytes, i); + int rc = aws_directory_create(segment); + aws_string_destroy(segment); + + if (rc != AWS_OP_SUCCESS) { + return rc; + } + } + } + return AWS_OP_SUCCESS; +} + #ifdef _MSC_VER # pragma warning(pop) #endif diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 61cc69dc..d62e9ece 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -280,3 +281,226 @@ static int s_sso_token_provider_sso_session_valid_config_test(struct aws_allocat } AWS_TEST_CASE(sso_token_provider_sso_session_valid_config_test, s_sso_token_provider_sso_session_valid_config_test); + +/* start_url should be same in `s_sso_profile_start_url` and `s_sso_profile_config_contents` */ +AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile_start_url, "https://d-123.awsapps.com/start"); +AWS_STATIC_STRING_FROM_LITERAL( + s_sso_profile_config_contents, + "[default]\n" + "sso_start_url = https://d-123.awsapps.com/start\n"); +/* session name should be same in both `s_sso_session_name` and `s_sso_session_config_contents`*/ +AWS_STATIC_STRING_FROM_LITERAL(s_sso_session_name, "session"); +AWS_STATIC_STRING_FROM_LITERAL( + s_sso_session_config_contents, + "[default]\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"); +AWS_STATIC_STRING_FROM_LITERAL( + s_valid_sso_token, + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2030-03-12T05:35:19Z\"}"); +AWS_STATIC_STRING_FROM_LITERAL( + s_expired_sso_token, + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); +AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); +AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); +AWS_STATIC_STRING_FROM_LITERAL(s_good_token, "ValidAccessToken"); + +static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + s_aws_mock_token_provider_sso_tester_init(allocator); + + /* redirect $HOME */ + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_session_config_contents)); + + struct aws_token_provider_sso_session_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .tls_ctx = s_tester.tls_ctx, + .bootstrap = s_tester.bootstrap, + }; + + struct aws_credentials_provider *provider = aws_token_provider_new_sso_session(allocator, &options); + ASSERT_NOT_NULL(provider); + + struct aws_get_credentials_test_callback_result callback_results; + aws_get_credentials_test_callback_result_init(&callback_results, 1); + int get_async_result = + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); + ASSERT_TRUE(get_async_result == AWS_OP_SUCCESS); + aws_wait_on_credentials_callback(&callback_results); + + ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); + + aws_get_credentials_test_callback_result_clean_up(&callback_results); + aws_credentials_provider_release(provider); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + + aws_string_destroy(token_path); + aws_string_destroy(config_file_str); + s_aws_mock_token_provider_sso_tester_cleanup(); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_sso_session_basic_success, s_sso_token_provider_sso_session_basic_success); + +static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + s_aws_mock_token_provider_sso_tester_init(allocator); + + /* redirect $HOME */ + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); + + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_session_config_contents)); + + struct aws_token_provider_sso_session_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .tls_ctx = s_tester.tls_ctx, + .bootstrap = s_tester.bootstrap, + }; + + struct aws_credentials_provider *provider = aws_token_provider_new_sso_session(allocator, &options); + ASSERT_NOT_NULL(provider); + + struct aws_get_credentials_test_callback_result callback_results; + aws_get_credentials_test_callback_result_init(&callback_results, 1); + int get_async_result = + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); + ASSERT_TRUE(get_async_result == AWS_OP_SUCCESS); + aws_wait_on_credentials_callback(&callback_results); + + ASSERT_NULL(callback_results.credentials); + ASSERT_INT_EQUALS(callback_results.last_error, AWS_AUTH_SSO_TOKEN_EXPIRED); + + aws_get_credentials_test_callback_result_clean_up(&callback_results); + aws_credentials_provider_release(provider); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + + aws_string_destroy(token_path); + aws_string_destroy(config_file_str); + s_aws_mock_token_provider_sso_tester_cleanup(); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_sso_session_expired_token, s_sso_token_provider_sso_session_expired_token); + +static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + s_aws_mock_token_provider_sso_tester_init(allocator); + + /* redirect $HOME */ + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_profile_config_contents)); + + struct aws_token_provider_sso_profile_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + }; + + struct aws_credentials_provider *provider = aws_token_provider_new_sso_profile(allocator, &options); + ASSERT_NOT_NULL(provider); + + struct aws_get_credentials_test_callback_result callback_results; + aws_get_credentials_test_callback_result_init(&callback_results, 1); + int get_async_result = + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); + ASSERT_TRUE(get_async_result == AWS_OP_SUCCESS); + aws_wait_on_credentials_callback(&callback_results); + + ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); + + aws_get_credentials_test_callback_result_clean_up(&callback_results); + aws_credentials_provider_release(provider); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + + aws_string_destroy(token_path); + aws_string_destroy(config_file_str); + s_aws_mock_token_provider_sso_tester_cleanup(); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_profile_basic_success, s_sso_token_provider_profile_basic_success); + +static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + s_aws_mock_token_provider_sso_tester_init(allocator); + + /* redirect $HOME */ + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); + + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_profile_config_contents)); + + struct aws_token_provider_sso_profile_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + }; + + struct aws_credentials_provider *provider = aws_token_provider_new_sso_profile(allocator, &options); + ASSERT_NOT_NULL(provider); + + struct aws_get_credentials_test_callback_result callback_results; + aws_get_credentials_test_callback_result_init(&callback_results, 1); + int get_async_result = + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); + ASSERT_TRUE(get_async_result == AWS_OP_SUCCESS); + aws_wait_on_credentials_callback(&callback_results); + + ASSERT_NULL(callback_results.credentials); + ASSERT_INT_EQUALS(callback_results.last_error, AWS_AUTH_SSO_TOKEN_EXPIRED); + + aws_get_credentials_test_callback_result_clean_up(&callback_results); + aws_credentials_provider_release(provider); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + + aws_string_destroy(token_path); + aws_string_destroy(config_file_str); + s_aws_mock_token_provider_sso_tester_cleanup(); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_profile_expired_token, s_sso_token_provider_profile_expired_token); From bfe3e180705a13a136a1308e881e3c72899197a0 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Sun, 12 Mar 2023 16:27:01 -0700 Subject: [PATCH 34/93] test token expiration --- tests/token_provider_sso_tests.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index d62e9ece..9df45382 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -299,14 +299,14 @@ AWS_STATIC_STRING_FROM_LITERAL( "sso_region = us-west-2\n"); AWS_STATIC_STRING_FROM_LITERAL( s_valid_sso_token, - "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2030-03-12T05:35:19Z\"}"); + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2099-03-12T05:35:19Z\"}"); AWS_STATIC_STRING_FROM_LITERAL( s_expired_sso_token, "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); AWS_STATIC_STRING_FROM_LITERAL(s_good_token, "ValidAccessToken"); - +static uint64_t s_good_token_expiration = 4076976919; static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_mock_token_provider_sso_tester_init(allocator); @@ -341,6 +341,8 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * aws_wait_on_credentials_callback(&callback_results); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); + ASSERT_INT_EQUALS( + aws_credentials_get_expiration_timepoint_seconds(callback_results.credentials), s_good_token_expiration); aws_get_credentials_test_callback_result_clean_up(&callback_results); aws_credentials_provider_release(provider); @@ -440,6 +442,8 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo aws_wait_on_credentials_callback(&callback_results); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); + ASSERT_INT_EQUALS( + aws_credentials_get_expiration_timepoint_seconds(callback_results.credentials), s_good_token_expiration); aws_get_credentials_test_callback_result_clean_up(&callback_results); aws_credentials_provider_release(provider); From 3d0fc7845eaa0b9d336ff562ba207f845ffbb123 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 13 Mar 2023 09:50:16 -0700 Subject: [PATCH 35/93] refactor --- include/aws/auth/credentials.h | 22 ++++++++++++++++------ source/credentials.c | 34 +++++++++++++++++----------------- tests/sso_token_util_tests.c | 6 +----- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index f5cd928c..95c85e10 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -340,6 +340,12 @@ struct aws_credentials_provider_sts_web_identity_options { struct aws_auth_http_system_vtable *function_table; }; +/* + * Configuration for the SSOCredentialsProvider that sends a GetRoleCredentialsRequest to the AWS Single + * Sign-On Service to maintain short-lived sessions to use for authentication. + * + * https://docs.aws.amazon.com/sdkref/latest/guide/feature-sso-credentials.html + */ struct aws_credentials_provider_sso_options { struct aws_credentials_provider_shutdown_options shutdown_options; @@ -355,11 +361,12 @@ struct aws_credentials_provider_sso_options { /* * Connection bootstrap to use for any network connections made while sourcing credentials + * Required. */ struct aws_client_bootstrap *bootstrap; /* - * Client TLS context to use when querying STS web identity provider. + * Client TLS context to use when querying SSO provider. * Required. */ struct aws_tls_ctx *tls_ctx; @@ -554,8 +561,8 @@ struct aws_credentials_provider_cognito_options { }; /** - * Configuration options for a provider that sources sso token information from the aws profile (by default - * ~/.aws/profile) and token from ~/.aws/sso/cache/.json. + * Configuration options for a provider that sources sso token information from the aws profile (by default + * ~/.aws/profile) and token from ~/.aws/sso/cache/.json. */ struct aws_token_provider_sso_profile_options { struct aws_credentials_provider_shutdown_options shutdown_options; @@ -571,6 +578,10 @@ struct aws_token_provider_sso_profile_options { struct aws_byte_cursor config_file_name_override; }; +/** + * Configuration options for a provider that sources sso token information from the aws profile (by default + * ~/.aws/profile) and token from ~/.aws/sso/cache/.json. + */ struct aws_token_provider_sso_session_options { struct aws_credentials_provider_shutdown_options shutdown_options; @@ -585,13 +596,12 @@ struct aws_token_provider_sso_session_options { struct aws_byte_cursor config_file_name_override; /* - * Connection bootstrap to use for any network connections made while sourcing credentials + * Connection bootstrap to use for any network connections made */ struct aws_client_bootstrap *bootstrap; /* - * Client TLS context to use when querying STS web identity provider. - * Required. + * Client TLS context to use for any network connections made. */ struct aws_tls_ctx *tls_ctx; }; diff --git a/source/credentials.c b/source/credentials.c index 026c170d..0cae0491 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -26,7 +26,7 @@ struct aws_token_identity { }; enum IdentityType { - CREDENTIALS_IDENTITY, + AWS_CREDENTIALS_IDENTITY, TOKEN_IDENTITY, ANONYMOUS_IDENTITY, ECC_IDENTITY, @@ -69,7 +69,7 @@ struct aws_credentials { */ uint64_t expiration_timepoint_seconds; - enum IdentityType type; + enum IdentityType identity_type; union { struct aws_credentials_identity credentials; struct aws_token_identity token; @@ -106,7 +106,7 @@ struct aws_credentials *aws_credentials_new( credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); - credentials->type = CREDENTIALS_IDENTITY; + credentials->identity_type = AWS_CREDENTIALS_IDENTITY; struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials; credentials_identity->access_key_id = aws_string_new_from_array(allocator, access_key_id_cursor.ptr, access_key_id_cursor.len); @@ -144,7 +144,7 @@ struct aws_credentials *aws_credentials_new_anonymous(struct aws_allocator *allo struct aws_credentials *credentials = aws_mem_calloc(allocator, 1, sizeof(struct aws_credentials)); credentials->allocator = allocator; - credentials->type = ANONYMOUS_IDENTITY; + credentials->identity_type = ANONYMOUS_IDENTITY; aws_atomic_init_int(&credentials->ref_count, 1); credentials->expiration_timepoint_seconds = UINT64_MAX; @@ -156,8 +156,8 @@ static void s_aws_credentials_destroy(struct aws_credentials *credentials) { if (credentials == NULL) { return; } - switch (credentials->type) { - case CREDENTIALS_IDENTITY: + switch (credentials->identity_type) { + case AWS_CREDENTIALS_IDENTITY: aws_string_destroy(credentials->identity.credentials.access_key_id); aws_string_destroy_secure(credentials->identity.credentials.secret_access_key); aws_string_destroy_secure(credentials->identity.credentials.session_token); @@ -202,8 +202,8 @@ static struct aws_byte_cursor s_empty_token_cursor = { }; struct aws_byte_cursor aws_credentials_get_access_key_id(const struct aws_credentials *credentials) { - switch (credentials->type) { - case CREDENTIALS_IDENTITY: + switch (credentials->identity_type) { + case AWS_CREDENTIALS_IDENTITY: if (credentials->identity.credentials.access_key_id != NULL) { return aws_byte_cursor_from_string(credentials->identity.credentials.access_key_id); } @@ -220,8 +220,8 @@ struct aws_byte_cursor aws_credentials_get_access_key_id(const struct aws_creden } struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_credentials *credentials) { - switch (credentials->type) { - case CREDENTIALS_IDENTITY: + switch (credentials->identity_type) { + case AWS_CREDENTIALS_IDENTITY: if (credentials->identity.credentials.secret_access_key != NULL) { return aws_byte_cursor_from_string(credentials->identity.credentials.secret_access_key); } @@ -233,8 +233,8 @@ struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_cr } struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_credentials *credentials) { - switch (credentials->type) { - case CREDENTIALS_IDENTITY: + switch (credentials->identity_type) { + case AWS_CREDENTIALS_IDENTITY: if (credentials->identity.credentials.session_token != NULL) { return aws_byte_cursor_from_string(credentials->identity.credentials.session_token); } @@ -251,7 +251,7 @@ struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_creden } struct aws_byte_cursor aws_credentials_get_token(const struct aws_credentials *credentials) { - switch (credentials->type) { + switch (credentials->identity_type) { case TOKEN_IDENTITY: if (credentials->identity.token.token != NULL) { return aws_byte_cursor_from_string(credentials->identity.token.token); @@ -268,7 +268,7 @@ uint64_t aws_credentials_get_expiration_timepoint_seconds(const struct aws_crede } struct aws_ecc_key_pair *aws_credentials_get_ecc_key_pair(const struct aws_credentials *credentials) { - if (credentials->type == ECC_IDENTITY) { + if (credentials->identity_type == ECC_IDENTITY) { return credentials->identity.ecc_identity.ecc_key; } return NULL; @@ -276,7 +276,7 @@ struct aws_ecc_key_pair *aws_credentials_get_ecc_key_pair(const struct aws_crede bool aws_credentials_is_anonymous(const struct aws_credentials *credentials) { AWS_PRECONDITION(credentials); - return credentials->type == ANONYMOUS_IDENTITY; + return credentials->identity_type == ANONYMOUS_IDENTITY; } struct aws_credentials *aws_credentials_new_from_string( @@ -319,7 +319,7 @@ struct aws_credentials *aws_credentials_new_ecc( credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds; aws_atomic_init_int(&credentials->ref_count, 1); aws_ecc_key_pair_acquire(ecc_key); - credentials->type = ECC_IDENTITY; + credentials->identity_type = ECC_IDENTITY; credentials->identity.ecc_identity.ecc_key = ecc_key; credentials->identity.ecc_identity.access_key_id = @@ -379,7 +379,7 @@ struct aws_credentials *aws_credentials_new_token( credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); - credentials->type = TOKEN_IDENTITY; + credentials->identity_type = TOKEN_IDENTITY; struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials; credentials_identity->access_key_id = aws_string_new_from_array(allocator, token.ptr, token.len); credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds; diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index 69f593a9..a9062787 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -7,8 +7,6 @@ #include "shared_credentials_test_definitions.h" #include -#include -#include static int s_parse_token_location_url_test(struct aws_allocator *allocator, void *ctx) { struct aws_string *start_url = aws_string_new_from_c_str(allocator, "https://d-92671207e4.awsapps.com/start"); @@ -44,12 +42,10 @@ AWS_TEST_CASE(parse_token_location_session_test, s_parse_token_location_session_ AWS_STATIC_STRING_FROM_LITERAL( s_valid_token_json, "{\"accessToken\": \"string\",\"expiresAt\": \"2019-11-14T04:05:45Z\",\"refreshToken\": \"string\",\"clientId\": " - "\"ABCDEFG323242423121312312312312312\",\"clientSecret\": \"ABCDE123\",\"registrationExpiresAt\": " + "\"123321\",\"clientSecret\": \"ABCDE123\",\"registrationExpiresAt\": " "\"2022-03-06T19:53:17Z\",\"region\": \"us-west-2\",\"startUrl\": \"https://d-abc123.awsapps.com/start\"}"); static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { - aws_auth_library_init(allocator); - struct aws_string *file_path = aws_create_process_unique_file_name(allocator); ASSERT_TRUE(aws_create_profile_file(file_path, s_valid_token_json) == AWS_OP_SUCCESS); struct aws_sso_token *token = aws_sso_token_new_from_file(allocator, file_path); From 3121c8ecd3546ac96f61733fae157890120bff4a Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 13 Mar 2023 09:52:25 -0700 Subject: [PATCH 36/93] rename --- source/credentials.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/source/credentials.c b/source/credentials.c index 0cae0491..ad87f174 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -71,8 +71,8 @@ struct aws_credentials { enum IdentityType identity_type; union { - struct aws_credentials_identity credentials; - struct aws_token_identity token; + struct aws_credentials_identity credentials_identity; + struct aws_token_identity token_identity; struct aws_ecc_identity ecc_identity; } identity; }; @@ -107,7 +107,7 @@ struct aws_credentials *aws_credentials_new( credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); credentials->identity_type = AWS_CREDENTIALS_IDENTITY; - struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials; + struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials_identity; credentials_identity->access_key_id = aws_string_new_from_array(allocator, access_key_id_cursor.ptr, access_key_id_cursor.len); if (credentials_identity->access_key_id == NULL) { @@ -158,9 +158,9 @@ static void s_aws_credentials_destroy(struct aws_credentials *credentials) { } switch (credentials->identity_type) { case AWS_CREDENTIALS_IDENTITY: - aws_string_destroy(credentials->identity.credentials.access_key_id); - aws_string_destroy_secure(credentials->identity.credentials.secret_access_key); - aws_string_destroy_secure(credentials->identity.credentials.session_token); + aws_string_destroy(credentials->identity.credentials_identity.access_key_id); + aws_string_destroy_secure(credentials->identity.credentials_identity.secret_access_key); + aws_string_destroy_secure(credentials->identity.credentials_identity.session_token); break; case ECC_IDENTITY: aws_string_destroy(credentials->identity.ecc_identity.access_key_id); @@ -168,7 +168,7 @@ static void s_aws_credentials_destroy(struct aws_credentials *credentials) { aws_ecc_key_pair_release(credentials->identity.ecc_identity.ecc_key); break; case TOKEN_IDENTITY: - aws_string_destroy(credentials->identity.token.token); + aws_string_destroy(credentials->identity.token_identity.token); break; default: break; @@ -204,8 +204,8 @@ static struct aws_byte_cursor s_empty_token_cursor = { struct aws_byte_cursor aws_credentials_get_access_key_id(const struct aws_credentials *credentials) { switch (credentials->identity_type) { case AWS_CREDENTIALS_IDENTITY: - if (credentials->identity.credentials.access_key_id != NULL) { - return aws_byte_cursor_from_string(credentials->identity.credentials.access_key_id); + if (credentials->identity.credentials_identity.access_key_id != NULL) { + return aws_byte_cursor_from_string(credentials->identity.credentials_identity.access_key_id); } break; case ECC_IDENTITY: @@ -222,8 +222,8 @@ struct aws_byte_cursor aws_credentials_get_access_key_id(const struct aws_creden struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_credentials *credentials) { switch (credentials->identity_type) { case AWS_CREDENTIALS_IDENTITY: - if (credentials->identity.credentials.secret_access_key != NULL) { - return aws_byte_cursor_from_string(credentials->identity.credentials.secret_access_key); + if (credentials->identity.credentials_identity.secret_access_key != NULL) { + return aws_byte_cursor_from_string(credentials->identity.credentials_identity.secret_access_key); } break; default: @@ -235,8 +235,8 @@ struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_cr struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_credentials *credentials) { switch (credentials->identity_type) { case AWS_CREDENTIALS_IDENTITY: - if (credentials->identity.credentials.session_token != NULL) { - return aws_byte_cursor_from_string(credentials->identity.credentials.session_token); + if (credentials->identity.credentials_identity.session_token != NULL) { + return aws_byte_cursor_from_string(credentials->identity.credentials_identity.session_token); } break; case ECC_IDENTITY: @@ -253,8 +253,8 @@ struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_creden struct aws_byte_cursor aws_credentials_get_token(const struct aws_credentials *credentials) { switch (credentials->identity_type) { case TOKEN_IDENTITY: - if (credentials->identity.token.token != NULL) { - return aws_byte_cursor_from_string(credentials->identity.token.token); + if (credentials->identity.token_identity.token != NULL) { + return aws_byte_cursor_from_string(credentials->identity.token_identity.token); } break; default: @@ -380,7 +380,7 @@ struct aws_credentials *aws_credentials_new_token( credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); credentials->identity_type = TOKEN_IDENTITY; - struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials; + struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials_identity; credentials_identity->access_key_id = aws_string_new_from_array(allocator, token.ptr, token.len); credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds; return credentials; From 1ae7910db94d5c7acd2375f54dc166eee3001963 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 13 Mar 2023 09:53:22 -0700 Subject: [PATCH 37/93] reset builder file --- builder.json | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/builder.json b/builder.json index 6bff5e69..f256c7c2 100644 --- a/builder.json +++ b/builder.json @@ -7,20 +7,12 @@ } }, "upstream": [ - { - "name": "aws-c-http" - }, - { - "name": "aws-c-cal" - }, - { - "name": "aws-c-sdkutils" - } + { "name": "aws-c-http" }, + { "name": "aws-c-cal" }, + { "name": "aws-c-sdkutils" } ], "downstream": [ - { - "name": "aws-c-s3" - } + { "name": "aws-c-s3" } ], "+cmake_args": [ "-DAWS_HAS_CI_ENVIRONMENT=ON" From 2054cce93662dd88add5ea3634e0a0610c9aba87 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 13 Mar 2023 10:08:34 -0700 Subject: [PATCH 38/93] cleanup --- include/aws/auth/auth.h | 1 - include/aws/auth/private/sso_token_utils.h | 5 +++-- source/auth.c | 13 ++++++++++++- source/credentials_provider_sts_web_identity.c | 1 - source/token_provider_sso_profile.c | 12 +++--------- source/token_provider_sso_session.c | 15 +++++---------- tests/token_provider_sso_tests.c | 14 ++++---------- 7 files changed, 27 insertions(+), 34 deletions(-) diff --git a/include/aws/auth/auth.h b/include/aws/auth/auth.h index 0ba3a5e5..b0092837 100644 --- a/include/aws/auth/auth.h +++ b/include/aws/auth/auth.h @@ -47,7 +47,6 @@ enum aws_auth_errors { AWS_AUTH_SSO_TOKEN_INVALID, AWS_AUTH_SSO_TOKEN_EXPIRED, AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE, - // TODO: add descriptions AWS_AUTH_ERROR_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_AUTH_PACKAGE_ID) }; diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index c0a04c86..f923a15f 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -7,9 +7,9 @@ */ #include -#include #include +/* structure to represent a parsed sso token */ struct aws_sso_token { struct aws_allocator *allocator; @@ -19,13 +19,14 @@ struct aws_sso_token { AWS_EXTERN_C_BEGIN -/* Construct token path which ~/.aws/sso/cache/.json */ +/* Construct token path which is ~/.aws/sso/cache/.json */ AWS_AUTH_API struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, const struct aws_string *input); AWS_AUTH_API void aws_sso_token_destroy(struct aws_sso_token *token); +/* Parse `aws_sso_token` from the give file path */ AWS_AUTH_API struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocator, const struct aws_string *file_path); diff --git a/source/auth.c b/source/auth.c index b3fb4d3f..3ac857d5 100644 --- a/source/auth.c +++ b/source/auth.c @@ -91,7 +91,18 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_AUTH( AWS_AUTH_CREDENTIALS_PROVIDER_DELEGATE_FAILURE, "Valid credentials could not be sourced by the delegate provider"), - + AWS_DEFINE_ERROR_INFO_AUTH( + AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE, + "Valid token could not be sourced by the sso token provider"), + AWS_DEFINE_ERROR_INFO_AUTH( + AWS_AUTH_SSO_TOKEN_INVALID, + "Token sourced by the sso token provider is invalid."), + AWS_DEFINE_ERROR_INFO_AUTH( + AWS_AUTH_SSO_TOKEN_EXPIRED, + "Token sourced by the sso token provider is expired."), + AWS_DEFINE_ERROR_INFO_AUTH( + AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE, + "Valid credentials could not be sourced by the sso credentials provider"), }; /* clang-format on */ diff --git a/source/credentials_provider_sts_web_identity.c b/source/credentials_provider_sts_web_identity.c index e86f0a48..c3e46971 100644 --- a/source/credentials_provider_sts_web_identity.c +++ b/source/credentials_provider_sts_web_identity.c @@ -751,7 +751,6 @@ static int s_credentials_provider_sts_web_identity_get_credentials_async( error: s_user_data_destroy(wrapped_user_data); - callback(NULL, aws_last_error(), user_data); return AWS_OP_ERR; } diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 038cb957..ce4153e1 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -8,9 +8,6 @@ #include #include #include -#include -#include -#include #ifdef _MSC_VER /* allow non-constant declared initializers. */ # pragma warning(disable : 4204) @@ -31,7 +28,7 @@ static int s_token_provider_profile_get_token_async( struct aws_sso_token *sso_token = NULL; struct aws_credentials *credentials = NULL; - bool success = false; + int result = AWS_OP_ERR; sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", (void *)provider); @@ -56,15 +53,12 @@ static int s_token_provider_profile_get_token_async( } callback(credentials, AWS_OP_SUCCESS, user_data); - success = true; + result = AWS_OP_SUCCESS; done: aws_sso_token_destroy(sso_token); aws_credentials_release(credentials); - if (!success) { - callback(NULL, aws_last_error(), user_data); - } - return AWS_OP_SUCCESS; + return result; } static void s_token_provider_profile_destroy(struct aws_credentials_provider *provider) { diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 956cd142..805cbbfa 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -8,9 +8,6 @@ #include #include #include -#include -#include -#include #ifdef _MSC_VER /* allow non-constant declared initializers. */ # pragma warning(disable : 4204) @@ -31,7 +28,7 @@ static int s_token_provider_sso_session_get_token_async( struct aws_token_provider_sso_session_impl *impl = provider->impl; struct aws_sso_token *sso_token = NULL; struct aws_credentials *credentials = NULL; - bool success = false; + int result = AWS_OP_ERR; sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); if (!sso_token) { @@ -39,7 +36,6 @@ static int s_token_provider_sso_session_get_token_async( goto done; } - /* TODO: Refresh token if it is within refresh window and refreshable */ /* check token expiration. */ struct aws_date_time now; aws_date_time_init_now(&now); @@ -49,6 +45,8 @@ static int s_token_provider_sso_session_get_token_async( goto done; } + /* TODO: Refresh token if it is within refresh window and refreshable */ + credentials = aws_credentials_new_token( provider->allocator, aws_byte_cursor_from_string(sso_token->token), @@ -57,15 +55,12 @@ static int s_token_provider_sso_session_get_token_async( goto done; } callback(credentials, AWS_OP_SUCCESS, user_data); - success = true; + result = AWS_OP_SUCCESS; done: aws_sso_token_destroy(sso_token); aws_credentials_release(credentials); - if (!success) { - callback(NULL, aws_last_error(), user_data); - } - return AWS_OP_SUCCESS; + return result; } static void s_token_provider_sso_session_destroy(struct aws_credentials_provider *provider) { diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 9df45382..7eef7212 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -389,13 +389,10 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * aws_get_credentials_test_callback_result_init(&callback_results, 1); int get_async_result = aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); - ASSERT_TRUE(get_async_result == AWS_OP_SUCCESS); - aws_wait_on_credentials_callback(&callback_results); + ASSERT_FALSE(get_async_result == AWS_OP_SUCCESS); - ASSERT_NULL(callback_results.credentials); - ASSERT_INT_EQUALS(callback_results.last_error, AWS_AUTH_SSO_TOKEN_EXPIRED); + ASSERT_INT_EQUALS(aws_last_error(), AWS_AUTH_SSO_TOKEN_EXPIRED); - aws_get_credentials_test_callback_result_clean_up(&callback_results); aws_credentials_provider_release(provider); /* reset $HOME */ @@ -488,13 +485,10 @@ static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allo aws_get_credentials_test_callback_result_init(&callback_results, 1); int get_async_result = aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); - ASSERT_TRUE(get_async_result == AWS_OP_SUCCESS); - aws_wait_on_credentials_callback(&callback_results); + ASSERT_FALSE(get_async_result == AWS_OP_SUCCESS); - ASSERT_NULL(callback_results.credentials); - ASSERT_INT_EQUALS(callback_results.last_error, AWS_AUTH_SSO_TOKEN_EXPIRED); + ASSERT_INT_EQUALS(aws_last_error(), AWS_AUTH_SSO_TOKEN_EXPIRED); - aws_get_credentials_test_callback_result_clean_up(&callback_results); aws_credentials_provider_release(provider); /* reset $HOME */ From 3afa155f68507f593ddf91222c6ae5c93b0d8bfd Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 13 Mar 2023 10:11:15 -0700 Subject: [PATCH 39/93] import cleanup --- source/credentials_provider_sso.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 9292f4d3..fc65e807 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -8,24 +8,13 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include #include #include #include -#include -#include -#include #include -#include #include #include -#include #if defined(_MSC_VER) # pragma warning(disable : 4204) From a7449eaf5b56536685fd866cde81c8b307096ccf Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 13 Mar 2023 10:14:29 -0700 Subject: [PATCH 40/93] import cleanup --- tests/credentials_provider_sso_tests.c | 15 --------------- tests/token_provider_sso_tests.c | 6 ------ 2 files changed, 21 deletions(-) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index f7e1f5ce..315fa853 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -8,24 +8,9 @@ #include "credentials_provider_utils.h" #include "shared_credentials_test_definitions.h" -#include -#include #include -#include -#include -#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile, "sso"); static int s_aws_credentials_provider_sso_test_init_config_profile( diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 7eef7212..7ba612bc 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -5,14 +5,8 @@ #include -#include #include -#include -#include #include -#include -#include -#include #include #include #include From d6ea07e0db610ae589341ddc02e3e423bc9758ec Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 13 Mar 2023 16:49:01 -0700 Subject: [PATCH 41/93] add a test for expired token in sso --- source/credentials_provider_sso.c | 7 +-- tests/CMakeLists.txt | 3 +- tests/credentials_provider_sso_tests.c | 69 +++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index fc65e807..bf5258b4 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -443,14 +443,15 @@ static void s_on_acquire_connection(struct aws_http_connection *connection, int struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; if (aws_credentials_provider_get_credentials(impl->token_provider, s_on_get_token_callback, user_data)) { + int last_error_code = aws_last_error(); AWS_LOGF_WARN( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "id=%p: failed to get a token, error code %d(%s)", (void *)sso_user_data->provider, - error_code, - aws_error_str(error_code)); + last_error_code, + aws_error_str(last_error_code)); - sso_user_data->error_code = error_code; + sso_user_data->error_code = last_error_code; s_finalize_get_credentials_query(sso_user_data); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 56c9fc97..965b01e6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -118,7 +118,8 @@ add_net_test_case(parse_sso_token_valid) add_net_test_case(credentials_provider_sso_failed_invalid_config) add_net_test_case(credentials_provider_sso_create_destroy_valid_config) add_net_test_case(credentials_provider_sso_connect_failure) -add_net_test_case(credentials_provider_sso_token_failure) +add_net_test_case(credentials_provider_sso_failure_token_missing) +add_net_test_case(credentials_provider_sso_failure_token_expired) add_net_test_case(credentials_provider_sso_request_failure) add_net_test_case(credentials_provider_sso_bad_response) add_net_test_case(credentials_provider_sso_retryable_error) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 315fa853..f6676e49 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -337,7 +337,7 @@ static int s_credentials_provider_sso_connect_failure(struct aws_allocator *allo } AWS_TEST_CASE(credentials_provider_sso_connect_failure, s_credentials_provider_sso_connect_failure); -static int s_credentials_provider_sso_token_failure(struct aws_allocator *allocator, void *ctx) { +static int s_credentials_provider_sso_failure_token_missing(struct aws_allocator *allocator, void *ctx) { (void)ctx; aws_credentials_provider_http_mock_tester_init(allocator); @@ -383,10 +383,75 @@ static int s_credentials_provider_sso_token_failure(struct aws_allocator *alloca return 0; } -AWS_TEST_CASE(credentials_provider_sso_token_failure, s_credentials_provider_sso_token_failure); +AWS_TEST_CASE(credentials_provider_sso_failure_token_missing, s_credentials_provider_sso_failure_token_missing); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); +AWS_STATIC_STRING_FROM_LITERAL( + s_expired_sso_token, + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); +static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_request_successful = false; + + struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); + + struct aws_byte_buf content_buf; + struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); + + struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); + ASSERT_TRUE(config_file_contents != NULL); + aws_byte_buf_clean_up(&content_buf); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); + aws_string_destroy(config_file_contents); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); + ASSERT_INT_EQUALS(credentials_provider_http_mock_tester.error_code, AWS_AUTH_SSO_TOKEN_EXPIRED); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + /* reset $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); + aws_string_destroy(actual_home); + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_failure_token_expired, s_credentials_provider_sso_failure_token_expired); AWS_STATIC_STRING_FROM_LITERAL( s_valid_sso_token, From 302386d745d65b845f1835196062531977347deb Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Tue, 14 Mar 2023 11:38:18 -0700 Subject: [PATCH 42/93] Adds comments --- include/aws/auth/credentials.h | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 95c85e10..313f1d9a 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -697,6 +697,14 @@ struct aws_credentials *aws_credentials_new_ecc_from_aws_credentials( struct aws_allocator *allocator, const struct aws_credentials *credentials); +/** + * Creates a set of AWS credentials based on a token with expiration. + * + * @param allocator memory allocator to use for all memory allocation + * @param token token for the credentials + * @param expiration_timepoint_in_seconds time at which these credentials expire + * @return a new pair of AWS credentials, or NULL + */ AWS_AUTH_API struct aws_credentials *aws_credentials_new_token( struct aws_allocator *allocator, @@ -747,10 +755,10 @@ AWS_AUTH_API struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_credentials *credentials); /** - * Get the AWS token from a set of credentials + * Get the token from a set of AWS credentials * - * @param credentials credentials to get the session token from - * @return a byte cursor to the session token or an empty byte cursor if there is no session token + * @param credentials credentials to get the token from + * @return a byte cursor to the token or an empty byte cursor if there is no token */ AWS_AUTH_API struct aws_byte_cursor aws_credentials_get_token(const struct aws_credentials *credentials); @@ -999,6 +1007,14 @@ struct aws_credentials_provider *aws_credentials_provider_new_sts_web_identity( struct aws_allocator *allocator, const struct aws_credentials_provider_sts_web_identity_options *options); +/** + * Creates a provider that sources credentials from SSO using a SSOToken. + * + * @param allocator memory allocator to use for all memory allocation + * @param options provider-specific configuration options + * + * @return the newly-constructed credentials provider, or NULL if an error occurred. + */ AWS_AUTH_API struct aws_credentials_provider *aws_credentials_provider_new_sso( struct aws_allocator *allocator, @@ -1083,7 +1099,7 @@ struct aws_credentials_provider *aws_credentials_provider_new_chain_default( /** * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws - * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json + * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json * * @param allocator memory allocator to use for all memory allocation * @param options provider-specific configuration options @@ -1095,6 +1111,15 @@ struct aws_credentials_provider *aws_token_provider_new_sso_profile( struct aws_allocator *allocator, const struct aws_token_provider_sso_profile_options *options); +/** + * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws + * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json + * + * @param allocator memory allocator to use for all memory allocation + * @param options provider-specific configuration options + * + * @return the newly-constructed credentials provider, or NULL if an error occurred. + */ AWS_AUTH_API struct aws_credentials_provider *aws_token_provider_new_sso_session( struct aws_allocator *allocator, From 0a11f1d7afaf0f1664d59d4970a955e0ba8b749f Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Tue, 14 Mar 2023 11:40:10 -0700 Subject: [PATCH 43/93] rename --- include/aws/auth/private/credentials_utils.h | 2 +- source/credentials_provider_sso.c | 2 +- source/credentials_utils.c | 2 +- source/token_provider_sso_profile.c | 2 +- source/token_provider_sso_session.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/aws/auth/private/credentials_utils.h b/include/aws/auth/private/credentials_utils.h index 590082cc..154753c3 100644 --- a/include/aws/auth/private/credentials_utils.h +++ b/include/aws/auth/private/credentials_utils.h @@ -166,7 +166,7 @@ enum aws_retry_error_type aws_credentials_provider_compute_retry_error_type(int * Loads an aws config profile collection */ AWS_AUTH_API -struct aws_profile_collection *aws_load_config( +struct aws_profile_collection *aws_load_profile_collection_from_config_file( struct aws_allocator *allocator, struct aws_byte_cursor config_file_name_override); diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index bf5258b4..33c8831f 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -646,7 +646,7 @@ static struct sso_parameters *s_parameters_new( goto on_finish; } - config_profile = aws_load_config(allocator, options->config_file_name_override); + config_profile = aws_load_profile_collection_from_config_file(allocator, options->config_file_name_override); if (!config_profile) { goto on_finish; } diff --git a/source/credentials_utils.c b/source/credentials_utils.c index a23fe184..c43b7283 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -320,7 +320,7 @@ enum aws_retry_error_type aws_credentials_provider_compute_retry_error_type(int return error_type; } -struct aws_profile_collection *aws_load_config( +struct aws_profile_collection *aws_load_profile_collection_from_config_file( struct aws_allocator *allocator, struct aws_byte_cursor config_file_name_override) { diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index ce4153e1..b86de7d7 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -95,7 +95,7 @@ static struct aws_string *s_construct_profile_token_path( aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } - config_collection = aws_load_config(allocator, config_file_name_override); + config_collection = aws_load_profile_collection_from_config_file(allocator, config_file_name_override); if (!config_collection) { AWS_LOGF_ERROR( diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 805cbbfa..bae4783b 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -97,7 +97,7 @@ static struct aws_string *s_verify_config_and_construct_token_path( AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token provider failed to resolve profile name"); goto cleanup; } - config_collection = aws_load_config(allocator, config_file_name_override); + config_collection = aws_load_profile_collection_from_config_file(allocator, config_file_name_override); const struct aws_profile *profile = aws_profile_collection_get_profile(config_collection, profile_name); From 356952272aa398d06f19034c65c9c0b41f5d64b0 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Tue, 14 Mar 2023 13:36:58 -0700 Subject: [PATCH 44/93] Update to latest sdkutils --- source/credentials_provider_sso.c | 4 ++-- source/token_provider_sso_session.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 33c8831f..7fd57f72 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -691,8 +691,8 @@ static struct sso_parameters *s_parameters_new( sso_region = aws_profile_get_property( aws_profile_collection_get_section( config_profile, - aws_profile_property_get_value(sso_session_property), - AWS_PROFILE_SECTION_TYPE_SSO_SESSION), + AWS_PROFILE_SECTION_TYPE_SSO_SESSION, + aws_profile_property_get_value(sso_session_property)), s_sso_region); } else { struct aws_token_provider_sso_profile_options token_provider_options = { diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index bae4783b..d1eb9101 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -121,7 +121,7 @@ static struct aws_string *s_verify_config_and_construct_token_path( } const struct aws_string *sso_session_name = aws_profile_property_get_value(sso_session_property); const struct aws_profile *session_profile = - aws_profile_collection_get_section(config_collection, sso_session_name, AWS_PROFILE_SECTION_TYPE_SSO_SESSION); + aws_profile_collection_get_section(config_collection, AWS_PROFILE_SECTION_TYPE_SSO_SESSION, sso_session_name); if (!session_profile) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find an sso-session"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); From d2a04732240162f31241737c5b3e62e80f995488 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Tue, 14 Mar 2023 13:43:48 -0700 Subject: [PATCH 45/93] move aws_create_directory to credentials_provider utils --- tests/credentials_provider_sso_tests.c | 12 +++++----- tests/credentials_provider_utils.c | 20 +++++++++++++++++ tests/credentials_provider_utils.h | 7 ++++++ tests/shared_credentials_test_definitions.h | 25 --------------------- tests/token_provider_sso_tests.c | 8 +++---- 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index f6676e49..1d55e848 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -403,7 +403,7 @@ static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator /* create token file */ struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); struct aws_byte_buf content_buf; @@ -471,7 +471,7 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo /* create token file */ struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; @@ -534,7 +534,7 @@ static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocat struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; @@ -599,7 +599,7 @@ static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allo struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; @@ -663,7 +663,7 @@ static int s_credentials_provider_sso_basic_success(struct aws_allocator *alloca struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; @@ -728,7 +728,7 @@ static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_byte_buf content_buf; diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index 02685bfd..2a370568 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -487,6 +487,26 @@ struct aws_credentials_provider *aws_credentials_provider_new_null( return provider; } +int aws_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path) { + const char local_platform_separator = aws_get_platform_directory_separator(); + + /* Create directory components and ensure use of platform separator at the same time. */ + for (size_t i = 0; i < path->len; ++i) { + if (aws_is_any_directory_separator((char)path->bytes[i])) { + ((char *)path->bytes)[i] = local_platform_separator; + + struct aws_string *segment = aws_string_new_from_array(allocator, path->bytes, i); + int rc = aws_directory_create(segment); + aws_string_destroy(segment); + + if (rc != AWS_OP_SUCCESS) { + return rc; + } + } + } + return AWS_OP_SUCCESS; +} + /* * Mocked Functions for Tests */ diff --git a/tests/credentials_provider_utils.h b/tests/credentials_provider_utils.h index 51d85125..39251346 100644 --- a/tests/credentials_provider_utils.h +++ b/tests/credentials_provider_utils.h @@ -93,6 +93,13 @@ struct aws_credentials_provider *aws_credentials_provider_new_null( struct aws_allocator *allocator, struct aws_credentials_provider_shutdown_options *shutdown_options); +/** + * Create the directory components of @path: + * - if @path ends in a path separator, create every directory component; + * - else, stop at the last path separator (parent directory of @path). + */ +int aws_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path); + /** * Mocked Function */ diff --git a/tests/shared_credentials_test_definitions.h b/tests/shared_credentials_test_definitions.h index 688ce69f..8662e429 100644 --- a/tests/shared_credentials_test_definitions.h +++ b/tests/shared_credentials_test_definitions.h @@ -66,31 +66,6 @@ static int aws_create_profile_file(const struct aws_string *file_name, const str return AWS_OP_SUCCESS; } -/** - * Create the directory components of @path: - * - if @path ends in a path separator, create every directory component; - * - else, stop at the last path separator (parent directory of @path). - */ -static int s_aws_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path) { - const char local_platform_separator = aws_get_platform_directory_separator(); - - /* Create directory components and ensure use of platform separator at the same time. */ - for (size_t i = 0; i < path->len; ++i) { - if (aws_is_any_directory_separator((char)path->bytes[i])) { - ((char *)path->bytes)[i] = local_platform_separator; - - struct aws_string *segment = aws_string_new_from_array(allocator, path->bytes, i); - int rc = aws_directory_create(segment); - aws_string_destroy(segment); - - if (rc != AWS_OP_SUCCESS) { - return rc; - } - } - } - return AWS_OP_SUCCESS; -} - #ifdef _MSC_VER # pragma warning(pop) #endif diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 7ba612bc..dd95014b 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -312,7 +312,7 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * /* create token file */ struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); @@ -364,7 +364,7 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * /* create token file */ struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); @@ -412,7 +412,7 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo /* create token file */ struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); @@ -462,7 +462,7 @@ static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allo /* create token file */ struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(s_aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); From 0886c91dd0a63d979edbbafa1f6faea8618f2bfd Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 16 Mar 2023 20:09:20 -0700 Subject: [PATCH 46/93] add a log --- source/sso_token_utils.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index d1b52e9d..23c3722b 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -68,7 +68,8 @@ struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, con } token_path_str = aws_string_new_from_buf(allocator, &token_path_buf); - + AWS_LOGF_INFO( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "successfully constructed token path: %s", aws_string_c_str(token_path_str)); cleanup: aws_byte_buf_clean_up(&token_path_buf); aws_byte_buf_clean_up(&sha1_buf); From f037f793ad413739ece7163846547e80960c6875 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 10:36:27 -0700 Subject: [PATCH 47/93] fix tests --- tests/sso_token_util_tests.c | 4 ++++ tests/token_provider_sso_tests.c | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index a9062787..833c580a 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -9,6 +9,7 @@ #include static int s_parse_token_location_url_test(struct aws_allocator *allocator, void *ctx) { + aws_auth_library_init(allocator); struct aws_string *start_url = aws_string_new_from_c_str(allocator, "https://d-92671207e4.awsapps.com/start"); struct aws_string *token_path = aws_construct_token_path(allocator, start_url); @@ -20,11 +21,13 @@ static int s_parse_token_location_url_test(struct aws_allocator *allocator, void aws_string_destroy(start_url); aws_string_destroy(token_path); + aws_auth_library_clean_up(); return AWS_OP_SUCCESS; } AWS_TEST_CASE(parse_token_location_url_test, s_parse_token_location_url_test); static int s_parse_token_location_session_test(struct aws_allocator *allocator, void *ctx) { + aws_auth_library_init(allocator); struct aws_string *session = aws_string_new_from_c_str(allocator, "admin"); struct aws_string *token_path = aws_construct_token_path(allocator, session); struct aws_byte_cursor token_cursor = aws_byte_cursor_from_string(token_path); @@ -35,6 +38,7 @@ static int s_parse_token_location_session_test(struct aws_allocator *allocator, aws_string_destroy(session); aws_string_destroy(token_path); + aws_auth_library_clean_up(); return AWS_OP_SUCCESS; } AWS_TEST_CASE(parse_token_location_session_test, s_parse_token_location_session_test); diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index dd95014b..63c7cbf6 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -22,7 +22,7 @@ struct sso_session_profile_example { static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocator *allocator, void *ctx) { (void)ctx; - + aws_auth_library_init(allocator); const struct sso_session_profile_example invalid_profile_examples[] = { { .name = "No config", @@ -58,6 +58,7 @@ static int s_sso_token_provider_profile_invalid_profile_test(struct aws_allocato } aws_string_destroy(config_file_str); + aws_auth_library_clean_up(); return AWS_OP_SUCCESS; } @@ -65,7 +66,7 @@ AWS_TEST_CASE(sso_token_provider_profile_invalid_profile_test, s_sso_token_provi static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator *allocator, void *ctx) { (void)ctx; - + aws_auth_library_init(allocator); static struct sso_session_profile_example s_valid_profile_examples[] = { { .name = "profile", @@ -99,6 +100,7 @@ static int s_sso_token_provider_profile_valid_profile_test(struct aws_allocator } aws_string_destroy(config_file_str); + aws_auth_library_clean_up(); return AWS_OP_SUCCESS; } From a6b35737282ea8852b09499805cc4997d1c68ffd Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 10:58:24 -0700 Subject: [PATCH 48/93] Fix logs and return error or callback --- source/credentials_provider_sso.c | 13 ++++++------- tests/credentials_provider_sso_tests.c | 3 +-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 7fd57f72..183aaf8a 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -155,7 +155,7 @@ static void s_finalize_get_credentials_query(struct sso_user_data *user_data) { AWS_LOGF_INFO( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) successfully queried credentials", (void *)user_data->provider); } else { - AWS_LOGF_WARN( + AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) failed to query credentials", (void *)user_data->provider); if (user_data->error_code == AWS_ERROR_SUCCESS) { @@ -388,7 +388,7 @@ static void s_on_get_token_callback(struct aws_credentials *credentials, int err struct sso_user_data *sso_user_data = user_data; if (error_code) { - AWS_LOGF_WARN( + AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "id=%p: failed to acquire a token, error code %d(%s)", (void *)sso_user_data->provider, @@ -401,7 +401,7 @@ static void s_on_get_token_callback(struct aws_credentials *credentials, int err struct aws_byte_cursor token = aws_credentials_get_token(credentials); if (token.len == 0 || token.ptr == NULL) { - AWS_LOGF_WARN( + AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "id=%p: token is empty with error code %d(%s)", (void *)sso_user_data->provider, @@ -425,7 +425,7 @@ static void s_on_acquire_connection(struct aws_http_connection *connection, int struct sso_user_data *sso_user_data = user_data; if (error_code) { - AWS_LOGF_WARN( + AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "id=%p: failed to acquire a connection, error code %d(%s)", (void *)sso_user_data->provider, @@ -444,7 +444,7 @@ static void s_on_acquire_connection(struct aws_http_connection *connection, int struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; if (aws_credentials_provider_get_credentials(impl->token_provider, s_on_get_token_callback, user_data)) { int last_error_code = aws_last_error(); - AWS_LOGF_WARN( + AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "id=%p: failed to get a token, error code %d(%s)", (void *)sso_user_data->provider, @@ -514,7 +514,7 @@ static int s_credentials_provider_sso_get_credentials_async( struct sso_user_data *wrapped_user_data = s_user_data_new(provider, callback, user_data); if (wrapped_user_data == NULL) { - goto on_error; + return AWS_OP_ERR; } if (aws_retry_strategy_acquire_retry_token( @@ -531,7 +531,6 @@ static int s_credentials_provider_sso_get_credentials_async( on_error: s_user_data_destroy(wrapped_user_data); - callback(NULL, aws_last_error(), user_data); return AWS_OP_ERR; } diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 1d55e848..09a7115d 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -260,7 +260,6 @@ AWS_STATIC_STRING_FROM_LITERAL(s_good_secret_access_key, "SuccessfulSecret"); AWS_STATIC_STRING_FROM_LITERAL(s_good_session_token, "SuccessfulToken"); static int s_good_response_expiration = 1678574216; static int s_verify_credentials(bool request_made, bool got_credentials, int expected_attempts) { - if (request_made) { ASSERT_CURSOR_VALUE_STRING_EQUALS( aws_byte_cursor_from_buf(&credentials_provider_http_mock_tester.request_path), s_expected_sso_request_path); @@ -281,6 +280,7 @@ static int s_verify_credentials(bool request_made, bool got_credentials, int exp aws_credentials_get_expiration_timepoint_seconds(credentials_provider_http_mock_tester.credentials), s_good_response_expiration); } else { + ASSERT_TRUE(credentials_provider_http_mock_tester.error_code); ASSERT_TRUE(credentials_provider_http_mock_tester.credentials == NULL); } @@ -324,7 +324,6 @@ static int s_credentials_provider_sso_connect_failure(struct aws_allocator *allo provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); aws_credentials_provider_http_mock_wait_for_credentials_result(); - ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); aws_credentials_provider_release(provider); From c969bff7876d736ee3bf7d68d49503cdcbe823af Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 11:12:43 -0700 Subject: [PATCH 49/93] explicit conversion --- source/token_provider_sso_profile.c | 2 +- source/token_provider_sso_session.c | 2 +- tests/credentials_provider_sso_tests.c | 9 ++++----- tests/sso_token_util_tests.c | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index b86de7d7..71529c1f 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -47,7 +47,7 @@ static int s_token_provider_profile_get_token_async( credentials = aws_credentials_new_token( provider->allocator, aws_byte_cursor_from_string(sso_token->token), - aws_date_time_as_epoch_secs(&sso_token->expiration)); + (uint64_t)aws_date_time_as_epoch_secs(&sso_token->expiration)); if (!credentials) { goto done; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index d1eb9101..166eccdc 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -50,7 +50,7 @@ static int s_token_provider_sso_session_get_token_async( credentials = aws_credentials_new_token( provider->allocator, aws_byte_cursor_from_string(sso_token->token), - aws_date_time_as_epoch_secs(&sso_token->expiration)); + (uint64_t)aws_date_time_as_epoch_secs(&sso_token->expiration)); if (!credentials) { goto done; } diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 09a7115d..a36f3ff1 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -260,11 +260,6 @@ AWS_STATIC_STRING_FROM_LITERAL(s_good_secret_access_key, "SuccessfulSecret"); AWS_STATIC_STRING_FROM_LITERAL(s_good_session_token, "SuccessfulToken"); static int s_good_response_expiration = 1678574216; static int s_verify_credentials(bool request_made, bool got_credentials, int expected_attempts) { - if (request_made) { - ASSERT_CURSOR_VALUE_STRING_EQUALS( - aws_byte_cursor_from_buf(&credentials_provider_http_mock_tester.request_path), s_expected_sso_request_path); - } - ASSERT_TRUE(credentials_provider_http_mock_tester.has_received_credentials_callback); if (got_credentials) { @@ -284,6 +279,10 @@ static int s_verify_credentials(bool request_made, bool got_credentials, int exp ASSERT_TRUE(credentials_provider_http_mock_tester.credentials == NULL); } + if (request_made) { + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_byte_cursor_from_buf(&credentials_provider_http_mock_tester.request_path), s_expected_sso_request_path); + } ASSERT_INT_EQUALS(credentials_provider_http_mock_tester.attempts, expected_attempts); return AWS_OP_SUCCESS; diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index 833c580a..415d6329 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -54,7 +54,7 @@ static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { ASSERT_TRUE(aws_create_profile_file(file_path, s_valid_token_json) == AWS_OP_SUCCESS); struct aws_sso_token *token = aws_sso_token_new_from_file(allocator, file_path); ASSERT_TRUE(aws_string_eq_c_str(token->token, "string")); - ASSERT_INT_EQUALS(aws_date_time_as_epoch_secs(&token->expiration), 1573704345); + ASSERT_INT_EQUALS((uint64_t)aws_date_time_as_epoch_secs(&token->expiration), 1573704345); aws_string_destroy(file_path); aws_sso_token_destroy(token); aws_auth_library_clean_up(); From 27749df239eb256537d6c7e3f22645826c7bc53d Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 11:19:32 -0700 Subject: [PATCH 50/93] explicit conversion --- source/credentials_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/credentials_utils.c b/source/credentials_utils.c index c43b7283..f87def09 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -139,7 +139,7 @@ static bool s_parse_expiration_value_from_json_object( } *expiration_timepoint_in_seconds = - (uint64_t)aws_timestamp_convert(expiration_value_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_SECS, NULL); + aws_timestamp_convert((uint64_t)expiration_value_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_SECS, NULL); return true; } From 8f69d0e9197dd8762441b6ef93328fb661edd7ac Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 11:26:01 -0700 Subject: [PATCH 51/93] add log --- source/token_provider_sso_profile.c | 1 + source/token_provider_sso_session.c | 1 + 2 files changed, 2 insertions(+) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 71529c1f..a4d2d297 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -49,6 +49,7 @@ static int s_token_provider_profile_get_token_async( aws_byte_cursor_from_string(sso_token->token), (uint64_t)aws_date_time_as_epoch_secs(&sso_token->expiration)); if (!credentials) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) Unable to construct credentials.", (void *)provider); goto done; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 166eccdc..9ee093d3 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -52,6 +52,7 @@ static int s_token_provider_sso_session_get_token_async( aws_byte_cursor_from_string(sso_token->token), (uint64_t)aws_date_time_as_epoch_secs(&sso_token->expiration)); if (!credentials) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) Unable to construct credentials.", (void *)provider); goto done; } callback(credentials, AWS_OP_SUCCESS, user_data); From affe702cb614da0ea61d5546967ec7e2a03a52a1 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 11:30:38 -0700 Subject: [PATCH 52/93] fix getenv --- tests/credentials_provider_sso_tests.c | 18 ++++++++++++------ tests/token_provider_sso_tests.c | 12 ++++++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index a36f3ff1..930bd363 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -394,7 +394,8 @@ static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator aws_credentials_provider_http_mock_tester_init(allocator); credentials_provider_http_mock_tester.is_request_successful = false; - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -462,7 +463,8 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo credentials_provider_http_mock_tester.is_request_successful = false; credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_400_BAD_REQUEST; - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -524,7 +526,8 @@ static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocat aws_credentials_provider_http_mock_tester_init(allocator); - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -589,7 +592,8 @@ static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allo aws_credentials_provider_http_mock_tester_init(allocator); credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR; - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -653,7 +657,8 @@ static int s_credentials_provider_sso_basic_success(struct aws_allocator *alloca (void)ctx; aws_credentials_provider_http_mock_tester_init(allocator); - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -718,7 +723,8 @@ static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator (void)ctx; aws_credentials_provider_http_mock_tester_init(allocator); - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 63c7cbf6..ce4a8ae0 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -308,7 +308,8 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * s_aws_mock_token_provider_sso_tester_init(allocator); /* redirect $HOME */ - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ @@ -360,7 +361,8 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * s_aws_mock_token_provider_sso_tester_init(allocator); /* redirect $HOME */ - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ @@ -408,7 +410,8 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo s_aws_mock_token_provider_sso_tester_init(allocator); /* redirect $HOME */ - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ @@ -458,7 +461,8 @@ static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allo s_aws_mock_token_provider_sso_tester_init(allocator); /* redirect $HOME */ - struct aws_string *actual_home = aws_string_new_from_c_str(allocator, getenv("HOME")); + struct aws_string *actual_home; + aws_get_environment_value(allocator, s_home_env_var, &actual_home); ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ From 3dfbe06365f547127cfcee503487652abca09cae Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 11:35:24 -0700 Subject: [PATCH 53/93] update log with time difference --- source/token_provider_sso_profile.c | 6 +++++- source/token_provider_sso_session.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index a4d2d297..81bf9cd4 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -39,7 +39,11 @@ static int s_token_provider_profile_get_token_async( struct aws_date_time now; aws_date_time_init_now(&now); if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) cached token is expired. difference in time is %ld", + (void *)provider, + aws_date_time_diff(&sso_token->expiration, &now)); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 9ee093d3..a99b6958 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -40,7 +40,11 @@ static int s_token_provider_sso_session_get_token_async( struct aws_date_time now; aws_date_time_init_now(&now); if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) cached token is expired. difference in time is %ld", + (void *)provider, + aws_date_time_diff(&sso_token->expiration, &now)); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } From f80c6cfbcaca7685ff789b2976589ce9e3839836 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 11:40:13 -0700 Subject: [PATCH 54/93] void ctx fix --- tests/sso_token_util_tests.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index 415d6329..fec869e7 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -9,6 +9,7 @@ #include static int s_parse_token_location_url_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; aws_auth_library_init(allocator); struct aws_string *start_url = aws_string_new_from_c_str(allocator, "https://d-92671207e4.awsapps.com/start"); struct aws_string *token_path = aws_construct_token_path(allocator, start_url); @@ -27,6 +28,7 @@ static int s_parse_token_location_url_test(struct aws_allocator *allocator, void AWS_TEST_CASE(parse_token_location_url_test, s_parse_token_location_url_test); static int s_parse_token_location_session_test(struct aws_allocator *allocator, void *ctx) { + (void)ctx; aws_auth_library_init(allocator); struct aws_string *session = aws_string_new_from_c_str(allocator, "admin"); struct aws_string *token_path = aws_construct_token_path(allocator, session); @@ -49,6 +51,7 @@ AWS_STATIC_STRING_FROM_LITERAL( "\"123321\",\"clientSecret\": \"ABCDE123\",\"registrationExpiresAt\": " "\"2022-03-06T19:53:17Z\",\"region\": \"us-west-2\",\"startUrl\": \"https://d-abc123.awsapps.com/start\"}"); static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { + (void)ctx; aws_auth_library_init(allocator); struct aws_string *file_path = aws_create_process_unique_file_name(allocator); ASSERT_TRUE(aws_create_profile_file(file_path, s_valid_token_json) == AWS_OP_SUCCESS); From 4f9eb6c064c2f5e0e4b542b39d70f01d124316dc Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 11:48:18 -0700 Subject: [PATCH 55/93] update expiration time --- tests/credentials_provider_sso_tests.c | 2 +- tests/token_provider_sso_tests.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index 930bd363..b4480886 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -454,7 +454,7 @@ AWS_TEST_CASE(credentials_provider_sso_failure_token_expired, s_credentials_prov AWS_STATIC_STRING_FROM_LITERAL( s_valid_sso_token, - "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2099-03-12T05:35:19Z\"}"); + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"9999-03-12T05:35:19Z\"}"); static int s_credentials_provider_sso_request_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index ce4a8ae0..187c3b91 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -295,14 +295,14 @@ AWS_STATIC_STRING_FROM_LITERAL( "sso_region = us-west-2\n"); AWS_STATIC_STRING_FROM_LITERAL( s_valid_sso_token, - "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2099-03-12T05:35:19Z\"}"); + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"9999-03-12T05:35:19Z\"}"); AWS_STATIC_STRING_FROM_LITERAL( s_expired_sso_token, "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); AWS_STATIC_STRING_FROM_LITERAL(s_good_token, "ValidAccessToken"); -static uint64_t s_good_token_expiration = 4076976919; +static uint64_t s_good_token_expiration = 253376832919; static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_mock_token_provider_sso_tester_init(allocator); From b55ff7152b530a3ac520500abbaaddd1201172b6 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 11:58:09 -0700 Subject: [PATCH 56/93] update log --- source/token_provider_sso_profile.c | 5 +++-- source/token_provider_sso_session.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 81bf9cd4..4889b06d 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -41,9 +41,10 @@ static int s_token_provider_profile_get_token_async( if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) cached token is expired. difference in time is %ld", + "(id=%p) cached token is expired. current time=%lf, expiration time=%lf", (void *)provider, - aws_date_time_diff(&sso_token->expiration, &now)); + aws_date_time_as_epoch_secs(&sso_token->expiration), + aws_date_time_as_epoch_secs(&now)); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index a99b6958..f2df5d25 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -42,9 +42,10 @@ static int s_token_provider_sso_session_get_token_async( if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) cached token is expired. difference in time is %ld", + "(id=%p) cached token is expired. current time=%lf, expiration time=%lf", (void *)provider, - aws_date_time_diff(&sso_token->expiration, &now)); + aws_date_time_as_epoch_secs(&sso_token->expiration), + aws_date_time_as_epoch_secs(&now)); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } From d45acfb371d5ad25664d4bc4b692838ab066a6ca Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 12:09:53 -0700 Subject: [PATCH 57/93] update log --- source/token_provider_sso_profile.c | 6 +++--- source/token_provider_sso_session.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 4889b06d..b9b85f4e 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -41,10 +41,10 @@ static int s_token_provider_profile_get_token_async( if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) cached token is expired. current time=%lf, expiration time=%lf", + "(id=%p) cached token is expired. current time=%ld, expiration time=%ld", (void *)provider, - aws_date_time_as_epoch_secs(&sso_token->expiration), - aws_date_time_as_epoch_secs(&now)); + sso_token->expiration.timestamp, + now.timestamp); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index f2df5d25..e3a9d08c 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -42,10 +42,10 @@ static int s_token_provider_sso_session_get_token_async( if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) cached token is expired. current time=%lf, expiration time=%lf", + "(id=%p) cached token is expired. current time=%ld, expiration time=%ld", (void *)provider, - aws_date_time_as_epoch_secs(&sso_token->expiration), - aws_date_time_as_epoch_secs(&now)); + sso_token->expiration.timestamp, + now.timestamp); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } From cc9c03f530c575c2227433a8af363b86e03e51ce Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 13:12:50 -0700 Subject: [PATCH 58/93] mock system clock for tests --- include/aws/auth/credentials.h | 9 ++++++- source/credentials_provider_sso.c | 2 ++ source/token_provider_sso_profile.c | 27 ++++++++++++------- source/token_provider_sso_session.c | 25 +++++++++++------- tests/credentials_provider_sso_tests.c | 35 +++++++++++++++++-------- tests/token_provider_sso_tests.c | 36 ++++++++++++++++---------- 6 files changed, 89 insertions(+), 45 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 3384add7..c546fc6c 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -386,8 +386,9 @@ struct aws_credentials_provider_sso_options { */ struct aws_tls_ctx *tls_ctx; - /* For mocking the http layer in tests, leave NULL otherwise */ + /* For mocking, leave NULL otherwise */ struct aws_auth_http_system_vtable *function_table; + aws_io_clock_fn *system_clock_fn; }; /** @@ -600,6 +601,9 @@ struct aws_token_provider_sso_profile_options { * Override path to the profile config file (~/.aws/config by default) */ struct aws_byte_cursor config_file_name_override; + + /* For mocking, leave NULL otherwise */ + aws_io_clock_fn *system_clock_fn; }; /** @@ -628,6 +632,9 @@ struct aws_token_provider_sso_session_options { * Client TLS context to use for any network connections made. */ struct aws_tls_ctx *tls_ctx; + + /* For mocking, leave NULL otherwise */ + aws_io_clock_fn *system_clock_fn; }; AWS_EXTERN_C_BEGIN diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 183aaf8a..c00c9424 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -680,6 +680,7 @@ static struct sso_parameters *s_parameters_new( .profile_name_override = options->profile_name_override, .bootstrap = options->bootstrap, .tls_ctx = options->tls_ctx, + .system_clock_fn = options->system_clock_fn, }; parameters->token_provider = aws_token_provider_new_sso_session(allocator, &token_provider_options); if (!parameters->token_provider) { @@ -697,6 +698,7 @@ static struct sso_parameters *s_parameters_new( struct aws_token_provider_sso_profile_options token_provider_options = { .config_file_name_override = options->config_file_name_override, .profile_name_override = options->profile_name_override, + .system_clock_fn = options->system_clock_fn, }; parameters->token_provider = aws_token_provider_new_sso_profile(allocator, &token_provider_options); diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index b9b85f4e..524502c0 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -8,6 +8,8 @@ #include #include #include +#include + #ifdef _MSC_VER /* allow non-constant declared initializers. */ # pragma warning(disable : 4204) @@ -18,6 +20,8 @@ */ struct aws_token_provider_profile_impl { struct aws_string *token_file_path; + + aws_io_clock_fn *system_clock_fn; }; static int s_token_provider_profile_get_token_async( @@ -36,15 +40,14 @@ static int s_token_provider_profile_get_token_async( } /* check token expiration. */ - struct aws_date_time now; - aws_date_time_init_now(&now); - if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) cached token is expired. current time=%ld, expiration time=%ld", - (void *)provider, - sso_token->expiration.timestamp, - now.timestamp); + uint64_t now_ms = UINT64_MAX; + if (impl->system_clock_fn(&now_ms) != AWS_OP_SUCCESS) { + goto done; + } + uint64_t now_seconds = aws_timestamp_convert(now_ms, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); + + if (aws_date_time_as_epoch_secs(&sso_token->expiration) - now_seconds < 0) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired."); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } @@ -169,7 +172,11 @@ struct aws_credentials_provider *aws_token_provider_new_sso_profile( aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_profile_vtable, impl); impl->token_file_path = aws_string_new_from_string(allocator, token_path); provider->shutdown_options = options->shutdown_options; - + if (options->system_clock_fn) { + impl->system_clock_fn = options->system_clock_fn; + } else { + impl->system_clock_fn = aws_sys_clock_get_ticks; + } aws_string_destroy(token_path); return provider; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index e3a9d08c..09416d77 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -8,6 +8,8 @@ #include #include #include +#include + #ifdef _MSC_VER /* allow non-constant declared initializers. */ # pragma warning(disable : 4204) @@ -18,6 +20,7 @@ */ struct aws_token_provider_sso_session_impl { struct aws_string *token_file_path; + aws_io_clock_fn *system_clock_fn; }; static int s_token_provider_sso_session_get_token_async( @@ -37,15 +40,14 @@ static int s_token_provider_sso_session_get_token_async( } /* check token expiration. */ - struct aws_date_time now; - aws_date_time_init_now(&now); - if (aws_date_time_diff(&sso_token->expiration, &now) < 0) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) cached token is expired. current time=%ld, expiration time=%ld", - (void *)provider, - sso_token->expiration.timestamp, - now.timestamp); + uint64_t now_nano = UINT64_MAX; + if (impl->system_clock_fn(&now_nano) != AWS_OP_SUCCESS) { + goto done; + } + uint64_t now_seconds = aws_timestamp_convert(now_nano, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); + + if (aws_date_time_as_epoch_secs(&sso_token->expiration) - now_seconds < 0) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired."); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } @@ -215,6 +217,11 @@ struct aws_credentials_provider *aws_token_provider_new_sso_session( aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_sso_session_vtable, impl); impl->token_file_path = aws_string_new_from_string(allocator, token_path); provider->shutdown_options = options->shutdown_options; + if (options->system_clock_fn) { + impl->system_clock_fn = options->system_clock_fn; + } else { + impl->system_clock_fn = aws_sys_clock_get_ticks; + } aws_string_destroy(token_path); return provider; diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index b4480886..e7e2d11c 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -9,6 +9,7 @@ #include "shared_credentials_test_definitions.h" #include +#include #include #include @@ -386,8 +387,10 @@ AWS_TEST_CASE(credentials_provider_sso_failure_token_missing, s_credentials_prov AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); AWS_STATIC_STRING_FROM_LITERAL( - s_expired_sso_token, + s_sso_token, "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); +static uint64_t s_sso_token_expiration_s = 1426138519; + static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -403,7 +406,7 @@ static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_byte_buf content_buf; struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); @@ -416,6 +419,9 @@ static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); aws_string_destroy(config_file_contents); + uint64_t nano_expiration = + aws_timestamp_convert(s_sso_token_expiration_s + 100, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); + mock_aws_set_system_time(nano_expiration); struct aws_credentials_provider_sso_options options = { .bootstrap = credentials_provider_http_mock_tester.bootstrap, .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, @@ -425,6 +431,7 @@ static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, .shutdown_user_data = NULL, }, + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); @@ -452,10 +459,6 @@ static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator } AWS_TEST_CASE(credentials_provider_sso_failure_token_expired, s_credentials_provider_sso_failure_token_expired); -AWS_STATIC_STRING_FROM_LITERAL( - s_valid_sso_token, - "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"9999-03-12T05:35:19Z\"}"); - static int s_credentials_provider_sso_request_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -472,7 +475,7 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_byte_buf content_buf; struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); @@ -485,6 +488,7 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); aws_string_destroy(config_file_contents); + mock_aws_set_system_time(0); struct aws_credentials_provider_sso_options options = { .bootstrap = credentials_provider_http_mock_tester.bootstrap, .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, @@ -494,6 +498,7 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, .shutdown_user_data = NULL, }, + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); @@ -536,7 +541,7 @@ static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocat ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_byte_buf content_buf; struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); @@ -552,6 +557,7 @@ static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocat struct aws_byte_cursor bad_json_cursor = aws_byte_cursor_from_string(s_bad_json_response); aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &bad_json_cursor); + mock_aws_set_system_time(0); struct aws_credentials_provider_sso_options options = { .bootstrap = credentials_provider_http_mock_tester.bootstrap, .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, @@ -561,6 +567,7 @@ static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocat .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, .shutdown_user_data = NULL, }, + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); @@ -602,7 +609,7 @@ static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allo ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_byte_buf content_buf; struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); @@ -618,6 +625,7 @@ static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allo struct aws_byte_cursor bad_json_cursor = aws_byte_cursor_from_string(s_bad_json_response); aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &bad_json_cursor); + mock_aws_set_system_time(0); struct aws_credentials_provider_sso_options options = { .bootstrap = credentials_provider_http_mock_tester.bootstrap, .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, @@ -627,6 +635,7 @@ static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allo .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, .shutdown_user_data = NULL, }, + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); @@ -667,7 +676,7 @@ static int s_credentials_provider_sso_basic_success(struct aws_allocator *alloca ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_byte_buf content_buf; struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); @@ -684,6 +693,7 @@ static int s_credentials_provider_sso_basic_success(struct aws_allocator *alloca struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + mock_aws_set_system_time(0); struct aws_credentials_provider_sso_options options = { .bootstrap = credentials_provider_http_mock_tester.bootstrap, .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, @@ -693,6 +703,7 @@ static int s_credentials_provider_sso_basic_success(struct aws_allocator *alloca .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, .shutdown_user_data = NULL, }, + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); @@ -733,7 +744,7 @@ static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_byte_buf content_buf; struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_profile_config_contents)); @@ -750,6 +761,7 @@ static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + mock_aws_set_system_time(0); struct aws_credentials_provider_sso_options options = { .bootstrap = credentials_provider_http_mock_tester.bootstrap, .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, @@ -759,6 +771,7 @@ static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, .shutdown_user_data = NULL, }, + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 187c3b91..b679ab66 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -294,15 +295,13 @@ AWS_STATIC_STRING_FROM_LITERAL( "sso_start_url = https://d-123.awsapps.com/start\n" "sso_region = us-west-2\n"); AWS_STATIC_STRING_FROM_LITERAL( - s_valid_sso_token, - "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"9999-03-12T05:35:19Z\"}"); -AWS_STATIC_STRING_FROM_LITERAL( - s_expired_sso_token, + s_sso_token, "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); + AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); AWS_STATIC_STRING_FROM_LITERAL(s_good_token, "ValidAccessToken"); -static uint64_t s_good_token_expiration = 253376832919; +static uint64_t s_token_expiration_s = 1426138519; static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_mock_token_provider_sso_tester_init(allocator); @@ -316,15 +315,16 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_session_config_contents)); - + mock_aws_set_system_time(0); struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), .tls_ctx = s_tester.tls_ctx, .bootstrap = s_tester.bootstrap, + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_token_provider_new_sso_session(allocator, &options); @@ -339,7 +339,7 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); ASSERT_INT_EQUALS( - aws_credentials_get_expiration_timepoint_seconds(callback_results.credentials), s_good_token_expiration); + aws_credentials_get_expiration_timepoint_seconds(callback_results.credentials), s_token_expiration_s); aws_get_credentials_test_callback_result_clean_up(&callback_results); aws_credentials_provider_release(provider); @@ -369,15 +369,18 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_session_config_contents)); - + uint64_t nano_expiration = + aws_timestamp_convert(s_token_expiration_s + 100, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); + mock_aws_set_system_time(nano_expiration); struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), .tls_ctx = s_tester.tls_ctx, .bootstrap = s_tester.bootstrap, + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_token_provider_new_sso_session(allocator, &options); @@ -396,7 +399,6 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * /* reset $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); aws_string_destroy(actual_home); - aws_string_destroy(token_path); aws_string_destroy(config_file_str); s_aws_mock_token_provider_sso_tester_cleanup(); @@ -418,13 +420,15 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_valid_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_profile_config_contents)); + mock_aws_set_system_time(0); struct aws_token_provider_sso_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_token_provider_new_sso_profile(allocator, &options); @@ -439,7 +443,7 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); ASSERT_INT_EQUALS( - aws_credentials_get_expiration_timepoint_seconds(callback_results.credentials), s_good_token_expiration); + aws_credentials_get_expiration_timepoint_seconds(callback_results.credentials), s_token_expiration_s); aws_get_credentials_test_callback_result_clean_up(&callback_results); aws_credentials_provider_release(provider); @@ -469,13 +473,17 @@ static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allo struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_expired_sso_token)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_profile_config_contents)); + uint64_t nano_expiration = + aws_timestamp_convert(s_token_expiration_s + 100, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); + mock_aws_set_system_time(nano_expiration); struct aws_token_provider_sso_profile_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .system_clock_fn = mock_aws_get_system_time, }; struct aws_credentials_provider *provider = aws_token_provider_new_sso_profile(allocator, &options); From 54f2353200957c57e853dcd747790b86dae7470e Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 13:16:53 -0700 Subject: [PATCH 59/93] fix warning --- source/token_provider_sso_profile.c | 2 +- source/token_provider_sso_session.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 524502c0..dbac21ef 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -47,7 +47,7 @@ static int s_token_provider_profile_get_token_async( uint64_t now_seconds = aws_timestamp_convert(now_ms, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); if (aws_date_time_as_epoch_secs(&sso_token->expiration) - now_seconds < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired."); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 09416d77..6ef363c2 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -47,7 +47,7 @@ static int s_token_provider_sso_session_get_token_async( uint64_t now_seconds = aws_timestamp_convert(now_nano, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); if (aws_date_time_as_epoch_secs(&sso_token->expiration) - now_seconds < 0) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired."); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } From 71d5736837e7375e795d302d6dac3c4b4c230cec Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 13:28:04 -0700 Subject: [PATCH 60/93] remove path for windows --- tests/sso_token_util_tests.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index fec869e7..b47469b7 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -16,7 +16,7 @@ static int s_parse_token_location_url_test(struct aws_allocator *allocator, void struct aws_byte_cursor token_cursor = aws_byte_cursor_from_string(token_path); struct aws_byte_cursor expected_token_cursor = - aws_byte_cursor_from_c_str("/.aws/sso/cache/13f9d35043871d073ab260e020f0ffde092cb14b.json"); + aws_byte_cursor_from_c_str("13f9d35043871d073ab260e020f0ffde092cb14b.json"); struct aws_byte_cursor find_cursor; ASSERT_SUCCESS(aws_byte_cursor_find_exact(&token_cursor, &expected_token_cursor, &find_cursor)); @@ -34,7 +34,7 @@ static int s_parse_token_location_session_test(struct aws_allocator *allocator, struct aws_string *token_path = aws_construct_token_path(allocator, session); struct aws_byte_cursor token_cursor = aws_byte_cursor_from_string(token_path); struct aws_byte_cursor expected_token_cursor = - aws_byte_cursor_from_c_str("/.aws/sso/cache/d033e22ae348aeb5660fc2140aec35850c4da997.json"); + aws_byte_cursor_from_c_str("d033e22ae348aeb5660fc2140aec35850c4da997.json"); struct aws_byte_cursor find_cursor; ASSERT_SUCCESS(aws_byte_cursor_find_exact(&token_cursor, &expected_token_cursor, &find_cursor)); From 370dfabda8f3ff5786f1ebd32add4832ed40e734 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 17 Mar 2023 17:34:32 -0700 Subject: [PATCH 61/93] stop trying to reset home --- source/credentials_provider_sso.c | 4 +-- tests/credentials_provider_sso_tests.c | 34 +++----------------------- tests/token_provider_sso_tests.c | 23 ----------------- 3 files changed, 5 insertions(+), 56 deletions(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index c00c9424..ae639090 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -412,7 +412,7 @@ static void s_on_get_token_callback(struct aws_credentials *credentials, int err s_finalize_get_credentials_query(user_data); return; } - AWS_LOGF_ERROR( + AWS_LOGF_INFO( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): successfully accquired a token", (void *)sso_user_data->provider); /* clear the result from previous attempt */ s_user_data_reset_request_and_response(sso_user_data); @@ -435,7 +435,7 @@ static void s_on_acquire_connection(struct aws_http_connection *connection, int s_finalize_get_credentials_query(user_data); return; } - AWS_LOGF_ERROR( + AWS_LOGF_INFO( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): successfully accquired a connection", (void *)sso_user_data->provider); diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c index e7e2d11c..ffc9b36c 100644 --- a/tests/credentials_provider_sso_tests.c +++ b/tests/credentials_provider_sso_tests.c @@ -397,8 +397,6 @@ static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator aws_credentials_provider_http_mock_tester_init(allocator); credentials_provider_http_mock_tester.is_request_successful = false; - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -450,10 +448,6 @@ static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator aws_credentials_provider_http_mock_wait_for_shutdown_callback(); aws_credentials_provider_http_mock_tester_cleanup(); - - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); aws_string_destroy(token_path); return 0; } @@ -466,8 +460,6 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo credentials_provider_http_mock_tester.is_request_successful = false; credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_400_BAD_REQUEST; - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -517,9 +509,6 @@ static int s_credentials_provider_sso_request_failure(struct aws_allocator *allo aws_credentials_provider_http_mock_tester_cleanup(); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); aws_string_destroy(token_path); return 0; } @@ -531,8 +520,6 @@ static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocat aws_credentials_provider_http_mock_tester_init(allocator); - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -586,9 +573,6 @@ static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocat aws_credentials_provider_http_mock_tester_cleanup(); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); aws_string_destroy(token_path); return 0; } @@ -599,8 +583,7 @@ static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allo aws_credentials_provider_http_mock_tester_init(allocator); credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR; - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); + /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -654,9 +637,6 @@ static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allo aws_credentials_provider_http_mock_tester_cleanup(); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); aws_string_destroy(token_path); return 0; } @@ -666,8 +646,7 @@ static int s_credentials_provider_sso_basic_success(struct aws_allocator *alloca (void)ctx; aws_credentials_provider_http_mock_tester_init(allocator); - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); + /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -722,9 +701,6 @@ static int s_credentials_provider_sso_basic_success(struct aws_allocator *alloca aws_credentials_provider_http_mock_tester_cleanup(); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); aws_string_destroy(token_path); return 0; } @@ -734,8 +710,7 @@ static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator (void)ctx; aws_credentials_provider_http_mock_tester_init(allocator); - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); + /* redirect $HOME */ ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); @@ -790,9 +765,6 @@ static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator aws_credentials_provider_http_mock_tester_cleanup(); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); aws_string_destroy(token_path); return 0; } diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index b679ab66..5cd459db 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -307,8 +307,6 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * s_aws_mock_token_provider_sso_tester_init(allocator); /* redirect $HOME */ - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ @@ -344,10 +342,6 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * aws_get_credentials_test_callback_result_clean_up(&callback_results); aws_credentials_provider_release(provider); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); - aws_string_destroy(token_path); aws_string_destroy(config_file_str); s_aws_mock_token_provider_sso_tester_cleanup(); @@ -361,8 +355,6 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * s_aws_mock_token_provider_sso_tester_init(allocator); /* redirect $HOME */ - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ @@ -396,9 +388,6 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * aws_credentials_provider_release(provider); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); aws_string_destroy(token_path); aws_string_destroy(config_file_str); s_aws_mock_token_provider_sso_tester_cleanup(); @@ -412,8 +401,6 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo s_aws_mock_token_provider_sso_tester_init(allocator); /* redirect $HOME */ - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ @@ -448,10 +435,6 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo aws_get_credentials_test_callback_result_clean_up(&callback_results); aws_credentials_provider_release(provider); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); - aws_string_destroy(token_path); aws_string_destroy(config_file_str); s_aws_mock_token_provider_sso_tester_cleanup(); @@ -465,8 +448,6 @@ static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allo s_aws_mock_token_provider_sso_tester_init(allocator); /* redirect $HOME */ - struct aws_string *actual_home; - aws_get_environment_value(allocator, s_home_env_var, &actual_home); ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ @@ -499,10 +480,6 @@ static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allo aws_credentials_provider_release(provider); - /* reset $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, actual_home)); - aws_string_destroy(actual_home); - aws_string_destroy(token_path); aws_string_destroy(config_file_str); s_aws_mock_token_provider_sso_tester_cleanup(); From de8d83e3a564399b3e63fe163fedb7baa2aadaa1 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 09:04:55 -0700 Subject: [PATCH 62/93] change to aws config --- include/aws/auth/credentials.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index c546fc6c..009a1eab 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -90,8 +90,8 @@ struct aws_credentials_provider_environment_options { }; /** - * Configuration options for a provider that sources credentials from the aws profile and credentials files - * (by default ~/.aws/profile and ~/.aws/credentials) + * Configuration options for a provider that sources credentials from the aws config and credentials files + * (by default ~/.aws/config and ~/.aws/credentials) */ struct aws_credentials_provider_profile_options { struct aws_credentials_provider_shutdown_options shutdown_options; @@ -114,7 +114,7 @@ struct aws_credentials_provider_profile_options { /** * (Optional) * Use a cached merged profile collection. A merge collection has both config file - * (~/.aws/profile) and credentials file based profile collection (~/.aws/credentials) using + * (~/.aws/config) and credentials file based profile collection (~/.aws/credentials) using * `aws_profile_collection_new_from_merge`. * If this option is provided, `config_file_name_override` and `credentials_file_name_override` will be ignored. */ @@ -493,7 +493,7 @@ struct aws_credentials_provider_chain_default_options { /** * (Optional) * Use a cached merged profile collection. A merge collection has both config file - * (~/.aws/profile) and credentials file based profile collection (~/.aws/credentials) using + * (~/.aws/config) and credentials file based profile collection (~/.aws/credentials) using * `aws_profile_collection_new_from_merge`. * If this option is provided, `config_file_name_override` and `credentials_file_name_override` will be ignored. */ @@ -587,7 +587,7 @@ struct aws_credentials_provider_cognito_options { /** * Configuration options for a provider that sources sso token information from the aws profile (by default - * ~/.aws/profile) and token from ~/.aws/sso/cache/.json. + * ~/.aws/config) and token from ~/.aws/sso/cache/.json. */ struct aws_token_provider_sso_profile_options { struct aws_credentials_provider_shutdown_options shutdown_options; @@ -608,7 +608,7 @@ struct aws_token_provider_sso_profile_options { /** * Configuration options for a provider that sources sso token information from the aws profile (by default - * ~/.aws/profile) and token from ~/.aws/sso/cache/.json. + * ~/.aws/config) and token from ~/.aws/sso/cache/.json. */ struct aws_token_provider_sso_session_options { struct aws_credentials_provider_shutdown_options shutdown_options; From acda1c870318bec12fc105526929032e39acfcdd Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 09:25:31 -0700 Subject: [PATCH 63/93] Fix new token --- include/aws/auth/credentials.h | 1 + source/auth.c | 1 + source/credentials.c | 10 +++++----- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 009a1eab..7f293651 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -1145,6 +1145,7 @@ struct aws_credentials_provider *aws_token_provider_new_sso_profile( /** * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json + * Note: Token refresh is not currently supported * * @param allocator memory allocator to use for all memory allocation * @param options provider-specific configuration options diff --git a/source/auth.c b/source/auth.c index 3ac857d5..5a0fbc8c 100644 --- a/source/auth.c +++ b/source/auth.c @@ -103,6 +103,7 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_AUTH( AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE, "Valid credentials could not be sourced by the sso credentials provider"), + }; /* clang-format on */ diff --git a/source/credentials.c b/source/credentials.c index ad87f174..c3d1361b 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -25,7 +25,7 @@ struct aws_token_identity { struct aws_string *token; }; -enum IdentityType { +enum aws_identity_type { AWS_CREDENTIALS_IDENTITY, TOKEN_IDENTITY, ANONYMOUS_IDENTITY, @@ -69,7 +69,7 @@ struct aws_credentials { */ uint64_t expiration_timepoint_seconds; - enum IdentityType identity_type; + enum aws_identity_type identity_type; union { struct aws_credentials_identity credentials_identity; struct aws_token_identity token_identity; @@ -168,7 +168,7 @@ static void s_aws_credentials_destroy(struct aws_credentials *credentials) { aws_ecc_key_pair_release(credentials->identity.ecc_identity.ecc_key); break; case TOKEN_IDENTITY: - aws_string_destroy(credentials->identity.token_identity.token); + aws_string_destroy_secure(credentials->identity.token_identity.token); break; default: break; @@ -380,8 +380,8 @@ struct aws_credentials *aws_credentials_new_token( credentials->allocator = allocator; aws_atomic_init_int(&credentials->ref_count, 1); credentials->identity_type = TOKEN_IDENTITY; - struct aws_credentials_identity *credentials_identity = &credentials->identity.credentials_identity; - credentials_identity->access_key_id = aws_string_new_from_array(allocator, token.ptr, token.len); + struct aws_token_identity *token_identity = &credentials->identity.token_identity; + token_identity->token = aws_string_new_from_array(allocator, token.ptr, token.len); credentials->expiration_timepoint_seconds = expiration_timepoint_in_seconds; return credentials; } From d19452143cde3435d6c5886c757c15882680f6a3 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 11:40:24 -0700 Subject: [PATCH 64/93] more tests --- source/sso_token_utils.c | 4 +- source/token_provider_sso_profile.c | 7 +-- source/token_provider_sso_session.c | 6 +-- tests/CMakeLists.txt | 4 ++ tests/credentials_provider_utils.c | 2 +- tests/credentials_provider_utils.h | 2 +- tests/sso_token_util_tests.c | 70 ++++++++++++++++++++++++++++- 7 files changed, 84 insertions(+), 11 deletions(-) diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index 23c3722b..1ad36076 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -39,6 +39,7 @@ struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, con if (aws_byte_buf_init_copy_from_cursor(&token_path_buf, allocator, home_dir_cursor)) { goto cleanup; } + /* append sso cache directory */ if (aws_byte_buf_append_dynamic(&token_path_buf, &cache_dir_cursor)) { goto cleanup; @@ -157,8 +158,7 @@ struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocato aws_json_value_destroy(document_root); aws_byte_buf_clean_up(&file_contents_buf); if (!success) { - aws_string_destroy(token->token); - aws_mem_release(allocator, token); + aws_sso_token_destroy(token); token = NULL; } return token; diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index dbac21ef..7b0694ee 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -40,11 +40,11 @@ static int s_token_provider_profile_get_token_async( } /* check token expiration. */ - uint64_t now_ms = UINT64_MAX; - if (impl->system_clock_fn(&now_ms) != AWS_OP_SUCCESS) { + uint64_t now_ns = UINT64_MAX; + if (impl->system_clock_fn(&now_ns) != AWS_OP_SUCCESS) { goto done; } - uint64_t now_seconds = aws_timestamp_convert(now_ms, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); + uint64_t now_seconds = aws_timestamp_convert(now_ns, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); if (aws_date_time_as_epoch_secs(&sso_token->expiration) - now_seconds < 0) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); @@ -141,6 +141,7 @@ static struct aws_string *s_construct_profile_token_path( if (!token_path) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to construct token path in profile"); + goto cleanup; } cleanup: diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 6ef363c2..d4d93ac8 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -40,11 +40,11 @@ static int s_token_provider_sso_session_get_token_async( } /* check token expiration. */ - uint64_t now_nano = UINT64_MAX; - if (impl->system_clock_fn(&now_nano) != AWS_OP_SUCCESS) { + uint64_t now_ns = UINT64_MAX; + if (impl->system_clock_fn(&now_ns) != AWS_OP_SUCCESS) { goto done; } - uint64_t now_seconds = aws_timestamp_convert(now_nano, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); + uint64_t now_seconds = aws_timestamp_convert(now_ns, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); if (aws_date_time_as_epoch_secs(&sso_token->expiration) - now_seconds < 0) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1ffd5273..beb29641 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -115,6 +115,10 @@ add_net_test_case(sso_token_provider_profile_expired_token) add_net_test_case(parse_token_location_url_test) add_net_test_case(parse_token_location_session_test) add_net_test_case(parse_sso_token_valid) +add_net_test_case(parse_sso_token_invalid) +add_net_test_case(parse_sso_token_invalid_missing_access_token) +add_net_test_case(parse_sso_token_missing_expires_at) +add_net_test_case(parse_sso_token_invalid_expires_at) add_net_test_case(credentials_provider_sso_failed_invalid_config) add_net_test_case(credentials_provider_sso_create_destroy_valid_config) diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index 2a370568..4888c98c 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -508,7 +508,7 @@ int aws_create_directory_components(struct aws_allocator *allocator, const struc } /* - * Mocked Functions for Tests + * Mocked HTTP connection manager for tests */ struct aws_credentials_provider_http_mock_tester credentials_provider_http_mock_tester; diff --git a/tests/credentials_provider_utils.h b/tests/credentials_provider_utils.h index 39251346..372fa33b 100644 --- a/tests/credentials_provider_utils.h +++ b/tests/credentials_provider_utils.h @@ -101,7 +101,7 @@ struct aws_credentials_provider *aws_credentials_provider_new_null( int aws_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path); /** - * Mocked Function + * Mocked HTTP connection manager for tests */ struct aws_credentials_provider_http_mock_tester { struct aws_tls_ctx *tls_ctx; diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index b47469b7..909ba61b 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -54,7 +54,7 @@ static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { (void)ctx; aws_auth_library_init(allocator); struct aws_string *file_path = aws_create_process_unique_file_name(allocator); - ASSERT_TRUE(aws_create_profile_file(file_path, s_valid_token_json) == AWS_OP_SUCCESS); + ASSERT_SUCCESS(aws_create_profile_file(file_path, s_valid_token_json)); struct aws_sso_token *token = aws_sso_token_new_from_file(allocator, file_path); ASSERT_TRUE(aws_string_eq_c_str(token->token, "string")); ASSERT_INT_EQUALS((uint64_t)aws_date_time_as_epoch_secs(&token->expiration), 1573704345); @@ -64,3 +64,71 @@ static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { return AWS_OP_SUCCESS; } AWS_TEST_CASE(parse_sso_token_valid, s_parse_sso_token_valid); + +AWS_STATIC_STRING_FROM_LITERAL(s_invalid_token_json, "asdasdas"); +static int s_parse_sso_token_invalid(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + aws_auth_library_init(allocator); + struct aws_string *file_path = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(file_path, s_invalid_token_json)); + ASSERT_NULL(aws_sso_token_new_from_file(allocator, file_path)); + ASSERT_INT_EQUALS(AWS_AUTH_SSO_TOKEN_INVALID, aws_last_error()); + aws_string_destroy(file_path); + aws_auth_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(parse_sso_token_invalid, s_parse_sso_token_invalid); + +AWS_STATIC_STRING_FROM_LITERAL( + s_missing_access_token_json, + "{\"expiresAt\": \"2019-11-14T04:05:45Z\",\"refreshToken\": \"string\",\"clientId\": " + "\"123321\",\"clientSecret\": \"ABCDE123\",\"registrationExpiresAt\": " + "\"2022-03-06T19:53:17Z\",\"region\": \"us-west-2\",\"startUrl\": \"https://d-abc123.awsapps.com/start\"}"); +static int s_parse_sso_token_invalid_missing_access_token(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + aws_auth_library_init(allocator); + struct aws_string *file_path = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(file_path, s_missing_access_token_json)); + ASSERT_NULL(aws_sso_token_new_from_file(allocator, file_path)); + ASSERT_INT_EQUALS(AWS_AUTH_SSO_TOKEN_INVALID, aws_last_error()); + aws_string_destroy(file_path); + aws_auth_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(parse_sso_token_invalid_missing_access_token, s_parse_sso_token_invalid_missing_access_token); + +AWS_STATIC_STRING_FROM_LITERAL( + s_missing_expires_at_json, + "{\"accessToken\": \"string\",\"refreshToken\": \"string\",\"clientId\": " + "\"123321\",\"clientSecret\": \"ABCDE123\",\"registrationExpiresAt\": " + "\"2022-03-06T19:53:17Z\",\"region\": \"us-west-2\",\"startUrl\": \"https://d-abc123.awsapps.com/start\"}"); +static int s_parse_sso_token_missing_expires_at(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + aws_auth_library_init(allocator); + struct aws_string *file_path = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(file_path, s_missing_expires_at_json)); + ASSERT_NULL(aws_sso_token_new_from_file(allocator, file_path)); + ASSERT_INT_EQUALS(AWS_AUTH_SSO_TOKEN_INVALID, aws_last_error()); + aws_string_destroy(file_path); + aws_auth_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(parse_sso_token_missing_expires_at, s_parse_sso_token_missing_expires_at); + +AWS_STATIC_STRING_FROM_LITERAL( + s_invalid_expires_at_json, + "{\"accessToken\": \"string\",\"expiresAt\": \"1234567\",\"refreshToken\": \"string\",\"clientId\": " + "\"123321\",\"clientSecret\": \"ABCDE123\",\"registrationExpiresAt\": " + "\"2022-03-06T19:53:17Z\",\"region\": \"us-west-2\",\"startUrl\": \"https://d-abc123.awsapps.com/start\"}"); +static int s_parse_sso_token_invalid_expires_at(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + aws_auth_library_init(allocator); + struct aws_string *file_path = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(file_path, s_invalid_expires_at_json)); + ASSERT_NULL(aws_sso_token_new_from_file(allocator, file_path)); + ASSERT_INT_EQUALS(AWS_AUTH_SSO_TOKEN_INVALID, aws_last_error()); + aws_string_destroy(file_path); + aws_auth_library_clean_up(); + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(parse_sso_token_invalid_expires_at, s_parse_sso_token_invalid_expires_at); From e55d86c97792c854c288c3ca42ce24b69a34078b Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 11:50:09 -0700 Subject: [PATCH 65/93] refactor token provider tests --- tests/sso_token_util_tests.c | 2 +- tests/token_provider_sso_tests.c | 47 ++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index 909ba61b..80b40c5d 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -65,7 +65,7 @@ static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { } AWS_TEST_CASE(parse_sso_token_valid, s_parse_sso_token_valid); -AWS_STATIC_STRING_FROM_LITERAL(s_invalid_token_json, "asdasdas"); +AWS_STATIC_STRING_FROM_LITERAL(s_invalid_token_json, "invalid json"); static int s_parse_sso_token_invalid(struct aws_allocator *allocator, void *ctx) { (void)ctx; aws_auth_library_init(allocator); diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 5cd459db..677c3066 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -171,6 +171,20 @@ static int s_sso_token_provider_sso_session_invalid_config_test(struct aws_alloc "access_key=fake_secret_key\nsso_region=us-east-" "1\nsso_start_url=url"), }, + { + .name = "sso_session with without sso_region", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\n[sso-session " + "dev]\nsso_start_url=url"), + }, + { + .name = "sso_session with without sso_start_url", + .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[profile " + "default]\naws_access_key_id=fake_access_key\naws_secret_" + "access_key=fake_secret_key\nsso_session=dev\n[sso-session " + "dev]\nsso_region=us-east-1"), + }, { .name = "sso_session with different profile region", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( @@ -232,21 +246,21 @@ static int s_sso_token_provider_sso_session_valid_config_test(struct aws_allocat "1\nsso_start_url=url"), }, { - .name = "with sso_region", + .name = "with profile sso_region", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_region=us-east-1\nsso_" "session=dev\n[sso-session dev]\nsso_region=us-east-" "1\nsso_start_url=url"), }, { - .name = "with sso_start_url", + .name = "with profile sso_start_url", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_start_url=url\nsso_" "session=dev\n[sso-session dev]\nsso_region=us-east-" "1\nsso_start_url=url"), }, { - .name = "with profile", + .name = "with profile sso_region and sso_start_url", .text = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL( "[default]\naws_access_key_id=fake_access_key\naws_secret_" "access_key=fake_secret_key\nsso_region=us-east-1\nsso_start_url=url\nsso_" @@ -330,9 +344,8 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * struct aws_get_credentials_test_callback_result callback_results; aws_get_credentials_test_callback_result_init(&callback_results, 1); - int get_async_result = - aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); - ASSERT_TRUE(get_async_result == AWS_OP_SUCCESS); + ASSERT_SUCCESS( + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results)); aws_wait_on_credentials_callback(&callback_results); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); @@ -380,12 +393,9 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * struct aws_get_credentials_test_callback_result callback_results; aws_get_credentials_test_callback_result_init(&callback_results, 1); - int get_async_result = - aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); - ASSERT_FALSE(get_async_result == AWS_OP_SUCCESS); - - ASSERT_INT_EQUALS(aws_last_error(), AWS_AUTH_SSO_TOKEN_EXPIRED); - + ASSERT_ERROR( + AWS_AUTH_SSO_TOKEN_EXPIRED, + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results)); aws_credentials_provider_release(provider); aws_string_destroy(token_path); @@ -423,9 +433,8 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo struct aws_get_credentials_test_callback_result callback_results; aws_get_credentials_test_callback_result_init(&callback_results, 1); - int get_async_result = - aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); - ASSERT_TRUE(get_async_result == AWS_OP_SUCCESS); + ASSERT_SUCCESS( + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results)); aws_wait_on_credentials_callback(&callback_results); ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); @@ -472,11 +481,9 @@ static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allo struct aws_get_credentials_test_callback_result callback_results; aws_get_credentials_test_callback_result_init(&callback_results, 1); - int get_async_result = - aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results); - ASSERT_FALSE(get_async_result == AWS_OP_SUCCESS); - - ASSERT_INT_EQUALS(aws_last_error(), AWS_AUTH_SSO_TOKEN_EXPIRED); + ASSERT_ERROR( + AWS_AUTH_SSO_TOKEN_EXPIRED, + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results)); aws_credentials_provider_release(provider); From 4e3d45ad5fddd16ea1982e8fe3dfcda2292b1f3f Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 11:55:14 -0700 Subject: [PATCH 66/93] remove sso credentials provider to split PR --- include/aws/auth/credentials.h | 49 -- source/credentials_provider_sso.c | 855 ------------------------- tests/CMakeLists.txt | 11 - tests/credentials_provider_sso_tests.c | 771 ---------------------- 4 files changed, 1686 deletions(-) delete mode 100644 source/credentials_provider_sso.c delete mode 100644 tests/credentials_provider_sso_tests.c diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 7f293651..0959561f 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -355,42 +355,6 @@ struct aws_credentials_provider_sts_web_identity_options { struct aws_auth_http_system_vtable *function_table; }; -/* - * Configuration for the SSOCredentialsProvider that sends a GetRoleCredentialsRequest to the AWS Single - * Sign-On Service to maintain short-lived sessions to use for authentication. - * - * https://docs.aws.amazon.com/sdkref/latest/guide/feature-sso-credentials.html - */ -struct aws_credentials_provider_sso_options { - struct aws_credentials_provider_shutdown_options shutdown_options; - - /* - * Override of what profile to use to source credentials from ('default' by default) - */ - struct aws_byte_cursor profile_name_override; - - /* - * Override path to the profile config file (~/.aws/config by default) - */ - struct aws_byte_cursor config_file_name_override; - - /* - * Connection bootstrap to use for any network connections made while sourcing credentials - * Required. - */ - struct aws_client_bootstrap *bootstrap; - - /* - * Client TLS context to use when querying SSO provider. - * Required. - */ - struct aws_tls_ctx *tls_ctx; - - /* For mocking, leave NULL otherwise */ - struct aws_auth_http_system_vtable *function_table; - aws_io_clock_fn *system_clock_fn; -}; - /** * Configuration options for the STS credentials provider */ @@ -1038,19 +1002,6 @@ struct aws_credentials_provider *aws_credentials_provider_new_sts_web_identity( struct aws_allocator *allocator, const struct aws_credentials_provider_sts_web_identity_options *options); -/** - * Creates a provider that sources credentials from SSO using a SSOToken. - * - * @param allocator memory allocator to use for all memory allocation - * @param options provider-specific configuration options - * - * @return the newly-constructed credentials provider, or NULL if an error occurred. - */ -AWS_AUTH_API -struct aws_credentials_provider *aws_credentials_provider_new_sso( - struct aws_allocator *allocator, - const struct aws_credentials_provider_sso_options *options); - /* * Creates a provider that sources credentials from running an external command or process * diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c deleted file mode 100644 index ae639090..00000000 --- a/source/credentials_provider_sso.c +++ /dev/null @@ -1,855 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -# pragma warning(disable : 4204) -# pragma warning(disable : 4232) -#endif /* _MSC_VER */ - -#define SSO_RESPONSE_SIZE_INITIAL 2048 -#define SSO_RESPONSE_SIZE_LIMIT 10000 -#define SSO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS 2 -#define SSO_CREDS_DEFAULT_DURATION_SECONDS 900 -#define SSO_MAX_ATTEMPTS 3 -#define SSO_RETRY_TIMEOUT_MS 100 - -struct aws_credentials_provider_sso_impl { - struct aws_http_connection_manager *connection_manager; - const struct aws_auth_http_system_vtable *function_table; - struct aws_string *endpoint; - struct aws_string *sso_account_id; - struct aws_string *sso_role_name; - struct aws_credentials_provider *token_provider; - struct aws_retry_strategy *retry_strategy; -}; - -/** - * sso_user_data - scratch data for each outstanding SSO query. - */ -struct sso_user_data { - /* immutable post-creation */ - struct aws_allocator *allocator; - struct aws_credentials_provider *provider; - aws_on_get_credentials_callback_fn *original_callback; - void *original_user_data; - - /* mutable */ - struct aws_http_connection *connection; - struct aws_http_message *request; - struct aws_byte_buf payload; - struct aws_retry_token *retry_token; - struct aws_byte_buf path_and_query; - struct aws_string *token; - - int status_code; - int error_code; - int attempt_count; -}; - -/* called in between retries. */ -static void s_user_data_reset_request_and_response(struct sso_user_data *user_data) { - aws_http_message_release(user_data->request); - user_data->request = NULL; - - aws_byte_buf_reset(&user_data->payload, true /* zero out */); - aws_string_destroy(user_data->token); - user_data->status_code = 0; - user_data->error_code = AWS_OP_SUCCESS; -} - -static void s_user_data_destroy(struct sso_user_data *user_data) { - if (user_data == NULL) { - return; - } - - s_user_data_reset_request_and_response(user_data); - - if (user_data->connection) { - struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; - - impl->function_table->aws_http_connection_manager_release_connection( - impl->connection_manager, user_data->connection); - } - - aws_byte_buf_clean_up(&user_data->payload); - aws_byte_buf_clean_up(&user_data->path_and_query); - aws_credentials_provider_release(user_data->provider); - aws_retry_token_release(user_data->retry_token); - aws_mem_release(user_data->allocator, user_data); -} - -static struct sso_user_data *s_user_data_new( - struct aws_credentials_provider *provider, - aws_on_get_credentials_callback_fn callback, - void *user_data) { - struct aws_credentials_provider_sso_impl *impl = provider->impl; - - struct sso_user_data *wrapped_user_data = aws_mem_calloc(provider->allocator, 1, sizeof(struct sso_user_data)); - wrapped_user_data->allocator = provider->allocator; - wrapped_user_data->provider = aws_credentials_provider_acquire(provider); - wrapped_user_data->original_user_data = user_data; - wrapped_user_data->original_callback = callback; - - struct aws_byte_cursor account_id_cursor = aws_byte_cursor_from_string(impl->sso_account_id); - struct aws_byte_cursor role_name_cursor = aws_byte_cursor_from_string(impl->sso_role_name); - struct aws_byte_cursor path_cursor = aws_byte_cursor_from_c_str("/federation/credentials?account_id="); - struct aws_byte_cursor role_name_param_cursor = aws_byte_cursor_from_c_str("&role_name="); - - if (aws_byte_buf_init_copy_from_cursor(&wrapped_user_data->path_and_query, provider->allocator, path_cursor) || - aws_byte_buf_append_encoding_uri_param(&wrapped_user_data->path_and_query, &account_id_cursor) || - aws_byte_buf_append_dynamic(&wrapped_user_data->path_and_query, &role_name_param_cursor) || - aws_byte_buf_append_encoding_uri_param(&wrapped_user_data->path_and_query, &role_name_cursor)) { - goto on_error; - } - - if (aws_byte_buf_init(&wrapped_user_data->payload, provider->allocator, SSO_RESPONSE_SIZE_INITIAL)) { - goto on_error; - } - - return wrapped_user_data; - -on_error: - s_user_data_destroy(wrapped_user_data); - - return NULL; -} - -/* - * No matter the result, this always gets called assuming that user_data is successfully allocated - */ -static void s_finalize_get_credentials_query(struct sso_user_data *user_data) { - /* Try to build credentials from whatever, if anything, was in the result */ - struct aws_credentials *credentials = NULL; - if (user_data->status_code == AWS_HTTP_STATUS_CODE_200_OK) { - struct aws_parse_credentials_from_json_doc_options parse_options = { - .access_key_id_name = "accessKeyId", - .secret_access_key_name = "secretAccessKey", - .token_name = "sessionToken", - .expiration_name = "expiration", - .top_level_object_name = "roleCredentials", - .token_required = true, - .expiration_required = true, - .expiration_format = AWS_PCEF_NUMBER_UNIX_EPOCH_MS, - }; - - credentials = aws_parse_credentials_from_json_document( - user_data->allocator, (const char *)user_data->payload.buffer, &parse_options); - } - - if (credentials) { - AWS_LOGF_INFO( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) successfully queried credentials", (void *)user_data->provider); - } else { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) failed to query credentials", (void *)user_data->provider); - - if (user_data->error_code == AWS_ERROR_SUCCESS) { - user_data->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE; - } - } - - /* pass the credentials back */ - user_data->original_callback(credentials, user_data->error_code, user_data->original_user_data); - - /* clean up */ - s_user_data_destroy(user_data); - aws_credentials_release(credentials); -} -static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *wrapped_user_data); - -static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_code, void *data) { - struct sso_user_data *user_data = data; - - /* set error code */ - user_data->error_code = error_code; - if (!error_code && user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK) { - user_data->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_HTTP_STATUS_FAILURE; - } - - struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; - struct aws_http_connection *connection = impl->function_table->aws_http_stream_get_connection(stream); - impl->function_table->aws_http_stream_release(stream); - impl->function_table->aws_http_connection_manager_release_connection(impl->connection_manager, connection); - - /* - * On anything other than a 200, if we can retry the request based on - * error response, retry it, otherwise, call the finalize function. - */ - if (user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK || error_code) { - enum aws_retry_error_type error_type = - aws_credentials_provider_compute_retry_error_type(user_data->status_code, error_code); - - /* don't retry client errors at all. */ - if (error_type != AWS_RETRY_ERROR_TYPE_CLIENT_ERROR) { - - if (aws_retry_strategy_schedule_retry(user_data->retry_token, error_type, s_on_retry_ready, user_data) == - AWS_OP_SUCCESS) { - return; - } - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): failed to schedule retry: %s", - (void *)user_data->provider, - aws_error_str(aws_last_error())); - } - } else if (aws_retry_token_record_success(user_data->retry_token)) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): failed to register operation success: %s", - (void *)user_data->provider, - aws_error_str(aws_last_error())); - } - - s_finalize_get_credentials_query(user_data); -} - -static int s_on_incoming_body_fn(struct aws_http_stream *stream, const struct aws_byte_cursor *body, void *user_data) { - - (void)stream; - - struct sso_user_data *sso_user_data = user_data; - struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; - - AWS_LOGF_TRACE( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) received %zu response bytes", - (void *)sso_user_data->provider, - body->len); - - if (body->len + sso_user_data->payload.len > SSO_RESPONSE_SIZE_LIMIT) { - impl->function_table->aws_http_connection_close(sso_user_data->connection); - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) response exceeded maximum allowed length", - (void *)sso_user_data->provider); - - return aws_raise_error(AWS_ERROR_SHORT_BUFFER); - } - - if (aws_byte_buf_append_dynamic(&sso_user_data->payload, body)) { - impl->function_table->aws_http_connection_close(sso_user_data->connection); - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) error appending payload: %s", - (void *)sso_user_data->provider, - aws_error_str(aws_last_error())); - - return AWS_OP_ERR; - } - - return AWS_OP_SUCCESS; -} - -static int s_on_incoming_headers_fn( - struct aws_http_stream *stream, - enum aws_http_header_block header_block, - const struct aws_http_header *header_array, - size_t num_headers, - void *user_data) { - - (void)header_array; - (void)num_headers; - - if (header_block != AWS_HTTP_HEADER_BLOCK_MAIN) { - return AWS_OP_SUCCESS; - } - - struct sso_user_data *sso_user_data = user_data; - if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN && sso_user_data->status_code == 0) { - struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; - if (impl->function_table->aws_http_stream_get_incoming_response_status(stream, &sso_user_data->status_code)) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) failed to get http status code: %s", - (void *)sso_user_data->provider, - aws_error_str(aws_last_error())); - - return AWS_OP_ERR; - } - AWS_LOGF_DEBUG( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) received http status code %d", - (void *)sso_user_data->provider, - sso_user_data->status_code); - } - - return AWS_OP_SUCCESS; -} - -/* Request headers. */ -AWS_STATIC_STRING_FROM_LITERAL(s_sso_token_header, "x-amz-sso_bearer_token"); -AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header, "User-Agent"); -AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header_value, "CRTAuthSSOCredentialsProvider"); - -static void s_query_credentials(struct sso_user_data *user_data) { - AWS_FATAL_ASSERT(user_data->connection); - struct aws_http_stream *stream = NULL; - struct aws_credentials_provider_sso_impl *impl = user_data->provider->impl; - - user_data->request = aws_http_message_new_request(user_data->allocator); - if (user_data->request == NULL) { - goto on_error; - } - - struct aws_http_header auth_header = { - .name = aws_byte_cursor_from_string(s_sso_token_header), - .value = aws_byte_cursor_from_string(user_data->token), - }; - struct aws_http_header host_header = { - .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Host"), - .value = aws_byte_cursor_from_string(impl->endpoint), - }; - struct aws_http_header user_agent_header = { - .name = aws_byte_cursor_from_string(s_sso_user_agent_header), - .value = aws_byte_cursor_from_string(s_sso_user_agent_header_value), - }; - - if (aws_http_message_add_header(user_data->request, auth_header) || - aws_http_message_add_header(user_data->request, host_header) || - aws_http_message_add_header(user_data->request, user_agent_header)) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) failed to add http headers with error: %s", - (void *)user_data->provider, - aws_error_debug_str(aws_last_error())); - goto on_error; - } - - if (aws_http_message_set_request_method(user_data->request, aws_http_method_get)) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) failed to set request method with error: %s", - (void *)user_data->provider, - aws_error_debug_str(aws_last_error())); - goto on_error; - } - - if (aws_http_message_set_request_path(user_data->request, aws_byte_cursor_from_buf(&user_data->path_and_query))) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) failed to set request path with error: %s", - (void *)user_data->provider, - aws_error_debug_str(aws_last_error())); - goto on_error; - } - - struct aws_http_make_request_options request_options = { - .self_size = sizeof(request_options), - .on_response_headers = s_on_incoming_headers_fn, - .on_response_header_block_done = NULL, - .on_response_body = s_on_incoming_body_fn, - .on_complete = s_on_stream_complete_fn, - .user_data = user_data, - .request = user_data->request, - }; - - stream = impl->function_table->aws_http_connection_make_request(user_data->connection, &request_options); - if (!stream) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) failed to make request with error: %s", - (void *)user_data->provider, - aws_error_debug_str(aws_last_error())); - goto on_error; - } - - if (impl->function_table->aws_http_stream_activate(stream)) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p) failed to activate the stream with error: %s", - (void *)user_data->provider, - aws_error_debug_str(aws_last_error())); - goto on_error; - } - - return; - -on_error: - impl->function_table->aws_http_stream_release(stream); - s_finalize_get_credentials_query(user_data); -} - -static void s_on_get_token_callback(struct aws_credentials *credentials, int error_code, void *user_data) { - struct sso_user_data *sso_user_data = user_data; - - if (error_code) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: failed to acquire a token, error code %d(%s)", - (void *)sso_user_data->provider, - error_code, - aws_error_str(error_code)); - sso_user_data->error_code = error_code; - s_finalize_get_credentials_query(sso_user_data); - return; - } - - struct aws_byte_cursor token = aws_credentials_get_token(credentials); - if (token.len == 0 || token.ptr == NULL) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: token is empty with error code %d(%s)", - (void *)sso_user_data->provider, - error_code, - aws_error_str(error_code)); - - sso_user_data->error_code = error_code; - s_finalize_get_credentials_query(user_data); - return; - } - AWS_LOGF_INFO( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): successfully accquired a token", (void *)sso_user_data->provider); - /* clear the result from previous attempt */ - s_user_data_reset_request_and_response(sso_user_data); - - sso_user_data->token = aws_string_new_from_cursor(sso_user_data->allocator, &token); - s_query_credentials(sso_user_data); -} - -static void s_on_acquire_connection(struct aws_http_connection *connection, int error_code, void *user_data) { - struct sso_user_data *sso_user_data = user_data; - - if (error_code) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: failed to acquire a connection, error code %d(%s)", - (void *)sso_user_data->provider, - error_code, - aws_error_str(error_code)); - sso_user_data->error_code = error_code; - s_finalize_get_credentials_query(user_data); - return; - } - AWS_LOGF_INFO( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): successfully accquired a connection", - (void *)sso_user_data->provider); - sso_user_data->connection = connection; - - struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; - if (aws_credentials_provider_get_credentials(impl->token_provider, s_on_get_token_callback, user_data)) { - int last_error_code = aws_last_error(); - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "id=%p: failed to get a token, error code %d(%s)", - (void *)sso_user_data->provider, - last_error_code, - aws_error_str(last_error_code)); - - sso_user_data->error_code = last_error_code; - s_finalize_get_credentials_query(sso_user_data); - } -} - -/* called for each retry. */ -static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *user_data) { - (void)token; - struct sso_user_data *sso_user_data = user_data; - struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; - - if (error_code) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): failed to acquire retry token: %s", - (void *)sso_user_data->provider, - aws_error_debug_str(error_code)); - sso_user_data->error_code = error_code; - s_finalize_get_credentials_query(sso_user_data); - return; - } - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): successfully acquired a retry token", - (void *)sso_user_data->provider); - impl->function_table->aws_http_connection_manager_acquire_connection( - impl->connection_manager, s_on_acquire_connection, sso_user_data); -} - -static void s_on_retry_token_acquired( - struct aws_retry_strategy *strategy, - int error_code, - struct aws_retry_token *token, - void *user_data) { - struct sso_user_data *sso_user_data = user_data; - (void)strategy; - - if (error_code) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): failed to acquire retry token: %s", - (void *)sso_user_data->provider, - aws_error_debug_str(error_code)); - sso_user_data->error_code = error_code; - s_finalize_get_credentials_query(sso_user_data); - return; - } - - sso_user_data->retry_token = token; - struct aws_credentials_provider_sso_impl *impl = sso_user_data->provider->impl; - impl->function_table->aws_http_connection_manager_acquire_connection( - impl->connection_manager, s_on_acquire_connection, user_data); -} - -static int s_credentials_provider_sso_get_credentials_async( - struct aws_credentials_provider *provider, - aws_on_get_credentials_callback_fn callback, - void *user_data) { - - struct aws_credentials_provider_sso_impl *impl = provider->impl; - - struct sso_user_data *wrapped_user_data = s_user_data_new(provider, callback, user_data); - if (wrapped_user_data == NULL) { - return AWS_OP_ERR; - } - - if (aws_retry_strategy_acquire_retry_token( - impl->retry_strategy, NULL, s_on_retry_token_acquired, wrapped_user_data, SSO_RETRY_TIMEOUT_MS)) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): failed to acquire retry token: %s", - (void *)provider, - aws_error_debug_str(aws_last_error())); - goto on_error; - } - - return AWS_OP_SUCCESS; - -on_error: - s_user_data_destroy(wrapped_user_data); - return AWS_OP_ERR; -} - -static void s_on_connection_manager_shutdown(void *user_data) { - struct aws_credentials_provider *provider = user_data; - - aws_credentials_provider_invoke_shutdown_callback(provider); - aws_mem_release(provider->allocator, provider); -} - -static void s_credentials_provider_sso_destroy(struct aws_credentials_provider *provider) { - - struct aws_credentials_provider_sso_impl *impl = provider->impl; - if (impl == NULL) { - return; - } - aws_string_destroy(impl->endpoint); - aws_string_destroy(impl->sso_account_id); - aws_string_destroy(impl->sso_role_name); - aws_retry_strategy_release(impl->retry_strategy); - aws_credentials_provider_release(impl->token_provider); - - /* aws_http_connection_manager_release will eventually leads to call of s_on_connection_manager_shutdown, - * which will do memory release for provider and impl. So We should be freeing impl - * related memory first, then call aws_http_connection_manager_release. - */ - if (impl->connection_manager) { - impl->function_table->aws_http_connection_manager_release(impl->connection_manager); - } else { - /* If provider setup failed halfway through, connection_manager might not exist. - * In this case invoke shutdown completion callback directly to finish cleanup */ - s_on_connection_manager_shutdown(provider); - } -} - -static struct aws_credentials_provider_vtable s_aws_credentials_provider_sso_vtable = { - .get_credentials = s_credentials_provider_sso_get_credentials_async, - .destroy = s_credentials_provider_sso_destroy, -}; - -static int s_construct_endpoint( - struct aws_allocator *allocator, - struct aws_byte_buf *endpoint, - const struct aws_string *region) { - // TODO: confirm logic - if (!allocator || !endpoint || !region) { - return AWS_ERROR_INVALID_ARGUMENT; - } - aws_byte_buf_clean_up(endpoint); - struct aws_byte_cursor sso_prefix = aws_byte_cursor_from_c_str("portal.sso."); - struct aws_byte_cursor region_cursor = aws_byte_cursor_from_string(region); - struct aws_byte_cursor amazonaws_cursor = aws_byte_cursor_from_c_str(".amazonaws.com"); - struct aws_byte_cursor cn_cursor = aws_byte_cursor_from_c_str(".cn"); - - if (aws_byte_buf_init_copy_from_cursor(endpoint, allocator, sso_prefix) || - aws_byte_buf_append_dynamic(endpoint, ®ion_cursor) || - aws_byte_buf_append_dynamic(endpoint, &amazonaws_cursor)) { - goto on_error; - } - - if (aws_string_eq_c_str_ignore_case(region, "cn-north-1") || - aws_string_eq_c_str_ignore_case(region, "cn-northwest-1")) { - if (aws_byte_buf_append_dynamic(endpoint, &cn_cursor)) { - goto on_error; - } - } - return AWS_OP_SUCCESS; - -on_error: - aws_byte_buf_clean_up(endpoint); - return AWS_OP_ERR; -} - -AWS_STATIC_STRING_FROM_LITERAL(s_sso_account_id, "sso_account_id"); -AWS_STATIC_STRING_FROM_LITERAL(s_sso_region, "sso_region"); -AWS_STATIC_STRING_FROM_LITERAL(s_sso_role_name, "sso_role_name"); -AWS_STATIC_STRING_FROM_LITERAL(s_sso_session, "sso_session"); - -struct sso_parameters { - struct aws_allocator *allocator; - struct aws_byte_buf endpoint; - struct aws_string *sso_account_id; - struct aws_string *sso_role_name; - struct aws_credentials_provider *token_provider; -}; - -static void s_parameters_destroy(struct sso_parameters *parameters) { - if (!parameters) { - return; - } - aws_byte_buf_clean_up(¶meters->endpoint); - aws_string_destroy(parameters->sso_account_id); - aws_string_destroy(parameters->sso_role_name); - aws_credentials_provider_release(parameters->token_provider); - aws_mem_release(parameters->allocator, parameters); -} - -static struct sso_parameters *s_parameters_new( - struct aws_allocator *allocator, - const struct aws_credentials_provider_sso_options *options) { - - struct sso_parameters *parameters = aws_mem_calloc(allocator, 1, sizeof(struct sso_parameters)); - parameters->allocator = allocator; - - struct aws_profile_collection *config_profile = NULL; - struct aws_string *profile_name = NULL; - bool success = false; - - profile_name = aws_get_profile_name(allocator, &options->profile_name_override); - if (!profile_name) { - AWS_LOGF_DEBUG(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: failed to resolve profile name"); - goto on_finish; - } - - config_profile = aws_load_profile_collection_from_config_file(allocator, options->config_file_name_override); - if (!config_profile) { - goto on_finish; - } - - const struct aws_profile *profile = aws_profile_collection_get_profile(config_profile, profile_name); - if (!profile) { - AWS_LOGF_DEBUG( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: failed to load \"%s\" profile", aws_string_c_str(profile_name)); - goto on_finish; - } - - const struct aws_profile_property *sso_account_id = aws_profile_get_property(profile, s_sso_account_id); - const struct aws_profile_property *sso_role_name = aws_profile_get_property(profile, s_sso_role_name); - const struct aws_profile_property *sso_region = NULL; - - if (!sso_account_id) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_account_id is missing"); - aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); - goto on_finish; - } - if (!sso_role_name) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_role_name is missing"); - aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); - goto on_finish; - } - - const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session); - /* read the approriate config based on sso_session property is available or not */ - if (sso_session_property) { - struct aws_token_provider_sso_session_options token_provider_options = { - .config_file_name_override = options->config_file_name_override, - .profile_name_override = options->profile_name_override, - .bootstrap = options->bootstrap, - .tls_ctx = options->tls_ctx, - .system_clock_fn = options->system_clock_fn, - }; - parameters->token_provider = aws_token_provider_new_sso_session(allocator, &token_provider_options); - if (!parameters->token_provider) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a sso token provider"); - aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); - goto on_finish; - } - sso_region = aws_profile_get_property( - aws_profile_collection_get_section( - config_profile, - AWS_PROFILE_SECTION_TYPE_SSO_SESSION, - aws_profile_property_get_value(sso_session_property)), - s_sso_region); - } else { - struct aws_token_provider_sso_profile_options token_provider_options = { - .config_file_name_override = options->config_file_name_override, - .profile_name_override = options->profile_name_override, - .system_clock_fn = options->system_clock_fn, - }; - - parameters->token_provider = aws_token_provider_new_sso_profile(allocator, &token_provider_options); - if (!parameters->token_provider) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a profile token provider"); - aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); - goto on_finish; - } - sso_region = aws_profile_get_property(profile, s_sso_region); - } - - if (!sso_region) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_region is missing"); - aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); - goto on_finish; - } - - parameters->sso_account_id = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_account_id)); - parameters->sso_role_name = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_role_name)); - /* determine endpoint */ - if (s_construct_endpoint(allocator, ¶meters->endpoint, aws_profile_property_get_value(sso_region))) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to construct sso endpoint"); - goto on_finish; - } - AWS_LOGF_DEBUG( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Successfully loaded all required parameters for sso credentials provider."); - success = true; - -on_finish: - - if (!success) { - s_parameters_destroy(parameters); - parameters = NULL; - } - aws_string_destroy(profile_name); - aws_profile_collection_release(config_profile); - - return parameters; -} - -struct aws_credentials_provider *aws_credentials_provider_new_sso( - struct aws_allocator *allocator, - const struct aws_credentials_provider_sso_options *options) { - - struct sso_parameters *parameters = s_parameters_new(allocator, options); - if (!parameters) { - return NULL; - } - - struct aws_credentials_provider *provider = NULL; - struct aws_credentials_provider_sso_impl *impl = NULL; - struct aws_tls_connection_options tls_connection_options; - - aws_mem_acquire_many( - allocator, - 2, - &provider, - sizeof(struct aws_credentials_provider), - &impl, - sizeof(struct aws_credentials_provider_sso_impl)); - - AWS_ZERO_STRUCT(*provider); - AWS_ZERO_STRUCT(*impl); - AWS_ZERO_STRUCT(tls_connection_options); - - aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_sso_vtable, impl); - - if (!options->tls_ctx) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): a TLS context must be provided", (void *)provider); - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - goto on_error; - } - - if (!options->bootstrap) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): a bootstrap instance must be provided", (void *)provider); - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - goto on_error; - } - - aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx); - struct aws_byte_cursor host = aws_byte_cursor_from_buf(¶meters->endpoint); - if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): failed to create a tls connection options with error %s", - (void *)provider, - aws_error_str(aws_last_error())); - goto on_error; - } - - struct aws_socket_options socket_options; - AWS_ZERO_STRUCT(socket_options); - socket_options.type = AWS_SOCKET_STREAM; - socket_options.domain = AWS_SOCKET_IPV4; - socket_options.connect_timeout_ms = (uint32_t)aws_timestamp_convert( - SSO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL); - - struct aws_http_connection_manager_options manager_options; - AWS_ZERO_STRUCT(manager_options); - manager_options.bootstrap = options->bootstrap; - manager_options.initial_window_size = SSO_RESPONSE_SIZE_LIMIT; - manager_options.socket_options = &socket_options; - manager_options.host = host; - manager_options.port = 443; - manager_options.max_connections = 2; - manager_options.shutdown_complete_callback = s_on_connection_manager_shutdown; - manager_options.shutdown_complete_user_data = provider; - manager_options.tls_connection_options = &tls_connection_options; - - impl->function_table = options->function_table; - if (impl->function_table == NULL) { - impl->function_table = g_aws_credentials_provider_http_function_table; - } - - impl->connection_manager = impl->function_table->aws_http_connection_manager_new(allocator, &manager_options); - if (impl->connection_manager == NULL) { - goto on_error; - } - - impl->token_provider = aws_credentials_provider_acquire(parameters->token_provider); - impl->endpoint = aws_string_new_from_buf(allocator, ¶meters->endpoint); - impl->sso_account_id = aws_string_new_from_string(allocator, parameters->sso_account_id); - impl->sso_role_name = aws_string_new_from_string(allocator, parameters->sso_role_name); - - provider->shutdown_options = options->shutdown_options; - - struct aws_standard_retry_options retry_options = { - .backoff_retry_options = - { - .el_group = options->bootstrap->event_loop_group, - .max_retries = SSO_MAX_ATTEMPTS, - }, - }; - impl->retry_strategy = aws_retry_strategy_new_standard(allocator, &retry_options); - if (!impl->retry_strategy) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "(id=%p): failed to create a retry strategy with error %s", - (void *)provider, - aws_error_debug_str(aws_last_error())); - goto on_error; - } - - s_parameters_destroy(parameters); - aws_tls_connection_options_clean_up(&tls_connection_options); - return provider; - -on_error: - aws_credentials_provider_destroy(provider); - s_parameters_destroy(parameters); - aws_tls_connection_options_clean_up(&tls_connection_options); - return NULL; -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index beb29641..f5d05e68 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -120,17 +120,6 @@ add_net_test_case(parse_sso_token_invalid_missing_access_token) add_net_test_case(parse_sso_token_missing_expires_at) add_net_test_case(parse_sso_token_invalid_expires_at) -add_net_test_case(credentials_provider_sso_failed_invalid_config) -add_net_test_case(credentials_provider_sso_create_destroy_valid_config) -add_net_test_case(credentials_provider_sso_connect_failure) -add_net_test_case(credentials_provider_sso_failure_token_missing) -add_net_test_case(credentials_provider_sso_failure_token_expired) -add_net_test_case(credentials_provider_sso_request_failure) -add_net_test_case(credentials_provider_sso_bad_response) -add_net_test_case(credentials_provider_sso_retryable_error) -add_net_test_case(credentials_provider_sso_basic_success) -add_net_test_case(credentials_provider_sso_basic_success_profile) - add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) add_test_case(imds_client_token_request_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c deleted file mode 100644 index ffc9b36c..00000000 --- a/tests/credentials_provider_sso_tests.c +++ /dev/null @@ -1,771 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -#include - -#include "credentials_provider_utils.h" -#include "shared_credentials_test_definitions.h" - -#include -#include -#include -#include - -AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile, "sso"); -static int s_aws_credentials_provider_sso_test_init_config_profile( - struct aws_allocator *allocator, - const struct aws_string *config_contents) { - - struct aws_string *config_file_path_str = aws_create_process_unique_file_name(allocator); - ASSERT_TRUE(config_file_path_str != NULL); - ASSERT_TRUE(aws_create_profile_file(config_file_path_str, config_contents) == AWS_OP_SUCCESS); - - ASSERT_TRUE( - aws_set_environment_value(s_default_config_path_env_variable_name, config_file_path_str) == AWS_OP_SUCCESS); - - ASSERT_TRUE(aws_set_environment_value(s_default_profile_env_variable_name, s_sso_profile) == AWS_OP_SUCCESS); - - aws_string_destroy(config_file_path_str); - return AWS_OP_SUCCESS; -} - -/* start_url should be same in `s_sso_profile_start_url` and `s_sso_profile_config_contents` */ -AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile_start_url, "https://d-123.awsapps.com/start"); -AWS_STATIC_STRING_FROM_LITERAL( - s_sso_profile_config_contents, - "[profile sso]\n" - "sso_start_url = https://d-123.awsapps.com/start\n" - "sso_region = us-west-2\n" - "sso_account_id = 123\n" - "sso_role_name = roleName\n"); -/* session name should be same in both `s_sso_session_name` and `s_sso_session_config_contents`*/ -AWS_STATIC_STRING_FROM_LITERAL(s_sso_session_name, "session"); -AWS_STATIC_STRING_FROM_LITERAL( - s_sso_session_config_contents, - "[profile sso]\n" - "sso_start_url = https://d-123.awsapps.com/start\n" - "sso_region = us-west-2\n" - "sso_account_id = 123\n" - "sso_role_name = roleName\n" - "sso_session = session\n" - "[sso-session session]\n" - "sso_start_url = https://d-123.awsapps.com/start\n" - "sso_region = us-west-2\n"); - -static int s_credentials_provider_sso_failed_invalid_config(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - const struct { - const char *name; - const char *text; - } invalid_config_examples[] = { - {"empty", ""}, - - {"profile without any sso config", "[profile sso]\naccessKey=access"}, - - {"profile without role_name", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_region=us-west-2\n"}, - - {"profile without account_id", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_region=us-west-2\n" - "sso_role_name=roleName\n"}, - - {"profile without region", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n"}, - - {"profile without start_url", - "[profile sso]\n" - "accessKey=access\n" - "sso_region=us-west-2\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n"}, - - {"profile with invalid session", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_region=us-west-2\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n" - "sso_session = session\n" - "[sso-session session]\n"}, - - {"session without start_url", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_region=us-west-2\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n" - "sso_session = session\n" - "[sso-session session]\n" - "sso_region = us-west-2\n"}, - - {"session without region", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_region=us-west-2\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n" - "sso_session = session\n" - "[sso-session session]\n" - "sso_start_url = https://d-123.awsapps.com/start\n"}, - - {"session with different region", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_region=us-east-1\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n" - "sso_session = session\n" - "[sso-session session]\n" - "sso_start_url = https://d-123.awsapps.com/start\n" - "sso_region = us-west-2\n"}, - - {"session with different start-url", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_region=us-west-2\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n" - "sso_session = session\n" - "[sso-session session]\n" - "sso_start_url = https://d-321.awsapps.com/start\n" - "sso_region = us-west-2\n"}, - }; - - aws_credentials_provider_http_mock_tester_init(allocator); - - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - }; - for (int i = 0; i < AWS_ARRAY_SIZE(invalid_config_examples); i++) { - printf("invalid config example [%d]: %s\n", i, invalid_config_examples[i].name); - struct aws_string *content = aws_string_new_from_c_str(allocator, invalid_config_examples[i].text); - ASSERT_TRUE(content != NULL); - s_aws_credentials_provider_sso_test_init_config_profile(allocator, content); - aws_string_destroy(content); - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NULL(provider); - } - - aws_credentials_provider_http_mock_tester_cleanup(); - - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_failed_invalid_config, s_credentials_provider_sso_failed_invalid_config); - -static int s_credentials_provider_sso_create_destroy_valid_config(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - const struct { - const char *name; - const char *text; - } valid_config_examples[] = { - - {"profile", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_account_id=123\n" - "sso_region=us-west-2\n" - "sso_role_name=roleName\n"}, - - {"session", - "[profile sso]\n" - "accessKey=access\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n" - "sso_session = session\n" - "[sso-session session]\n" - "sso_start_url = https://d-123.awsapps.com/start\n" - "sso_region = us-west-2\n"}, - - {"session with profile", - "[profile sso]\n" - "accessKey=access\n" - "sso_start_url=https://d-123.awsapps.com/start\n" - "sso_region=us-west-2\n" - "sso_account_id=123\n" - "sso_role_name=roleName\n" - "sso_session = session\n" - "[sso-session session]\n" - "sso_start_url = https://d-123.awsapps.com/start\n" - "sso_region = us-west-2\n"}, - - }; - - aws_credentials_provider_http_mock_tester_init(allocator); - - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - }; - for (int i = 0; i < AWS_ARRAY_SIZE(valid_config_examples); i++) { - printf("valid config example [%d]: %s\n", i, valid_config_examples[i].name); - struct aws_string *content = aws_string_new_from_c_str(allocator, valid_config_examples[i].text); - ASSERT_TRUE(content != NULL); - s_aws_credentials_provider_sso_test_init_config_profile(allocator, content); - aws_string_destroy(content); - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - aws_credentials_provider_release(provider); - } - - aws_credentials_provider_http_mock_tester_cleanup(); - - return 0; -} -AWS_TEST_CASE( - credentials_provider_sso_create_destroy_valid_config, - s_credentials_provider_sso_create_destroy_valid_config); - -AWS_STATIC_STRING_FROM_LITERAL( - s_expected_sso_request_path, - "/federation/credentials?account_id=123&role_name=roleName"); - -AWS_STATIC_STRING_FROM_LITERAL( - s_good_response, - "{\"roleCredentials\": {\"accessKeyId\": \"SuccessfulAccessKey\",\"secretAccessKey\": " - "\"SuccessfulSecret\",\"sessionToken\": \"SuccessfulToken\",\"expiration\": 1678574216000}}"); -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, "SuccessfulToken"); -static int s_good_response_expiration = 1678574216; -static int s_verify_credentials(bool request_made, bool got_credentials, int expected_attempts) { - ASSERT_TRUE(credentials_provider_http_mock_tester.has_received_credentials_callback); - - if (got_credentials) { - ASSERT_TRUE(credentials_provider_http_mock_tester.credentials != NULL); - ASSERT_CURSOR_VALUE_STRING_EQUALS( - aws_credentials_get_access_key_id(credentials_provider_http_mock_tester.credentials), s_good_access_key_id); - ASSERT_CURSOR_VALUE_STRING_EQUALS( - aws_credentials_get_secret_access_key(credentials_provider_http_mock_tester.credentials), - s_good_secret_access_key); - ASSERT_CURSOR_VALUE_STRING_EQUALS( - aws_credentials_get_session_token(credentials_provider_http_mock_tester.credentials), s_good_session_token); - ASSERT_INT_EQUALS( - aws_credentials_get_expiration_timepoint_seconds(credentials_provider_http_mock_tester.credentials), - s_good_response_expiration); - } else { - ASSERT_TRUE(credentials_provider_http_mock_tester.error_code); - ASSERT_TRUE(credentials_provider_http_mock_tester.credentials == NULL); - } - - if (request_made) { - ASSERT_CURSOR_VALUE_STRING_EQUALS( - aws_byte_cursor_from_buf(&credentials_provider_http_mock_tester.request_path), s_expected_sso_request_path); - } - ASSERT_INT_EQUALS(credentials_provider_http_mock_tester.attempts, expected_attempts); - - return AWS_OP_SUCCESS; -} - -static int s_credentials_provider_sso_connect_failure(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - credentials_provider_http_mock_tester.is_connection_acquire_successful = false; - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_get_credentials( - provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); - - aws_credentials_provider_http_mock_wait_for_credentials_result(); - ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_connect_failure, s_credentials_provider_sso_connect_failure); - -static int s_credentials_provider_sso_failure_token_missing(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - credentials_provider_http_mock_tester.is_request_successful = false; - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_get_credentials( - provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); - - aws_credentials_provider_http_mock_wait_for_credentials_result(); - - ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_failure_token_missing, s_credentials_provider_sso_failure_token_missing); - -AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); -AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); -AWS_STATIC_STRING_FROM_LITERAL( - s_sso_token, - "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); -static uint64_t s_sso_token_expiration_s = 1426138519; - -static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - credentials_provider_http_mock_tester.is_request_successful = false; - - /* redirect $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); - - /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); - ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - uint64_t nano_expiration = - aws_timestamp_convert(s_sso_token_expiration_s + 100, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); - mock_aws_set_system_time(nano_expiration); - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - .system_clock_fn = mock_aws_get_system_time, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_get_credentials( - provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); - - aws_credentials_provider_http_mock_wait_for_credentials_result(); - - ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); - ASSERT_INT_EQUALS(credentials_provider_http_mock_tester.error_code, AWS_AUTH_SSO_TOKEN_EXPIRED); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - aws_string_destroy(token_path); - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_failure_token_expired, s_credentials_provider_sso_failure_token_expired); - -static int s_credentials_provider_sso_request_failure(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - credentials_provider_http_mock_tester.is_request_successful = false; - credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_400_BAD_REQUEST; - - /* redirect $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); - - /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); - ASSERT_NOT_NULL(token_path); - ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - mock_aws_set_system_time(0); - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - .system_clock_fn = mock_aws_get_system_time, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_get_credentials( - provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); - - aws_credentials_provider_http_mock_wait_for_credentials_result(); - - ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 1 /*expected attempts*/)); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - aws_string_destroy(token_path); - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_request_failure, s_credentials_provider_sso_request_failure); - -AWS_STATIC_STRING_FROM_LITERAL(s_bad_json_response, "{ \"accessKey\": \"bad\"}"); -static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - - /* redirect $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); - - /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); - ASSERT_NOT_NULL(token_path); - - ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - struct aws_byte_cursor bad_json_cursor = aws_byte_cursor_from_string(s_bad_json_response); - aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &bad_json_cursor); - - mock_aws_set_system_time(0); - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - .system_clock_fn = mock_aws_get_system_time, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_get_credentials( - provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); - - aws_credentials_provider_http_mock_wait_for_credentials_result(); - - ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 1 /*expected attempts*/)); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - aws_string_destroy(token_path); - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_bad_response, s_credentials_provider_sso_bad_response); - -static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR; - - /* redirect $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); - - /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); - ASSERT_NOT_NULL(token_path); - - ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - struct aws_byte_cursor bad_json_cursor = aws_byte_cursor_from_string(s_bad_json_response); - aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &bad_json_cursor); - - mock_aws_set_system_time(0); - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - .system_clock_fn = mock_aws_get_system_time, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_get_credentials( - provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); - - aws_credentials_provider_http_mock_wait_for_credentials_result(); - - ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 4 /*expected attempts*/)); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - aws_string_destroy(token_path); - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_retryable_error, s_credentials_provider_sso_retryable_error); - -static int s_credentials_provider_sso_basic_success(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - - /* redirect $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); - - /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); - ASSERT_NOT_NULL(token_path); - - ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - /* set the response */ - struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); - aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); - - mock_aws_set_system_time(0); - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - .system_clock_fn = mock_aws_get_system_time, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_get_credentials( - provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); - - aws_credentials_provider_http_mock_wait_for_credentials_result(); - - ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 1 /*expected attempts*/)); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - aws_string_destroy(token_path); - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_basic_success, s_credentials_provider_sso_basic_success); - -static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - - aws_credentials_provider_http_mock_tester_init(allocator); - - /* redirect $HOME */ - ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); - - /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); - ASSERT_NOT_NULL(token_path); - - ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); - ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); - - struct aws_byte_buf content_buf; - struct aws_byte_buf existing_content = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_profile_config_contents)); - aws_byte_buf_init_copy(&content_buf, allocator, &existing_content); - - struct aws_string *config_file_contents = aws_string_new_from_array(allocator, content_buf.buffer, content_buf.len); - ASSERT_TRUE(config_file_contents != NULL); - aws_byte_buf_clean_up(&content_buf); - - s_aws_credentials_provider_sso_test_init_config_profile(allocator, config_file_contents); - aws_string_destroy(config_file_contents); - - /* set the response */ - struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); - aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); - - mock_aws_set_system_time(0); - struct aws_credentials_provider_sso_options options = { - .bootstrap = credentials_provider_http_mock_tester.bootstrap, - .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, - .function_table = &aws_credentials_provider_http_mock_function_table, - .shutdown_options = - { - .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, - .shutdown_user_data = NULL, - }, - .system_clock_fn = mock_aws_get_system_time, - }; - - struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); - ASSERT_NOT_NULL(provider); - - aws_credentials_provider_get_credentials( - provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); - - aws_credentials_provider_http_mock_wait_for_credentials_result(); - - ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 1 /*expected attempts*/)); - - aws_credentials_provider_release(provider); - - aws_credentials_provider_http_mock_wait_for_shutdown_callback(); - - aws_credentials_provider_http_mock_tester_cleanup(); - - aws_string_destroy(token_path); - return 0; -} -AWS_TEST_CASE(credentials_provider_sso_basic_success_profile, s_credentials_provider_sso_basic_success_profile); From 38afb6d03826e2a050cd4ed785f7183543c7f447 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 14:48:27 -0700 Subject: [PATCH 67/93] remove default case --- source/credentials.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/credentials.c b/source/credentials.c index c3d1361b..59d0a069 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -170,7 +170,7 @@ static void s_aws_credentials_destroy(struct aws_credentials *credentials) { case TOKEN_IDENTITY: aws_string_destroy_secure(credentials->identity.token_identity.token); break; - default: + case ANONYMOUS_IDENTITY: break; } From da5bc122c513f3b962b199ff15146df2e2fc67b0 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 14:49:09 -0700 Subject: [PATCH 68/93] remove ms stuff from this PR --- include/aws/auth/private/credentials_utils.h | 1 - source/credentials_utils.c | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/include/aws/auth/private/credentials_utils.h b/include/aws/auth/private/credentials_utils.h index 154753c3..c8e491d0 100644 --- a/include/aws/auth/private/credentials_utils.h +++ b/include/aws/auth/private/credentials_utils.h @@ -73,7 +73,6 @@ struct aws_auth_http_system_vtable { enum aws_parse_credentials_expiration_format { AWS_PCEF_STRING_ISO_8601_DATE, AWS_PCEF_NUMBER_UNIX_EPOCH, - AWS_PCEF_NUMBER_UNIX_EPOCH_MS, }; struct aws_parse_credentials_from_json_doc_options { diff --git a/source/credentials_utils.c b/source/credentials_utils.c index f87def09..41aecd73 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -129,20 +129,6 @@ static bool s_parse_expiration_value_from_json_object( return true; } - case AWS_PCEF_NUMBER_UNIX_EPOCH_MS: { - double expiration_value_ms = 0; - if (aws_json_value_get_number(value, &expiration_value_ms)) { - AWS_LOGF_INFO( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "Unabled to extract credentials Expiration field from Json document."); - return false; - } - - *expiration_timepoint_in_seconds = - aws_timestamp_convert((uint64_t)expiration_value_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_SECS, NULL); - return true; - } - default: return false; } From 59502222912d1433b036104d370ed485522320aa Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 14:55:22 -0700 Subject: [PATCH 69/93] remove mocked http connection manager --- tests/credentials_provider_utils.c | 300 ----------------------------- tests/credentials_provider_utils.h | 69 ------- 2 files changed, 369 deletions(-) diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index 4888c98c..5873821a 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -83,25 +83,6 @@ void aws_wait_on_credentials_callback(struct aws_get_credentials_test_callback_r } } -/* - * Mock provider - */ - -struct aws_auth_http_system_vtable aws_credentials_provider_http_mock_function_table = { - .aws_http_connection_manager_new = aws_credentials_provider_http_mock_connection_manager_new, - .aws_http_connection_manager_release = aws_credentials_provider_http_mock_connection_manager_release, - .aws_http_connection_manager_acquire_connection = - aws_credentials_provider_http_mock_connection_manager_acquire_connection, - .aws_http_connection_manager_release_connection = - aws_credentials_provider_http_mock_connection_manager_release_connection, - .aws_http_connection_make_request = aws_credentials_provider_http_mock_make_request, - .aws_http_stream_activate = aws_credentials_provider_http_mock_stream_activate, - .aws_http_stream_get_connection = aws_credentials_provider_http_mock_stream_get_connection, - .aws_http_stream_get_incoming_response_status = - aws_credentials_provider_http_mock_stream_get_incoming_response_status, - .aws_http_stream_release = aws_credentials_provider_http_mock_stream_release, - .aws_http_connection_close = aws_credentials_provider_http_mock_connection_close}; - struct aws_credentials_provider_mock_impl { struct aws_array_list results; size_t next_result; @@ -506,284 +487,3 @@ int aws_create_directory_components(struct aws_allocator *allocator, const struc } return AWS_OP_SUCCESS; } - -/* - * Mocked HTTP connection manager for tests - */ -struct aws_credentials_provider_http_mock_tester credentials_provider_http_mock_tester; - -int aws_credentials_provider_http_mock_tester_init(struct aws_allocator *allocator) { - aws_auth_library_init(allocator); - - AWS_ZERO_STRUCT(credentials_provider_http_mock_tester); - - struct aws_tls_ctx_options tls_ctx_options; - aws_tls_ctx_options_init_default_client(&tls_ctx_options, allocator); - credentials_provider_http_mock_tester.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_options); - ASSERT_NOT_NULL(credentials_provider_http_mock_tester.tls_ctx); - - credentials_provider_http_mock_tester.el_group = aws_event_loop_group_new_default(allocator, 0, NULL); - - struct aws_host_resolver_default_options resolver_options = { - .el_group = credentials_provider_http_mock_tester.el_group, - .max_entries = 8, - }; - credentials_provider_http_mock_tester.resolver = aws_host_resolver_new_default(allocator, &resolver_options); - - struct aws_client_bootstrap_options bootstrap_options = { - .event_loop_group = credentials_provider_http_mock_tester.el_group, - .host_resolver = credentials_provider_http_mock_tester.resolver, - }; - credentials_provider_http_mock_tester.bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); - - if (aws_array_list_init_dynamic( - &credentials_provider_http_mock_tester.response_data_callbacks, - allocator, - 10, - sizeof(struct aws_byte_cursor))) { - return AWS_OP_ERR; - } - - if (aws_byte_buf_init(&credentials_provider_http_mock_tester.request_path, allocator, 256)) { - return AWS_OP_ERR; - } - if (aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256)) { - return AWS_OP_ERR; - } - - if (aws_mutex_init(&credentials_provider_http_mock_tester.lock)) { - return AWS_OP_ERR; - } - - if (aws_condition_variable_init(&credentials_provider_http_mock_tester.signal)) { - return AWS_OP_ERR; - } - - /* default to everything successful */ - credentials_provider_http_mock_tester.is_connection_acquire_successful = true; - credentials_provider_http_mock_tester.is_request_successful = true; - - return AWS_OP_SUCCESS; -} - -void aws_credentials_provider_http_mock_tester_cleanup(void) { - aws_tls_ctx_release(credentials_provider_http_mock_tester.tls_ctx); - aws_client_bootstrap_release(credentials_provider_http_mock_tester.bootstrap); - aws_host_resolver_release(credentials_provider_http_mock_tester.resolver); - aws_event_loop_group_release(credentials_provider_http_mock_tester.el_group); - aws_array_list_clean_up(&credentials_provider_http_mock_tester.response_data_callbacks); - aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_path); - aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); - aws_condition_variable_clean_up(&credentials_provider_http_mock_tester.signal); - aws_mutex_clean_up(&credentials_provider_http_mock_tester.lock); - aws_credentials_release(credentials_provider_http_mock_tester.credentials); - aws_auth_library_clean_up(); -} - -void aws_credentials_provider_http_mock_on_shutdown_complete(void *user_data) { - (void)user_data; - aws_mutex_lock(&credentials_provider_http_mock_tester.lock); - credentials_provider_http_mock_tester.has_received_shutdown_callback = true; - aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); - - aws_condition_variable_notify_one(&credentials_provider_http_mock_tester.signal); -} - -bool aws_credentials_provider_http_mock_has_received_shutdown_callback(void *user_data) { - (void)user_data; - - return credentials_provider_http_mock_tester.has_received_shutdown_callback; -} - -void aws_credentials_provider_http_mock_wait_for_shutdown_callback() { - aws_mutex_lock(&credentials_provider_http_mock_tester.lock); - aws_condition_variable_wait_pred( - &credentials_provider_http_mock_tester.signal, - &credentials_provider_http_mock_tester.lock, - aws_credentials_provider_http_mock_has_received_shutdown_callback, - NULL); - aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); -} - -struct mock_connection_manager { - struct aws_allocator *allocator; - aws_http_connection_manager_shutdown_complete_fn *shutdown_complete_callback; - void *shutdown_complete_user_data; -}; - -struct aws_http_connection_manager *aws_credentials_provider_http_mock_connection_manager_new( - struct aws_allocator *allocator, - const struct aws_http_connection_manager_options *options) { - - struct mock_connection_manager *mock_manager = aws_mem_calloc(allocator, 1, sizeof(struct mock_connection_manager)); - mock_manager->allocator = allocator; - mock_manager->shutdown_complete_callback = options->shutdown_complete_callback; - mock_manager->shutdown_complete_user_data = options->shutdown_complete_user_data; - return (struct aws_http_connection_manager *)mock_manager; -} - -void aws_credentials_provider_http_mock_connection_manager_release(struct aws_http_connection_manager *manager) { - struct mock_connection_manager *mock_manager = (struct mock_connection_manager *)manager; - mock_manager->shutdown_complete_callback(mock_manager->shutdown_complete_user_data); - aws_mem_release(mock_manager->allocator, mock_manager); -} - -void aws_credentials_provider_http_mock_connection_manager_acquire_connection( - 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 (credentials_provider_http_mock_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); - } -} - -int aws_credentials_provider_http_mock_connection_manager_release_connection( - struct aws_http_connection_manager *manager, - struct aws_http_connection *connection) { - - (void)manager; - (void)connection; - - return AWS_OP_SUCCESS; -} - -void aws_credentials_provider_http_mock_invoke_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); -} - -struct aws_http_stream *aws_credentials_provider_http_mock_make_request( - 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); - struct aws_input_stream *body_stream = aws_http_message_get_body_stream(options->request); - struct aws_allocator *allocator = credentials_provider_http_mock_tester.request_body.allocator; - aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); - aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256); - if (body_stream) { - aws_input_stream_read(body_stream, &credentials_provider_http_mock_tester.request_body); - } - aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_path); - - struct aws_byte_cursor request_path_cursor; - aws_http_message_get_request_path(options->request, &request_path_cursor); - aws_byte_buf_init_copy_from_cursor( - &credentials_provider_http_mock_tester.request_path, allocator, request_path_cursor); - credentials_provider_http_mock_tester.attempts++; - credentials_provider_http_mock_tester.request_options = *options; - - return (struct aws_http_stream *)1; -} - -int aws_credentials_provider_http_mock_stream_activate(struct aws_http_stream *stream) { - (void)stream; - aws_credentials_provider_http_mock_invoke_request_callbacks( - &credentials_provider_http_mock_tester.request_options, - &credentials_provider_http_mock_tester.response_data_callbacks, - credentials_provider_http_mock_tester.is_request_successful); - return AWS_OP_SUCCESS; -} - -int aws_credentials_provider_http_mock_stream_get_incoming_response_status( - const struct aws_http_stream *stream, - int *out_status_code) { - (void)stream; - - if (credentials_provider_http_mock_tester.response_code) { - *out_status_code = credentials_provider_http_mock_tester.response_code; - } else { - *out_status_code = AWS_HTTP_STATUS_CODE_200_OK; - } - - return AWS_OP_SUCCESS; -} - -void aws_credentials_provider_http_mock_stream_release(struct aws_http_stream *stream) { - (void)stream; -} - -void aws_credentials_provider_http_mock_connection_close(struct aws_http_connection *connection) { - (void)connection; -} - -struct aws_http_connection *aws_credentials_provider_http_mock_stream_get_connection( - const struct aws_http_stream *stream) { - (void)stream; - return (struct aws_http_connection *)1; -} - -bool aws_credentials_provider_http_mock_has_received_credentials_callback(void *user_data) { - (void)user_data; - - return credentials_provider_http_mock_tester.has_received_credentials_callback; -} - -void aws_credentials_provider_http_mock_wait_for_credentials_result(void) { - aws_mutex_lock(&credentials_provider_http_mock_tester.lock); - aws_condition_variable_wait_pred( - &credentials_provider_http_mock_tester.signal, - &credentials_provider_http_mock_tester.lock, - aws_credentials_provider_http_mock_has_received_credentials_callback, - NULL); - aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); -} - -void aws_credentials_provider_http_mock_get_credentials_callback( - struct aws_credentials *credentials, - int error_code, - void *user_data) { - (void)user_data; - - aws_mutex_lock(&credentials_provider_http_mock_tester.lock); - credentials_provider_http_mock_tester.has_received_credentials_callback = true; - credentials_provider_http_mock_tester.credentials = credentials; - credentials_provider_http_mock_tester.error_code = error_code; - if (credentials != NULL) { - aws_credentials_acquire(credentials); - } - aws_condition_variable_notify_one(&credentials_provider_http_mock_tester.signal); - aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); -} diff --git a/tests/credentials_provider_utils.h b/tests/credentials_provider_utils.h index 372fa33b..935e2f66 100644 --- a/tests/credentials_provider_utils.h +++ b/tests/credentials_provider_utils.h @@ -100,73 +100,4 @@ struct aws_credentials_provider *aws_credentials_provider_new_null( */ int aws_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path); -/** - * Mocked HTTP connection manager for tests - */ -struct aws_credentials_provider_http_mock_tester { - struct aws_tls_ctx *tls_ctx; - struct aws_event_loop_group *el_group; - struct aws_host_resolver *resolver; - struct aws_client_bootstrap *bootstrap; - - struct aws_byte_buf request_path; - struct aws_byte_buf request_body; - struct aws_http_make_request_options request_options; - - 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; - - int attempts; - int response_code; - int error_code; -}; - -extern struct aws_credentials_provider_http_mock_tester credentials_provider_http_mock_tester; -int aws_credentials_provider_http_mock_tester_init(struct aws_allocator *allocator); -void aws_credentials_provider_http_mock_tester_cleanup(void); -void aws_credentials_provider_http_mock_on_shutdown_complete(void *user_data); -bool aws_credentials_provider_http_mock_has_received_shutdown_callback(void *user_data); -void aws_credentials_provider_http_mock_wait_for_shutdown_callback(void); -struct aws_http_connection_manager *aws_credentials_provider_http_mock_connection_manager_new( - struct aws_allocator *allocator, - const struct aws_http_connection_manager_options *options); -void aws_credentials_provider_http_mock_connection_manager_release(struct aws_http_connection_manager *manager); -void aws_credentials_provider_http_mock_connection_manager_acquire_connection( - struct aws_http_connection_manager *manager, - aws_http_connection_manager_on_connection_setup_fn *callback, - void *user_data); -int aws_credentials_provider_http_mock_connection_manager_release_connection( - struct aws_http_connection_manager *manager, - struct aws_http_connection *connection); -void aws_credentials_provider_http_mock_invoke_request_callbacks( - const struct aws_http_make_request_options *options, - struct aws_array_list *data_callbacks, - bool is_request_successful); -struct aws_http_stream *aws_credentials_provider_http_mock_make_request( - struct aws_http_connection *client_connection, - const struct aws_http_make_request_options *options); -int aws_credentials_provider_http_mock_stream_activate(struct aws_http_stream *stream); -int aws_credentials_provider_http_mock_stream_get_incoming_response_status( - const struct aws_http_stream *stream, - int *out_status_code); -void aws_credentials_provider_http_mock_stream_release(struct aws_http_stream *stream); -void aws_credentials_provider_http_mock_connection_close(struct aws_http_connection *connection); -struct aws_http_connection *aws_credentials_provider_http_mock_stream_get_connection( - const struct aws_http_stream *stream); -bool aws_credentials_provider_http_mock_has_received_credentials_callback(void *user_data); -void aws_credentials_provider_http_mock_wait_for_credentials_result(void); -void aws_credentials_provider_http_mock_get_credentials_callback( - struct aws_credentials *credentials, - int error_code, - void *user_data); -extern struct aws_auth_http_system_vtable aws_credentials_provider_http_mock_function_table; - #endif /* AWS_AUTH_CREDENTIALS_PROVIDER_MOCK_H */ From 6ae2410562ad433f2415c8a148216342714c2027 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 20 Mar 2023 15:00:16 -0700 Subject: [PATCH 70/93] remove top level object change --- include/aws/auth/private/credentials_utils.h | 1 - source/credentials_utils.c | 13 +------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/include/aws/auth/private/credentials_utils.h b/include/aws/auth/private/credentials_utils.h index c8e491d0..f512e1c7 100644 --- a/include/aws/auth/private/credentials_utils.h +++ b/include/aws/auth/private/credentials_utils.h @@ -80,7 +80,6 @@ struct aws_parse_credentials_from_json_doc_options { const char *secret_access_key_name; const char *token_name; const char *expiration_name; - const char *top_level_object_name; enum aws_parse_credentials_expiration_format expiration_format; bool token_required; bool expiration_required; diff --git a/source/credentials_utils.c b/source/credentials_utils.c index 41aecd73..73d2095f 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -262,18 +262,7 @@ struct aws_credentials *aws_parse_credentials_from_json_document( AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse document as Json document."); return NULL; } - - struct aws_json_value *top_level_object = NULL; - if (options->top_level_object_name) { - top_level_object = aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str("roleCredentials")); - if (!top_level_object) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse document as Json document."); - } - } - - struct aws_credentials *credentials = aws_parse_credentials_from_aws_json_object( - allocator, top_level_object ? top_level_object : document_root, options); - + struct aws_credentials *credentials = aws_parse_credentials_from_aws_json_object(allocator, document_root, options); aws_json_value_destroy(document_root); return credentials; } From 39fd11513c7815c7b3140b07b7a31d2584759525 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Tue, 21 Mar 2023 15:16:41 -0700 Subject: [PATCH 71/93] fix add test_case --- tests/CMakeLists.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f5d05e68..07610d07 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -103,22 +103,22 @@ if (AWS_HAS_CI_ENVIRONMENT) add_net_test_case(credentials_provider_cognito_success_unauthenticated) endif() -add_net_test_case(sso_token_provider_profile_invalid_profile_test) -add_net_test_case(sso_token_provider_profile_valid_profile_test) +add_test_case(sso_token_provider_profile_invalid_profile_test) +add_test_case(sso_token_provider_profile_valid_profile_test) add_net_test_case(sso_token_provider_sso_session_invalid_config_test) add_net_test_case(sso_token_provider_sso_session_valid_config_test) add_net_test_case(sso_token_provider_sso_session_basic_success) add_net_test_case(sso_token_provider_sso_session_expired_token) -add_net_test_case(sso_token_provider_profile_basic_success) -add_net_test_case(sso_token_provider_profile_expired_token) +add_test_case(sso_token_provider_profile_basic_success) +add_test_case(sso_token_provider_profile_expired_token) -add_net_test_case(parse_token_location_url_test) -add_net_test_case(parse_token_location_session_test) -add_net_test_case(parse_sso_token_valid) -add_net_test_case(parse_sso_token_invalid) -add_net_test_case(parse_sso_token_invalid_missing_access_token) -add_net_test_case(parse_sso_token_missing_expires_at) -add_net_test_case(parse_sso_token_invalid_expires_at) +add_test_case(parse_token_location_url_test) +add_test_case(parse_token_location_session_test) +add_test_case(parse_sso_token_valid) +add_test_case(parse_sso_token_invalid) +add_test_case(parse_sso_token_invalid_missing_access_token) +add_test_case(parse_sso_token_missing_expires_at) +add_test_case(parse_sso_token_invalid_expires_at) add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) From d709f5995553cb0560f825b893bb7cfedde83ac4 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 27 Mar 2023 15:25:06 -0700 Subject: [PATCH 72/93] move token provider to private header file --- include/aws/auth/credentials.h | 81 ---------------- .../aws/auth/private/sso_token_providers.h | 97 +++++++++++++++++++ include/aws/auth/private/sso_token_utils.h | 2 +- source/token_provider_sso_profile.c | 1 + source/token_provider_sso_session.c | 1 + 5 files changed, 100 insertions(+), 82 deletions(-) create mode 100644 include/aws/auth/private/sso_token_providers.h diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 0959561f..e727e561 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -549,58 +549,6 @@ struct aws_credentials_provider_cognito_options { struct aws_auth_http_system_vtable *function_table; }; -/** - * Configuration options for a provider that sources sso token information from the aws profile (by default - * ~/.aws/config) and token from ~/.aws/sso/cache/.json. - */ -struct aws_token_provider_sso_profile_options { - struct aws_credentials_provider_shutdown_options shutdown_options; - - /* - * Override of what profile to use to source credentials from ('default' by default) - */ - struct aws_byte_cursor profile_name_override; - - /* - * Override path to the profile config file (~/.aws/config by default) - */ - struct aws_byte_cursor config_file_name_override; - - /* For mocking, leave NULL otherwise */ - aws_io_clock_fn *system_clock_fn; -}; - -/** - * Configuration options for a provider that sources sso token information from the aws profile (by default - * ~/.aws/config) and token from ~/.aws/sso/cache/.json. - */ -struct aws_token_provider_sso_session_options { - struct aws_credentials_provider_shutdown_options shutdown_options; - - /* - * Override of what profile to use to source credentials from ('default' by default) - */ - struct aws_byte_cursor profile_name_override; - - /* - * Override path to the profile config file (~/.aws/config by default) - */ - struct aws_byte_cursor config_file_name_override; - - /* - * Connection bootstrap to use for any network connections made - */ - struct aws_client_bootstrap *bootstrap; - - /* - * Client TLS context to use for any network connections made. - */ - struct aws_tls_ctx *tls_ctx; - - /* For mocking, leave NULL otherwise */ - aws_io_clock_fn *system_clock_fn; -}; - AWS_EXTERN_C_BEGIN /* @@ -1079,35 +1027,6 @@ struct aws_credentials_provider *aws_credentials_provider_new_chain_default( struct aws_allocator *allocator, const struct aws_credentials_provider_chain_default_options *options); -/** - * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws - * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json - * - * @param allocator memory allocator to use for all memory allocation - * @param options provider-specific configuration options - * - * @return the newly-constructed credentials provider, or NULL if an error occurred. - */ -AWS_AUTH_API -struct aws_credentials_provider *aws_token_provider_new_sso_profile( - struct aws_allocator *allocator, - const struct aws_token_provider_sso_profile_options *options); - -/** - * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws - * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json - * Note: Token refresh is not currently supported - * - * @param allocator memory allocator to use for all memory allocation - * @param options provider-specific configuration options - * - * @return the newly-constructed credentials provider, or NULL if an error occurred. - */ -AWS_AUTH_API -struct aws_credentials_provider *aws_token_provider_new_sso_session( - struct aws_allocator *allocator, - const struct aws_token_provider_sso_session_options *options); - AWS_AUTH_API extern const struct aws_auth_http_system_vtable *g_aws_credentials_provider_http_function_table; AWS_EXTERN_C_END diff --git a/include/aws/auth/private/sso_token_providers.h b/include/aws/auth/private/sso_token_providers.h new file mode 100644 index 00000000..3f741adb --- /dev/null +++ b/include/aws/auth/private/sso_token_providers.h @@ -0,0 +1,97 @@ +#ifndef AWS_AUTH_TOKEN_PROVIDERS_PRIVATE_H +#define AWS_AUTH_TOKEN_PROVIDERS_PRIVATE_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include + +/** + * Configuration options for a provider that sources sso token information from the aws profile (by default + * ~/.aws/config) and token from ~/.aws/sso/cache/.json. + */ +struct aws_token_provider_sso_profile_options { + struct aws_credentials_provider_shutdown_options shutdown_options; + + /* + * Override of what profile to use to source credentials from ('default' by default) + */ + struct aws_byte_cursor profile_name_override; + + /* + * Override path to the profile config file (~/.aws/config by default) + */ + struct aws_byte_cursor config_file_name_override; + + /* For mocking, leave NULL otherwise */ + aws_io_clock_fn *system_clock_fn; +}; + +/** + * Configuration options for a provider that sources sso token information from the aws profile (by default + * ~/.aws/config) and token from ~/.aws/sso/cache/.json. + */ +struct aws_token_provider_sso_session_options { + struct aws_credentials_provider_shutdown_options shutdown_options; + + /* + * Override of what profile to use to source credentials from ('default' by default) + */ + struct aws_byte_cursor profile_name_override; + + /* + * Override path to the profile config file (~/.aws/config by default) + */ + struct aws_byte_cursor config_file_name_override; + + /* + * Connection bootstrap to use for any network connections made + */ + struct aws_client_bootstrap *bootstrap; + + /* + * Client TLS context to use for any network connections made. + */ + struct aws_tls_ctx *tls_ctx; + + /* For mocking, leave NULL otherwise */ + aws_io_clock_fn *system_clock_fn; +}; + +AWS_EXTERN_C_BEGIN + +/** + * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws + * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json + * + * @param allocator memory allocator to use for all memory allocation + * @param options provider-specific configuration options + * + * @return the newly-constructed credentials provider, or NULL if an error occurred. + */ +AWS_AUTH_API +struct aws_credentials_provider *aws_token_provider_new_sso_profile( + struct aws_allocator *allocator, + const struct aws_token_provider_sso_profile_options *options); + +/** + * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws + * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json + * Note: Token refresh is not currently supported + * + * @param allocator memory allocator to use for all memory allocation + * @param options provider-specific configuration options + * + * @return the newly-constructed credentials provider, or NULL if an error occurred. + */ +AWS_AUTH_API +struct aws_credentials_provider *aws_token_provider_new_sso_session( + struct aws_allocator *allocator, + const struct aws_token_provider_sso_session_options *options); + +AWS_EXTERN_C_END + +#endif /* AWS_AUTH_TOKEN_PROVIDERS_PRIVATE_H */ diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index f923a15f..8f9056e7 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -32,4 +32,4 @@ struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocato AWS_EXTERN_C_END -#endif /* AWS_AUTH_CREDENTIALS_PRIVATE_H */ +#endif /* AWS_AUTH_TOKEN_PRIVATE_H */ diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 7b0694ee..700dc163 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -7,6 +7,7 @@ #include #include +#include #include #include diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index d4d93ac8..63bfeba3 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -7,6 +7,7 @@ #include #include +#include #include #include From 00ed28ee6829bbe46ef3c852ee4d0e70fa3ca824 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 27 Mar 2023 15:35:20 -0700 Subject: [PATCH 73/93] add header file in tests --- tests/token_provider_sso_tests.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 677c3066..a2c01de5 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -5,6 +5,7 @@ #include +#include #include #include #include From 8c80d9e29e94122b7e42491b1109b8953c098b0d Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 29 Mar 2023 15:39:21 -0700 Subject: [PATCH 74/93] PR feedback --- include/aws/auth/private/sso_token_utils.h | 2 +- source/credentials.c | 6 +++- source/credentials_utils.c | 8 +---- source/sso_token_utils.c | 34 ++++++++++--------- source/token_provider_sso_profile.c | 20 +++++------- source/token_provider_sso_session.c | 38 ++++++++++++++-------- tests/sso_token_util_tests.c | 4 +-- tests/token_provider_sso_tests.c | 10 +++--- 8 files changed, 65 insertions(+), 57 deletions(-) diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index 8f9056e7..5972a861 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -21,7 +21,7 @@ AWS_EXTERN_C_BEGIN /* Construct token path which is ~/.aws/sso/cache/.json */ AWS_AUTH_API -struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, const struct aws_string *input); +struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, const struct aws_string *input); AWS_AUTH_API void aws_sso_token_destroy(struct aws_sso_token *token); diff --git a/source/credentials.c b/source/credentials.c index 59d0a069..7fa79427 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -9,18 +9,21 @@ #include #include +/* aws ecc identity which contains the data needed to sign a Sigv4a AWS request */ struct aws_ecc_identity { struct aws_string *access_key_id; struct aws_string *session_token; struct aws_ecc_key_pair *ecc_key; }; +/* aws credentials identity which contains the data needed to sign an authenticated AWS request */ struct aws_credentials_identity { struct aws_string *access_key_id; struct aws_string *secret_access_key; struct aws_string *session_token; }; +/* aws_token identity contains only a token to represent token only identities like a bearer token. */ struct aws_token_identity { struct aws_string *token; }; @@ -33,7 +36,8 @@ enum aws_identity_type { }; /* - * A structure that wraps the public/private data needed to sign an authenticated AWS request + * A structure that wraps the different types of credentials that the customer can provider to establish their + * identity. */ struct aws_credentials { struct aws_allocator *allocator; diff --git a/source/credentials_utils.c b/source/credentials_utils.c index 73d2095f..ed297234 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -308,7 +308,7 @@ struct aws_profile_collection *aws_load_profile_collection_from_config_file( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to resolve config file path: %s", aws_error_str(aws_last_error())); - goto on_error; + return NULL; } config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG); @@ -323,14 +323,8 @@ struct aws_profile_collection *aws_load_profile_collection_from_config_file( "Failed to build config profile collection from file at (%s) : %s", aws_string_c_str(config_file_path), aws_error_str(aws_last_error())); - goto on_error; } aws_string_destroy(config_file_path); return config_profiles; - -on_error: - aws_string_destroy(config_file_path); - aws_profile_collection_destroy(config_profiles); - return NULL; } diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index 1ad36076..55970ff2 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -15,10 +15,10 @@ AWS_STATIC_STRING_FROM_LITERAL(s_sso_cache_directory, "/.aws/sso/cache/"); -struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, const struct aws_string *input) { +struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, const struct aws_string *input) { AWS_PRECONDITION(input); - struct aws_string *token_path_str = NULL; + struct aws_string *sso_token_path_str = NULL; struct aws_string *home_directory = aws_get_home_directory(allocator); if (!home_directory) { @@ -30,18 +30,18 @@ struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, con struct aws_byte_cursor input_cursor = aws_byte_cursor_from_string(input); struct aws_byte_cursor json_cursor = aws_byte_cursor_from_c_str(".json"); - struct aws_byte_buf token_path_buf; - AWS_ZERO_STRUCT(token_path_buf); + struct aws_byte_buf sso_token_path_buf; + AWS_ZERO_STRUCT(sso_token_path_buf); struct aws_byte_buf sha1_buf; AWS_ZERO_STRUCT(sha1_buf); /* append home directory */ - if (aws_byte_buf_init_copy_from_cursor(&token_path_buf, allocator, home_dir_cursor)) { + if (aws_byte_buf_init_copy_from_cursor(&sso_token_path_buf, allocator, home_dir_cursor)) { goto cleanup; } /* append sso cache directory */ - if (aws_byte_buf_append_dynamic(&token_path_buf, &cache_dir_cursor)) { + if (aws_byte_buf_append_dynamic(&sso_token_path_buf, &cache_dir_cursor)) { goto cleanup; } @@ -51,31 +51,33 @@ struct aws_string *aws_construct_token_path(struct aws_allocator *allocator, con goto cleanup; } struct aws_byte_cursor sha1_cursor = aws_byte_cursor_from_buf(&sha1_buf); - if (aws_hex_encode_append_dynamic(&sha1_cursor, &token_path_buf)) { + if (aws_hex_encode_append_dynamic(&sha1_cursor, &sso_token_path_buf)) { goto cleanup; } /* append .json */ - if (aws_byte_buf_append_dynamic(&token_path_buf, &json_cursor)) { + if (aws_byte_buf_append_dynamic(&sso_token_path_buf, &json_cursor)) { goto cleanup; } // Use platform-specific directory separator. const char local_platform_separator = aws_get_platform_directory_separator(); - for (size_t i = 0; i < token_path_buf.len; ++i) { - if (aws_is_any_directory_separator((char)token_path_buf.buffer[i])) { - ((char *)token_path_buf.buffer)[i] = local_platform_separator; + for (size_t i = 0; i < sso_token_path_buf.len; ++i) { + if (aws_is_any_directory_separator((char)sso_token_path_buf.buffer[i])) { + ((char *)sso_token_path_buf.buffer)[i] = local_platform_separator; } } - token_path_str = aws_string_new_from_buf(allocator, &token_path_buf); + sso_token_path_str = aws_string_new_from_buf(allocator, &sso_token_path_buf); AWS_LOGF_INFO( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "successfully constructed token path: %s", aws_string_c_str(token_path_str)); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "successfully constructed token path: %s", + aws_string_c_str(sso_token_path_str)); cleanup: - aws_byte_buf_clean_up(&token_path_buf); + aws_byte_buf_clean_up(&sso_token_path_buf); aws_byte_buf_clean_up(&sha1_buf); aws_string_destroy(home_directory); - return token_path_str; + return sso_token_path_str; } void aws_sso_token_destroy(struct aws_sso_token *sso_token) { @@ -110,7 +112,7 @@ struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocato if (document_root == NULL) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso token: failed to parse access token file %s", + "sso token: failed to parse sso token file %s", aws_string_c_str(file_path)); aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto cleanup; diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 700dc163..c9fa6d76 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -20,12 +20,12 @@ * sso-token profile provider implementation */ struct aws_token_provider_profile_impl { - struct aws_string *token_file_path; + struct aws_string *sso_token_file_path; aws_io_clock_fn *system_clock_fn; }; -static int s_token_provider_profile_get_token_async( +static int s_token_provider_profile_get_token( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, void *user_data) { @@ -34,9 +34,9 @@ static int s_token_provider_profile_get_token_async( struct aws_sso_token *sso_token = NULL; struct aws_credentials *credentials = NULL; int result = AWS_OP_ERR; - sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); + sso_token = aws_sso_token_new_from_file(provider->allocator, impl->sso_token_file_path); if (!sso_token) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", (void *)provider); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) failed to get sso token from file", (void *)provider); goto done; } @@ -45,9 +45,8 @@ static int s_token_provider_profile_get_token_async( if (impl->system_clock_fn(&now_ns) != AWS_OP_SUCCESS) { goto done; } - uint64_t now_seconds = aws_timestamp_convert(now_ns, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); - if (aws_date_time_as_epoch_secs(&sso_token->expiration) - now_seconds < 0) { + if (aws_date_time_as_nanos(&sso_token->expiration) <= now_ns) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; @@ -77,14 +76,14 @@ static void s_token_provider_profile_destroy(struct aws_credentials_provider *pr return; } - aws_string_destroy(impl->token_file_path); + aws_string_destroy(impl->sso_token_file_path); aws_credentials_provider_invoke_shutdown_callback(provider); aws_mem_release(provider->allocator, provider); } static struct aws_credentials_provider_vtable s_aws_token_provider_profile_vtable = { - .get_credentials = s_token_provider_profile_get_token_async, + .get_credentials = s_token_provider_profile_get_token, .destroy = s_token_provider_profile_destroy, }; @@ -138,7 +137,7 @@ static struct aws_string *s_construct_profile_token_path( goto cleanup; } - token_path = aws_construct_token_path(allocator, aws_profile_property_get_value(sso_start_url_property)); + token_path = aws_construct_sso_token_path(allocator, aws_profile_property_get_value(sso_start_url_property)); if (!token_path) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to construct token path in profile"); @@ -172,13 +171,12 @@ struct aws_credentials_provider *aws_token_provider_new_sso_profile( AWS_ZERO_STRUCT(*provider); AWS_ZERO_STRUCT(*impl); aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_profile_vtable, impl); - impl->token_file_path = aws_string_new_from_string(allocator, token_path); + impl->sso_token_file_path = token_path; provider->shutdown_options = options->shutdown_options; if (options->system_clock_fn) { impl->system_clock_fn = options->system_clock_fn; } else { impl->system_clock_fn = aws_sys_clock_get_ticks; } - aws_string_destroy(token_path); return provider; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 63bfeba3..e1ada011 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -20,7 +20,7 @@ * sso-session token provider implementation */ struct aws_token_provider_sso_session_impl { - struct aws_string *token_file_path; + struct aws_string *sso_token_file_path; aws_io_clock_fn *system_clock_fn; }; @@ -34,7 +34,7 @@ static int s_token_provider_sso_session_get_token_async( struct aws_credentials *credentials = NULL; int result = AWS_OP_ERR; - sso_token = aws_sso_token_new_from_file(provider->allocator, impl->token_file_path); + sso_token = aws_sso_token_new_from_file(provider->allocator, impl->sso_token_file_path); if (!sso_token) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", (void *)provider); goto done; @@ -45,9 +45,8 @@ static int s_token_provider_sso_session_get_token_async( if (impl->system_clock_fn(&now_ns) != AWS_OP_SUCCESS) { goto done; } - uint64_t now_seconds = aws_timestamp_convert(now_ns, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL); - if (aws_date_time_as_epoch_secs(&sso_token->expiration) - now_seconds < 0) { + if (aws_date_time_as_nanos(&sso_token->expiration) <= now_ns) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; @@ -78,7 +77,7 @@ static void s_token_provider_sso_session_destroy(struct aws_credentials_provider return; } - aws_string_destroy(impl->token_file_path); + aws_string_destroy(impl->sso_token_file_path); aws_credentials_provider_invoke_shutdown_callback(provider); aws_mem_release(provider->allocator, provider); @@ -93,13 +92,24 @@ AWS_STRING_FROM_LITERAL(s_sso_session_name, "sso_session"); AWS_STRING_FROM_LITERAL(s_sso_region_name, "sso_region"); AWS_STRING_FROM_LITERAL(s_sso_start_url_name, "sso_start_url"); -static struct aws_string *s_verify_config_and_construct_token_path( +/** + * Parses the config file to validate and construct a token path. A valid profile with sso session is as follow + * [profile sso-profile] + * sso_session = dev + * sso_account_id = 012345678901 + * sso_role_name = SampleRole + * + * [sso-session dev] + * sso_region = us-east-1 + * sso_start_url = https://d-abc123.awsapps.com/start + */ +static struct aws_string *s_verify_config_and_construct_sso_token_path( struct aws_allocator *allocator, struct aws_byte_cursor profile_name_override, struct aws_byte_cursor config_file_name_override) { struct aws_profile_collection *config_collection = NULL; struct aws_string *profile_name = NULL; - struct aws_string *token_path = NULL; + struct aws_string *sso_token_path = NULL; profile_name = aws_get_profile_name(allocator, &profile_name_override); if (!profile_name) { @@ -129,6 +139,8 @@ static struct aws_string *s_verify_config_and_construct_token_path( goto cleanup; } const struct aws_string *sso_session_name = aws_profile_property_get_value(sso_session_property); + + /* parse sso_session */ const struct aws_profile *session_profile = aws_profile_collection_get_section(config_collection, AWS_PROFILE_SECTION_TYPE_SSO_SESSION, sso_session_name); if (!session_profile) { @@ -156,10 +168,10 @@ static struct aws_string *s_verify_config_and_construct_token_path( goto cleanup; } + /* Verify sso_region & start_url are the same in profile section if they exist */ const struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property); const struct aws_string *sso_start_url = aws_profile_property_get_value(sso_start_url_property); - /* Verify sso_region & start_url are the same in profile section if they exist */ const struct aws_profile_property *profile_sso_region_property = aws_profile_get_property(profile, s_sso_region_name); const struct aws_profile_property *profile_sso_start_url_property = @@ -182,12 +194,12 @@ static struct aws_string *s_verify_config_and_construct_token_path( goto cleanup; } - token_path = aws_construct_token_path(allocator, sso_session_name); + sso_token_path = aws_construct_sso_token_path(allocator, sso_session_name); cleanup: aws_string_destroy(profile_name); aws_profile_collection_release(config_collection); - return token_path; + return sso_token_path; } struct aws_credentials_provider *aws_token_provider_new_sso_session( @@ -198,7 +210,7 @@ struct aws_credentials_provider *aws_token_provider_new_sso_session( AWS_ASSERT(options->bootstrap); AWS_ASSERT(options->tls_ctx); - struct aws_string *token_path = s_verify_config_and_construct_token_path( + struct aws_string *token_path = s_verify_config_and_construct_sso_token_path( allocator, options->profile_name_override, options->config_file_name_override); if (!token_path) { return NULL; @@ -216,14 +228,12 @@ struct aws_credentials_provider *aws_token_provider_new_sso_session( AWS_ZERO_STRUCT(*provider); AWS_ZERO_STRUCT(*impl); aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_sso_session_vtable, impl); - impl->token_file_path = aws_string_new_from_string(allocator, token_path); + impl->sso_token_file_path = token_path; provider->shutdown_options = options->shutdown_options; if (options->system_clock_fn) { impl->system_clock_fn = options->system_clock_fn; } else { impl->system_clock_fn = aws_sys_clock_get_ticks; } - - aws_string_destroy(token_path); return provider; } diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index 80b40c5d..b749ec3b 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -12,7 +12,7 @@ static int s_parse_token_location_url_test(struct aws_allocator *allocator, void (void)ctx; aws_auth_library_init(allocator); struct aws_string *start_url = aws_string_new_from_c_str(allocator, "https://d-92671207e4.awsapps.com/start"); - struct aws_string *token_path = aws_construct_token_path(allocator, start_url); + struct aws_string *token_path = aws_construct_sso_token_path(allocator, start_url); struct aws_byte_cursor token_cursor = aws_byte_cursor_from_string(token_path); struct aws_byte_cursor expected_token_cursor = @@ -31,7 +31,7 @@ static int s_parse_token_location_session_test(struct aws_allocator *allocator, (void)ctx; aws_auth_library_init(allocator); struct aws_string *session = aws_string_new_from_c_str(allocator, "admin"); - struct aws_string *token_path = aws_construct_token_path(allocator, session); + struct aws_string *token_path = aws_construct_sso_token_path(allocator, session); struct aws_byte_cursor token_cursor = aws_byte_cursor_from_string(token_path); struct aws_byte_cursor expected_token_cursor = aws_byte_cursor_from_c_str("d033e22ae348aeb5660fc2140aec35850c4da997.json"); diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index a2c01de5..148cfe6b 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -325,7 +325,7 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); @@ -372,7 +372,7 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_session_name); + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); @@ -380,7 +380,7 @@ static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator * struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_sso_session_config_contents)); uint64_t nano_expiration = - aws_timestamp_convert(s_token_expiration_s + 100, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); + aws_timestamp_convert(s_token_expiration_s + 1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); mock_aws_set_system_time(nano_expiration); struct aws_token_provider_sso_session_options options = { .config_file_name_override = aws_byte_cursor_from_string(config_file_str), @@ -415,7 +415,7 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_profile_start_url); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); @@ -461,7 +461,7 @@ static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allo ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); /* create token file */ - struct aws_string *token_path = aws_construct_token_path(allocator, s_sso_profile_start_url); + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_profile_start_url); ASSERT_NOT_NULL(token_path); ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); From bd78bb5219092f64e640858cc0646404a6d24068 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 10:06:36 -0700 Subject: [PATCH 75/93] deep copy token path for now --- source/token_provider_sso_profile.c | 4 +++- source/token_provider_sso_session.c | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index c9fa6d76..3d91598c 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -171,12 +171,14 @@ struct aws_credentials_provider *aws_token_provider_new_sso_profile( AWS_ZERO_STRUCT(*provider); AWS_ZERO_STRUCT(*impl); aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_profile_vtable, impl); - impl->sso_token_file_path = token_path; + impl->sso_token_file_path = aws_string_new_from_string(allocator, token_path); provider->shutdown_options = options->shutdown_options; if (options->system_clock_fn) { impl->system_clock_fn = options->system_clock_fn; } else { impl->system_clock_fn = aws_sys_clock_get_ticks; } + + aws_string_destroy(token_path); return provider; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index e1ada011..46ab8351 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -36,7 +36,8 @@ static int s_token_provider_sso_session_get_token_async( sso_token = aws_sso_token_new_from_file(provider->allocator, impl->sso_token_file_path); if (!sso_token) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) unable to read file.", (void *)provider); + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) failed to get sso token from file.", (void *)provider); goto done; } @@ -228,12 +229,14 @@ struct aws_credentials_provider *aws_token_provider_new_sso_session( AWS_ZERO_STRUCT(*provider); AWS_ZERO_STRUCT(*impl); aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_sso_session_vtable, impl); - impl->sso_token_file_path = token_path; + impl->sso_token_file_path = aws_string_new_from_string(allocator, token_path); provider->shutdown_options = options->shutdown_options; if (options->system_clock_fn) { impl->system_clock_fn = options->system_clock_fn; } else { impl->system_clock_fn = aws_sys_clock_get_ticks; } + + aws_string_destroy(token_path); return provider; } From d2c9fc106bb853eb7d5489a661e13a6890595af6 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 12:15:17 -0700 Subject: [PATCH 76/93] remove async --- source/token_provider_sso_session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 46ab8351..098de79c 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -24,7 +24,7 @@ struct aws_token_provider_sso_session_impl { aws_io_clock_fn *system_clock_fn; }; -static int s_token_provider_sso_session_get_token_async( +static int s_token_provider_sso_session_get_token( struct aws_credentials_provider *provider, aws_on_get_credentials_callback_fn callback, void *user_data) { @@ -85,7 +85,7 @@ static void s_token_provider_sso_session_destroy(struct aws_credentials_provider } static struct aws_credentials_provider_vtable s_aws_token_provider_sso_session_vtable = { - .get_credentials = s_token_provider_sso_session_get_token_async, + .get_credentials = s_token_provider_sso_session_get_token, .destroy = s_token_provider_sso_session_destroy, }; From 8cbe45010d419dbef71ec623734fa2c418444f6b Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 12:40:46 -0700 Subject: [PATCH 77/93] try constructing platform specific paths --- source/sso_token_utils.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index 55970ff2..d5e829b2 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -13,8 +13,6 @@ # pragma warning(disable : 4232) #endif /* _MSC_VER */ -AWS_STATIC_STRING_FROM_LITERAL(s_sso_cache_directory, "/.aws/sso/cache/"); - struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, const struct aws_string *input) { AWS_PRECONDITION(input); @@ -26,7 +24,6 @@ struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, } struct aws_byte_cursor home_dir_cursor = aws_byte_cursor_from_string(home_directory); - struct aws_byte_cursor cache_dir_cursor = aws_byte_cursor_from_string(s_sso_cache_directory); struct aws_byte_cursor input_cursor = aws_byte_cursor_from_string(input); struct aws_byte_cursor json_cursor = aws_byte_cursor_from_c_str(".json"); @@ -40,8 +37,20 @@ struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, goto cleanup; } + /* append /.aws/sso/cache/ with platform specific directory separator */ + const char local_platform_separator = aws_get_platform_directory_separator(); + struct aws_byte_cursor aws_dir_cursor = aws_byte_cursor_from_c_str(".aws"); + struct aws_byte_cursor sso_dir_cursor = aws_byte_cursor_from_c_str("sso"); + struct aws_byte_cursor cache_dir_cursor = aws_byte_cursor_from_c_str("cache"); + /* append sso cache directory */ - if (aws_byte_buf_append_dynamic(&sso_token_path_buf, &cache_dir_cursor)) { + if (aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator) || + aws_byte_buf_append_dynamic(&sso_token_path_buf, &aws_dir_cursor) || + aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator) || + aws_byte_buf_append_dynamic(&sso_token_path_buf, &sso_dir_cursor) || + aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator) || + aws_byte_buf_append_dynamic(&sso_token_path_buf, &cache_dir_cursor) || + aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator)) { goto cleanup; } @@ -60,14 +69,6 @@ struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, goto cleanup; } - // Use platform-specific directory separator. - const char local_platform_separator = aws_get_platform_directory_separator(); - for (size_t i = 0; i < sso_token_path_buf.len; ++i) { - if (aws_is_any_directory_separator((char)sso_token_path_buf.buffer[i])) { - ((char *)sso_token_path_buf.buffer)[i] = local_platform_separator; - } - } - sso_token_path_str = aws_string_new_from_buf(allocator, &sso_token_path_buf); AWS_LOGF_INFO( AWS_LS_AUTH_CREDENTIALS_PROVIDER, From 85786823e3ce6157fcd3c5f6fd9de246880da8fe Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 13:03:19 -0700 Subject: [PATCH 78/93] rename to access_token --- include/aws/auth/private/sso_token_utils.h | 2 +- source/sso_token_utils.c | 4 ++-- source/token_provider_sso_profile.c | 2 +- source/token_provider_sso_session.c | 2 +- tests/sso_token_util_tests.c | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index 5972a861..2cae138d 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -13,7 +13,7 @@ struct aws_sso_token { struct aws_allocator *allocator; - struct aws_string *token; + struct aws_string *access_token; struct aws_date_time expiration; }; diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index d5e829b2..bc378a2e 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -86,7 +86,7 @@ void aws_sso_token_destroy(struct aws_sso_token *sso_token) { return; } - aws_string_destroy(sso_token->token); + aws_string_destroy(sso_token->access_token); aws_mem_release(sso_token->allocator, sso_token); } @@ -152,7 +152,7 @@ struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocato aws_raise_error(AWS_AUTH_SSO_TOKEN_INVALID); goto cleanup; } - token->token = aws_string_new_from_cursor(allocator, &access_token_cursor); + token->access_token = aws_string_new_from_cursor(allocator, &access_token_cursor); token->expiration = expiration; success = true; diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 3d91598c..c7926a99 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -54,7 +54,7 @@ static int s_token_provider_profile_get_token( credentials = aws_credentials_new_token( provider->allocator, - aws_byte_cursor_from_string(sso_token->token), + aws_byte_cursor_from_string(sso_token->access_token), (uint64_t)aws_date_time_as_epoch_secs(&sso_token->expiration)); if (!credentials) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) Unable to construct credentials.", (void *)provider); diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 098de79c..0c87ac6e 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -57,7 +57,7 @@ static int s_token_provider_sso_session_get_token( credentials = aws_credentials_new_token( provider->allocator, - aws_byte_cursor_from_string(sso_token->token), + aws_byte_cursor_from_string(sso_token->access_token), (uint64_t)aws_date_time_as_epoch_secs(&sso_token->expiration)); if (!credentials) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) Unable to construct credentials.", (void *)provider); diff --git a/tests/sso_token_util_tests.c b/tests/sso_token_util_tests.c index b749ec3b..b9688bd4 100644 --- a/tests/sso_token_util_tests.c +++ b/tests/sso_token_util_tests.c @@ -55,11 +55,11 @@ static int s_parse_sso_token_valid(struct aws_allocator *allocator, void *ctx) { aws_auth_library_init(allocator); struct aws_string *file_path = aws_create_process_unique_file_name(allocator); ASSERT_SUCCESS(aws_create_profile_file(file_path, s_valid_token_json)); - struct aws_sso_token *token = aws_sso_token_new_from_file(allocator, file_path); - ASSERT_TRUE(aws_string_eq_c_str(token->token, "string")); - ASSERT_INT_EQUALS((uint64_t)aws_date_time_as_epoch_secs(&token->expiration), 1573704345); + struct aws_sso_token *sso_token = aws_sso_token_new_from_file(allocator, file_path); + ASSERT_TRUE(aws_string_eq_c_str(sso_token->access_token, "string")); + ASSERT_INT_EQUALS((uint64_t)aws_date_time_as_epoch_secs(&sso_token->expiration), 1573704345); aws_string_destroy(file_path); - aws_sso_token_destroy(token); + aws_sso_token_destroy(sso_token); aws_auth_library_clean_up(); return AWS_OP_SUCCESS; } From fd2b7f908ad1597fdb500a4b5e879980c332ab1d Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 13:31:08 -0700 Subject: [PATCH 79/93] improve logs --- source/token_provider_sso_profile.c | 12 +++++------- source/token_provider_sso_session.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index c7926a99..b9c0577a 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -100,7 +100,7 @@ static struct aws_string *s_construct_profile_token_path( profile_name = aws_get_profile_name(allocator, &profile_name_override); if (!profile_name) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to resolve profile name"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-profile: failed to resolve profile name"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } @@ -109,7 +109,7 @@ static struct aws_string *s_construct_profile_token_path( if (!config_collection) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-profile: token parser could not load or parse" + "token-provider-sso-profile: could not load or parse" " a config file."); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; @@ -120,7 +120,7 @@ static struct aws_string *s_construct_profile_token_path( if (!profile) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-profile: token provider could not load" + "token-provider-sso-profile: could not load" " a profile at %s.", aws_string_c_str(profile_name)); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); @@ -131,16 +131,14 @@ static struct aws_string *s_construct_profile_token_path( aws_profile_get_property(profile, s_profile_sso_start_url_name); if (!sso_start_url_property) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to find sso_start_url in profile"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-profile: failed to find sso_start_url"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } token_path = aws_construct_sso_token_path(allocator, aws_profile_property_get_value(sso_start_url_property)); if (!token_path) { - AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-profile: token parser failed to construct token path in profile"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-profile: failed to construct token path"); goto cleanup; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 0c87ac6e..561739cb 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -134,7 +134,7 @@ static struct aws_string *s_verify_config_and_construct_sso_token_path( if (!sso_session_property) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-session: token provider could not find an sso-session at profile %s", + "token-provider-sso-session: token provider could not find an sso-session at profile %s", aws_string_c_str(profile_name)); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; @@ -145,7 +145,7 @@ static struct aws_string *s_verify_config_and_construct_sso_token_path( const struct aws_profile *session_profile = aws_profile_collection_get_section(config_collection, AWS_PROFILE_SECTION_TYPE_SSO_SESSION, sso_session_name); if (!session_profile) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find an sso-session"); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-session: failed to find an sso-session"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } @@ -157,14 +157,15 @@ static struct aws_string *s_verify_config_and_construct_sso_token_path( if (!sso_region_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find sso_region in sso-session"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-session: failed to find sso_region in sso-session"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } if (!sso_start_url_property) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token parser failed to find sso_start_url in sso-session"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "token-provider-sso-session: failed to find sso_start_url in sso-session"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } @@ -181,7 +182,8 @@ static struct aws_string *s_verify_config_and_construct_sso_token_path( if (profile_sso_region_property && !aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) { AWS_LOGF_ERROR( - AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: profile & sso-session have different value for sso_region"); + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "token-provider-sso-session: profile & sso-session have different value for sso_region"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } @@ -190,7 +192,7 @@ static struct aws_string *s_verify_config_and_construct_sso_token_path( !aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) { AWS_LOGF_ERROR( AWS_LS_AUTH_CREDENTIALS_PROVIDER, - "sso-session: profile & sso-session have different value for sso_start_url"); + "token-provider-sso-session: profile & sso-session have different value for sso_start_url"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } From 749a74da7011f463e0cc62686d151d3f60a9f9ed Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 14:28:13 -0700 Subject: [PATCH 80/93] remove comment --- source/sso_token_utils.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index bc378a2e..a4e72ced 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -43,7 +43,6 @@ struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, struct aws_byte_cursor sso_dir_cursor = aws_byte_cursor_from_c_str("sso"); struct aws_byte_cursor cache_dir_cursor = aws_byte_cursor_from_c_str("cache"); - /* append sso cache directory */ if (aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator) || aws_byte_buf_append_dynamic(&sso_token_path_buf, &aws_dir_cursor) || aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator) || From c549d77f87a3e9912cf1edb622a5cd289f43ed16 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 14:37:07 -0700 Subject: [PATCH 81/93] re add comment --- tests/credentials_provider_utils.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index 5873821a..fabb584a 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -83,6 +83,9 @@ void aws_wait_on_credentials_callback(struct aws_get_credentials_test_callback_r } } +/* + * Mock provider + */ struct aws_credentials_provider_mock_impl { struct aws_array_list results; size_t next_result; From 5afddc0acc8553aff7bc1c8a9f14aae52635745f Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 14:37:43 -0700 Subject: [PATCH 82/93] add blank line --- tests/credentials_provider_utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/credentials_provider_utils.h b/tests/credentials_provider_utils.h index 935e2f66..08cd8733 100644 --- a/tests/credentials_provider_utils.h +++ b/tests/credentials_provider_utils.h @@ -8,6 +8,7 @@ #include #include + #include #include #include From 405a2c56f6f4257e16ae785a7837d2cbd0c275aa Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 14:42:20 -0700 Subject: [PATCH 83/93] update logs --- source/token_provider_sso_profile.c | 2 +- source/token_provider_sso_session.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index b9c0577a..8c09e87e 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -47,7 +47,7 @@ static int s_token_provider_profile_get_token( } if (aws_date_time_as_nanos(&sso_token->expiration) <= now_ns) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached sso token is expired.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 561739cb..14839f93 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -48,7 +48,7 @@ static int s_token_provider_sso_session_get_token( } if (aws_date_time_as_nanos(&sso_token->expiration) <= now_ns) { - AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached token is expired.", (void *)provider); + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached sso token is expired.", (void *)provider); aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED); goto done; } From d7677a177e3936c1018f9efe2d9bb92ffc6210a5 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 14:43:38 -0700 Subject: [PATCH 84/93] update name to sso token --- source/token_provider_sso_profile.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 8c09e87e..52e11af3 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -89,14 +89,14 @@ static struct aws_credentials_provider_vtable s_aws_token_provider_profile_vtabl AWS_STRING_FROM_LITERAL(s_profile_sso_start_url_name, "sso_start_url"); -static struct aws_string *s_construct_profile_token_path( +static struct aws_string *s_construct_profile_sso_token_path( struct aws_allocator *allocator, struct aws_byte_cursor profile_name_override, struct aws_byte_cursor config_file_name_override) { struct aws_profile_collection *config_collection = NULL; struct aws_string *profile_name = NULL; - struct aws_string *token_path = NULL; + struct aws_string *sso_token_path = NULL; profile_name = aws_get_profile_name(allocator, &profile_name_override); if (!profile_name) { @@ -136,8 +136,8 @@ static struct aws_string *s_construct_profile_token_path( goto cleanup; } - token_path = aws_construct_sso_token_path(allocator, aws_profile_property_get_value(sso_start_url_property)); - if (!token_path) { + sso_token_path = aws_construct_sso_token_path(allocator, aws_profile_property_get_value(sso_start_url_property)); + if (!sso_token_path) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-profile: failed to construct token path"); goto cleanup; } @@ -145,14 +145,14 @@ static struct aws_string *s_construct_profile_token_path( cleanup: aws_string_destroy(profile_name); aws_profile_collection_release(config_collection); - return token_path; + return sso_token_path; } struct aws_credentials_provider *aws_token_provider_new_sso_profile( struct aws_allocator *allocator, const struct aws_token_provider_sso_profile_options *options) { - struct aws_string *token_path = - s_construct_profile_token_path(allocator, options->profile_name_override, options->config_file_name_override); + struct aws_string *token_path = s_construct_profile_sso_token_path( + allocator, options->profile_name_override, options->config_file_name_override); if (!token_path) { return NULL; } From b555c3d37a844121a782f94d5b466f32c7d2ed23 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 30 Mar 2023 15:28:13 -0700 Subject: [PATCH 85/93] use aws_normalize_directory_separator --- source/sso_token_utils.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/source/sso_token_utils.c b/source/sso_token_utils.c index a4e72ced..7b90ef43 100644 --- a/source/sso_token_utils.c +++ b/source/sso_token_utils.c @@ -37,19 +37,9 @@ struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, goto cleanup; } - /* append /.aws/sso/cache/ with platform specific directory separator */ - const char local_platform_separator = aws_get_platform_directory_separator(); - struct aws_byte_cursor aws_dir_cursor = aws_byte_cursor_from_c_str(".aws"); - struct aws_byte_cursor sso_dir_cursor = aws_byte_cursor_from_c_str("sso"); - struct aws_byte_cursor cache_dir_cursor = aws_byte_cursor_from_c_str("cache"); - - if (aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator) || - aws_byte_buf_append_dynamic(&sso_token_path_buf, &aws_dir_cursor) || - aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator) || - aws_byte_buf_append_dynamic(&sso_token_path_buf, &sso_dir_cursor) || - aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator) || - aws_byte_buf_append_dynamic(&sso_token_path_buf, &cache_dir_cursor) || - aws_byte_buf_append_byte_dynamic(&sso_token_path_buf, local_platform_separator)) { + /* append sso cache directory */ + struct aws_byte_cursor sso_cache_dir_cursor = aws_byte_cursor_from_c_str("/.aws/sso/cache/"); + if (aws_byte_buf_append_dynamic(&sso_token_path_buf, &sso_cache_dir_cursor)) { goto cleanup; } @@ -68,6 +58,9 @@ struct aws_string *aws_construct_sso_token_path(struct aws_allocator *allocator, goto cleanup; } + /* use platform-specific directory separator. */ + aws_normalize_directory_separator(&sso_token_path_buf); + sso_token_path_str = aws_string_new_from_buf(allocator, &sso_token_path_buf); AWS_LOGF_INFO( AWS_LS_AUTH_CREDENTIALS_PROVIDER, From 722334892c25501a35022006d1694d24aa9e32dd Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 3 Apr 2023 11:19:34 -0700 Subject: [PATCH 86/93] Add docs for legacy way --- include/aws/auth/private/sso_token_providers.h | 1 + source/credentials_utils.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/aws/auth/private/sso_token_providers.h b/include/aws/auth/private/sso_token_providers.h index 3f741adb..5e5cdd91 100644 --- a/include/aws/auth/private/sso_token_providers.h +++ b/include/aws/auth/private/sso_token_providers.h @@ -66,6 +66,7 @@ AWS_EXTERN_C_BEGIN /** * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json + * This is the used by the legacy way which doesn't support refreshing credentials. * * @param allocator memory allocator to use for all memory allocation * @param options provider-specific configuration options diff --git a/source/credentials_utils.c b/source/credentials_utils.c index ed297234..363f0604 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0. */ -#include #include +#include #include #include From 288373b5b80a462f1a470bb11d4bd755a158da8b Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 3 Apr 2023 15:10:08 -0700 Subject: [PATCH 87/93] move token api to sso_token_utils --- include/aws/auth/credentials.h | 23 ---------------------- include/aws/auth/private/sso_token_utils.h | 23 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index e727e561..9c73f5d9 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -640,20 +640,6 @@ struct aws_credentials *aws_credentials_new_ecc_from_aws_credentials( struct aws_allocator *allocator, const struct aws_credentials *credentials); -/** - * Creates a set of AWS credentials based on a token with expiration. - * - * @param allocator memory allocator to use for all memory allocation - * @param token token for the credentials - * @param expiration_timepoint_in_seconds time at which these credentials expire - * @return a new pair of AWS credentials, or NULL - */ -AWS_AUTH_API -struct aws_credentials *aws_credentials_new_token( - struct aws_allocator *allocator, - struct aws_byte_cursor token, - uint64_t expiration_timepoint_in_seconds); - /** * Add a reference to some credentials * @@ -697,15 +683,6 @@ struct aws_byte_cursor aws_credentials_get_secret_access_key(const struct aws_cr AWS_AUTH_API struct aws_byte_cursor aws_credentials_get_session_token(const struct aws_credentials *credentials); -/** - * Get the token from a set of AWS credentials - * - * @param credentials credentials to get the token from - * @return a byte cursor to the token or an empty byte cursor if there is no token - */ -AWS_AUTH_API -struct aws_byte_cursor aws_credentials_get_token(const struct aws_credentials *credentials); - /** * Get the expiration timepoint (in seconds since epoch) associated with a set of credentials * diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index 2cae138d..42c5c2bf 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -30,6 +30,29 @@ void aws_sso_token_destroy(struct aws_sso_token *token); AWS_AUTH_API struct aws_sso_token *aws_sso_token_new_from_file(struct aws_allocator *allocator, const struct aws_string *file_path); +/** + * Creates a set of AWS credentials based on a token with expiration. + * + * @param allocator memory allocator to use for all memory allocation + * @param token token for the credentials + * @param expiration_timepoint_in_seconds time at which these credentials expire + * @return a new pair of AWS credentials, or NULL + */ +AWS_AUTH_API +struct aws_credentials *aws_credentials_new_token( + struct aws_allocator *allocator, + struct aws_byte_cursor token, + uint64_t expiration_timepoint_in_seconds); + +/** + * Get the token from a set of AWS credentials + * + * @param credentials credentials to get the token from + * @return a byte cursor to the token or an empty byte cursor if there is no token + */ +AWS_AUTH_API +struct aws_byte_cursor aws_credentials_get_token(const struct aws_credentials *credentials); + AWS_EXTERN_C_END #endif /* AWS_AUTH_TOKEN_PRIVATE_H */ From 6663f964df901c295fd3a293af37a0ab731f4eb7 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 3 Apr 2023 15:23:50 -0700 Subject: [PATCH 88/93] remove clock.h --- include/aws/auth/private/sso_token_utils.h | 1 + source/credentials_utils.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index 42c5c2bf..e007a599 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -7,6 +7,7 @@ */ #include +#include #include /* structure to represent a parsed sso token */ diff --git a/source/credentials_utils.c b/source/credentials_utils.c index 363f0604..2901854a 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include From f8a6b49de823de83dc772663019926fee85a9346 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 3 Apr 2023 15:46:56 -0700 Subject: [PATCH 89/93] test including token utils --- source/credentials.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/credentials.c b/source/credentials.c index 7fa79427..3340663d 100644 --- a/source/credentials.c +++ b/source/credentials.c @@ -5,6 +5,7 @@ #include +#include #include #include #include From 72ada93ce7f2e7c1f01bd69065cd85acf9edccfe Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 3 Apr 2023 16:14:27 -0700 Subject: [PATCH 90/93] remove include from token utils --- include/aws/auth/private/sso_token_utils.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/aws/auth/private/sso_token_utils.h b/include/aws/auth/private/sso_token_utils.h index e007a599..42c5c2bf 100644 --- a/include/aws/auth/private/sso_token_utils.h +++ b/include/aws/auth/private/sso_token_utils.h @@ -7,7 +7,6 @@ */ #include -#include #include /* structure to represent a parsed sso token */ From 4fe3fff051f0b09852256bf1ce81342b3f38e8ff Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Tue, 4 Apr 2023 11:35:30 -0700 Subject: [PATCH 91/93] Fix comment --- include/aws/auth/private/sso_token_providers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/aws/auth/private/sso_token_providers.h b/include/aws/auth/private/sso_token_providers.h index 5e5cdd91..d3c19807 100644 --- a/include/aws/auth/private/sso_token_providers.h +++ b/include/aws/auth/private/sso_token_providers.h @@ -66,7 +66,7 @@ AWS_EXTERN_C_BEGIN /** * Creates a provider that sources sso token based credentials from key-value profiles loaded from the aws * config("~/.aws/config" by default) and ~/.aws/sso/cache/.json - * This is the used by the legacy way which doesn't support refreshing credentials. + * This is the legacy way which doesn't support refreshing credentials. * * @param allocator memory allocator to use for all memory allocation * @param options provider-specific configuration options From b761bdc4c90257495e52981cf0f99699360bb998 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 1 May 2023 09:12:00 -0700 Subject: [PATCH 92/93] Implement SSO Credentials Provider Part 2 (#191) --- include/aws/auth/credentials.h | 56 ++ include/aws/auth/private/credentials_utils.h | 2 + .../aws/auth/private/sso_token_providers.h | 14 + source/credentials_provider_sso.c | 851 ++++++++++++++++ source/credentials_utils.c | 33 +- source/token_provider_sso_profile.c | 16 +- source/token_provider_sso_session.c | 19 +- tests/CMakeLists.txt | 17 + tests/credentials_provider_sso_tests.c | 937 ++++++++++++++++++ tests/credentials_provider_utils.c | 301 ++++++ tests/credentials_provider_utils.h | 71 ++ tests/token_provider_sso_tests.c | 108 ++ 12 files changed, 2411 insertions(+), 14 deletions(-) create mode 100644 source/credentials_provider_sso.c create mode 100644 tests/credentials_provider_sso_tests.c diff --git a/include/aws/auth/credentials.h b/include/aws/auth/credentials.h index 9c73f5d9..1d7bdd9e 100644 --- a/include/aws/auth/credentials.h +++ b/include/aws/auth/credentials.h @@ -355,6 +355,49 @@ struct aws_credentials_provider_sts_web_identity_options { struct aws_auth_http_system_vtable *function_table; }; +/* + * Configuration for the SSOCredentialsProvider that sends a GetRoleCredentialsRequest to the AWS Single + * Sign-On Service to maintain short-lived sessions to use for authentication. + * + * https://docs.aws.amazon.com/sdkref/latest/guide/feature-sso-credentials.html + */ +struct aws_credentials_provider_sso_options { + struct aws_credentials_provider_shutdown_options shutdown_options; + + /* + * Override of what profile to use to source credentials from ('default' by default) + */ + struct aws_byte_cursor profile_name_override; + + /* + * Override path to the profile config file (~/.aws/config by default) + */ + struct aws_byte_cursor config_file_name_override; + + /** + * (Optional) + * Use a cached config profile collection. You can also pass a merged collection. + * config_file_name_override will be ignored if this option is provided. + */ + struct aws_profile_collection *config_file_cached; + + /* + * Connection bootstrap to use for any network connections made while sourcing credentials + * Required. + */ + struct aws_client_bootstrap *bootstrap; + + /* + * Client TLS context to use when querying SSO provider. + * Required. + */ + struct aws_tls_ctx *tls_ctx; + + /* For mocking, leave NULL otherwise */ + struct aws_auth_http_system_vtable *function_table; + aws_io_clock_fn *system_clock_fn; +}; + /** * Configuration options for the STS credentials provider */ @@ -927,6 +970,19 @@ struct aws_credentials_provider *aws_credentials_provider_new_sts_web_identity( struct aws_allocator *allocator, const struct aws_credentials_provider_sts_web_identity_options *options); +/** + * Creates a provider that sources credentials from SSO using a SSOToken. + * + * @param allocator memory allocator to use for all memory allocation + * @param options provider-specific configuration options + * + * @return the newly-constructed credentials provider, or NULL if an error occurred. + */ +AWS_AUTH_API +struct aws_credentials_provider *aws_credentials_provider_new_sso( + struct aws_allocator *allocator, + const struct aws_credentials_provider_sso_options *options); + /* * Creates a provider that sources credentials from running an external command or process * diff --git a/include/aws/auth/private/credentials_utils.h b/include/aws/auth/private/credentials_utils.h index 0252e214..598c3ba0 100644 --- a/include/aws/auth/private/credentials_utils.h +++ b/include/aws/auth/private/credentials_utils.h @@ -73,6 +73,7 @@ struct aws_auth_http_system_vtable { enum aws_parse_credentials_expiration_format { AWS_PCEF_STRING_ISO_8601_DATE, AWS_PCEF_NUMBER_UNIX_EPOCH, + AWS_PCEF_NUMBER_UNIX_EPOCH_MS, }; struct aws_parse_credentials_from_json_doc_options { @@ -80,6 +81,7 @@ struct aws_parse_credentials_from_json_doc_options { const char *secret_access_key_name; const char *token_name; const char *expiration_name; + const char *top_level_object_name; enum aws_parse_credentials_expiration_format expiration_format; bool token_required; bool expiration_required; diff --git a/include/aws/auth/private/sso_token_providers.h b/include/aws/auth/private/sso_token_providers.h index d3c19807..a9f93079 100644 --- a/include/aws/auth/private/sso_token_providers.h +++ b/include/aws/auth/private/sso_token_providers.h @@ -26,6 +26,13 @@ struct aws_token_provider_sso_profile_options { */ struct aws_byte_cursor config_file_name_override; + /** + * (Optional) + * Use a cached config profile collection. You can also pass a merged collection. + * config_file_name_override will be ignored if this option is provided. + */ + struct aws_profile_collection *config_file_cached; + /* For mocking, leave NULL otherwise */ aws_io_clock_fn *system_clock_fn; }; @@ -47,6 +54,13 @@ struct aws_token_provider_sso_session_options { */ struct aws_byte_cursor config_file_name_override; + /** + * (Optional) + * Use a cached config profile collection. You can also pass a merged collection. + * config_file_name_override will be ignored if this option is provided. + */ + struct aws_profile_collection *config_file_cached; + /* * Connection bootstrap to use for any network connections made */ diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c new file mode 100644 index 00000000..382e08c0 --- /dev/null +++ b/source/credentials_provider_sso.c @@ -0,0 +1,851 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# pragma warning(disable : 4204) +#endif /* _MSC_VER */ + +#define SSO_RESPONSE_SIZE_INITIAL 2048 +#define SSO_RESPONSE_SIZE_LIMIT 10000 +#define SSO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS 2 +#define SSO_MAX_ATTEMPTS 3 +#define SSO_RETRY_TIMEOUT_MS 100 + +struct aws_credentials_provider_sso_impl { + struct aws_http_connection_manager *connection_manager; + const struct aws_auth_http_system_vtable *function_table; + struct aws_string *endpoint; + struct aws_string *sso_account_id; + struct aws_string *sso_role_name; + struct aws_credentials_provider *token_provider; + struct aws_retry_strategy *retry_strategy; +}; + +/** + * aws_sso_query_context - context for each outstanding SSO query. + */ +struct aws_sso_query_context { + /* immutable post-creation */ + struct aws_allocator *allocator; + struct aws_credentials_provider *provider; + aws_on_get_credentials_callback_fn *original_callback; + void *original_user_data; + + /* mutable */ + struct aws_http_connection *connection; + struct aws_http_message *request; + struct aws_byte_buf payload; + struct aws_retry_token *retry_token; + struct aws_byte_buf path_and_query; + struct aws_string *token; + + int status_code; + int error_code; +}; + +/* called in between retries. */ +static void s_sso_query_context_reset_request_specific_data(struct aws_sso_query_context *sso_query_context) { + if (sso_query_context->request) { + aws_http_message_release(sso_query_context->request); + sso_query_context->request = NULL; + } + if (sso_query_context->connection) { + struct aws_credentials_provider_sso_impl *provider_impl = sso_query_context->provider->impl; + int result = provider_impl->function_table->aws_http_connection_manager_release_connection( + provider_impl->connection_manager, sso_query_context->connection); + (void)result; + AWS_ASSERT(result == AWS_OP_SUCCESS); + sso_query_context->connection = NULL; + } + if (sso_query_context->token) { + aws_string_destroy_secure(sso_query_context->token); + sso_query_context->token = NULL; + } + sso_query_context->status_code = 0; + sso_query_context->error_code = 0; +} + +static void s_sso_query_context_destroy(struct aws_sso_query_context *sso_query_context) { + if (sso_query_context == NULL) { + return; + } + + s_sso_query_context_reset_request_specific_data(sso_query_context); + aws_byte_buf_clean_up(&sso_query_context->payload); + aws_byte_buf_clean_up(&sso_query_context->path_and_query); + aws_credentials_provider_release(sso_query_context->provider); + aws_retry_token_release(sso_query_context->retry_token); + aws_mem_release(sso_query_context->allocator, sso_query_context); +} + +static struct aws_sso_query_context *s_sso_query_context_new( + struct aws_credentials_provider *provider, + aws_on_get_credentials_callback_fn callback, + void *user_data) { + struct aws_credentials_provider_sso_impl *impl = provider->impl; + + struct aws_sso_query_context *sso_query_context = + aws_mem_calloc(provider->allocator, 1, sizeof(struct aws_sso_query_context)); + sso_query_context->allocator = provider->allocator; + sso_query_context->provider = aws_credentials_provider_acquire(provider); + sso_query_context->original_user_data = user_data; + sso_query_context->original_callback = callback; + + /* construct path and query */ + struct aws_byte_cursor account_id_cursor = aws_byte_cursor_from_string(impl->sso_account_id); + struct aws_byte_cursor role_name_cursor = aws_byte_cursor_from_string(impl->sso_role_name); + struct aws_byte_cursor path_cursor = aws_byte_cursor_from_c_str("/federation/credentials?account_id="); + struct aws_byte_cursor role_name_param_cursor = aws_byte_cursor_from_c_str("&role_name="); + + if (aws_byte_buf_init_copy_from_cursor(&sso_query_context->path_and_query, provider->allocator, path_cursor) || + aws_byte_buf_append_encoding_uri_param(&sso_query_context->path_and_query, &account_id_cursor) || + aws_byte_buf_append_dynamic(&sso_query_context->path_and_query, &role_name_param_cursor) || + aws_byte_buf_append_encoding_uri_param(&sso_query_context->path_and_query, &role_name_cursor)) { + goto on_error; + } + + if (aws_byte_buf_init(&sso_query_context->payload, provider->allocator, SSO_RESPONSE_SIZE_INITIAL)) { + goto on_error; + } + + return sso_query_context; + +on_error: + s_sso_query_context_destroy(sso_query_context); + + return NULL; +} + +/* + * No matter the result, this always gets called assuming that sso_query_context is successfully allocated + */ +static void s_finalize_get_credentials_query(struct aws_sso_query_context *sso_query_context) { + struct aws_credentials *credentials = NULL; + + if (sso_query_context->error_code == AWS_ERROR_SUCCESS) { + /* parse credentials */ + struct aws_parse_credentials_from_json_doc_options parse_options = { + .access_key_id_name = "accessKeyId", + .secret_access_key_name = "secretAccessKey", + .token_name = "sessionToken", + .expiration_name = "expiration", + .top_level_object_name = "roleCredentials", + .token_required = true, + .expiration_required = true, + .expiration_format = AWS_PCEF_NUMBER_UNIX_EPOCH_MS, + }; + + credentials = aws_parse_credentials_from_json_document( + sso_query_context->allocator, aws_byte_cursor_from_buf(&sso_query_context->payload), &parse_options); + } + + if (credentials) { + AWS_LOGF_INFO( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) successfully queried credentials", + (void *)sso_query_context->provider); + } else { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) failed to query credentials", + (void *)sso_query_context->provider); + + if (sso_query_context->error_code == AWS_ERROR_SUCCESS) { + sso_query_context->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE; + } + } + + /* pass the credentials back */ + sso_query_context->original_callback( + credentials, sso_query_context->error_code, sso_query_context->original_user_data); + + /* clean up */ + s_sso_query_context_destroy(sso_query_context); + aws_credentials_release(credentials); +} +static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *user_data); + +static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_code, void *user_data) { + struct aws_sso_query_context *sso_query_context = user_data; + + struct aws_credentials_provider_sso_impl *impl = sso_query_context->provider->impl; + impl->function_table->aws_http_stream_release(stream); + + /* set error code */ + sso_query_context->error_code = error_code; + impl->function_table->aws_http_stream_get_incoming_response_status(stream, &sso_query_context->status_code); + if (error_code == AWS_OP_SUCCESS && sso_query_context->status_code != AWS_HTTP_STATUS_CODE_200_OK) { + sso_query_context->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_HTTP_STATUS_FAILURE; + } + + /* + * If we can retry the request based on error response or http status code failure, retry it, otherwise, call the + * finalize function. + */ + if (error_code || sso_query_context->status_code != AWS_HTTP_STATUS_CODE_200_OK) { + enum aws_retry_error_type error_type = + aws_credentials_provider_compute_retry_error_type(sso_query_context->status_code, error_code); + + /* don't retry client errors at all. */ + if (error_type != AWS_RETRY_ERROR_TYPE_CLIENT_ERROR) { + if (aws_retry_strategy_schedule_retry( + sso_query_context->retry_token, error_type, s_on_retry_ready, sso_query_context) == + AWS_OP_SUCCESS) { + AWS_LOGF_INFO( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): successfully scheduled a retry", + (void *)sso_query_context->provider); + return; + } + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to schedule retry: %s", + (void *)sso_query_context->provider, + aws_error_str(aws_last_error())); + sso_query_context->error_code = aws_last_error(); + } + } else { + int result = aws_retry_token_record_success(sso_query_context->retry_token); + (void)result; + AWS_ASSERT(result == AWS_ERROR_SUCCESS); + } + + s_finalize_get_credentials_query(sso_query_context); +} + +static int s_on_incoming_body_fn(struct aws_http_stream *stream, const struct aws_byte_cursor *body, void *user_data) { + + (void)stream; + + struct aws_sso_query_context *sso_query_context = user_data; + + AWS_LOGF_TRACE( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) received %zu response bytes", + (void *)sso_query_context->provider, + body->len); + + if (body->len + sso_query_context->payload.len > SSO_RESPONSE_SIZE_LIMIT) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) response exceeded maximum allowed length", + (void *)sso_query_context->provider); + + return aws_raise_error(AWS_ERROR_SHORT_BUFFER); + } + + if (aws_byte_buf_append_dynamic(&sso_query_context->payload, body)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) error appending response payload: %s", + (void *)sso_query_context->provider, + aws_error_str(aws_last_error())); + + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; +} + +/* Request headers. */ +AWS_STATIC_STRING_FROM_LITERAL(s_sso_token_header, "x-amz-sso_bearer_token"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header, "User-Agent"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header_value, "CRTAuthSSOCredentialsProvider"); + +static void s_query_credentials(struct aws_sso_query_context *sso_query_context) { + AWS_FATAL_ASSERT(sso_query_context->connection); + struct aws_http_stream *stream = NULL; + struct aws_credentials_provider_sso_impl *impl = sso_query_context->provider->impl; + + sso_query_context->request = aws_http_message_new_request(sso_query_context->allocator); + if (sso_query_context->request == NULL) { + goto on_error; + } + + struct aws_http_header auth_header = { + .name = aws_byte_cursor_from_string(s_sso_token_header), + .value = aws_byte_cursor_from_string(sso_query_context->token), + }; + struct aws_http_header host_header = { + .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Host"), + .value = aws_byte_cursor_from_string(impl->endpoint), + }; + struct aws_http_header user_agent_header = { + .name = aws_byte_cursor_from_string(s_sso_user_agent_header), + .value = aws_byte_cursor_from_string(s_sso_user_agent_header_value), + }; + + if (aws_http_message_add_header(sso_query_context->request, auth_header) || + aws_http_message_add_header(sso_query_context->request, host_header) || + aws_http_message_add_header(sso_query_context->request, user_agent_header)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) failed to add http header with error: %s", + (void *)sso_query_context->provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + if (aws_http_message_set_request_method(sso_query_context->request, aws_http_method_get)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) failed to set request method with error: %s", + (void *)sso_query_context->provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + if (aws_http_message_set_request_path( + sso_query_context->request, aws_byte_cursor_from_buf(&sso_query_context->path_and_query))) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) failed to set request path with error: %s", + (void *)sso_query_context->provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + struct aws_http_make_request_options request_options = { + .self_size = sizeof(request_options), + .on_response_headers = NULL, + .on_response_header_block_done = NULL, + .on_response_body = s_on_incoming_body_fn, + .on_complete = s_on_stream_complete_fn, + .user_data = sso_query_context, + .request = sso_query_context->request, + }; + + stream = impl->function_table->aws_http_connection_make_request(sso_query_context->connection, &request_options); + if (!stream) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) failed to make request with error: %s", + (void *)sso_query_context->provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + if (impl->function_table->aws_http_stream_activate(stream)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p) failed to activate the stream with error: %s", + (void *)sso_query_context->provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + return; + +on_error: + sso_query_context->error_code = aws_last_error(); + impl->function_table->aws_http_stream_release(stream); + s_finalize_get_credentials_query(sso_query_context); +} + +static void s_on_get_token_callback(struct aws_credentials *credentials, int error_code, void *user_data) { + struct aws_sso_query_context *sso_query_context = user_data; + + if (error_code) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: failed to acquire a token, error code %d(%s)", + (void *)sso_query_context->provider, + error_code, + aws_error_str(error_code)); + sso_query_context->error_code = error_code; + s_finalize_get_credentials_query(sso_query_context); + return; + } + + struct aws_byte_cursor token = aws_credentials_get_token(credentials); + AWS_LOGF_INFO( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): successfully accquired a token", + (void *)sso_query_context->provider); + + sso_query_context->token = aws_string_new_from_cursor(sso_query_context->allocator, &token); + s_query_credentials(sso_query_context); +} + +static void s_on_acquire_connection(struct aws_http_connection *connection, int error_code, void *user_data) { + struct aws_sso_query_context *sso_query_context = user_data; + + if (error_code) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: failed to acquire a connection, error code %d(%s)", + (void *)sso_query_context->provider, + error_code, + aws_error_str(error_code)); + sso_query_context->error_code = error_code; + s_finalize_get_credentials_query(sso_query_context); + return; + } + AWS_LOGF_INFO( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): successfully accquired a connection", + (void *)sso_query_context->provider); + sso_query_context->connection = connection; + + struct aws_credentials_provider_sso_impl *impl = sso_query_context->provider->impl; + if (aws_credentials_provider_get_credentials(impl->token_provider, s_on_get_token_callback, user_data)) { + int last_error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "id=%p: failed to get a token, error code %d(%s)", + (void *)sso_query_context->provider, + last_error_code, + aws_error_str(last_error_code)); + + sso_query_context->error_code = last_error_code; + s_finalize_get_credentials_query(sso_query_context); + } +} + +/* called for each retry. */ +static void s_on_retry_ready(struct aws_retry_token *token, int error_code, void *user_data) { + (void)token; + struct aws_sso_query_context *sso_query_context = user_data; + struct aws_credentials_provider_sso_impl *impl = sso_query_context->provider->impl; + + if (error_code) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to schedule retry with error: %s", + (void *)sso_query_context->provider, + aws_error_debug_str(error_code)); + sso_query_context->error_code = error_code; + s_finalize_get_credentials_query(sso_query_context); + return; + } + + /* clear the result from previous attempt */ + s_sso_query_context_reset_request_specific_data(sso_query_context); + + impl->function_table->aws_http_connection_manager_acquire_connection( + impl->connection_manager, s_on_acquire_connection, sso_query_context); +} + +static void s_on_retry_token_acquired( + struct aws_retry_strategy *strategy, + int error_code, + struct aws_retry_token *token, + void *user_data) { + struct aws_sso_query_context *sso_query_context = user_data; + (void)strategy; + + if (error_code) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to acquire retry token: %s", + (void *)sso_query_context->provider, + aws_error_debug_str(error_code)); + sso_query_context->error_code = error_code; + s_finalize_get_credentials_query(sso_query_context); + return; + } + + sso_query_context->retry_token = token; + struct aws_credentials_provider_sso_impl *impl = sso_query_context->provider->impl; + impl->function_table->aws_http_connection_manager_acquire_connection( + impl->connection_manager, s_on_acquire_connection, user_data); +} + +static int s_credentials_provider_sso_get_credentials( + struct aws_credentials_provider *provider, + aws_on_get_credentials_callback_fn callback, + void *user_data) { + + struct aws_credentials_provider_sso_impl *impl = provider->impl; + + struct aws_sso_query_context *sso_query_context = s_sso_query_context_new(provider, callback, user_data); + if (sso_query_context == NULL) { + return AWS_OP_ERR; + } + + if (aws_retry_strategy_acquire_retry_token( + impl->retry_strategy, NULL, s_on_retry_token_acquired, sso_query_context, SSO_RETRY_TIMEOUT_MS)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to acquire retry token: %s", + (void *)provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + return AWS_OP_SUCCESS; + +on_error: + s_sso_query_context_destroy(sso_query_context); + return AWS_OP_ERR; +} + +static void s_on_connection_manager_shutdown(void *user_data) { + struct aws_credentials_provider *provider = user_data; + + aws_credentials_provider_invoke_shutdown_callback(provider); + aws_mem_release(provider->allocator, provider); +} + +static void s_credentials_provider_sso_destroy(struct aws_credentials_provider *provider) { + + struct aws_credentials_provider_sso_impl *impl = provider->impl; + if (impl == NULL) { + return; + } + aws_string_destroy(impl->endpoint); + aws_string_destroy(impl->sso_account_id); + aws_string_destroy(impl->sso_role_name); + aws_retry_strategy_release(impl->retry_strategy); + aws_credentials_provider_release(impl->token_provider); + + /* aws_http_connection_manager_release will eventually leads to call of s_on_connection_manager_shutdown, + * which will do memory release for provider and impl. So We should be freeing impl + * related memory first, then call aws_http_connection_manager_release. + */ + if (impl->connection_manager) { + impl->function_table->aws_http_connection_manager_release(impl->connection_manager); + } else { + /* If provider setup failed halfway through, connection_manager might not exist. + * In this case invoke shutdown completion callback directly to finish cleanup */ + s_on_connection_manager_shutdown(provider); + } +} + +static struct aws_credentials_provider_vtable s_aws_credentials_provider_sso_vtable = { + .get_credentials = s_credentials_provider_sso_get_credentials, + .destroy = s_credentials_provider_sso_destroy, +}; + +static int s_construct_sso_portal_endpoint( + struct aws_allocator *allocator, + struct aws_byte_buf *out_endpoint, + const struct aws_string *region) { + AWS_PRECONDITION(allocator); + AWS_PRECONDITION(out_endpoint); + + if (!region) { + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + aws_byte_buf_clean_up(out_endpoint); + struct aws_byte_cursor sso_prefix = aws_byte_cursor_from_c_str("portal.sso."); + struct aws_byte_cursor region_cursor = aws_byte_cursor_from_string(region); + struct aws_byte_cursor amazonaws_cursor = aws_byte_cursor_from_c_str(".amazonaws.com"); + struct aws_byte_cursor cn_cursor = aws_byte_cursor_from_c_str(".cn"); + + if (aws_byte_buf_init_copy_from_cursor(out_endpoint, allocator, sso_prefix) || + aws_byte_buf_append_dynamic(out_endpoint, ®ion_cursor) || + aws_byte_buf_append_dynamic(out_endpoint, &amazonaws_cursor)) { + goto on_error; + } + + if (aws_string_eq_c_str_ignore_case(region, "cn-north-1") || + aws_string_eq_c_str_ignore_case(region, "cn-northwest-1")) { + if (aws_byte_buf_append_dynamic(out_endpoint, &cn_cursor)) { + goto on_error; + } + } + return AWS_OP_SUCCESS; + +on_error: + aws_byte_buf_clean_up(out_endpoint); + return AWS_OP_ERR; +} + +AWS_STATIC_STRING_FROM_LITERAL(s_sso_account_id, "sso_account_id"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_region, "sso_region"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_role_name, "sso_role_name"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_session, "sso_session"); + +struct sso_parameters { + struct aws_allocator *allocator; + struct aws_byte_buf endpoint; + struct aws_string *sso_account_id; + struct aws_string *sso_role_name; + struct aws_credentials_provider *token_provider; +}; + +static void s_parameters_destroy(struct sso_parameters *parameters) { + if (!parameters) { + return; + } + aws_byte_buf_clean_up(¶meters->endpoint); + aws_string_destroy(parameters->sso_account_id); + aws_string_destroy(parameters->sso_role_name); + aws_credentials_provider_release(parameters->token_provider); + aws_mem_release(parameters->allocator, parameters); +} + +/** + * Read the config file and construct profile or sso_session token provider based on sso_session property. + * + * If the profile contains sso_session property, a valid config example is as follow. + * [profile sso-profile] + * sso_session = dev + * sso_account_id = 012345678901 + * sso_role_name = SampleRole + * + * [sso-session dev] + * sso_region = us-east-1 + * sso_start_url = https://d-abc123.awsapps.com/start + * + * If the profile does't contains sso_session, the legacy valid config example is as follow. + * [profile sso-profile] + * sso_account_id = 012345678901 + * sso_region = us-east-1 + * sso_role_name = SampleRole + * sso_start_url = https://d-abc123.awsapps.com/start-beta + */ +static struct sso_parameters *s_parameters_new( + struct aws_allocator *allocator, + const struct aws_credentials_provider_sso_options *options) { + + struct sso_parameters *parameters = aws_mem_calloc(allocator, 1, sizeof(struct sso_parameters)); + parameters->allocator = allocator; + + struct aws_profile_collection *config_profile_collection = NULL; + struct aws_string *profile_name = NULL; + bool success = false; + + profile_name = aws_get_profile_name(allocator, &options->profile_name_override); + if (!profile_name) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: failed to resolve profile name"); + goto on_finish; + } + if (options->config_file_cached) { + /* Use cached config file */ + config_profile_collection = aws_profile_collection_acquire(options->config_file_cached); + } else { + /* load config file */ + config_profile_collection = + aws_load_profile_collection_from_config_file(allocator, options->config_file_name_override); + } + + if (!config_profile_collection) { + goto on_finish; + } + + const struct aws_profile *profile = aws_profile_collection_get_profile(config_profile_collection, profile_name); + if (!profile) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: failed to load \"%s\" profile", aws_string_c_str(profile_name)); + goto on_finish; + } + + const struct aws_profile_property *sso_account_id = aws_profile_get_property(profile, s_sso_account_id); + const struct aws_profile_property *sso_role_name = aws_profile_get_property(profile, s_sso_role_name); + const struct aws_profile_property *sso_region = NULL; + + if (!sso_account_id) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_account_id is missing"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + if (!sso_role_name) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_role_name is missing"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + + const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session); + /* create the appropriate token provider based on sso_session property is available or not */ + if (sso_session_property) { + /* construct sso_session token provider */ + struct aws_token_provider_sso_session_options token_provider_options = { + .config_file_name_override = options->config_file_name_override, + .config_file_cached = config_profile_collection, + .profile_name_override = options->profile_name_override, + .bootstrap = options->bootstrap, + .tls_ctx = options->tls_ctx, + .system_clock_fn = options->system_clock_fn, + }; + parameters->token_provider = aws_token_provider_new_sso_session(allocator, &token_provider_options); + if (!parameters->token_provider) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a sso token provider"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + sso_region = aws_profile_get_property( + aws_profile_collection_get_section( + config_profile_collection, + AWS_PROFILE_SECTION_TYPE_SSO_SESSION, + aws_profile_property_get_value(sso_session_property)), + s_sso_region); + } else { + /* construct profile token provider */ + struct aws_token_provider_sso_profile_options token_provider_options = { + .config_file_name_override = options->config_file_name_override, + .config_file_cached = config_profile_collection, + .profile_name_override = options->profile_name_override, + .system_clock_fn = options->system_clock_fn, + }; + + parameters->token_provider = aws_token_provider_new_sso_profile(allocator, &token_provider_options); + if (!parameters->token_provider) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: unable to create a profile token provider"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + sso_region = aws_profile_get_property(profile, s_sso_region); + } + + if (!sso_region) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso: sso_region is missing"); + aws_raise_error(AWS_AUTH_CREDENTIALS_PROVIDER_SSO_SOURCE_FAILURE); + goto on_finish; + } + + parameters->sso_account_id = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_account_id)); + parameters->sso_role_name = aws_string_new_from_string(allocator, aws_profile_property_get_value(sso_role_name)); + /* determine endpoint */ + if (s_construct_sso_portal_endpoint(allocator, ¶meters->endpoint, aws_profile_property_get_value(sso_region))) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to construct sso endpoint"); + goto on_finish; + } + AWS_LOGF_DEBUG( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Successfully loaded all required parameters for sso credentials provider."); + success = true; + +on_finish: + if (!success) { + s_parameters_destroy(parameters); + parameters = NULL; + } + aws_string_destroy(profile_name); + aws_profile_collection_release(config_profile_collection); + + return parameters; +} + +struct aws_credentials_provider *aws_credentials_provider_new_sso( + struct aws_allocator *allocator, + const struct aws_credentials_provider_sso_options *options) { + + struct sso_parameters *parameters = s_parameters_new(allocator, options); + if (!parameters) { + return NULL; + } + + struct aws_credentials_provider *provider = NULL; + struct aws_credentials_provider_sso_impl *impl = NULL; + struct aws_tls_connection_options tls_connection_options; + + aws_mem_acquire_many( + allocator, + 2, + &provider, + sizeof(struct aws_credentials_provider), + &impl, + sizeof(struct aws_credentials_provider_sso_impl)); + + AWS_ZERO_STRUCT(*provider); + AWS_ZERO_STRUCT(*impl); + AWS_ZERO_STRUCT(tls_connection_options); + + aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_sso_vtable, impl); + + if (!options->tls_ctx) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): a TLS context must be provided", (void *)provider); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + goto on_error; + } + + if (!options->bootstrap) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p): a bootstrap instance must be provided", (void *)provider); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + goto on_error; + } + + aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx); + struct aws_byte_cursor host = aws_byte_cursor_from_buf(¶meters->endpoint); + if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to create a tls connection options with error %s", + (void *)provider, + aws_error_str(aws_last_error())); + goto on_error; + } + + struct aws_socket_options socket_options; + AWS_ZERO_STRUCT(socket_options); + socket_options.type = AWS_SOCKET_STREAM; + socket_options.domain = AWS_SOCKET_IPV4; + socket_options.connect_timeout_ms = (uint32_t)aws_timestamp_convert( + SSO_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL); + + struct aws_http_connection_manager_options manager_options; + AWS_ZERO_STRUCT(manager_options); + manager_options.bootstrap = options->bootstrap; + manager_options.initial_window_size = SSO_RESPONSE_SIZE_LIMIT; + manager_options.socket_options = &socket_options; + manager_options.host = host; + manager_options.port = 443; + manager_options.max_connections = 2; + manager_options.shutdown_complete_callback = s_on_connection_manager_shutdown; + manager_options.shutdown_complete_user_data = provider; + manager_options.tls_connection_options = &tls_connection_options; + + impl->function_table = options->function_table; + if (impl->function_table == NULL) { + impl->function_table = g_aws_credentials_provider_http_function_table; + } + + impl->connection_manager = impl->function_table->aws_http_connection_manager_new(allocator, &manager_options); + if (impl->connection_manager == NULL) { + goto on_error; + } + + impl->token_provider = aws_credentials_provider_acquire(parameters->token_provider); + impl->endpoint = aws_string_new_from_buf(allocator, ¶meters->endpoint); + impl->sso_account_id = aws_string_new_from_string(allocator, parameters->sso_account_id); + impl->sso_role_name = aws_string_new_from_string(allocator, parameters->sso_role_name); + + provider->shutdown_options = options->shutdown_options; + + struct aws_standard_retry_options retry_options = { + .backoff_retry_options = + { + .el_group = options->bootstrap->event_loop_group, + .max_retries = SSO_MAX_ATTEMPTS, + }, + }; + impl->retry_strategy = aws_retry_strategy_new_standard(allocator, &retry_options); + if (!impl->retry_strategy) { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "(id=%p): failed to create a retry strategy with error %s", + (void *)provider, + aws_error_debug_str(aws_last_error())); + goto on_error; + } + + s_parameters_destroy(parameters); + aws_tls_connection_options_clean_up(&tls_connection_options); + return provider; + +on_error: + aws_credentials_provider_destroy(provider); + s_parameters_destroy(parameters); + aws_tls_connection_options_clean_up(&tls_connection_options); + return NULL; +} diff --git a/source/credentials_utils.c b/source/credentials_utils.c index bcfa7c0f..a1ee268c 100644 --- a/source/credentials_utils.c +++ b/source/credentials_utils.c @@ -4,8 +4,8 @@ */ #include -#include +#include #include #include #include @@ -13,6 +13,7 @@ #include #include #include +#include #if defined(_MSC_VER) # pragma warning(disable : 4232) @@ -128,6 +129,20 @@ static bool s_parse_expiration_value_from_json_object( return true; } + case AWS_PCEF_NUMBER_UNIX_EPOCH_MS: { + double expiration_value_ms = 0; + if (aws_json_value_get_number(value, &expiration_value_ms)) { + AWS_LOGF_INFO( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "Unabled to extract credentials Expiration field from Json document."); + return false; + } + + *expiration_timepoint_in_seconds = + aws_timestamp_convert((uint64_t)expiration_value_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_SECS, NULL); + return true; + } + default: return false; } @@ -254,13 +269,27 @@ struct aws_credentials *aws_parse_credentials_from_json_document( struct aws_allocator *allocator, struct aws_byte_cursor document, const struct aws_parse_credentials_from_json_doc_options *options) { + struct aws_credentials *credentials = NULL; struct aws_json_value *document_root = aws_json_value_new_from_string(allocator, document); if (document_root == NULL) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse document as Json document."); return NULL; } - struct aws_credentials *credentials = aws_parse_credentials_from_aws_json_object(allocator, document_root, options); + + struct aws_json_value *top_level_object = NULL; + if (options->top_level_object_name) { + top_level_object = + aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str(options->top_level_object_name)); + if (!top_level_object) { + AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "failed to parse top level object in json document."); + goto done; + } + } + + credentials = aws_parse_credentials_from_aws_json_object( + allocator, top_level_object ? top_level_object : document_root, options); +done: aws_json_value_destroy(document_root); return credentials; } diff --git a/source/token_provider_sso_profile.c b/source/token_provider_sso_profile.c index 52e11af3..6e2a3521 100644 --- a/source/token_provider_sso_profile.c +++ b/source/token_provider_sso_profile.c @@ -91,20 +91,25 @@ AWS_STRING_FROM_LITERAL(s_profile_sso_start_url_name, "sso_start_url"); static struct aws_string *s_construct_profile_sso_token_path( struct aws_allocator *allocator, - struct aws_byte_cursor profile_name_override, - struct aws_byte_cursor config_file_name_override) { + const struct aws_token_provider_sso_profile_options *options) { struct aws_profile_collection *config_collection = NULL; struct aws_string *profile_name = NULL; struct aws_string *sso_token_path = NULL; - profile_name = aws_get_profile_name(allocator, &profile_name_override); + profile_name = aws_get_profile_name(allocator, &options->profile_name_override); if (!profile_name) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-profile: failed to resolve profile name"); aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE); goto cleanup; } - config_collection = aws_load_profile_collection_from_config_file(allocator, config_file_name_override); + if (options->config_file_cached) { + /* Use cached config file */ + config_collection = aws_profile_collection_acquire(options->config_file_cached); + } else { + /* load config file */ + config_collection = aws_load_profile_collection_from_config_file(allocator, options->config_file_name_override); + } if (!config_collection) { AWS_LOGF_ERROR( @@ -151,8 +156,7 @@ static struct aws_string *s_construct_profile_sso_token_path( struct aws_credentials_provider *aws_token_provider_new_sso_profile( struct aws_allocator *allocator, const struct aws_token_provider_sso_profile_options *options) { - struct aws_string *token_path = s_construct_profile_sso_token_path( - allocator, options->profile_name_override, options->config_file_name_override); + struct aws_string *token_path = s_construct_profile_sso_token_path(allocator, options); if (!token_path) { return NULL; } diff --git a/source/token_provider_sso_session.c b/source/token_provider_sso_session.c index 14839f93..e3a17b7f 100644 --- a/source/token_provider_sso_session.c +++ b/source/token_provider_sso_session.c @@ -106,18 +106,26 @@ AWS_STRING_FROM_LITERAL(s_sso_start_url_name, "sso_start_url"); */ static struct aws_string *s_verify_config_and_construct_sso_token_path( struct aws_allocator *allocator, - struct aws_byte_cursor profile_name_override, - struct aws_byte_cursor config_file_name_override) { + const struct aws_token_provider_sso_session_options *options) { struct aws_profile_collection *config_collection = NULL; struct aws_string *profile_name = NULL; struct aws_string *sso_token_path = NULL; - profile_name = aws_get_profile_name(allocator, &profile_name_override); + profile_name = aws_get_profile_name(allocator, &options->profile_name_override); if (!profile_name) { AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token provider failed to resolve profile name"); goto cleanup; } - config_collection = aws_load_profile_collection_from_config_file(allocator, config_file_name_override); + if (options->config_file_cached) { + /* Use cached config file */ + config_collection = aws_profile_collection_acquire(options->config_file_cached); + } else { + /* load config file */ + config_collection = aws_load_profile_collection_from_config_file(allocator, options->config_file_name_override); + } + if (!config_collection) { + goto cleanup; + } const struct aws_profile *profile = aws_profile_collection_get_profile(config_collection, profile_name); @@ -213,8 +221,7 @@ struct aws_credentials_provider *aws_token_provider_new_sso_session( AWS_ASSERT(options->bootstrap); AWS_ASSERT(options->tls_ctx); - struct aws_string *token_path = s_verify_config_and_construct_sso_token_path( - allocator, options->profile_name_override, options->config_file_name_override); + struct aws_string *token_path = s_verify_config_and_construct_sso_token_path(allocator, options); if (!token_path) { return NULL; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 07610d07..943a12fe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -108,8 +108,10 @@ add_test_case(sso_token_provider_profile_valid_profile_test) add_net_test_case(sso_token_provider_sso_session_invalid_config_test) add_net_test_case(sso_token_provider_sso_session_valid_config_test) add_net_test_case(sso_token_provider_sso_session_basic_success) +add_net_test_case(sso_token_provider_sso_session_config_file_cached) add_net_test_case(sso_token_provider_sso_session_expired_token) add_test_case(sso_token_provider_profile_basic_success) +add_test_case(sso_token_provider_profile_cached_config_file) add_test_case(sso_token_provider_profile_expired_token) add_test_case(parse_token_location_url_test) @@ -120,6 +122,21 @@ add_test_case(parse_sso_token_invalid_missing_access_token) add_test_case(parse_sso_token_missing_expires_at) add_test_case(parse_sso_token_invalid_expires_at) +add_net_test_case(credentials_provider_sso_failed_invalid_config) +add_net_test_case(credentials_provider_sso_create_destroy_valid_config) +add_net_test_case(credentials_provider_sso_connect_failure) +add_net_test_case(credentials_provider_sso_failure_token_missing) +add_net_test_case(credentials_provider_sso_failure_token_expired) +add_net_test_case(credentials_provider_sso_failure_token_empty) +add_net_test_case(credentials_provider_sso_request_failure) +add_net_test_case(credentials_provider_sso_bad_response) +add_net_test_case(credentials_provider_sso_retryable_error) +add_net_test_case(credentials_provider_sso_basic_success) +add_net_test_case(credentials_provider_sso_basic_success_cached_config_file) +add_net_test_case(credentials_provider_sso_basic_success_profile) +add_net_test_case(credentials_provider_sso_basic_success_profile_cached_config_file) +add_net_test_case(credentials_provider_sso_basic_success_after_failure) + add_test_case(imds_client_new_release) add_test_case(imds_client_connect_failure) add_test_case(imds_client_token_request_failure) diff --git a/tests/credentials_provider_sso_tests.c b/tests/credentials_provider_sso_tests.c new file mode 100644 index 00000000..93ee775d --- /dev/null +++ b/tests/credentials_provider_sso_tests.c @@ -0,0 +1,937 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include "credentials_provider_utils.h" +#include "shared_credentials_test_definitions.h" + +#include +#include +#include +#include + +AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile, "sso"); +static int s_aws_credentials_provider_sso_test_init_config_profile( + struct aws_allocator *allocator, + const struct aws_string *config_contents) { + + struct aws_string *config_file_path_str = aws_create_process_unique_file_name(allocator); + ASSERT_TRUE(config_file_path_str != NULL); + ASSERT_TRUE(aws_create_profile_file(config_file_path_str, config_contents) == AWS_OP_SUCCESS); + + ASSERT_TRUE( + aws_set_environment_value(s_default_config_path_env_variable_name, config_file_path_str) == AWS_OP_SUCCESS); + + ASSERT_TRUE(aws_set_environment_value(s_default_profile_env_variable_name, s_sso_profile) == AWS_OP_SUCCESS); + + aws_string_destroy(config_file_path_str); + return AWS_OP_SUCCESS; +} + +/* start_url should be same in `s_sso_profile_start_url` and `s_sso_profile_config_contents` */ +AWS_STATIC_STRING_FROM_LITERAL(s_sso_profile_start_url, "https://d-123.awsapps.com/start"); +AWS_STATIC_STRING_FROM_LITERAL( + s_sso_profile_config_contents, + "[profile sso]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n" + "sso_account_id = 123\n" + "sso_role_name = roleName\n"); +/* session name should be same in both `s_sso_session_name` and `s_sso_session_config_contents`*/ +AWS_STATIC_STRING_FROM_LITERAL(s_sso_session_name, "session"); +AWS_STATIC_STRING_FROM_LITERAL( + s_sso_session_config_contents, + "[profile sso]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n" + "sso_account_id = 123\n" + "sso_role_name = roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"); +AWS_STATIC_STRING_FROM_LITERAL( + s_expected_sso_request_path, + "/federation/credentials?account_id=123&role_name=roleName"); + +static int s_credentials_provider_sso_failed_invalid_config(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + const struct { + const char *name; + const char *text; + } invalid_config_examples[] = { + {"empty", ""}, + + {"profile without any sso config", "[profile sso]\naccessKey=access"}, + + {"profile without role_name", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n"}, + + {"profile without account_id", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_role_name=roleName\n"}, + + {"profile without region", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n"}, + + {"profile without start_url", + "[profile sso]\n" + "accessKey=access\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n"}, + + {"profile with invalid session", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n"}, + + {"session without start_url", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_region = us-west-2\n"}, + + {"session without region", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n"}, + + {"session with different region", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-east-1\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"}, + + {"session with different start-url", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-321.awsapps.com/start\n" + "sso_region = us-west-2\n"}, + }; + + aws_credentials_provider_http_mock_tester_init(allocator); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + for (int i = 0; i < AWS_ARRAY_SIZE(invalid_config_examples); i++) { + printf("invalid config example [%d]: %s\n", i, invalid_config_examples[i].name); + struct aws_string *content = aws_string_new_from_c_str(allocator, invalid_config_examples[i].text); + ASSERT_TRUE(content != NULL); + s_aws_credentials_provider_sso_test_init_config_profile(allocator, content); + aws_string_destroy(content); + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NULL(provider); + } + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_failed_invalid_config, s_credentials_provider_sso_failed_invalid_config); + +static int s_credentials_provider_sso_create_destroy_valid_config(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + const struct { + const char *name; + const char *text; + } valid_config_examples[] = { + + {"profile", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_account_id=123\n" + "sso_region=us-west-2\n" + "sso_role_name=roleName\n"}, + + {"session", + "[profile sso]\n" + "accessKey=access\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"}, + + {"session with profile", + "[profile sso]\n" + "accessKey=access\n" + "sso_start_url=https://d-123.awsapps.com/start\n" + "sso_region=us-west-2\n" + "sso_account_id=123\n" + "sso_role_name=roleName\n" + "sso_session = session\n" + "[sso-session session]\n" + "sso_start_url = https://d-123.awsapps.com/start\n" + "sso_region = us-west-2\n"}, + + }; + + aws_credentials_provider_http_mock_tester_init(allocator); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + for (int i = 0; i < AWS_ARRAY_SIZE(valid_config_examples); i++) { + printf("valid config example [%d]: %s\n", i, valid_config_examples[i].name); + struct aws_string *content = aws_string_new_from_c_str(allocator, valid_config_examples[i].text); + ASSERT_TRUE(content != NULL); + s_aws_credentials_provider_sso_test_init_config_profile(allocator, content); + aws_string_destroy(content); + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + aws_credentials_provider_release(provider); + } + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE( + credentials_provider_sso_create_destroy_valid_config, + s_credentials_provider_sso_create_destroy_valid_config); + +AWS_STATIC_STRING_FROM_LITERAL( + s_good_response, + "{\"roleCredentials\": {\"accessKeyId\": \"SuccessfulAccessKey\",\"secretAccessKey\": " + "\"SuccessfulSecret\",\"sessionToken\": \"SuccessfulToken\",\"expiration\": 1678574216000}}"); +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, "SuccessfulToken"); +static int s_good_response_expiration = 1678574216; +static int s_verify_credentials(bool request_made, bool got_credentials, int expected_attempts) { + ASSERT_TRUE(credentials_provider_http_mock_tester.has_received_credentials_callback); + + if (got_credentials) { + ASSERT_TRUE(credentials_provider_http_mock_tester.credentials != NULL); + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_credentials_get_access_key_id(credentials_provider_http_mock_tester.credentials), s_good_access_key_id); + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_credentials_get_secret_access_key(credentials_provider_http_mock_tester.credentials), + s_good_secret_access_key); + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_credentials_get_session_token(credentials_provider_http_mock_tester.credentials), s_good_session_token); + ASSERT_INT_EQUALS( + aws_credentials_get_expiration_timepoint_seconds(credentials_provider_http_mock_tester.credentials), + s_good_response_expiration); + } else { + ASSERT_TRUE(credentials_provider_http_mock_tester.error_code); + ASSERT_TRUE(credentials_provider_http_mock_tester.credentials == NULL); + } + + if (request_made) { + ASSERT_CURSOR_VALUE_STRING_EQUALS( + aws_byte_cursor_from_buf(&credentials_provider_http_mock_tester.request_path), s_expected_sso_request_path); + } + ASSERT_INT_EQUALS(credentials_provider_http_mock_tester.attempts, expected_attempts); + + return AWS_OP_SUCCESS; +} + +static int s_credentials_provider_sso_connect_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_connection_acquire_successful = false; + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_connect_failure, s_credentials_provider_sso_connect_failure); + +AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); +AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); + +static int s_credentials_provider_sso_failure_token_missing(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_request_successful = false; + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_failure_token_missing, s_credentials_provider_sso_failure_token_missing); + +AWS_STATIC_STRING_FROM_LITERAL( + s_sso_token, + "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); +static uint64_t s_sso_token_expiration_s = 1426138519; + +static int s_credentials_provider_sso_failure_token_expired(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_request_successful = false; + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + uint64_t nano_expiration = + aws_timestamp_convert(s_sso_token_expiration_s + 100, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); + mock_aws_set_system_time(nano_expiration); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); + ASSERT_INT_EQUALS(credentials_provider_http_mock_tester.error_code, AWS_AUTH_SSO_TOKEN_EXPIRED); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_failure_token_expired, s_credentials_provider_sso_failure_token_expired); + +AWS_STATIC_STRING_FROM_LITERAL(s_sso_empty_token, "{\"accessToken\": \"\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); +static int s_credentials_provider_sso_failure_token_empty(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_request_successful = false; + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_empty_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(false /*no request*/, false /*get creds*/, 0 /*expected attempts*/)); + ASSERT_INT_EQUALS(credentials_provider_http_mock_tester.error_code, AWS_ERROR_INVALID_ARGUMENT); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_failure_token_empty, s_credentials_provider_sso_failure_token_empty); + +static int s_credentials_provider_sso_request_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.is_request_successful = false; + credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_400_BAD_REQUEST; + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_request_failure, s_credentials_provider_sso_request_failure); + +AWS_STATIC_STRING_FROM_LITERAL(s_bad_json_response, "{ \"accessKey\": \"bad\"}"); +static int s_credentials_provider_sso_bad_response(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + + struct aws_byte_cursor bad_json_cursor = aws_byte_cursor_from_string(s_bad_json_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &bad_json_cursor); + + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_bad_response, s_credentials_provider_sso_bad_response); + +static int s_credentials_provider_sso_retryable_error(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.response_code = AWS_HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR; + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + + struct aws_byte_cursor bad_json_cursor = aws_byte_cursor_from_string(s_bad_json_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &bad_json_cursor); + + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, false /*get creds*/, 4 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_retryable_error, s_credentials_provider_sso_retryable_error); + +static int s_credentials_provider_sso_basic_success(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + + /* set the response */ + struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_basic_success, s_credentials_provider_sso_basic_success); +AWS_STATIC_STRING_FROM_LITERAL(s_invalid_config, "invalid config"); +static int s_credentials_provider_sso_basic_success_cached_config_file(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_invalid_config); + + struct aws_byte_buf profile_buffer = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + struct aws_profile_collection *config_collection = + aws_profile_collection_new_from_buffer(allocator, &profile_buffer, AWS_PST_CONFIG); + + /* set the response */ + struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .config_file_cached = config_collection, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + aws_string_destroy(token_path); + aws_profile_collection_release(config_collection); + + return 0; +} +AWS_TEST_CASE( + credentials_provider_sso_basic_success_cached_config_file, + s_credentials_provider_sso_basic_success_cached_config_file); + +static int s_credentials_provider_sso_basic_success_profile(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_profile_start_url); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_profile_config_contents); + + /* set the response */ + struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE(credentials_provider_sso_basic_success_profile, s_credentials_provider_sso_basic_success_profile); + +static int s_credentials_provider_sso_basic_success_profile_cached_config_file( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_profile_start_url); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_invalid_config); + + struct aws_byte_buf profile_buffer = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_profile_config_contents)); + struct aws_profile_collection *config_collection = + aws_profile_collection_new_from_buffer(allocator, &profile_buffer, AWS_PST_CONFIG); + + /* set the response */ + struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .config_file_cached = config_collection, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 1 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + aws_credentials_provider_http_mock_tester_cleanup(); + aws_profile_collection_release(config_collection); + aws_string_destroy(token_path); + + return 0; +} +AWS_TEST_CASE( + credentials_provider_sso_basic_success_profile_cached_config_file, + s_credentials_provider_sso_basic_success_profile_cached_config_file); + +static int s_credentials_provider_sso_basic_success_after_failure(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_credentials_provider_http_mock_tester_init(allocator); + credentials_provider_http_mock_tester.failure_count = 2; + credentials_provider_http_mock_tester.failure_response_code = AWS_HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR; + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + s_aws_credentials_provider_sso_test_init_config_profile(allocator, s_sso_session_config_contents); + + /* set the response */ + struct aws_byte_cursor good_response_cursor = aws_byte_cursor_from_string(s_good_response); + aws_array_list_push_back(&credentials_provider_http_mock_tester.response_data_callbacks, &good_response_cursor); + + mock_aws_set_system_time(0); + struct aws_credentials_provider_sso_options options = { + .bootstrap = credentials_provider_http_mock_tester.bootstrap, + .tls_ctx = credentials_provider_http_mock_tester.tls_ctx, + .function_table = &aws_credentials_provider_http_mock_function_table, + .shutdown_options = + { + .shutdown_callback = aws_credentials_provider_http_mock_on_shutdown_complete, + .shutdown_user_data = NULL, + }, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_credentials_provider_new_sso(allocator, &options); + ASSERT_NOT_NULL(provider); + + aws_credentials_provider_get_credentials( + provider, aws_credentials_provider_http_mock_get_credentials_callback, NULL); + + aws_credentials_provider_http_mock_wait_for_credentials_result(); + + ASSERT_SUCCESS(s_verify_credentials(true /*request made*/, true /*get creds*/, 3 /*expected attempts*/)); + + aws_credentials_provider_release(provider); + + aws_credentials_provider_http_mock_wait_for_shutdown_callback(); + + aws_credentials_provider_http_mock_tester_cleanup(); + + aws_string_destroy(token_path); + return 0; +} +AWS_TEST_CASE( + credentials_provider_sso_basic_success_after_failure, + s_credentials_provider_sso_basic_success_after_failure); diff --git a/tests/credentials_provider_utils.c b/tests/credentials_provider_utils.c index fabb584a..58051593 100644 --- a/tests/credentials_provider_utils.c +++ b/tests/credentials_provider_utils.c @@ -490,3 +490,304 @@ int aws_create_directory_components(struct aws_allocator *allocator, const struc } return AWS_OP_SUCCESS; } + +/* + * Mocked HTTP connection manager for tests + */ + +struct aws_auth_http_system_vtable aws_credentials_provider_http_mock_function_table = { + .aws_http_connection_manager_new = aws_credentials_provider_http_mock_connection_manager_new, + .aws_http_connection_manager_release = aws_credentials_provider_http_mock_connection_manager_release, + .aws_http_connection_manager_acquire_connection = + aws_credentials_provider_http_mock_connection_manager_acquire_connection, + .aws_http_connection_manager_release_connection = + aws_credentials_provider_http_mock_connection_manager_release_connection, + .aws_http_connection_make_request = aws_credentials_provider_http_mock_make_request, + .aws_http_stream_activate = aws_credentials_provider_http_mock_stream_activate, + .aws_http_stream_get_connection = aws_credentials_provider_http_mock_stream_get_connection, + .aws_http_stream_get_incoming_response_status = + aws_credentials_provider_http_mock_stream_get_incoming_response_status, + .aws_http_stream_release = aws_credentials_provider_http_mock_stream_release, + .aws_http_connection_close = aws_credentials_provider_http_mock_connection_close}; + +struct aws_credentials_provider_http_mock_tester credentials_provider_http_mock_tester; + +int aws_credentials_provider_http_mock_tester_init(struct aws_allocator *allocator) { + aws_auth_library_init(allocator); + + AWS_ZERO_STRUCT(credentials_provider_http_mock_tester); + + struct aws_tls_ctx_options tls_ctx_options; + aws_tls_ctx_options_init_default_client(&tls_ctx_options, allocator); + credentials_provider_http_mock_tester.tls_ctx = aws_tls_client_ctx_new(allocator, &tls_ctx_options); + ASSERT_NOT_NULL(credentials_provider_http_mock_tester.tls_ctx); + + credentials_provider_http_mock_tester.el_group = aws_event_loop_group_new_default(allocator, 0, NULL); + + struct aws_host_resolver_default_options resolver_options = { + .el_group = credentials_provider_http_mock_tester.el_group, + .max_entries = 8, + }; + credentials_provider_http_mock_tester.resolver = aws_host_resolver_new_default(allocator, &resolver_options); + + struct aws_client_bootstrap_options bootstrap_options = { + .event_loop_group = credentials_provider_http_mock_tester.el_group, + .host_resolver = credentials_provider_http_mock_tester.resolver, + }; + credentials_provider_http_mock_tester.bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + + if (aws_array_list_init_dynamic( + &credentials_provider_http_mock_tester.response_data_callbacks, + allocator, + 10, + sizeof(struct aws_byte_cursor))) { + return AWS_OP_ERR; + } + + if (aws_byte_buf_init(&credentials_provider_http_mock_tester.request_path, allocator, 256)) { + return AWS_OP_ERR; + } + if (aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256)) { + return AWS_OP_ERR; + } + + if (aws_mutex_init(&credentials_provider_http_mock_tester.lock)) { + return AWS_OP_ERR; + } + + if (aws_condition_variable_init(&credentials_provider_http_mock_tester.signal)) { + return AWS_OP_ERR; + } + + /* default to everything successful */ + credentials_provider_http_mock_tester.is_connection_acquire_successful = true; + credentials_provider_http_mock_tester.is_request_successful = true; + + return AWS_OP_SUCCESS; +} + +void aws_credentials_provider_http_mock_tester_cleanup(void) { + aws_tls_ctx_release(credentials_provider_http_mock_tester.tls_ctx); + aws_client_bootstrap_release(credentials_provider_http_mock_tester.bootstrap); + aws_host_resolver_release(credentials_provider_http_mock_tester.resolver); + aws_event_loop_group_release(credentials_provider_http_mock_tester.el_group); + aws_array_list_clean_up(&credentials_provider_http_mock_tester.response_data_callbacks); + aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_path); + aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); + aws_condition_variable_clean_up(&credentials_provider_http_mock_tester.signal); + aws_mutex_clean_up(&credentials_provider_http_mock_tester.lock); + aws_credentials_release(credentials_provider_http_mock_tester.credentials); + aws_auth_library_clean_up(); +} + +void aws_credentials_provider_http_mock_on_shutdown_complete(void *user_data) { + (void)user_data; + aws_mutex_lock(&credentials_provider_http_mock_tester.lock); + credentials_provider_http_mock_tester.has_received_shutdown_callback = true; + aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); + + aws_condition_variable_notify_one(&credentials_provider_http_mock_tester.signal); +} + +bool aws_credentials_provider_http_mock_has_received_shutdown_callback(void *user_data) { + (void)user_data; + + return credentials_provider_http_mock_tester.has_received_shutdown_callback; +} + +void aws_credentials_provider_http_mock_wait_for_shutdown_callback(void) { + aws_mutex_lock(&credentials_provider_http_mock_tester.lock); + aws_condition_variable_wait_pred( + &credentials_provider_http_mock_tester.signal, + &credentials_provider_http_mock_tester.lock, + aws_credentials_provider_http_mock_has_received_shutdown_callback, + NULL); + aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); +} + +struct mock_connection_manager { + struct aws_allocator *allocator; + aws_http_connection_manager_shutdown_complete_fn *shutdown_complete_callback; + void *shutdown_complete_user_data; +}; + +struct aws_http_connection_manager *aws_credentials_provider_http_mock_connection_manager_new( + struct aws_allocator *allocator, + const struct aws_http_connection_manager_options *options) { + + struct mock_connection_manager *mock_manager = aws_mem_calloc(allocator, 1, sizeof(struct mock_connection_manager)); + mock_manager->allocator = allocator; + mock_manager->shutdown_complete_callback = options->shutdown_complete_callback; + mock_manager->shutdown_complete_user_data = options->shutdown_complete_user_data; + return (struct aws_http_connection_manager *)mock_manager; +} + +void aws_credentials_provider_http_mock_connection_manager_release(struct aws_http_connection_manager *manager) { + struct mock_connection_manager *mock_manager = (struct mock_connection_manager *)manager; + mock_manager->shutdown_complete_callback(mock_manager->shutdown_complete_user_data); + aws_mem_release(mock_manager->allocator, mock_manager); +} + +void aws_credentials_provider_http_mock_connection_manager_acquire_connection( + 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 (credentials_provider_http_mock_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); + } +} + +int aws_credentials_provider_http_mock_connection_manager_release_connection( + struct aws_http_connection_manager *manager, + struct aws_http_connection *connection) { + + (void)manager; + (void)connection; + + return AWS_OP_SUCCESS; +} + +void aws_credentials_provider_http_mock_invoke_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"); + if (options->on_response_headers) { + 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); +} + +struct aws_http_stream *aws_credentials_provider_http_mock_make_request( + 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); + struct aws_input_stream *body_stream = aws_http_message_get_body_stream(options->request); + struct aws_allocator *allocator = credentials_provider_http_mock_tester.request_body.allocator; + aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_body); + aws_byte_buf_init(&credentials_provider_http_mock_tester.request_body, allocator, 256); + if (body_stream) { + aws_input_stream_read(body_stream, &credentials_provider_http_mock_tester.request_body); + } + aws_byte_buf_clean_up(&credentials_provider_http_mock_tester.request_path); + + struct aws_byte_cursor request_path_cursor; + aws_http_message_get_request_path(options->request, &request_path_cursor); + aws_byte_buf_init_copy_from_cursor( + &credentials_provider_http_mock_tester.request_path, allocator, request_path_cursor); + credentials_provider_http_mock_tester.attempts++; + credentials_provider_http_mock_tester.request_options = *options; + + return (struct aws_http_stream *)1; +} + +int aws_credentials_provider_http_mock_stream_activate(struct aws_http_stream *stream) { + (void)stream; + aws_credentials_provider_http_mock_invoke_request_callbacks( + &credentials_provider_http_mock_tester.request_options, + &credentials_provider_http_mock_tester.response_data_callbacks, + credentials_provider_http_mock_tester.is_request_successful); + return AWS_OP_SUCCESS; +} + +int aws_credentials_provider_http_mock_stream_get_incoming_response_status( + const struct aws_http_stream *stream, + int *out_status_code) { + (void)stream; + + if (credentials_provider_http_mock_tester.failure_count) { + credentials_provider_http_mock_tester.failure_count--; + *out_status_code = credentials_provider_http_mock_tester.failure_response_code; + } else if (credentials_provider_http_mock_tester.response_code) { + *out_status_code = credentials_provider_http_mock_tester.response_code; + } else { + *out_status_code = AWS_HTTP_STATUS_CODE_200_OK; + } + + return AWS_OP_SUCCESS; +} + +void aws_credentials_provider_http_mock_stream_release(struct aws_http_stream *stream) { + (void)stream; +} + +void aws_credentials_provider_http_mock_connection_close(struct aws_http_connection *connection) { + (void)connection; +} + +struct aws_http_connection *aws_credentials_provider_http_mock_stream_get_connection( + const struct aws_http_stream *stream) { + (void)stream; + return (struct aws_http_connection *)1; +} + +bool aws_credentials_provider_http_mock_has_received_credentials_callback(void *user_data) { + (void)user_data; + + return credentials_provider_http_mock_tester.has_received_credentials_callback; +} + +void aws_credentials_provider_http_mock_wait_for_credentials_result(void) { + aws_mutex_lock(&credentials_provider_http_mock_tester.lock); + aws_condition_variable_wait_pred( + &credentials_provider_http_mock_tester.signal, + &credentials_provider_http_mock_tester.lock, + aws_credentials_provider_http_mock_has_received_credentials_callback, + NULL); + aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); +} + +void aws_credentials_provider_http_mock_get_credentials_callback( + struct aws_credentials *credentials, + int error_code, + void *user_data) { + (void)user_data; + + aws_mutex_lock(&credentials_provider_http_mock_tester.lock); + credentials_provider_http_mock_tester.has_received_credentials_callback = true; + credentials_provider_http_mock_tester.credentials = credentials; + credentials_provider_http_mock_tester.error_code = error_code; + if (credentials != NULL) { + aws_credentials_acquire(credentials); + } + aws_condition_variable_notify_one(&credentials_provider_http_mock_tester.signal); + aws_mutex_unlock(&credentials_provider_http_mock_tester.lock); +} diff --git a/tests/credentials_provider_utils.h b/tests/credentials_provider_utils.h index 08cd8733..5f752d91 100644 --- a/tests/credentials_provider_utils.h +++ b/tests/credentials_provider_utils.h @@ -101,4 +101,75 @@ struct aws_credentials_provider *aws_credentials_provider_new_null( */ int aws_create_directory_components(struct aws_allocator *allocator, const struct aws_string *path); +/** + * Mocked HTTP connection manager for tests + */ +struct aws_credentials_provider_http_mock_tester { + struct aws_tls_ctx *tls_ctx; + struct aws_event_loop_group *el_group; + struct aws_host_resolver *resolver; + struct aws_client_bootstrap *bootstrap; + + struct aws_byte_buf request_path; + struct aws_byte_buf request_body; + struct aws_http_make_request_options request_options; + + 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; + + int attempts; + int response_code; + int error_code; + int failure_response_code; + int failure_count; +}; + +extern struct aws_credentials_provider_http_mock_tester credentials_provider_http_mock_tester; +int aws_credentials_provider_http_mock_tester_init(struct aws_allocator *allocator); +void aws_credentials_provider_http_mock_tester_cleanup(void); +void aws_credentials_provider_http_mock_on_shutdown_complete(void *user_data); +bool aws_credentials_provider_http_mock_has_received_shutdown_callback(void *user_data); +void aws_credentials_provider_http_mock_wait_for_shutdown_callback(void); +struct aws_http_connection_manager *aws_credentials_provider_http_mock_connection_manager_new( + struct aws_allocator *allocator, + const struct aws_http_connection_manager_options *options); +void aws_credentials_provider_http_mock_connection_manager_release(struct aws_http_connection_manager *manager); +void aws_credentials_provider_http_mock_connection_manager_acquire_connection( + struct aws_http_connection_manager *manager, + aws_http_connection_manager_on_connection_setup_fn *callback, + void *user_data); +int aws_credentials_provider_http_mock_connection_manager_release_connection( + struct aws_http_connection_manager *manager, + struct aws_http_connection *connection); +void aws_credentials_provider_http_mock_invoke_request_callbacks( + const struct aws_http_make_request_options *options, + struct aws_array_list *data_callbacks, + bool is_request_successful); +struct aws_http_stream *aws_credentials_provider_http_mock_make_request( + struct aws_http_connection *client_connection, + const struct aws_http_make_request_options *options); +int aws_credentials_provider_http_mock_stream_activate(struct aws_http_stream *stream); +int aws_credentials_provider_http_mock_stream_get_incoming_response_status( + const struct aws_http_stream *stream, + int *out_status_code); +void aws_credentials_provider_http_mock_stream_release(struct aws_http_stream *stream); +void aws_credentials_provider_http_mock_connection_close(struct aws_http_connection *connection); +struct aws_http_connection *aws_credentials_provider_http_mock_stream_get_connection( + const struct aws_http_stream *stream); +bool aws_credentials_provider_http_mock_has_received_credentials_callback(void *user_data); +void aws_credentials_provider_http_mock_wait_for_credentials_result(void); +void aws_credentials_provider_http_mock_get_credentials_callback( + struct aws_credentials *credentials, + int error_code, + void *user_data); +extern struct aws_auth_http_system_vtable aws_credentials_provider_http_mock_function_table; + #endif /* AWS_AUTH_CREDENTIALS_PROVIDER_MOCK_H */ diff --git a/tests/token_provider_sso_tests.c b/tests/token_provider_sso_tests.c index 148cfe6b..f61dec0e 100644 --- a/tests/token_provider_sso_tests.c +++ b/tests/token_provider_sso_tests.c @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -312,6 +313,7 @@ AWS_STATIC_STRING_FROM_LITERAL( AWS_STATIC_STRING_FROM_LITERAL( s_sso_token, "{\"accessToken\": \"ValidAccessToken\",\"expiresAt\": \"2015-03-12T05:35:19Z\"}"); +AWS_STATIC_STRING_FROM_LITERAL(s_invalid_config, "invalid config"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_var, "HOME"); AWS_STATIC_STRING_FROM_LITERAL(s_home_env_current_directory, "."); @@ -364,6 +366,60 @@ static int s_sso_token_provider_sso_session_basic_success(struct aws_allocator * AWS_TEST_CASE(sso_token_provider_sso_session_basic_success, s_sso_token_provider_sso_session_basic_success); +static int s_sso_token_provider_sso_session_config_file_cached(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + s_aws_mock_token_provider_sso_tester_init(allocator); + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_session_name); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_invalid_config)); + + struct aws_byte_buf profile_buffer = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_session_config_contents)); + struct aws_profile_collection *config_collection = + aws_profile_collection_new_from_buffer(allocator, &profile_buffer, AWS_PST_CONFIG); + + mock_aws_set_system_time(0); + struct aws_token_provider_sso_session_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .config_file_cached = config_collection, + .tls_ctx = s_tester.tls_ctx, + .bootstrap = s_tester.bootstrap, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_token_provider_new_sso_session(allocator, &options); + ASSERT_NOT_NULL(provider); + + struct aws_get_credentials_test_callback_result callback_results; + aws_get_credentials_test_callback_result_init(&callback_results, 1); + ASSERT_SUCCESS( + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results)); + aws_wait_on_credentials_callback(&callback_results); + + ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); + ASSERT_INT_EQUALS( + aws_credentials_get_expiration_timepoint_seconds(callback_results.credentials), s_token_expiration_s); + + aws_get_credentials_test_callback_result_clean_up(&callback_results); + aws_credentials_provider_release(provider); + + aws_string_destroy(token_path); + aws_string_destroy(config_file_str); + s_aws_mock_token_provider_sso_tester_cleanup(); + aws_profile_collection_release(config_collection); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_sso_session_config_file_cached, s_sso_token_provider_sso_session_config_file_cached); + static int s_sso_token_provider_sso_session_expired_token(struct aws_allocator *allocator, void *ctx) { (void)ctx; s_aws_mock_token_provider_sso_tester_init(allocator); @@ -452,6 +508,58 @@ static int s_sso_token_provider_profile_basic_success(struct aws_allocator *allo } AWS_TEST_CASE(sso_token_provider_profile_basic_success, s_sso_token_provider_profile_basic_success); +static int s_sso_token_provider_profile_cached_config_file(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + s_aws_mock_token_provider_sso_tester_init(allocator); + + /* redirect $HOME */ + ASSERT_SUCCESS(aws_set_environment_value(s_home_env_var, s_home_env_current_directory)); + + /* create token file */ + struct aws_string *token_path = aws_construct_sso_token_path(allocator, s_sso_profile_start_url); + ASSERT_NOT_NULL(token_path); + ASSERT_SUCCESS(aws_create_directory_components(allocator, token_path)); + ASSERT_SUCCESS(aws_create_profile_file(token_path, s_sso_token)); + + struct aws_string *config_file_str = aws_create_process_unique_file_name(allocator); + ASSERT_SUCCESS(aws_create_profile_file(config_file_str, s_invalid_config)); + + struct aws_byte_buf profile_buffer = aws_byte_buf_from_c_str(aws_string_c_str(s_sso_profile_config_contents)); + + struct aws_profile_collection *config_collection = + aws_profile_collection_new_from_buffer(allocator, &profile_buffer, AWS_PST_CONFIG); + + mock_aws_set_system_time(0); + struct aws_token_provider_sso_profile_options options = { + .config_file_name_override = aws_byte_cursor_from_string(config_file_str), + .config_file_cached = config_collection, + .system_clock_fn = mock_aws_get_system_time, + }; + + struct aws_credentials_provider *provider = aws_token_provider_new_sso_profile(allocator, &options); + ASSERT_NOT_NULL(provider); + + struct aws_get_credentials_test_callback_result callback_results; + aws_get_credentials_test_callback_result_init(&callback_results, 1); + ASSERT_SUCCESS( + aws_credentials_provider_get_credentials(provider, aws_test_get_credentials_async_callback, &callback_results)); + aws_wait_on_credentials_callback(&callback_results); + + ASSERT_CURSOR_VALUE_STRING_EQUALS(aws_credentials_get_token(callback_results.credentials), s_good_token); + ASSERT_INT_EQUALS( + aws_credentials_get_expiration_timepoint_seconds(callback_results.credentials), s_token_expiration_s); + + aws_get_credentials_test_callback_result_clean_up(&callback_results); + aws_credentials_provider_release(provider); + + aws_string_destroy(token_path); + aws_string_destroy(config_file_str); + s_aws_mock_token_provider_sso_tester_cleanup(); + aws_profile_collection_release(config_collection); + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(sso_token_provider_profile_cached_config_file, s_sso_token_provider_profile_cached_config_file); static int s_sso_token_provider_profile_expired_token(struct aws_allocator *allocator, void *ctx) { (void)ctx; From 56d18dd1cb6d492390d8b3ed3befb6fcfb12b09e Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Mon, 1 May 2023 09:14:10 -0700 Subject: [PATCH 93/93] update user agent value --- source/credentials_provider_sso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/credentials_provider_sso.c b/source/credentials_provider_sso.c index 382e08c0..e7c39e75 100644 --- a/source/credentials_provider_sso.c +++ b/source/credentials_provider_sso.c @@ -268,7 +268,7 @@ static int s_on_incoming_body_fn(struct aws_http_stream *stream, const struct aw /* Request headers. */ AWS_STATIC_STRING_FROM_LITERAL(s_sso_token_header, "x-amz-sso_bearer_token"); AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header, "User-Agent"); -AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header_value, "CRTAuthSSOCredentialsProvider"); +AWS_STATIC_STRING_FROM_LITERAL(s_sso_user_agent_header_value, "aws-sdk-crt/sso-credentials-provider"); static void s_query_credentials(struct aws_sso_query_context *sso_query_context) { AWS_FATAL_ASSERT(sso_query_context->connection);