Explorar o código

feat(dynamo): accept wrapped secrets as passwords

Andrew Mayorov %!s(int64=2) %!d(string=hai) anos
pai
achega
93eaf0caee

+ 1 - 1
apps/emqx_bridge_dynamo/src/emqx_bridge_dynamo.app.src

@@ -1,6 +1,6 @@
 {application, emqx_bridge_dynamo, [
     {description, "EMQX Enterprise Dynamo Bridge"},
-    {vsn, "0.1.3"},
+    {vsn, "0.1.4"},
     {registered, []},
     {applications, [
         kernel,

+ 5 - 8
apps/emqx_bridge_dynamo/src/emqx_bridge_dynamo_connector.erl

@@ -45,12 +45,10 @@ fields(config) ->
                 #{required => true, desc => ?DESC("aws_access_key_id")}
             )},
         {aws_secret_access_key,
-            mk(
-                binary(),
+            emqx_schema_secret:mk(
                 #{
                     required => true,
-                    desc => ?DESC("aws_secret_access_key"),
-                    sensitive => true
+                    desc => ?DESC("aws_secret_access_key")
                 }
             )},
         {pool_size, fun emqx_connector_schema_lib:pool_size/1},
@@ -89,7 +87,7 @@ on_start(
             host => Host,
             port => Port,
             aws_access_key_id => to_str(AccessKeyID),
-            aws_secret_access_key => to_str(SecretAccessKey),
+            aws_secret_access_key => SecretAccessKey,
             schema => Schema
         }},
         {pool_size, PoolSize}
@@ -182,9 +180,8 @@ do_query(
     end.
 
 connect(Opts) ->
-    Options = proplists:get_value(config, Opts),
-    {ok, _Pid} = Result = emqx_bridge_dynamo_connector_client:start_link(Options),
-    Result.
+    Config = proplists:get_value(config, Opts),
+    {ok, _Pid} = emqx_bridge_dynamo_connector_client:start_link(Config).
 
 parse_template(Config) ->
     Templates =

+ 9 - 10
apps/emqx_bridge_dynamo/src/emqx_bridge_dynamo_connector_client.erl

@@ -20,8 +20,7 @@
     handle_cast/2,
     handle_info/2,
     terminate/2,
-    code_change/3,
-    format_status/2
+    code_change/3
 ]).
 
 -ifdef(TEST).
@@ -62,11 +61,13 @@ start_link(Options) ->
 %% Initialize dynamodb data bridge
 init(#{
     aws_access_key_id := AccessKeyID,
-    aws_secret_access_key := SecretAccessKey,
+    aws_secret_access_key := Secret,
     host := Host,
     port := Port,
     schema := Schema
 }) ->
