token_provider_sso_session.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/auth/credentials.h>
  6. #include <aws/auth/private/aws_profile.h>
  7. #include <aws/auth/private/credentials_utils.h>
  8. #include <aws/auth/private/sso_token_providers.h>
  9. #include <aws/auth/private/sso_token_utils.h>
  10. #include <aws/common/clock.h>
  11. #ifdef _MSC_VER
  12. /* allow non-constant declared initializers. */
  13. # pragma warning(disable : 4204)
  14. #endif
  15. /*
  16. * sso-session token provider implementation
  17. */
  18. struct aws_token_provider_sso_session_impl {
  19. struct aws_string *sso_token_file_path;
  20. aws_io_clock_fn *system_clock_fn;
  21. };
  22. static int s_token_provider_sso_session_get_token(
  23. struct aws_credentials_provider *provider,
  24. aws_on_get_credentials_callback_fn callback,
  25. void *user_data) {
  26. struct aws_token_provider_sso_session_impl *impl = provider->impl;
  27. struct aws_sso_token *sso_token = NULL;
  28. struct aws_credentials *credentials = NULL;
  29. int result = AWS_OP_ERR;
  30. sso_token = aws_sso_token_new_from_file(provider->allocator, impl->sso_token_file_path);
  31. if (!sso_token) {
  32. AWS_LOGF_ERROR(
  33. AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) failed to get sso token from file.", (void *)provider);
  34. goto done;
  35. }
  36. /* check token expiration. */
  37. uint64_t now_ns = UINT64_MAX;
  38. if (impl->system_clock_fn(&now_ns) != AWS_OP_SUCCESS) {
  39. goto done;
  40. }
  41. if (aws_date_time_as_nanos(&sso_token->expiration) <= now_ns) {
  42. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) cached sso token is expired.", (void *)provider);
  43. aws_raise_error(AWS_AUTH_SSO_TOKEN_EXPIRED);
  44. goto done;
  45. }
  46. /* TODO: Refresh token if it is within refresh window and refreshable */
  47. credentials = aws_credentials_new_token(
  48. provider->allocator,
  49. aws_byte_cursor_from_string(sso_token->access_token),
  50. (uint64_t)aws_date_time_as_epoch_secs(&sso_token->expiration));
  51. if (!credentials) {
  52. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "(id=%p) Unable to construct credentials.", (void *)provider);
  53. goto done;
  54. }
  55. callback(credentials, AWS_OP_SUCCESS, user_data);
  56. result = AWS_OP_SUCCESS;
  57. done:
  58. aws_sso_token_destroy(sso_token);
  59. aws_credentials_release(credentials);
  60. return result;
  61. }
  62. static void s_token_provider_sso_session_destroy(struct aws_credentials_provider *provider) {
  63. struct aws_token_provider_sso_session_impl *impl = provider->impl;
  64. if (impl == NULL) {
  65. return;
  66. }
  67. aws_string_destroy(impl->sso_token_file_path);
  68. aws_credentials_provider_invoke_shutdown_callback(provider);
  69. aws_mem_release(provider->allocator, provider);
  70. }
  71. static struct aws_credentials_provider_vtable s_aws_token_provider_sso_session_vtable = {
  72. .get_credentials = s_token_provider_sso_session_get_token,
  73. .destroy = s_token_provider_sso_session_destroy,
  74. };
  75. AWS_STRING_FROM_LITERAL(s_sso_session_name, "sso_session");
  76. AWS_STRING_FROM_LITERAL(s_sso_region_name, "sso_region");
  77. AWS_STRING_FROM_LITERAL(s_sso_start_url_name, "sso_start_url");
  78. /**
  79. * Parses the config file to validate and construct a token path. A valid profile with sso session is as follow
  80. * [profile sso-profile]
  81. * sso_session = dev
  82. * sso_account_id = 012345678901
  83. * sso_role_name = SampleRole
  84. *
  85. * [sso-session dev]
  86. * sso_region = us-east-1
  87. * sso_start_url = https://d-abc123.awsapps.com/start
  88. */
  89. static struct aws_string *s_verify_config_and_construct_sso_token_path(
  90. struct aws_allocator *allocator,
  91. const struct aws_token_provider_sso_session_options *options) {
  92. struct aws_profile_collection *config_collection = NULL;
  93. struct aws_string *profile_name = NULL;
  94. struct aws_string *sso_token_path = NULL;
  95. profile_name = aws_get_profile_name(allocator, &options->profile_name_override);
  96. if (!profile_name) {
  97. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "sso-session: token provider failed to resolve profile name");
  98. goto cleanup;
  99. }
  100. if (options->config_file_cached) {
  101. /* Use cached config file */
  102. config_collection = aws_profile_collection_acquire(options->config_file_cached);
  103. } else {
  104. /* load config file */
  105. config_collection = aws_load_profile_collection_from_config_file(allocator, options->config_file_name_override);
  106. }
  107. if (!config_collection) {
  108. goto cleanup;
  109. }
  110. const struct aws_profile *profile = aws_profile_collection_get_profile(config_collection, profile_name);
  111. if (!profile) {
  112. AWS_LOGF_ERROR(
  113. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  114. "sso-session: token provider could not load"
  115. " a profile at %s.",
  116. aws_string_c_str(profile_name));
  117. goto cleanup;
  118. }
  119. const struct aws_profile_property *sso_session_property = aws_profile_get_property(profile, s_sso_session_name);
  120. if (!sso_session_property) {
  121. AWS_LOGF_ERROR(
  122. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  123. "token-provider-sso-session: token provider could not find an sso-session at profile %s",
  124. aws_string_c_str(profile_name));
  125. aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE);
  126. goto cleanup;
  127. }
  128. const struct aws_string *sso_session_name = aws_profile_property_get_value(sso_session_property);
  129. /* parse sso_session */
  130. const struct aws_profile *session_profile =
  131. aws_profile_collection_get_section(config_collection, AWS_PROFILE_SECTION_TYPE_SSO_SESSION, sso_session_name);
  132. if (!session_profile) {
  133. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-session: failed to find an sso-session");
  134. aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE);
  135. goto cleanup;
  136. }
  137. const struct aws_profile_property *sso_region_property =
  138. aws_profile_get_property(session_profile, s_sso_region_name);
  139. const struct aws_profile_property *sso_start_url_property =
  140. aws_profile_get_property(session_profile, s_sso_start_url_name);
  141. if (!sso_region_property) {
  142. AWS_LOGF_ERROR(
  143. AWS_LS_AUTH_CREDENTIALS_PROVIDER, "token-provider-sso-session: failed to find sso_region in sso-session");
  144. aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE);
  145. goto cleanup;
  146. }
  147. if (!sso_start_url_property) {
  148. AWS_LOGF_ERROR(
  149. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  150. "token-provider-sso-session: failed to find sso_start_url in sso-session");
  151. aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE);
  152. goto cleanup;
  153. }
  154. /* Verify sso_region & start_url are the same in profile section if they exist */
  155. const struct aws_string *sso_region = aws_profile_property_get_value(sso_region_property);
  156. const struct aws_string *sso_start_url = aws_profile_property_get_value(sso_start_url_property);
  157. const struct aws_profile_property *profile_sso_region_property =
  158. aws_profile_get_property(profile, s_sso_region_name);
  159. const struct aws_profile_property *profile_sso_start_url_property =
  160. aws_profile_get_property(profile, s_sso_start_url_name);
  161. if (profile_sso_region_property &&
  162. !aws_string_eq(sso_region, aws_profile_property_get_value(profile_sso_region_property))) {
  163. AWS_LOGF_ERROR(
  164. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  165. "token-provider-sso-session: profile & sso-session have different value for sso_region");
  166. aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE);
  167. goto cleanup;
  168. }
  169. if (profile_sso_start_url_property &&
  170. !aws_string_eq(sso_start_url, aws_profile_property_get_value(profile_sso_start_url_property))) {
  171. AWS_LOGF_ERROR(
  172. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  173. "token-provider-sso-session: profile & sso-session have different value for sso_start_url");
  174. aws_raise_error(AWS_AUTH_SSO_TOKEN_PROVIDER_SOURCE_FAILURE);
  175. goto cleanup;
  176. }
  177. sso_token_path = aws_construct_sso_token_path(allocator, sso_session_name);
  178. cleanup:
  179. aws_string_destroy(profile_name);
  180. aws_profile_collection_release(config_collection);
  181. return sso_token_path;
  182. }
  183. struct aws_credentials_provider *aws_token_provider_new_sso_session(
  184. struct aws_allocator *allocator,
  185. const struct aws_token_provider_sso_session_options *options) {
  186. /* Currently, they are not used but they will be required when we implement the refresh token functionality. */
  187. AWS_ASSERT(options->bootstrap);
  188. AWS_ASSERT(options->tls_ctx);
  189. struct aws_string *token_path = s_verify_config_and_construct_sso_token_path(allocator, options);
  190. if (!token_path) {
  191. return NULL;
  192. }
  193. struct aws_credentials_provider *provider = NULL;
  194. struct aws_token_provider_sso_session_impl *impl = NULL;
  195. aws_mem_acquire_many(
  196. allocator,
  197. 2,
  198. &provider,
  199. sizeof(struct aws_credentials_provider),
  200. &impl,
  201. sizeof(struct aws_token_provider_sso_session_impl));
  202. AWS_ZERO_STRUCT(*provider);
  203. AWS_ZERO_STRUCT(*impl);
  204. aws_credentials_provider_init_base(provider, allocator, &s_aws_token_provider_sso_session_vtable, impl);
  205. impl->sso_token_file_path = aws_string_new_from_string(allocator, token_path);
  206. provider->shutdown_options = options->shutdown_options;
  207. if (options->system_clock_fn) {
  208. impl->system_clock_fn = options->system_clock_fn;
  209. } else {
  210. impl->system_clock_fn = aws_sys_clock_get_ticks;
  211. }
  212. aws_string_destroy(token_path);
  213. return provider;
  214. }