aws-sdk-core 3.131.1 → 3.188.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +459 -0
- data/VERSION +1 -1
- data/lib/aws-defaults/default_configuration.rb +4 -4
- data/lib/aws-sdk-core/arn.rb +13 -0
- data/lib/aws-sdk-core/binary/encode_handler.rb +12 -1
- data/lib/aws-sdk-core/credential_provider.rb +3 -0
- data/lib/aws-sdk-core/credential_provider_chain.rb +8 -5
- data/lib/aws-sdk-core/ecs_credentials.rb +177 -53
- data/lib/aws-sdk-core/endpoints/condition.rb +41 -0
- data/lib/aws-sdk-core/endpoints/endpoint.rb +17 -0
- data/lib/aws-sdk-core/endpoints/endpoint_rule.rb +75 -0
- data/lib/aws-sdk-core/endpoints/error_rule.rb +42 -0
- data/lib/aws-sdk-core/endpoints/function.rb +80 -0
- data/lib/aws-sdk-core/endpoints/matchers.rb +131 -0
- data/lib/aws-sdk-core/endpoints/reference.rb +31 -0
- data/lib/aws-sdk-core/endpoints/rule.rb +25 -0
- data/lib/aws-sdk-core/endpoints/rule_set.rb +52 -0
- data/lib/aws-sdk-core/endpoints/rules_provider.rb +37 -0
- data/lib/aws-sdk-core/endpoints/templater.rb +58 -0
- data/lib/aws-sdk-core/endpoints/tree_rule.rb +45 -0
- data/lib/aws-sdk-core/endpoints/url.rb +60 -0
- data/lib/aws-sdk-core/endpoints.rb +78 -0
- data/lib/aws-sdk-core/errors.rb +14 -1
- data/lib/aws-sdk-core/ini_parser.rb +7 -0
- data/lib/aws-sdk-core/instance_profile_credentials.rb +52 -30
- data/lib/aws-sdk-core/json/error_handler.rb +20 -1
- data/lib/aws-sdk-core/json/handler.rb +8 -1
- data/lib/aws-sdk-core/json/parser.rb +27 -2
- data/lib/aws-sdk-core/log/formatter.rb +6 -0
- data/lib/aws-sdk-core/pageable_response.rb +10 -1
- data/lib/aws-sdk-core/param_validator.rb +2 -2
- data/lib/aws-sdk-core/plugins/bearer_authorization.rb +67 -0
- data/lib/aws-sdk-core/plugins/checksum_algorithm.rb +1 -1
- data/lib/aws-sdk-core/plugins/credentials_configuration.rb +24 -0
- data/lib/aws-sdk-core/plugins/endpoint_discovery.rb +6 -2
- data/lib/aws-sdk-core/plugins/jsonvalue_converter.rb +34 -6
- data/lib/aws-sdk-core/plugins/recursion_detection.rb +14 -3
- data/lib/aws-sdk-core/plugins/regional_endpoint.rb +111 -30
- data/lib/aws-sdk-core/plugins/request_compression.rb +217 -0
- data/lib/aws-sdk-core/plugins/retries/error_inspector.rb +2 -1
- data/lib/aws-sdk-core/plugins/sign.rb +201 -0
- data/lib/aws-sdk-core/plugins/signature_v2.rb +1 -0
- data/lib/aws-sdk-core/plugins/signature_v4.rb +13 -7
- data/lib/aws-sdk-core/plugins/user_agent.rb +117 -14
- data/lib/aws-sdk-core/refreshing_credentials.rb +0 -6
- data/lib/aws-sdk-core/refreshing_token.rb +71 -0
- data/lib/aws-sdk-core/rest/handler.rb +1 -1
- data/lib/aws-sdk-core/rest/request/headers.rb +2 -6
- data/lib/aws-sdk-core/rest/request/querystring_builder.rb +43 -29
- data/lib/aws-sdk-core/shared_config.rb +106 -6
- data/lib/aws-sdk-core/sso_credentials.rb +80 -45
- data/lib/aws-sdk-core/sso_token_provider.rb +135 -0
- data/lib/aws-sdk-core/static_token_provider.rb +14 -0
- data/lib/aws-sdk-core/structure.rb +6 -4
- data/lib/aws-sdk-core/stubbing/stub_data.rb +11 -0
- data/lib/aws-sdk-core/token.rb +31 -0
- data/lib/aws-sdk-core/token_provider.rb +15 -0
- data/lib/aws-sdk-core/token_provider_chain.rb +51 -0
- data/lib/aws-sdk-core/waiters/poller.rb +3 -1
- data/lib/aws-sdk-core/xml/error_handler.rb +7 -0
- data/lib/aws-sdk-core/xml/parser/engines/oga.rb +2 -0
- data/lib/aws-sdk-core.rb +14 -0
- data/lib/aws-sdk-sso/client.rb +71 -11
- data/lib/aws-sdk-sso/endpoint_parameters.rb +66 -0
- data/lib/aws-sdk-sso/endpoint_provider.rb +57 -0
- data/lib/aws-sdk-sso/endpoints.rb +72 -0
- data/lib/aws-sdk-sso/plugins/endpoints.rb +76 -0
- data/lib/aws-sdk-sso/types.rb +8 -43
- data/lib/aws-sdk-sso.rb +5 -1
- data/lib/aws-sdk-ssooidc/client.rb +935 -0
- data/lib/aws-sdk-ssooidc/client_api.rb +271 -0
- data/lib/aws-sdk-ssooidc/customizations.rb +1 -0
- data/lib/aws-sdk-ssooidc/endpoint_parameters.rb +66 -0
- data/lib/aws-sdk-ssooidc/endpoint_provider.rb +57 -0
- data/lib/aws-sdk-ssooidc/endpoints.rb +72 -0
- data/lib/aws-sdk-ssooidc/errors.rb +321 -0
- data/lib/aws-sdk-ssooidc/plugins/endpoints.rb +76 -0
- data/lib/aws-sdk-ssooidc/resource.rb +26 -0
- data/lib/aws-sdk-ssooidc/types.rb +755 -0
- data/lib/aws-sdk-ssooidc.rb +59 -0
- data/lib/aws-sdk-sts/client.rb +298 -245
- data/lib/aws-sdk-sts/client_api.rb +12 -1
- data/lib/aws-sdk-sts/endpoint_parameters.rb +78 -0
- data/lib/aws-sdk-sts/endpoint_provider.rb +112 -0
- data/lib/aws-sdk-sts/endpoints.rb +136 -0
- data/lib/aws-sdk-sts/plugins/endpoints.rb +84 -0
- data/lib/aws-sdk-sts/presigner.rb +14 -16
- data/lib/aws-sdk-sts/types.rb +128 -197
- data/lib/aws-sdk-sts.rb +5 -1
- data/lib/seahorse/client/async_base.rb +0 -1
- data/lib/seahorse/client/configuration.rb +1 -5
- data/lib/seahorse/client/h2/connection.rb +12 -11
- data/lib/seahorse/client/net_http/patches.rb +1 -4
- data/lib/seahorse/client/plugins/h2.rb +3 -3
- data/lib/seahorse/client/plugins/request_callback.rb +40 -9
- data/lib/seahorse/client/response.rb +6 -0
- data/lib/seahorse/model/operation.rb +3 -0
- data/lib/seahorse/util.rb +4 -0
- metadata +49 -7
| @@ -0,0 +1,71 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'thread'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Aws
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              # Module/mixin used by token provider classes that can be refreshed. This
         | 