+    %% TODO: teach `erlcloud` to to accept 0-arity closures as passwords.
+    SecretAccessKey = to_str(emqx_secret:unwrap(Secret)),
     erlcloud_ddb2:configure(AccessKeyID, SecretAccessKey, Host, Port, Schema),
     {ok, #{}}.
 
@@ -101,13 +102,6 @@ terminate(_Reason, _State) ->
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
--spec format_status(
-    Opt :: normal | terminate,
-    Status :: list()
-) -> Status :: term().
-format_status(_Opt, Status) ->
-    Status.
-
 %%%===================================================================
 %%% Internal functions
 %%%===================================================================
@@ -184,3 +178,8 @@ convert2binary(Value) when is_list(Value) ->
     unicode:characters_to_binary(Value);
 convert2binary(Value) when is_map(Value) ->
     emqx_utils_json:encode(Value).
+
+to_str(List) when is_list(List) ->
+    List;
+to_str(Bin) when is_binary(Bin) ->
+    erlang:binary_to_list(Bin).

+ 30 - 25
apps/emqx_bridge_dynamo/test/emqx_bridge_dynamo_SUITE.erl

@@ -22,8 +22,6 @@
 -define(BATCH_SIZE, 10).
 -define(PAYLOAD, <<"HELLO">>).
 
--define(GET_CONFIG(KEY__, CFG__), proplists:get_value(KEY__, CFG__)).
-
 %% How to run it locally (all commands are run in $PROJ_ROOT dir):
 %% run ct in docker container
 %% run script:
@@ -84,7 +82,9 @@ end_per_group(_Group, _Config) ->
     ok.
 
 init_per_suite(Config) ->
-    Config.
+    SecretFile = filename:join(?config(priv_dir, Config), "secret"),
+    ok = file:write_file(SecretFile, <<?SECRET_ACCESS_KEY>>),
+    [{dynamo_secretfile, SecretFile} | Config].
 
 end_per_suite(_Config) ->
     emqx_mgmt_api_test_util:end_suite(),
@@ -158,32 +158,35 @@ common_init(ConfigT) ->
     end.
 
 dynamo_config(BridgeType, Config) ->
-    Port = integer_to_list(?GET_CONFIG(port, Config)),
-    Url = "http://" ++ ?GET_CONFIG(host, Config) ++ ":" ++ Port,
+    Host = ?config(host, Config),
+    Port = ?config(port, Config),
     Name = atom_to_binary(?MODULE),
-    BatchSize = ?GET_CONFIG(batch_size, Config),
-    QueryMode = ?GET_CONFIG(query_mode, Config),
+    BatchSize = ?config(batch_size, Config),
+    QueryMode = ?config(query_mode, Config),
+    SecretFile = ?config(dynamo_secretfile, Config),
     ConfigString =
         io_lib:format(
-            "bridges.~s.~s {\n"
-            "  enable = true\n"
-            "  url = ~p\n"
-            "  table = ~p\n"
-            "  aws_access_key_id = ~p\n"
-            "  aws_secret_access_key = ~p\n"
-            "  resource_opts = {\n"
-            "    request_ttl = 500ms\n"
-            "    batch_size = ~b\n"
-            "    query_mode = ~s\n"
-            "  }\n"
-            "}",
+            "bridges.~s.~s {"
+            "\n   enable = true"
+            "\n   url = \"http://~s:~p\""
+            "\n   table = ~p"
+            "\n   aws_access_key_id = ~p"
+            "\n   aws_secret_access_key = ~p"
+            "\n   resource_opts = {"
+            "\n     request_ttl = 500ms"
+            "\n     batch_size = ~b"
+            "\n     query_mode = ~s"
+            "\n   }"
+            "\n }",
             [
                 BridgeType,
                 Name,
-                Url,
+                Host,
+                Port,
                 ?TABLE,
                 ?ACCESS_KEY_ID,
-                ?SECRET_ACCESS_KEY,
+                %% NOTE: using file-based secrets with HOCON configs
+                "file://" ++ SecretFile,
                 BatchSize,
                 QueryMode
             ]
@@ -252,8 +255,8 @@ delete_table(_Config) ->
     erlcloud_ddb2:delete_table(?TABLE_BIN).
 
 setup_dynamo(Config) ->
-    Host = ?GET_CONFIG(host, Config),
-    Port = ?GET_CONFIG(port, Config),
+    Host = ?config(host, Config),
+    Port = ?config(port, Config),
     erlcloud_ddb2:configure(?ACCESS_KEY_ID, ?SECRET_ACCESS_KEY, Host, Port, ?SCHEMA).
 
 directly_setup_dynamo() ->
@@ -313,7 +316,9 @@ t_setup_via_http_api_and_publish(Config) ->
     PgsqlConfig0 = ?config(dynamo_config, Config),
     PgsqlConfig = PgsqlConfig0#{
         <<"name">> => Name,
-        <<"type">> => BridgeType
+        <<"type">> => BridgeType,
+        %% NOTE: using literal secret with HTTP API requests.
+        <<"aws_secret_access_key">> => <<?SECRET_ACCESS_KEY>>
     },
     ?assertMatch(
         {ok, _},
@@ -400,7 +405,7 @@ t_simple_query(Config) ->
     ),
     Request = {get_item, {<<"id">>, <<"not_exists">>}},
     Result = query_resource(Config, Request),
-    case ?GET_CONFIG(batch_size, Config) of
+    case ?config(batch_size, Config) of
         ?BATCH_SIZE ->
             ?assertMatch({error, {unrecoverable_error, {invalid_request, _}}}, Result);
         1 ->