1: -module(aws_signature_v4_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("common_test/include/ct.hrl"). 5: -include_lib("eunit/include/eunit.hrl"). 6: 7: -define(ACCESS_KEY_ID, "AKIAIAOAONIULXQGMOUA"). 8: -define(SECRET_ACCESS_KEY, "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"). 9: -define(REGION, "eu-west-1"). 10: -define(HOST, "s3-eu-west-1.amazonaws.com"). 11: -define(DATE, {{2014, 1, 2}, {0, 0, 0}}). 12: -define(BUCKET, "testbucket"). 13: -define(EXPIRATION_TIME, 3600). 14: 15: all() -> [ 16: signs_basic_url, 17: signs_urls_with_additional_headers, 18: handles_unicode_path, 19: formats_datetime_timestamp, 20: pads_datetime_timestamp, 21: formats_date_timestamp, 22: pads_date_timestamp, 23: composes_scope_from_values, 24: does_not_encode_unreserved_set, 25: does_encode_reserved_set, 26: does_encode_utf8_characters 27: ]. 28: 29: %% Tests 30: 31: %% sign 32: 33: signs_basic_url(Config) -> 34: Path = <<"/", ?BUCKET, "/test/key">>, 35: Signature = sign(Path, ?config(queries, Config), ?config(headers, Config)), 36: ?assertEqual(<<"85bf825e9b67d7838f354b74d97253317584919d8a77b7b50675c2005f003f1f">>, 37: Signature). 38: 39: signs_urls_with_additional_headers(Config) -> 40: Headers = maps:put(<<"content-length">>, <<"12">>, ?config(headers, Config)), 41: Queries = queries(Headers, ?DATE, ?EXPIRATION_TIME), 42: Path = <<"/", ?BUCKET, "/test/key">>, 43: Signature = sign(Path, Queries, Headers), 44: ?assertEqual(<<"0ac4c8569e7a9e50af1a34db59678b4f23b98ff9734719b2727fe2e971626d72">>, 45: Signature). 46: 47: handles_unicode_path(Config) -> 48: Datetime = {{2017, 1, 3}, {14, 47, 32}}, 49: Queries = queries(headers(), Datetime, 900), 50: Path = <<"/♠♣♥♦/test/key"/utf8>>, 51: Signature = sign(Path, Queries, ?config(headers, Config), Datetime), 52: ?assertEqual(<<"384a2f2a8f9f799abdd633971e48f9e100236d6be25a66513b59ea9dea43e76b">>, 53: Signature). 54: 55: %% datetime_iso8601 56: 57: formats_datetime_timestamp(_Config) -> 58: Timestamp = {{2016, 12, 10}, {21, 13, 20}}, 59: ?assertEqual(<<"20161210T211320Z">>, aws_signature_v4:datetime_iso8601(Timestamp)). 60: 61: pads_datetime_timestamp(_Config) -> 62: Timestamp = {{2016, 2, 1}, {1, 3, 0}}, 63: ?assertEqual(<<"20160201T010300Z">>, aws_signature_v4:datetime_iso8601(Timestamp)). 64: 65: %% date_iso8601 66: 67: formats_date_timestamp(_Config) -> 68: Timestamp = {{1234, 12, 31}, {0, 0, 0}}, 69: ?assertEqual(<<"12341231">>, aws_signature_v4:date_iso8601(Timestamp)). 70: 71: pads_date_timestamp(_Config) -> 72: Timestamp = {{1234, 2, 3}, {0, 0, 0}}, 73: ?assertEqual(<<"12340203">>, aws_signature_v4:date_iso8601(Timestamp)). 74: 75: %% compose_scope 76: 77: composes_scope_from_values(_Config) -> 78: Timestamp = {{1234, 2, 3}, {0, 0, 0}}, 79: Scope = aws_signature_v4:compose_scope(Timestamp, <<"Region">>, <<"Service">>), 80: ?assertEqual(<<"12340203/Region/Service/aws4_request">>, Scope). 81: 82: %% uri_encode 83: 84: does_not_encode_unreserved_set(_Config) -> 85: Uncoded = <<"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~~">>, 86: ?assertEqual(Uncoded, aws_signature_v4:uri_encode(Uncoded)). 87: 88: does_encode_reserved_set(_Config) -> 89: Uncoded = <<"!*'();:@&=+$,/?%#[] ">>, 90: Encoded = <<"%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D%20">>, 91: ?assertEqual(Encoded, aws_signature_v4:uri_encode(Uncoded)). 92: 93: does_encode_utf8_characters(_Config) -> 94: Uncoded = <<"zażółć gęślą jaźń"/utf8>>, 95: Encoded = <<"za%C5%BC%C3%B3%C5%82%C4%87%20g%C4%99%C5%9Bl%C4%85%20ja%C5%BA%C5%84">>, 96: ?assertEqual(Encoded, aws_signature_v4:uri_encode(Uncoded)). 97: 98: %% Fixtures 99: 100: init_per_testcase(_, Config) -> 101: [{headers, headers()}, {queries, queries(headers(), ?DATE, ?EXPIRATION_TIME)} | Config]. 102: 103: %% Helpers 104: 105: headers() -> 106: #{<<"host">> => <<?HOST>>}. 107: 108: queries(Headers, UTCDateTime, ExpirationTime) -> 109: Scope = aws_signature_v4:compose_scope(UTCDateTime, <<?REGION>>, <<"s3">>), 110: #{ 111: <<"X-Amz-Algorithm">> => <<"AWS4-HMAC-SHA256">>, 112: <<"X-Amz-Credential">> => <<?ACCESS_KEY_ID, "/", Scope/binary>>, 113: <<"X-Amz-Date">> => aws_signature_v4:datetime_iso8601(UTCDateTime), 114: <<"X-Amz-Expires">> => integer_to_binary(ExpirationTime), 115: <<"X-Amz-SignedHeaders">> => join_headers(Headers) 116: }. 117: 118: join_headers(Headers) -> 119: maps:fold( 120: fun 121: (Key, _, <<>>) -> Key; 122: (Key, _, Acc) -> <<Acc/binary, ";", Key/binary>> 123: end, <<>>, Headers). 124: 125: sign(URI, Queries, Headers) -> 126: sign(URI, Queries, Headers, ?DATE). 127: 128: sign(URI, Queries, Headers, UTCDateTime) -> 129: aws_signature_v4:sign(<<"PUT">>, URI, Queries, Headers, UTCDateTime, 130: <<?REGION>>, <<"s3">>, <<?SECRET_ACCESS_KEY>>).