| 8 | 
            +
              # provides basic refresh logic in a thread-safe manner. Classes mixing in
         | 
| 9 | 
            +
              # this module are expected to implement a #refresh method that populates
         | 
| 10 | 
            +
              # the following instance variable:
         | 
| 11 | 
            +
              #
         | 
| 12 | 
            +
              # * `@token` [Token] - {Aws::Token} object with the `expiration` and `token`
         | 
| 13 | 
            +
              #       fields set.
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              # @api private
         | 
| 16 | 
            +
              module RefreshingToken
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def initialize(options = {})
         | 
| 19 | 
            +
                  @mutex = Mutex.new
         | 
| 20 | 
            +
                  @before_refresh = options.delete(:before_refresh) if Hash === options
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  @before_refresh.call(self) if @before_refresh
         | 
| 23 | 
            +
                  refresh
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                # @return [Token]
         | 
| 27 | 
            +
                def token
         | 
| 28 | 
            +
                  refresh_if_near_expiration
         | 
| 29 | 
            +
                  @token
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # @return [Time,nil]
         | 
| 33 | 
            +
                def expiration
         | 
| 34 | 
            +
                  refresh_if_near_expiration
         | 
| 35 | 
            +
                  @expiration
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                # Refresh token.
         | 
| 39 | 
            +
                # @return [void]
         | 
| 40 | 
            +
                def refresh!
         | 
| 41 | 
            +
                  @mutex.synchronize do
         | 
| 42 | 
            +
                    @before_refresh.call(self) if @before_refresh
         | 
| 43 | 
            +
                    refresh
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                private
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                # Refreshes token if it is within
         | 
| 50 | 
            +
                # 5 minutes of expiration.
         | 
| 51 | 
            +
                def refresh_if_near_expiration
         | 
| 52 | 
            +
                  if near_expiration?
         | 
| 53 | 
            +
                    @mutex.synchronize do
         | 
| 54 | 
            +
                      if near_expiration?
         | 
| 55 | 
            +
                        @before_refresh.call(self) if @before_refresh
         | 
| 56 | 
            +
                        refresh
         | 
| 57 | 
            +
                      end
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def near_expiration?
         | 
| 63 | 
            +
                  if @token && @token.expiration
         | 
| 64 | 
            +
                    # are we within 5 minutes of expiration?
         | 
| 65 | 
            +
                    (Time.now.to_i + 5 * 60) > @token.expiration.to_i
         | 
| 66 | 
            +
                  else
         | 
| 67 | 
            +
                    true
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
            end
         | 
| @@ -53,12 +53,8 @@ module Aws | |
| 53 53 | 
             
                      return if !value || value.empty?
         | 
| 54 54 | 
             
                      headers[ref.location_name] = value
         | 
| 55 55 | 
             
                        .compact
         | 
| 56 | 
            -
                        .map { |s| escape_header_list_string(s.to_s) }
         | 
| 57 | 
            -
                        .join( | 
| 58 | 
            -
                    end
         | 
| 59 | 
            -
             | 
| 60 | 
            -
                    def escape_header_list_string(s)
         | 
| 61 | 
            -
                      (s.include?('"') || s.include?(",")) ? "\"#{s.gsub('"', '\"')}\"" : s
         | 
| 56 | 
            +
                        .map { |s| Seahorse::Util.escape_header_list_string(s.to_s) }
         | 
| 57 | 
            +
                        .join(',')
         | 
| 62 58 | 
             
                    end
         | 
| 63 59 |  | 
| 64 60 | 
             
                    def apply_header_map(headers, ref, values)
         | 
| @@ -4,9 +4,16 @@ module Aws | |
| 4 4 | 
             
              module Rest
         | 
| 5 5 | 
             
                module Request
         | 
| 6 6 | 
             
                  class QuerystringBuilder
         | 
| 7 | 
            -
             | 
| 8 7 | 
             
                    include Seahorse::Model::Shapes
         | 
| 9 8 |  | 
| 9 | 
            +
                    SUPPORTED_TYPES = [
         | 
| 10 | 
            +
                      BooleanShape,
         | 
| 11 | 
            +
                      FloatShape,
         | 
| 12 | 
            +
                      IntegerShape,
         | 
| 13 | 
            +
                      StringShape,
         | 
| 14 | 
            +
                      TimestampShape
         | 
| 15 | 
            +
                    ].freeze
         | 
| 16 | 
            +
             | 
| 10 17 | 
             
                    # Provide shape references and param values:
         | 
| 11 18 | 
             
                    #
         | 
| 12 19 | 
             
                    #     [
         | 
| @@ -33,29 +40,12 @@ module Aws | |
| 33 40 | 
             
                    def build_part(shape_ref, param_value)
         | 
| 34 41 | 
             
                      case shape_ref.shape
         | 
| 35 42 | 
             
                      # supported scalar types
         | 
| 36 | 
            -
                      when  | 
| 37 | 
            -
                         | 
| 38 | 
            -
                        "#{param_name}=#{escape(param_value.to_s)}"
         | 
| 39 | 
            -
                      when TimestampShape
         | 
| 40 | 
            -
                        param_name = shape_ref.location_name
         | 
| 41 | 
            -
                        "#{param_name}=#{escape(timestamp(shape_ref, param_value))}"
         | 
| 43 | 
            +
                      when *SUPPORTED_TYPES
         | 
| 44 | 
            +
                        "#{shape_ref.location_name}=#{query_value(shape_ref, param_value)}"
         | 
| 42 45 | 
             
                      when MapShape
         | 
| 43 | 
            -
                         | 
| 44 | 
            -
                          query_map_of_string(param_value)
         | 
| 45 | 
            -
                        elsif ListShape === shape_ref.shape.value.shape
         | 
| 46 | 
            -
                          query_map_of_string_list(param_value)
         | 
| 47 | 
            -
                        else
         | 
| 48 | 
            -
                          msg = "only map of string and string list supported"
         | 
| 49 | 
            -
                          raise NotImplementedError, msg
         | 
| 50 | 
            -
                        end
         | 
| 46 | 
            +
                        generate_query_map(shape_ref, param_value)
         | 
| 51 47 | 
             
                      when ListShape
         | 
| 52 | 
            -
                         | 
| 53 | 
            -
                          list_of_strings(shape_ref.location_name, param_value)
         | 
| 54 | 
            -
                        else
         | 
| 55 | 
            -
                          msg = "Only list of strings supported, got "\
         | 
| 56 | 
            -
                                "#{shape_ref.shape.member.shape.class.name}"
         | 
| 57 | 
            -
                          raise NotImplementedError, msg
         | 
| 58 | 
            -
                        end
         | 
| 48 | 
            +
                        generate_query_list(shape_ref, param_value)
         | 
| 59 49 | 
             
                      else
         | 
| 60 50 | 
             
                        raise NotImplementedError
         | 
| 61 51 | 
             
                      end
         | 
| @@ -71,6 +61,37 @@ module Aws | |
| 71 61 | 
             
                      end
         | 
| 72 62 | 
             
                    end
         | 
| 73 63 |  | 
| 64 | 
            +
                    def query_value(ref, value)
         | 
| 65 | 
            +
                      case ref.shape
         | 
| 66 | 
            +
                      when TimestampShape
         | 
| 67 | 
            +
                        escape(timestamp(ref, value))
         | 
| 68 | 
            +
                      when *SUPPORTED_TYPES
         | 
| 69 | 
            +
                        escape(value.to_s)
         | 
| 70 | 
            +
                      else
         | 
| 71 | 
            +
                        raise NotImplementedError
         | 
| 72 | 
            +
                      end
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    def generate_query_list(ref, values)
         | 
| 76 | 
            +
                      member_ref = ref.shape.member
         | 
| 77 | 
            +
                      values.map do |value|
         | 
| 78 | 
            +
                        value = query_value(member_ref, value)
         | 
| 79 | 
            +
                        "#{ref.location_name}=#{value}"
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    def generate_query_map(ref, value)
         | 
| 84 | 
            +
                      case ref.shape.value.shape
         | 
| 85 | 
            +
                      when StringShape
         | 
| 86 | 
            +
                        query_map_of_string(value)
         | 
| 87 | 
            +
                      when ListShape
         | 
| 88 | 
            +
                        query_map_of_string_list(value)
         | 
| 89 | 
            +
                      else
         | 
| 90 | 
            +
                        msg = 'Only map of string and string list supported'
         | 
| 91 | 
            +
                        raise NotImplementedError, msg
         | 
| 92 | 
            +
                      end
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
             | 
| 74 95 | 
             
                    def query_map_of_string(hash)
         | 
| 75 96 | 
             
                      list = []
         | 
| 76 97 | 
             
                      hash.each_pair do |key, value|
         | 
| @@ -89,16 +110,9 @@ module Aws | |
| 89 110 | 
             
                      list
         | 
| 90 111 | 
             
                    end
         | 
| 91 112 |  | 
| 92 | 
            -
                    def list_of_strings(name, values)
         | 
| 93 | 
            -
                      values.map do |value|
         | 
| 94 | 
            -
                        "#{name}=#{escape(value)}"
         | 
| 95 | 
            -
                      end
         | 
| 96 | 
            -
                    end
         | 
| 97 | 
            -
             | 
| 98 113 | 
             
                    def escape(string)
         | 
| 99 114 | 
             
                      Seahorse::Util.uri_escape(string)
         | 
| 100 115 | 
             
                    end
         | 
| 101 | 
            -
             | 
| 102 116 | 
             
                  end
         | 
| 103 117 | 
             
                end
         | 
| 104 118 | 
             
              end
         | 
| @@ -3,7 +3,11 @@ | |
| 3 3 | 
             
            module Aws
         | 
| 4 4 | 
             
              # @api private
         | 
| 5 5 | 
             
              class SharedConfig
         | 
| 6 | 
            -
                 | 
| 6 | 
            +
                SSO_CREDENTIAL_PROFILE_KEYS = %w[sso_account_id sso_role_name].freeze
         | 
| 7 | 
            +
                SSO_PROFILE_KEYS = %w[sso_session sso_start_url sso_region sso_account_id sso_role_name].freeze
         | 
| 8 | 
            +
                SSO_TOKEN_PROFILE_KEYS = %w[sso_session].freeze
         | 
| 9 | 
            +
                SSO_SESSION_KEYS = %w[sso_region sso_start_url].freeze
         | 
| 10 | 
            +
             | 
| 7 11 |  | 
| 8 12 | 
             
                # @return [String]
         | 
| 9 13 | 
             
                attr_reader :credentials_path
         | 
| @@ -51,10 +55,12 @@ module Aws | |
| 51 55 | 
             
                  @config_enabled = options[:config_enabled]
         | 
| 52 56 | 
             
                  @credentials_path = options[:credentials_path] ||
         | 
| 53 57 | 
             
                                      determine_credentials_path
         | 
| 58 | 
            +
                  @credentials_path = File.expand_path(@credentials_path) if @credentials_path
         | 
| 54 59 | 
             
                  @parsed_credentials = {}
         | 
| 55 60 | 
             
                  load_credentials_file if loadable?(@credentials_path)
         | 
| 56 61 | 
             
                  if @config_enabled
         | 
| 57 62 | 
             
                    @config_path = options[:config_path] || determine_config_path
         | 
| 63 | 
            +
                    @config_path = File.expand_path(@config_path) if @config_path
         | 
| 58 64 | 
             
                    load_config_file if loadable?(@config_path)
         | 
| 59 65 | 
             
                  end
         | 
| 60 66 | 
             
                end
         | 
| @@ -149,6 +155,38 @@ module Aws | |
| 149 155 | 
             
                  credentials
         | 
| 150 156 | 
             
                end
         | 
| 151 157 |  | 
| 158 | 
            +
                # Attempts to load from shared config or shared credentials file.
         | 
| 159 | 
            +
                # Will always attempt first to load from the shared credentials
         | 
| 160 | 
            +
                # file, if present.
         | 
| 161 | 
            +
                def sso_token_from_config(opts = {})
         | 
| 162 | 
            +
                  p = opts[:profile] || @profile_name
         | 
| 163 | 
            +
                  token = sso_token_from_profile(@parsed_credentials, p)
         | 
| 164 | 
            +
                  if @parsed_config
         | 
| 165 | 
            +
                    token ||= sso_token_from_profile(@parsed_config, p)
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
                  token
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                # Source a custom configured endpoint from the shared configuration file
         | 
| 171 | 
            +
                #
         | 
| 172 | 
            +
                # @param [Hash] opts
         | 
| 173 | 
            +
                # @option opts [String] :profile
         | 
| 174 | 
            +
                # @option opts [String] :service_id
         | 
| 175 | 
            +
                def configured_endpoint(opts = {})
         | 
| 176 | 
            +
                  # services section is only allowed in the shared config file (not credentials)
         | 
| 177 | 
            +
                  profile = opts[:profile] || @profile_name
         | 
| 178 | 
            +
                  service_id = opts[:service_id]&.gsub(" ", "_")&.downcase
         | 
| 179 | 
            +
                  if @parsed_config && (prof_config = @parsed_config[profile])
         | 
| 180 | 
            +
                    services_section_name = prof_config['services']
         | 
| 181 | 
            +
                    if (services_config = @parsed_config["services #{services_section_name}"]) &&
         | 
| 182 | 
            +
                      (service_config = services_config[service_id])
         | 
| 183 | 
            +
                      return service_config['endpoint_url'] if service_config['endpoint_url']
         | 
| 184 | 
            +
                    end
         | 
| 185 | 
            +
                    return prof_config['endpoint_url']
         | 
| 186 | 
            +
                  end
         | 
| 187 | 
            +
                  nil
         | 
| 188 | 
            +
                end
         | 
| 189 | 
            +
             | 
| 152 190 | 
             
                # Add an accessor method (similar to attr_reader) to return a configuration value
         | 
| 153 191 | 
             
                # Uses the get_config_value below to control where
         | 
| 154 192 | 
             
                # values are loaded from
         | 
| @@ -167,6 +205,7 @@ module Aws | |
| 167 205 | 
             
                  :use_fips_endpoint,
         | 
| 168 206 | 
             
                  :ec2_metadata_service_endpoint,
         | 
| 169 207 | 
             
                  :ec2_metadata_service_endpoint_mode,
         | 
| 208 | 
            +
                  :ec2_metadata_v1_disabled,
         | 
| 170 209 | 
             
                  :max_attempts,
         | 
| 171 210 | 
             
                  :retry_mode,
         | 
| 172 211 | 
             
                  :adaptive_retry_wait_to_fill,
         | 
| @@ -179,7 +218,11 @@ module Aws | |
| 179 218 | 
             
                  :s3_use_arn_region,
         | 
| 180 219 | 
             
                  :s3_us_east_1_regional_endpoint,
         | 
| 181 220 | 
             
                  :s3_disable_multiregion_access_points,
         | 
| 182 | 
            -
                  :defaults_mode
         | 
| 221 | 
            +
                  :defaults_mode,
         | 
| 222 | 
            +
                  :sdk_ua_app_id,
         | 
| 223 | 
            +
                  :disable_request_compression,
         | 
| 224 | 
            +
                  :request_min_compression_size_bytes,
         | 
| 225 | 
            +
                  :ignore_configured_endpoint_urls
         | 
| 183 226 | 
             
                )
         | 
