diff --git a/Makefile b/Makefile index 9a21980..614a6e8 100644 --- a/Makefile +++ b/Makefile @@ -9,4 +9,4 @@ clean: @rebar3 clean -a test: - @rebar3 eunit + @rebar3 test diff --git a/elvis.config b/elvis.config new file mode 100644 index 0000000..d908358 --- /dev/null +++ b/elvis.config @@ -0,0 +1,32 @@ +[ + { + elvis, + [ + {config, + [#{dirs => ["src"], + filter => "*.erl", + ruleset => erl_files, + rules => [ + {elvis_style, line_length, #{limit => 100}}, + {elvis_style, nesting_level, #{level => 4}}, + %% the default rule included {right, ","} and not {right, "=>"} or {left, "=>"} + { + elvis_style, + operator_spaces, + #{rules => [{right, "++"}, {left, "++"}, {right, "=>"}, {left, "=>"}]} + } + ] + }, + #{dirs => ["."], + filter => "rebar.config", + ruleset => rebar_config + }, + #{dirs => ["."], + filter => "elvis.config", + ruleset => elvis_config + } + ] + } + ] + } +]. diff --git a/include/erliam.hrl b/include/erliam.hrl index 25337b1..55ae24e 100644 --- a/include/erliam.hrl +++ b/include/erliam.hrl @@ -2,8 +2,8 @@ -type iso_datetime() :: string(). % "YYYY-MM-DDTHH:MM:SSZ" -record(credentials, { - expiration :: iso_datetime(), - security_token :: string(), % required when using temporary credentials + expiration :: undefined | iso_datetime(), + security_token :: undefined | string(), % required when using temporary credentials secret_access_key :: string(), - access_key_id :: string() + access_key_id :: string() }). diff --git a/old.rebar.config b/old.rebar.config new file mode 100644 index 0000000..aab45fe --- /dev/null +++ b/old.rebar.config @@ -0,0 +1,5 @@ +{erl_opts, [debug_info]}. + +{deps, [ + {jiffy, ".*", {git, "https://github.com/davisp/jiffy.git", {tag, "0.15.2"}}} +]}. diff --git a/rebar.config b/rebar.config index 5af1aad..ea58813 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,44 @@ % -*- mode: erlang -*- -{erl_opts, [debug_info]}. +{erl_opts, [ + warn_unused_vars, + warn_export_all, + warn_shadow_vars, + warn_unused_import, + warn_unused_function, + warn_bif_clash, + warn_unused_record, + warn_deprecated_function, + warn_obsolete_guard, + strict_validation, + warn_export_vars, + warn_exported_vars, + debug_info +]}. -{deps, [ - {jiffy, ".*", - {git, "https://github.com/davisp/jiffy.git", {tag, "0.14.11"}}} - ]}. +{minimum_otp_vsn, "20"}. + +{cover_enabled, true}. + +{cover_opts, [verbose]}. + +{deps, [{jiffy, "0.15.2"}]}. + +{dialyzer, [ + {warnings, [no_return, error_handling]}, + {plt_apps, top_level_deps}, + {plt_extra_apps, []}, + {plt_location, local}, + {base_plt_apps, [erts, stdlib, kernel, inets, crypto, ssl]}, + {base_plt_location, global} +]}. + +{xref_checks,[ + undefined_function_calls, + locals_not_used, + deprecated_function_calls, + deprecated_functions +]}. + +{alias, [{test, [xref, dialyzer, lint, eunit, cover]}]}. + +{plugins, [{rebar3_lint, "0.1.10"}]}. diff --git a/rebar.config.script b/rebar.config.script new file mode 100644 index 0000000..706ffdd --- /dev/null +++ b/rebar.config.script @@ -0,0 +1,8 @@ +IsRebar3 = erlang:function_exported(rebar3, main, 1), +case IsRebar3 of + false -> + {ok, OldConfig} = file:consult("old.rebar.config"), + OldConfig; + true -> + CONFIG +end. diff --git a/src/awsv4.erl b/src/awsv4.erl index 46cc2ca..0a166ad 100644 --- a/src/awsv4.erl +++ b/src/awsv4.erl @@ -145,14 +145,12 @@ long_term_credentials(AccessKeyId, SecretAccessKey) -> -spec credentials_from_plist(list({expiration | token | access_key_id | secret_access_key, iodata() | undefined})) -> credentials(). credentials_from_plist(Plist) -> - lists:foldl(fun ({Name, N}, Acc) -> - setelement(N, Acc, erliam_util:getkey(Name, Plist)) - end, - #credentials{}, - [{expiration, #credentials.expiration}, - {token, #credentials.security_token}, - {access_key_id, #credentials.access_key_id}, - {secret_access_key, #credentials.secret_access_key}]). + #credentials{ + expiration = erliam_util:getkey(expiration, Plist), + security_token = erliam_util:getkey(token, Plist), + access_key_id = erliam_util:getkey(access_key_id, Plist), + secret_access_key = erliam_util:getkey(secret_access_key, Plist) + }. %%%% INTERNAL FUNCTIONS @@ -259,75 +257,104 @@ basic_headers_test() -> "20140629T022822Z", "Kinesis_20131202.ListStreams", "POST", "/", [], #{}, "something"]), - Expected = flattened([{"authorization", - ["AWS4-HMAC-SHA256 Credential=accesskey/20140629/us-east-1/kinesis/aws4_request", - ",SignedHeaders=host;x-amz-date;x-amz-security-token;x-amz-target", - ",Signature=847fee48568298911772356fe332443bf2679c48fd42695a84aaa0d0e7f28c66"]}, - {"x-amz-content-sha256", - "3fc9b689459d738f8c88a3a48aa9e33542016b7a4052e001aaa536fca74813cb"}, - {"host","kinesis.us-east-1.amazonaws.com"}, - {"x-amz-date","20140629T022822Z"}, - {"x-amz-security-token","securitytoken"}, - {"x-amz-target","Kinesis_20131202.ListStreams"}]), + Expected = flattened([ + {"authorization", [ + "AWS4-HMAC-SHA256 Credential=accesskey/20140629/us-east-1/kinesis/aws4_request", + ",SignedHeaders=host;x-amz-date;x-amz-security-token;x-amz-target", + ",Signature=847fee48568298911772356fe332443bf2679c48fd42695a84aaa0d0e7f28c66" + ]}, + {"x-amz-content-sha256", + "3fc9b689459d738f8c88a3a48aa9e33542016b7a4052e001aaa536fca74813cb"}, + {"host","kinesis.us-east-1.amazonaws.com"}, + {"x-amz-date","20140629T022822Z"}, + {"x-amz-security-token","securitytoken"}, + {"x-amz-target","Kinesis_20131202.ListStreams"} + ]), ?assertEqual(Expected, Actual). aws4_example1_test() -> %% get-vanilla-query-order-key-case from aws4 test suite - Actual = flattened_headers([#credentials{secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", - access_key_id = "AKIDEXAMPLE"}, - #{aws_date => "20150830T123600Z", - service => "service", - region => "us-east-1", - host => "example.amazonaws.com", - query_params => #{"Param2" => "value2", - "Param1" => "value1"}}]), - Expected = flattened([{"authorization", - ["AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request", - ",SignedHeaders=host;x-amz-date", - ",Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500"]}, - {"x-amz-content-sha256", - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, - {"host", "example.amazonaws.com"}, - {"x-amz-date", "20150830T123600Z"}]), + Actual = flattened_headers([ + #credentials{ + secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", + access_key_id = "AKIDEXAMPLE" + }, + #{ + aws_date => "20150830T123600Z", + service => "service", + region => "us-east-1", + host => "example.amazonaws.com", + query_params => #{"Param2" => "value2", "Param1" => "value1"} + } + ]), + Expected = flattened([ + {"authorization", [ + "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request", + ",SignedHeaders=host;x-amz-date", + ",Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500" + ]}, + {"x-amz-content-sha256", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + {"host", "example.amazonaws.com"}, + {"x-amz-date", "20150830T123600Z"} + ]), ?assertEqual(Expected, Actual). aws4_example2_test() -> %% post-vanilla-query from aws4 test suite - Actual = flattened_headers([#credentials{secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", - access_key_id = "AKIDEXAMPLE"}, - #{aws_date => "20150830T123600Z", - service => "service", - region => "us-east-1", - host => "example.amazonaws.com", - method => "POST", - query_params => #{"Param1" => "value1"}}]), - Expected = flattened([{"authorization", - ["AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request", - ",SignedHeaders=host;x-amz-date", - ",Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11"]}, - {"x-amz-content-sha256", - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, - {"host", "example.amazonaws.com"}, - {"x-amz-date", "20150830T123600Z"}]), + Actual = flattened_headers([ + #credentials{ + secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", + access_key_id = "AKIDEXAMPLE" + }, + #{ + aws_date => "20150830T123600Z", + service => "service", + region => "us-east-1", + host => "example.amazonaws.com", + method => "POST", + query_params => #{"Param1" => "value1"} + } + ]), + Expected = flattened([ + {"authorization", [ + "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request", + ",SignedHeaders=host;x-amz-date", + ",Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11" + ]}, + {"x-amz-content-sha256", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + {"host", "example.amazonaws.com"}, + {"x-amz-date", "20150830T123600Z"} + ]), ?assertEqual(Expected, Actual). aws4_example3_test() -> %% get-unreserved from aws4 test suite - Actual = flattened_headers([#credentials{secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", - access_key_id = "AKIDEXAMPLE"}, - #{aws_date => "20150830T123600Z", - service => "service", - region => "us-east-1", - host => "example.amazonaws.com", - path => "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"}]), - Expected = flattened([{"authorization", - ["AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request", - ",SignedHeaders=host;x-amz-date", - ",Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f"]}, - {"x-amz-content-sha256", - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, - {"host", "example.amazonaws.com"}, - {"x-amz-date", "20150830T123600Z"}]), + Actual = flattened_headers([ + #credentials{ + secret_access_key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", + access_key_id = "AKIDEXAMPLE" + }, + #{ + aws_date => "20150830T123600Z", + service => "service", + region => "us-east-1", + host => "example.amazonaws.com", + path => "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + } + ]), + Expected = flattened([ + {"authorization", [ + "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request", + ",SignedHeaders=host;x-amz-date", + ",Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f" + ]}, + {"x-amz-content-sha256", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + {"host", "example.amazonaws.com"}, + {"x-amz-date", "20150830T123600Z"} + ]), ?assertEqual(Expected, Actual). diff --git a/src/erliam_srv.erl b/src/erliam_srv.erl index ce750d4..026dda0 100644 --- a/src/erliam_srv.erl +++ b/src/erliam_srv.erl @@ -48,9 +48,13 @@ invalidate() -> init([]) -> ets:new(?TAB, [named_table, public, {read_concurrency, true}]), - R = update_credentials(), - timer:send_interval(1000, refresh), - {R, #state{}}. + case update_credentials() of + {error, Error} -> + {stop, Error}; + ok -> + timer:send_interval(1000, refresh), + {ok, #state{}} + end. handle_call(invalidate, _From, State) -> update_credentials(), @@ -117,7 +121,8 @@ parse_exptime([Y1,Y2,Y3,Y4, $-, Mon1,Mon2, $-, D1,D2, $T, H1,H2, $:, Min1,Min2, {list_to_integer([H1,H2]), list_to_integer([Min1,Min2]), list_to_integer([S1,S2])}}; -parse_exptime([Y1,Y2,Y3,Y4, $-, Mon1,Mon2, $-, D1,D2, $T, H1,H2, $:, Min1,Min2, $:, S1,S2, $.,_,_,_, $Z]) -> +parse_exptime( + [Y1,Y2,Y3,Y4, $-, Mon1,Mon2, $-, D1,D2, $T, H1,H2, $:, Min1,Min2, $:, S1,S2, $.,_,_,_, $Z]) -> parse_exptime([Y1,Y2,Y3,Y4, $-, Mon1,Mon2, $-, D1,D2, $T, H1,H2, $:, Min1,Min2, $:, S1,S2, $Z]). diff --git a/src/imds.erl b/src/imds.erl index bfd9805..6d716ef 100644 --- a/src/imds.erl +++ b/src/imds.erl @@ -139,20 +139,27 @@ metadata_response_to_token_proplist(Body) -> {<<"AccessKeyId">>, access_key_id}, {<<"SecretAccessKey">>, secret_access_key}, {<<"Token">>, token}], + case get_code(Body) of + {success, Plist} -> + lists:foldl( + fun({Element, Value}, Acc) -> + case erliam_util:getkey(Element, Targets) of + undefined -> + Acc; + AtomName -> + [{AtomName, binary_to_list(Value)} | Acc] + end + end, [], Plist); + Error -> + Error + end. + +get_code(Body) -> case jiffy:decode(Body) of {Plist} -> case erliam_util:getkey(<<"Code">>, Plist) of <<"Success">> -> - lists:foldl(fun ({Element, Value}, Acc) -> - case erliam_util:getkey(Element, Targets) of - undefined -> - Acc; - AtomName -> - [{AtomName, binary_to_list(Value)} | Acc] - end - end, - [], - Plist); + {success, Plist}; _ -> {error, failed_token_response} end; @@ -194,7 +201,11 @@ imds_tokens(Suffix) -> -include_lib("eunit/include/eunit.hrl"). metadata_response_to_proplist_test() -> - Body = <<"{\"Code\":\"Success\",\"LastUpdated\":\"2014-10-17T15:17:07-07:00\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"XYZZY\",\"SecretAccessKey\":\"FLOOBLE\",\"Token\":\"BAZZLE\",\"Expiration\":\"2014-10-18T09:00:30Z\"}">>, + Body = << + "{\"Code\":\"Success\",\"LastUpdated\":\"2014-10-17T15:17:07-07:00\"," + "\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"XYZZY\",\"SecretAccessKey\":" + "\"FLOOBLE\",\"Token\":\"BAZZLE\",\"Expiration\":\"2014-10-18T09:00:30Z\"}" + >>, Result = metadata_response_to_token_proplist(Body), Expected = [{expiration, "2014-10-18T09:00:30Z"}, {access_key_id, "XYZZY"},