feat(dynamo): accept wrapped secrets as passwords

This commit is contained in:
Andrew Mayorov 2023-11-09 17:26:04 +07:00
parent b9b506c4a2
commit 93eaf0caee
No known key found for this signature in database
GPG Key ID: 2837C62ACFBFED5D
4 changed files with 45 additions and 44 deletions

View File

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

View File

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

View File

@ -20,8 +20,7 @@
handle_cast/2, handle_cast/2,
handle_info/2, handle_info/2,
terminate/2, terminate/2,
code_change/3, code_change/3
format_status/2
]). ]).
-ifdef(TEST). -ifdef(TEST).
@ -62,11 +61,13 @@ start_link(Options) ->
%% Initialize dynamodb data bridge %% Initialize dynamodb data bridge
init(#{ init(#{
aws_access_key_id := AccessKeyID, aws_access_key_id := AccessKeyID,
aws_secret_access_key := SecretAccessKey, aws_secret_access_key := Secret,
host := Host, host := Host,
port := Port, port := Port,
schema := Schema 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), erlcloud_ddb2:configure(AccessKeyID, SecretAccessKey, Host, Port, Schema),
{ok, #{}}. {ok, #{}}.
@ -101,13 +102,6 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
-spec format_status(
Opt :: normal | terminate,
Status :: list()
) -> Status :: term().
format_status(_Opt, Status) ->
Status.
%%%=================================================================== %%%===================================================================
%%% Internal functions %%% Internal functions
%%%=================================================================== %%%===================================================================
@ -184,3 +178,8 @@ convert2binary(Value) when is_list(Value) ->
unicode:characters_to_binary(Value); unicode:characters_to_binary(Value);
convert2binary(Value) when is_map(Value) -> convert2binary(Value) when is_map(Value) ->
emqx_utils_json:encode(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).

View File

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