Last active
March 8, 2022 07:41
-
-
Save dongoclam/e36f6560124460c5c932b6a84afbd683 to your computer and use it in GitHub Desktop.
Examples of the Complete Version 4 Signing Process
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "faraday" | |
class CloudSearchService | |
METHOD = "GET" | |
SHA256 = "sha256" | |
ALGORITHM = "AWS4-HMAC-SHA256" | |
ENDPOINT = "iam.us-east-1.amazonaws.com" | |
CANONICAL_URI = "/" | |
REGION = "us-east-1" | |
SERVICE = "cloudsearch" | |
AWS4_REQUEST = "aws4_request" | |
SIGNED_HEADERS = "host;x-amz-date" | |
DATE_TIME_FORMAT = "%Y%m%dT%H%M%SZ" | |
DATE_FORMAT = "%Y%m%d" | |
def initialize | |
@amzdate = Time.now.utc.strftime DATE_TIME_FORMAT | |
@datestamp = amzdate.to_date.strftime DATE_FORMAT | |
@credential_scope = [datestamp, REGION, SERVICE, AWS4_REQUEST].join("/") | |
@host = Resolv::DNS.new.getresource(RESOURCE, Resolv::DNS::Resource::IN::CNAME).name.to_s | |
end | |
def perform | |
return parse_data if data_present? | |
create_error_log | |
false | |
rescue Faraday::ConnectionFailed, ActiveSupport::JSON.parse_error | |
Rails.logger.info "#{self.class}: Connect to api failed" | |
false | |
end | |
private | |
attr_reader :amzdate, :datestamp, :credential_scope, :host | |
def parse_data | |
response_body["hits"]["hit"].map{|resource| resource["fields"].symbolize_keys} | |
end | |
def data_present? | |
response_body && response_body["error"].blank? | |
end | |
def response_body | |
@response_body ||= JSON.parse response.body if response.present? | |
end | |
def create_error_log | |
if response_body && response_body["error"].present? | |
Rails.logger.info "#{self.class}: #{response_body["error"]["message"]}" | |
else | |
Rails.logger.info "#{self.class}: Client error" | |
end | |
end | |
def response | |
@response ||= faraday_connect.get do |request| | |
request.headers["X-Amz-Date"] = amzdate | |
request.headers["Authorization"] = auth_header | |
end | |
end | |
def faraday_connect | |
Faraday.new url: request_url do |faraday| | |
faraday.request :url_encoded | |
faraday.adapter Faraday.default_adapter | |
end | |
end | |
def request_url | |
"https://#{host}#{CANONICAL_URI}?#{request_parameters}" | |
end | |
def auth_header | |
"#{ALGORITHM} Credential=#{ENV["API_ACCESS_KEY"]}/#{credential_scope}, | |
SignedHeaders=#{SIGNED_HEADERS}, Signature=#{signature}" | |
end | |
def signature | |
string_to_sign = [ALGORITHM, amzdate, credential_scope, request_hash].join("\n") | |
OpenSSL::HMAC.hexdigest SHA256, signature_key, string_to_sign | |
end | |
def signature_key | |
k_date = OpenSSL::HMAC.digest SHA256, "AWS4#{ENV["API_SECRET_KEY"]}", datestamp | |
k_region = OpenSSL::HMAC.digest SHA256, k_date, REGION | |
k_service = OpenSSL::HMAC.digest SHA256, k_region, SERVICE | |
OpenSSL::HMAC.digest SHA256, k_service, AWS4_REQUEST | |
end | |
def request_hash | |
canonical_headers = "host:#{host}\nx-amz-date:#{amzdate}\n" | |
payload_hash = Digest::SHA256.hexdigest("") | |
canonical_request = [METHOD, CANONICAL_URI, request_parameters, | |
canonical_headers, SIGNED_HEADERS, payload_hash].join("\n") | |
Digest::SHA256.hexdigest canonical_request | |
end | |
def request_parameters | |
[ | |
{"Action": "DescribeRegions"}, | |
{"Version": "2013-10-15"} | |
].map(&:to_query).join("&") | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment