Class: HTTPX::Plugins::AWSSigV4::Signer
- Inherits:
-
Object
- Object
- HTTPX::Plugins::AWSSigV4::Signer
- Defined in:
- lib/httpx/plugins/aws_sigv4.rb,
sig/plugins/aws_sigv4.rbs
Overview
Signs requests using the AWS sigv4 signing.
Class Method Summary collapse
Instance Method Summary collapse
- #hexdigest(value) ⇒ String
- #hexhmac(key, value) ⇒ String
- #hmac(key, value) ⇒ String
-
#initialize(service:, region:, credentials: nil, username: nil, password: nil, security_token: nil, provider_prefix: "aws", header_provider_field: "amz", unsigned_headers: [], apply_checksum_header: true, algorithm: "SHA256") ⇒ Signer
constructor
A new instance of Signer.
- #sign!(request) ⇒ void
Constructor Details
#initialize(service:, region:, credentials: nil, username: nil, password: nil, security_token: nil, provider_prefix: "aws", header_provider_field: "amz", unsigned_headers: [], apply_checksum_header: true, algorithm: "SHA256") ⇒ Signer
Returns a new instance of Signer.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/httpx/plugins/aws_sigv4.rb', line 17 def initialize( service:, region:, credentials: nil, username: nil, password: nil, security_token: nil, provider_prefix: "aws", header_provider_field: "amz", unsigned_headers: [], apply_checksum_header: true, algorithm: "SHA256" ) @credentials = credentials || Credentials.new(username, password, security_token) @service = service @region = region @unsigned_headers = Set.new(unsigned_headers.map(&:downcase)) @unsigned_headers << "authorization" @unsigned_headers << "x-amzn-trace-id" @unsigned_headers << "expect" @apply_checksum_header = apply_checksum_header @provider_prefix = provider_prefix @header_provider_field = header_provider_field @algorithm = algorithm end |
Class Method Details
.new(arg0) ⇒ instance .new(params) ⇒ instance
31 32 |
# File 'sig/plugins/aws_sigv4.rbs', line 31
def self.new: (instance) -> instance
| (**untyped params) -> instance
|
Instance Method Details
#hexdigest(value) ⇒ String
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/httpx/plugins/aws_sigv4.rb', line 112 def hexdigest(value) digest = OpenSSL::Digest.new(@algorithm) if value.respond_to?(:read) if value.respond_to?(:to_path) # files, pathnames digest.file(value.to_path).hexdigest else # gzipped request bodies raise Error, "request body must be rewindable" unless value.respond_to?(:rewind) buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR) begin IO.copy_stream(value, buffer) buffer.flush digest.file(buffer.to_path).hexdigest ensure value.rewind buffer.close buffer.unlink end end else # error on endless generators raise Error, "hexdigest for endless enumerators is not supported" if value.unbounded_body? mb_buffer = value.each.with_object("".b) do |chunk, b| b << chunk break if b.bytesize >= 1024 * 1024 end digest.hexdigest(mb_buffer) end end |
#hexhmac(key, value) ⇒ String
152 153 154 |
# File 'lib/httpx/plugins/aws_sigv4.rb', line 152 def hexhmac(key, value) OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(@algorithm), key, value) end |
#hmac(key, value) ⇒ String
148 149 150 |
# File 'lib/httpx/plugins/aws_sigv4.rb', line 148 def hmac(key, value) OpenSSL::HMAC.digest(OpenSSL::Digest.new(@algorithm), key, value) end |
#sign!(request) ⇒ void
This method returns an undefined value.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/httpx/plugins/aws_sigv4.rb', line 46 def sign!(request) lower_provider_prefix = "#{@provider_prefix}4" upper_provider_prefix = lower_provider_prefix.upcase downcased_algorithm = @algorithm.downcase datetime = (request.headers["x-#{@header_provider_field}-date"] ||= Time.now.utc.strftime("%Y%m%dT%H%M%SZ")) date = datetime[0, 8] content_hashed = request.headers["x-#{@header_provider_field}-content-#{downcased_algorithm}"] || hexdigest(request.body) request.headers["x-#{@header_provider_field}-content-#{downcased_algorithm}"] ||= content_hashed if @apply_checksum_header request.headers["x-#{@header_provider_field}-security-token"] ||= @credentials.security_token if @credentials.security_token signature_headers = request.headers.each.reject do |k, _| @unsigned_headers.include?(k) end # aws sigv4 needs to declare the host, regardless of protocol version signature_headers << ["host", request.] unless request.headers.key?("host") signature_headers.sort_by!(&:first) signed_headers = signature_headers.map(&:first).join(";") canonical_headers = signature_headers.map do |k, v| # eliminate whitespace between value fields, unless it's a quoted value "#{k}:#{v.start_with?("\"") && v.end_with?("\"") ? v : v.gsub(/\s+/, " ").strip}\n" end.join # canonical request creq = "#{request.verb}" \ "\n#{request.canonical_path}" \ "\n#{request.canonical_query}" \ "\n#{canonical_headers}" \ "\n#{signed_headers}" \ "\n#{content_hashed}" credential_scope = "#{date}" \ "/#{@region}" \ "/#{@service}" \ "/#{lower_provider_prefix}_request" algo_line = "#{upper_provider_prefix}-HMAC-#{@algorithm}" # string to sign sts = "#{algo_line}" \ "\n#{datetime}" \ "\n#{credential_scope}" \ "\n#{OpenSSL::Digest.new(@algorithm).hexdigest(creq)}" # signature k_date = hmac("#{upper_provider_prefix}#{@credentials.password}", date) k_region = hmac(k_date, @region) k_service = hmac(k_region, @service) k_credentials = hmac(k_service, "#{lower_provider_prefix}_request") sig = hexhmac(k_credentials, sts) credential = "#{@credentials.username}/#{credential_scope}" # apply signature request.headers["authorization"] = "#{algo_line} " \ "Credential=#{credential}, " \ "SignedHeaders=#{signed_headers}, " \ "Signature=#{sig}" end |