-
Notifications
You must be signed in to change notification settings - Fork 533
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #41 from CargoSense/interalize_auth
Internalize authentication code for easier adaptation.
- Loading branch information
Showing
6 changed files
with
157 additions
and
47 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
defmodule ExAws.Auth do | ||
import ExAws.Auth.Utils | ||
alias Timex.DateFormat | ||
|
||
def headers(http_method, url, service, config, headers, body) do | ||
now = %{Timex.Date.now | ms: 0} | ||
headers = [ | ||
{"host", URI.parse(url).host}, | ||
{"x-amz-date", amz_date(now)} | | ||
headers | ||
] | ||
|
||
auth_header = auth_header( | ||
config[:access_key_id], | ||
config[:secret_access_key], | ||
http_method |> method_string, | ||
url, | ||
config[:region], | ||
service |> service_name, | ||
headers, | ||
body, | ||
now) | ||
|
||
[{"Authorization", auth_header} | headers ] | ||
end | ||
|
||
def auth_header(access_key, secret_key, http_method, url, region, service, headers, body, now) do | ||
date = DateFormat.format!(now, "%Y%m%d", :strftime) | ||
scope = "#{date}/#{region}/#{service}/aws4_request" | ||
|
||
signing_key = build_signing_key(secret_key, date, region, service) | ||
|
||
signature = http_method | ||
|> build_canonical_request(url, headers, body) | ||
|> build_string_to_sign(now, scope) | ||
|> sign_with(signing_key) | ||
|
||
[ | ||
"AWS4-HMAC-SHA256 Credential=", access_key, "/", scope, ",", | ||
"SignedHeaders=", signed_headers(headers), ",", | ||
"Signature=", signature | ||
] |> IO.iodata_to_binary | ||
end | ||
|
||
def build_signing_key(secret_key, date, region, service) do | ||
["AWS4", secret_key] | ||
|> hmac_sha256(date) | ||
|> hmac_sha256(region) | ||
|> hmac_sha256(service) | ||
|> hmac_sha256("aws4_request") | ||
end | ||
|
||
def build_string_to_sign(canonical_request, now, scope) do | ||
timestamp = now |> ExAws.Auth.Utils.amz_date | ||
hashed_canonical_request = ExAws.Auth.Utils.hash_sha256(canonical_request) | ||
|
||
[ | ||
"AWS4-HMAC-SHA256", "\n", | ||
timestamp, "\n", | ||
scope, "\n", | ||
hashed_canonical_request | ||
] |> IO.iodata_to_binary | ||
end | ||
|
||
def sign_with(string_to_sign, signing_key) do | ||
signing_key | ||
|> hmac_sha256(string_to_sign) | ||
|> bytes_to_hex | ||
end | ||
|
||
def signed_headers(headers) do | ||
headers | ||
|> Enum.map(fn({k, _}) -> String.downcase(k) end) | ||
|> Enum.sort(&(&1 < &2)) | ||
|> Enum.join(";") | ||
end | ||
|
||
def build_canonical_request(http_method, url, headers, body) do | ||
uri = URI.parse(url) | ||
http_method = http_method |> String.upcase | ||
|
||
query_params = uri.query |> canonical_query_params | ||
|
||
headers = headers |> canonical_headers | ||
header_string = headers | ||
|> Enum.map(fn {k, v} -> "#{k}:#{v}" end) | ||
|> Enum.join("\n") | ||
|
||
signed_headers_list = headers | ||
|> Keyword.keys | ||
|> Enum.join(";") | ||
|
||
[ | ||
http_method, "\n", | ||
uri.path, "\n", | ||
query_params, "\n", | ||
header_string, "\n", | ||
"\n", | ||
signed_headers_list, "\n", | ||
ExAws.Auth.Utils.hash_sha256(body) | ||
] |> IO.iodata_to_binary | ||
end | ||
|
||
def canonical_query_params(nil), do: "" | ||
def canonical_query_params(params) do | ||
params | ||
|> URI.query_decoder | ||
|> Enum.sort(fn {k1, _}, {k2, _} -> k1 < k2 end) | ||
|> URI.encode_query | ||
end | ||
|
||
def canonical_headers(headers) do | ||
headers | ||
|> Enum.map(fn {k, v} -> {String.downcase(k), String.strip(v)} end) | ||
|> Enum.sort(fn {k1, _}, {k2, _} -> k1 < k2 end) | ||
end | ||
end |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
defmodule ExAws.Auth.Utils do | ||
def amz_date(now) do | ||
now | ||
|> Timex.DateFormat.format!("{ISOz}") | ||
|> String.replace("-", "") | ||
|> String.replace(":", "") | ||
end | ||
|
||
def hash_sha256(data) do | ||
:sha256 | ||
|> :crypto.hash(data) | ||
|> bytes_to_hex | ||
end | ||
|
||
def hmac_sha256(key, data) do | ||
:crypto.hmac(:sha256, key, data) | ||
end | ||
|
||
def bytes_to_hex(bytes) do | ||
bytes | ||
|> Base.encode16 | ||
|> String.downcase | ||
end | ||
|
||
def service_name(service), do: service |> Atom.to_string | ||
|
||
def method_string(method) do | ||
method | ||
|> Atom.to_string | ||
|> String.upcase | ||
end | ||
end |
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,13 @@ | ||
%{"aws_auth": {:hex, :aws_auth, "0.2.3"}, | ||
"earmark": {:hex, :earmark, "0.1.15"}, | ||
"ex_doc": {:hex, :ex_doc, "0.7.2"}, | ||
%{"earmark": {:hex, :earmark, "0.1.17"}, | ||
"ex_doc": {:hex, :ex_doc, "0.7.3"}, | ||
"hackney": {:hex, :hackney, "1.1.0"}, | ||
"httpoison": {:hex, :httpoison, "0.6.2"}, | ||
"httpotion": {:hex, :httpotion, "2.0.0"}, | ||
"ibrowse": {:git, "git://github.com/cmullaparthi/ibrowse.git", "d2e369ff42666c3574b8b7ec26f69027895c4d94", [tag: "v4.1.1"]}, | ||
"idna": {:hex, :idna, "1.0.2"}, | ||
"jsx": {:hex, :jsx, "2.5.3"}, | ||
"mixunit": {:hex, :mixunit, "0.9.1"}, | ||
"mixunit": {:hex, :mixunit, "0.9.2"}, | ||
"poison": {:hex, :poison, "1.2.1"}, | ||
"ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.4"}, | ||
"ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.5"}, | ||
"sweet_xml": {:hex, :sweet_xml, "0.2.1"}, | ||
"timex": {:hex, :timex, "0.13.4"}} |