| 184 227 |  | 
| 185 228 | 
             
                private
         | 
| @@ -314,13 +357,53 @@ module Aws | |
| 314 357 | 
             
                def sso_credentials_from_profile(cfg, profile)
         | 
| 315 358 | 
             
                  if @parsed_config &&
         | 
| 316 359 | 
             
                     (prof_config = cfg[profile]) &&
         | 
| 317 | 
            -
                     !(prof_config.keys &  | 
| 360 | 
            +
                     !(prof_config.keys & SSO_CREDENTIAL_PROFILE_KEYS).empty?
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                    if sso_session_name = prof_config['sso_session']
         | 
| 363 | 
            +
                      sso_session = sso_session(cfg, profile, sso_session_name)
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                      sso_region = sso_session['sso_region']
         | 
| 366 | 
            +
                      sso_start_url = sso_session['sso_start_url']
         | 
| 367 | 
            +
             | 
| 368 | 
            +
                      # validate sso_region and sso_start_url don't conflict if set on profile and session
         | 
| 369 | 
            +
                      if prof_config['sso_region'] &&  prof_config['sso_region'] != sso_region
         | 
| 370 | 
            +
                        raise ArgumentError,
         | 
| 371 | 
            +
                              "sso-session #{sso_session_name}'s sso_region (#{sso_region}) " \
         | 
| 372 | 
            +
                                "does not match the profile #{profile}'s sso_region (#{prof_config['sso_region']}'"
         | 
| 373 | 
            +
                      end
         | 
| 374 | 
            +
                      if prof_config['sso_start_url'] &&  prof_config['sso_start_url'] != sso_start_url
         | 
| 375 | 
            +
                        raise ArgumentError,
         | 
| 376 | 
            +
                              "sso-session #{sso_session_name}'s sso_start_url (#{sso_start_url}) " \
         | 
| 377 | 
            +
                                "does not match the profile #{profile}'s sso_start_url (#{prof_config['sso_start_url']}'"
         | 
| 378 | 
            +
                      end
         | 
| 379 | 
            +
                    else
         | 
| 380 | 
            +
                      sso_region = prof_config['sso_region']
         | 
| 381 | 
            +
                      sso_start_url = prof_config['sso_start_url']
         | 
| 382 | 
            +
                    end
         | 
| 318 383 |  | 
| 319 384 | 
             
                    SSOCredentials.new(
         | 
| 320 | 
            -
                      sso_start_url: prof_config['sso_start_url'],
         | 
| 321 | 
            -
                      sso_region: prof_config['sso_region'],
         | 
| 322 385 | 
             
                      sso_account_id: prof_config['sso_account_id'],
         | 
| 323 | 
            -
                      sso_role_name: prof_config['sso_role_name']
         | 
| 386 | 
            +
                      sso_role_name: prof_config['sso_role_name'],
         | 
| 387 | 
            +
                      sso_session: prof_config['sso_session'],
         | 
| 388 | 
            +
                      sso_region: sso_region,
         | 
| 389 | 
            +
                      sso_start_url: sso_start_url
         | 
| 390 | 
            +
                      )
         | 
| 391 | 
            +
                  end
         | 
| 392 | 
            +
                end
         | 
| 393 | 
            +
             | 
| 394 | 
            +
                # If the required sso_ profile values are present, attempt to construct
         | 
| 395 | 
            +
                # SSOTokenProvider
         | 
| 396 | 
            +
                def sso_token_from_profile(cfg, profile)
         | 
| 397 | 
            +
                  if @parsed_config &&
         | 
| 398 | 
            +
                    (prof_config = cfg[profile]) &&
         | 
| 399 | 
            +
                    !(prof_config.keys & SSO_TOKEN_PROFILE_KEYS).empty?
         | 
| 400 | 
            +
             | 
| 401 | 
            +
                    sso_session_name = prof_config['sso_session']
         | 
| 402 | 
            +
                    sso_session = sso_session(cfg, profile, sso_session_name)
         | 
| 403 | 
            +
             | 
| 404 | 
            +
                    SSOTokenProvider.new(
         | 
| 405 | 
            +
                      sso_session: sso_session_name,
         | 
| 406 | 
            +
                      sso_region: sso_session['sso_region']
         | 
| 324 407 | 
             
                    )
         | 
| 325 408 | 
             
                  end
         | 
| 326 409 | 
             
                end
         | 
| @@ -374,5 +457,22 @@ module Aws | |
| 374 457 | 
             
                  ret ||= 'default'
         | 
| 375 458 | 
             
                  ret
         | 
| 376 459 | 
             
                end
         | 
| 460 | 
            +
             | 
| 461 | 
            +
                def sso_session(cfg, profile, sso_session_name)
         | 
| 462 | 
            +
                  # aws sso-configure may add quotes around sso session names with whitespace
         | 
| 463 | 
            +
                  sso_session = cfg["sso-session #{sso_session_name}"] || cfg["sso-session '#{sso_session_name}'"]
         | 
| 464 | 
            +
             | 
| 465 | 
            +
                  unless sso_session
         | 
| 466 | 
            +
                    raise ArgumentError,
         | 
| 467 | 
            +
                      "sso-session #{sso_session_name} must be defined in the config file. " \
         | 
| 468 | 
            +
                                "Referenced by profile #{profile}"
         | 
| 469 | 
            +
                  end
         | 
| 470 | 
            +
             | 
| 471 | 
            +
                  unless sso_session['sso_region']
         | 
| 472 | 
            +
                    raise ArgumentError, "sso-session #{sso_session_name} missing required parameter: sso_region"
         | 
| 473 | 
            +
                  end
         | 
| 474 | 
            +
             | 
| 475 | 
            +
                  sso_session
         | 
| 476 | 
            +
                end
         | 
| 377 477 | 
             
              end
         | 
| 378 478 | 
             
            end
         | 
| @@ -3,24 +3,19 @@ | |
| 3 3 | 
             
            module Aws
         | 
| 4 4 | 
             
              # An auto-refreshing credential provider that assumes a role via
         | 
| 5 5 | 
             
              # {Aws::SSO::Client#get_role_credentials} using a cached access
         | 
| 6 | 
            -
              # token.  | 
| 7 | 
            -
              #  | 
| 8 | 
            -
              #  | 
| 9 | 
            -
              #
         | 
| 10 | 
            -
              #  | 
| 11 | 
            -
              #  | 
| 12 | 
            -
              # access token generated and cached from `aws login` will also expire.
         | 
| 13 | 
            -
              # Once this token expires, it will not be usable to refresh AWS credentials,
         | 
| 14 | 
            -
              # and another token will be needed. The SDK does not manage refreshing of
         | 
| 15 | 
            -
              # the token value, but this can be done by running `aws login` with the
         | 
| 16 | 
            -
              # correct profile.
         | 
| 6 | 
            +
              # token. When `sso_session` is specified, token refresh logic from
         | 
| 7 | 
            +
              # {Aws::SSOTokenProvider} will be used to refresh the token if possible.
         | 
| 8 | 
            +
              # This class does NOT implement the SSO login token flow - tokens
         | 
| 9 | 
            +
              # must generated separately by running `aws login` from the
         | 
| 10 | 
            +
              # AWS CLI with the correct profile. The `SSOCredentials` will
         | 
| 11 | 
            +
              # auto-refresh the AWS credentials from SSO.
         | 
| 17 12 | 
             
              #
         | 
| 18 13 | 
             
              #     # You must first run aws sso login --profile your-sso-profile
         | 
| 19 14 | 
             
              #     sso_credentials = Aws::SSOCredentials.new(
         | 
| 20 15 | 
             
              #       sso_account_id: '123456789',
         | 
| 21 16 | 
             
              #       sso_role_name: "role_name",
         | 
| 22 17 | 
             
              #       sso_region: "us-east-1",
         | 
| 23 | 
            -
              #        | 
| 18 | 
            +
              #       sso_session: 'my_sso_session'
         | 
| 24 19 | 
             
              #     )
         | 
| 25 20 | 
             
              #     ec2 = Aws::EC2::Client.new(credentials: sso_credentials)
         | 
| 26 21 | 
             
              #
         | 
| @@ -35,7 +30,8 @@ module Aws | |
| 35 30 | 
             
                include RefreshingCredentials
         | 
| 36 31 |  | 
| 37 32 | 
             
                # @api private
         | 
| 38 | 
            -
                 | 
| 33 | 
            +
                LEGACY_REQUIRED_OPTS =         [:sso_start_url, :sso_account_id, :sso_region, :sso_role_name].freeze
         | 
| 34 | 
            +
                TOKEN_PROVIDER_REQUIRED_OPTS = [:sso_session, :sso_account_id, :sso_region, :sso_role_name].freeze
         | 
| 39 35 |  | 
| 40 36 | 
             
                # @api private
         | 
| 41 37 | 
             
                SSO_LOGIN_GUIDANCE = 'The SSO session associated with this profile has '\
         | 
| @@ -45,17 +41,23 @@ module Aws | |
| 45 41 | 
             
                # @option options [required, String] :sso_account_id The AWS account ID
         | 
| 46 42 | 
             
                #   that temporary AWS credentials will be resolved for
         | 
| 47 43 | 
             
                #
         | 
| 48 | 
            -
                # @option options [required, String] :sso_region The AWS region where the
         | 
| 49 | 
            -
                #   SSO directory for the given sso_start_url is hosted.
         | 
| 50 | 
            -
                #
         | 
| 51 44 | 
             
                # @option options [required, String] :sso_role_name The corresponding
         | 
| 52 45 | 
             
                #   IAM role in the AWS account that temporary AWS credentials
         | 
| 53 46 | 
             
                #   will be resolved for.
         | 
| 54 47 | 
             
                #
         | 
| 55 | 
            -
                # @option options [required, String] : | 
| 56 | 
            -
                #    | 
| 48 | 
            +
                # @option options [required, String] :sso_region The AWS region where the
         | 
| 49 | 
            +
                #   SSO directory for the given sso_start_url is hosted.
         | 
| 50 | 
            +
                #
         | 
| 51 | 
            +
                # @option options [String] :sso_session The SSO Token used for fetching
         | 
| 52 | 
            +
                #   the token. If provided, refresh logic from the {Aws::SSOTokenProvider}
         | 
| 53 | 
            +
                #   will be used.
         | 
| 54 | 
            +
                #
         | 
| 55 | 
            +
                # @option options [String] :sso_start_url (legacy profiles) If provided,
         | 
| 56 | 
            +
                #   legacy token fetch behavior will be used, which does not support
         | 
| 57 | 
            +
                #   token refreshing.  The start URL is provided by the SSO
         | 
| 58 | 
            +
                #   service via the console and is the URL used to
         | 
| 57 59 | 
             
                #   login to the SSO directory. This is also sometimes referred to as
         | 
| 58 | 
            -
                #   the "User Portal URL"
         | 
| 60 | 
            +
                #   the "User Portal URL".
         | 
| 59 61 | 
             
                #
         | 
| 60 62 | 
             
                # @option options [SSO::Client] :client Optional `SSO::Client`.  If not
         | 
| 61 63 | 
             
                #   provided, a client will be constructed.
         | 
| @@ -65,27 +67,52 @@ module Aws | |
| 65 67 | 
             
                #   with an instance of this object when
         | 
| 66 68 | 
             
                #   AWS credentials are required and need to be refreshed.
         | 
| 67 69 | 
             
                def initialize(options = {})
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                   | 
| 70 | 
            -
             | 
| 71 | 
            -
                     | 
| 70 | 
            +
                  options = options.select {|k, v| !v.nil? }
         | 
| 71 | 
            +
                  if (options[:sso_session])
         | 
| 72 | 
            +
                    missing_keys = TOKEN_PROVIDER_REQUIRED_OPTS.select { |k| options[k].nil? }
         | 
| 73 | 
            +
                    unless missing_keys.empty?
         | 
| 74 | 
            +
                      raise ArgumentError, "Missing required keys: #{missing_keys}"
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
                    @legacy = false
         | 
| 77 | 
            +
                    @sso_role_name = options.delete(:sso_role_name)
         | 
| 78 | 
            +
                    @sso_account_id = options.delete(:sso_account_id)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    # if client has been passed, don't pass through to SSOTokenProvider
         | 
| 81 | 
            +
                    @client = options.delete(:client)
         | 
| 82 | 
            +
                    options.delete(:sso_start_url)
         | 
| 83 | 
            +
                    @token_provider = Aws::SSOTokenProvider.new(options.dup)
         | 
| 84 | 
            +
                    @sso_session = options.delete(:sso_session)
         | 
| 85 | 
            +
                    @sso_region = options.delete(:sso_region)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    unless @client
         | 
| 88 | 
            +
                      client_opts = {}
         | 
| 89 | 
            +
                      options.each_pair { |k,v| client_opts[k] = v unless CLIENT_EXCLUDE_OPTIONS.include?(k) }
         | 
| 90 | 
            +
                      client_opts[:region] = @sso_region
         | 
| 91 | 
            +
                      client_opts[:credentials] = nil
         | 
| 92 | 
            +
                      @client = Aws::SSO::Client.new(client_opts)
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
                  else # legacy behavior
         | 
| 95 | 
            +
                    missing_keys = LEGACY_REQUIRED_OPTS.select { |k| options[k].nil? }
         | 
| 96 | 
            +
                    unless missing_keys.empty?
         | 
| 97 | 
            +
                      raise ArgumentError, "Missing required keys: #{missing_keys}"
         | 
| 98 | 
            +
                    end
         | 
| 99 | 
            +
                    @legacy = true
         | 
| 100 | 
            +
                    @sso_start_url = options.delete(:sso_start_url)
         | 
| 101 | 
            +
                    @sso_region = options.delete(:sso_region)
         | 
| 102 | 
            +
                    @sso_role_name = options.delete(:sso_role_name)
         | 
| 103 | 
            +
                    @sso_account_id = options.delete(:sso_account_id)
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    # validate we can read the token file
         | 
| 106 | 
            +
                    read_cached_token
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    client_opts = {}
         | 
| 109 | 
            +
                    options.each_pair { |k,v| client_opts[k] = v unless CLIENT_EXCLUDE_OPTIONS.include?(k) }
         | 
| 110 | 
            +
                    client_opts[:region] = @sso_region
         | 
| 111 | 
            +
                    client_opts[:credentials] = nil
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                    @client = options[:client] || Aws::SSO::Client.new(client_opts)
         | 
| 72 114 | 
             
                  end
         | 
| 73 115 |  | 
| 74 | 
            -
                  @sso_start_url = options.delete(:sso_start_url)
         | 
| 75 | 
            -
                  @sso_region = options.delete(:sso_region)
         | 
| 76 | 
            -
                  @sso_role_name = options.delete(:sso_role_name)
         | 
| 77 | 
            -
                  @sso_account_id = options.delete(:sso_account_id)
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                  # validate we can read the token file
         | 
| 80 | 
            -
                  read_cached_token
         | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
                  client_opts = {}
         | 
| 84 | 
            -
                  options.each_pair { |k,v| client_opts[k] = v unless CLIENT_EXCLUDE_OPTIONS.include?(k) }
         | 
| 85 | 
            -
                  client_opts[:region] = @sso_region
         | 
| 86 | 
            -
                  client_opts[:credentials] = nil
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                  @client = options[:client] || Aws::SSO::Client.new(client_opts)
         | 
| 89 116 | 
             
                  @async_refresh = true
         | 
| 90 117 | 
             
                  super
         | 
| 91 118 | 
             
                end
         | 
| @@ -111,19 +138,27 @@ module Aws | |
| 111 138 | 
             
                end
         | 
| 112 139 |  | 
| 113 140 | 
             
                def refresh
         | 
| 114 | 
            -
                   | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 141 | 
            +
                  c = if @legacy
         | 
| 142 | 
            +
                        cached_token = read_cached_token
         | 
| 143 | 
            +
                        @client.get_role_credentials(
         | 
| 144 | 
            +
                          account_id: @sso_account_id,
         | 
| 145 | 
            +
                          role_name: @sso_role_name,
         | 
| 146 | 
            +
                          access_token: cached_token['accessToken']
         | 
| 147 | 
            +
                        ).role_credentials
         | 
| 148 | 
            +
                      else
         | 
| 149 | 
            +
                        @client.get_role_credentials(
         | 
| 150 | 
            +
                          account_id: @sso_account_id,
         | 
| 151 | 
            +
                          role_name: @sso_role_name,
         | 
| 152 | 
            +
                          access_token: @token_provider.token.token
         | 
| 153 | 
            +
                        ).role_credentials
         | 
| 154 | 
            +
                      end
         | 
| 120 155 |  | 
| 121 156 | 
             
                  @credentials = Credentials.new(
         | 
| 122 157 | 
             
                    c.access_key_id,
         | 
| 123 158 | 
             
                    c.secret_access_key,
         | 
| 124 159 | 
             
                    c.session_token
         | 
| 125 160 | 
             
                  )
         | 
| 126 | 
            -
                  @expiration = c.expiration
         | 
| 161 | 
            +
                  @expiration = Time.at(c.expiration / 1000.0)
         | 
| 127 162 | 
             
                end
         | 
| 128 163 |  | 
| 129 164 | 
             
                def sso_cache_file
         | 
| @@ -0,0 +1,135 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Aws
         | 
| 4 | 
            +
              class SSOTokenProvider
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                include TokenProvider
         | 
| 7 | 
            +
                include RefreshingToken
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                # @api private
         | 
| 10 | 
            +
                SSO_REQUIRED_OPTS = [:sso_region, :sso_session].freeze
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                # @api private
         | 
| 13 | 
            +
                SSO_LOGIN_GUIDANCE = 'The SSO session associated with this profile has '\
         | 
| 14 | 
            +
                'expired or is otherwise invalid. To refresh this SSO session run '\
         | 
| 15 | 
            +
                'aws sso login with the corresponding profile.'.freeze
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                # @option options [required, String] :sso_region The AWS region where the
         | 
| 18 | 
            +
                #   SSO directory for the given sso_start_url is hosted.
         | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                # @option options [required, String] :sso_session The SSO Session used to
         | 
| 21 | 
            +
                #   for fetching this token.
         | 
| 22 | 
            +
                #
         | 
| 23 | 
            +
                # @option options [SSOOIDC::Client] :client Optional `SSOOIDC::Client`.  If not
         | 
| 24 | 
            +
                #   provided, a client will be constructed.
         | 
| 25 | 
            +
                #
         | 
| 26 | 
            +
                # @option options [Callable] before_refresh Proc called before
         | 
| 27 | 
            +
                #   credentials are refreshed. `before_refresh` is called
         | 
| 28 | 
            +
                #   with an instance of this object when
         | 
| 29 | 
            +
                #   AWS credentials are required and need to be refreshed.
         | 
| 30 | 
            +
                def initialize(options = {})
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  missing_keys = SSO_REQUIRED_OPTS.select { |k| options[k].nil? }
         | 
| 33 | 
            +
                  unless missing_keys.empty?
         | 
| 34 | 
            +
                    raise ArgumentError, "Missing required keys: #{missing_keys}"
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  @sso_session = options.delete(:sso_session)
         | 
| 38 | 
            +
                  @sso_region = options.delete(:sso_region)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  options[:region] = @sso_region
         | 
| 41 | 
            +
                  options[:credentials] = nil
         | 
| 42 | 
            +
                  options[:token_provider] = nil
         | 
| 43 | 
            +
                  @client = options[:client] || Aws::SSOOIDC::Client.new(options)
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  super
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                # @return [SSOOIDC::Client]
         | 
| 49 | 
            +
                attr_reader :client
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                private
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def refresh
         | 
| 54 | 
            +
                  # token is valid and not in refresh window - do not refresh it.
         | 
| 55 | 
            +
                  return if @token && @token.expiration && !near_expiration?
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  # token may not exist or is out of the expiration window
         | 
| 58 | 
            +
                  # attempt to refresh from disk first (another process/application may have refreshed already)
         | 
| 59 | 
            +
                  token_json = read_cached_token
         | 
| 60 | 
            +
                  @token = Token.new(token_json['accessToken'], token_json['expiresAt'])
         | 
| 61 | 
            +
                  return if @token && @token.expiration && !near_expiration?
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  # The token is expired and needs to be refreshed
         | 
| 64 | 
            +
                  if can_refresh_token?(token_json)
         | 
| 65 | 
            +
                    begin
         | 
| 66 | 
            +
                      current_time = Time.now
         | 
| 67 | 
            +
                      resp = @client.create_token(
         | 
| 68 | 
            +
                        grant_type: 'refresh_token',
         | 
| 69 | 
            +
                        client_id: token_json['clientId'],
         | 
| 70 | 
            +
                        client_secret: token_json['clientSecret'],
         | 
| 71 | 
            +
                        refresh_token: token_json['refreshToken']
         | 
| 72 | 
            +
                      )
         | 
| 73 | 
            +
                      token_json['accessToken'] = resp.access_token
         | 
| 74 | 
            +
                      token_json['expiresAt'] = current_time + resp.expires_in
         | 
| 75 | 
            +
                      @token = Token.new(token_json['accessToken'], token_json['expiresAt'])
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                      if resp.refresh_token
         | 
| 78 | 
            +
                        token_json['refreshToken'] = resp.refresh_token
         | 
| 79 | 
            +
                      else
         | 
| 80 | 
            +
                        token_json.delete('refreshToken')
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                      update_token_cache(token_json)
         | 
| 84 | 
            +
                    rescue
         | 
| 85 | 
            +
                      # refresh has failed, continue attempting to use the token if its not hard expired
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  if !@token.expiration || @token.expiration < Time.now
         | 
| 90 | 
            +
                    # Token is hard expired, raise an exception
         | 
| 91 | 
            +
                    raise Errors::InvalidSSOToken, 'Token is invalid and failed to refresh.'
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def read_cached_token
         | 
| 96 | 
            +
                  cached_token = Json.load(File.read(sso_cache_file))
         | 
| 97 | 
            +
                  # validation
         | 
| 98 | 
            +
                  unless cached_token['accessToken'] && cached_token['expiresAt']
         | 
| 99 | 
            +
                    raise ArgumentError, 'Missing required field(s)'
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
                  cached_token['expiresAt'] = Time.parse(cached_token['expiresAt'])
         | 
| 102 | 
            +
                  cached_token
         | 
| 103 | 
            +
                rescue Errno::ENOENT, Aws::Json::ParseError, ArgumentError
         | 
| 104 | 
            +
                  raise Errors::InvalidSSOToken, SSO_LOGIN_GUIDANCE
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                def update_token_cache(token_json)
         | 
| 108 | 
            +
                  cached_token = token_json.dup
         | 
| 109 | 
            +
                  cached_token['expiresAt'] = cached_token['expiresAt'].iso8601
         | 
| 110 | 
            +
                  File.write(sso_cache_file, Json.dump(cached_token))
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def sso_cache_file
         | 
| 114 | 
            +
                  sso_session_sha1 = OpenSSL::Digest::SHA1.hexdigest(@sso_session.encode('utf-8'))
         | 
| 115 | 
            +
                  File.join(Dir.home, '.aws', 'sso', 'cache', "#{sso_session_sha1}.json")
         | 
| 116 | 
            +
                rescue ArgumentError
         | 
| 117 | 
            +
                  # Dir.home raises ArgumentError when ENV['home'] is not set
         | 
| 118 | 
            +
                  raise ArgumentError, "Unable to load sso_cache_file: ENV['HOME'] is not set."
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                # return true if all required fields are present
         | 
| 122 | 
            +
                # return false if registrationExpiresAt exists and is later than now
         | 
| 123 | 
            +
                def can_refresh_token?(token_json)
         | 
| 124 | 
            +
                  if token_json['clientId'] &&
         | 
| 125 | 
            +
                    token_json['clientSecret'] &&
         | 
| 126 | 
            +
                    token_json['refreshToken']
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                    return !token_json['registrationExpiresAt'] ||
         | 
| 129 | 
            +
                      Time.parse(token_json['registrationExpiresAt']) > Time.now
         | 
| 130 | 
            +
                  else
         | 
| 131 | 
            +
                    false
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
              end
         | 
| 135 | 
            +
            end
         |