feat(pgsql): accept wrapped secrets as passwords

That are coming from `emqx_schema_secret`. Also adapt pgsql-related
connectors.
This commit is contained in:
Andrew Mayorov 2023-11-07 15:35:15 +07:00
parent 2b36b48f98
commit 4385b2f020
No known key found for this signature in database
GPG Key ID: 2837C62ACFBFED5D
4 changed files with 37 additions and 30 deletions

View File

@ -183,31 +183,33 @@ pgsql_config(BridgeType, Config) ->
end, end,
QueryMode = ?config(query_mode, Config), QueryMode = ?config(query_mode, Config),
TlsEnabled = ?config(enable_tls, Config), TlsEnabled = ?config(enable_tls, Config),
%% NOTE: supplying password through a file here, to verify that it works.
Password = create_passfile(BridgeType, Config),
ConfigString = ConfigString =
io_lib:format( io_lib:format(
"bridges.~s.~s {\n" "bridges.~s.~s {"
" enable = true\n" "\n enable = true"
" server = ~p\n" "\n server = ~p"
" database = ~p\n" "\n database = ~p"
" username = ~p\n" "\n username = ~p"
" password = ~p\n" "\n password = ~p"
" sql = ~p\n" "\n sql = ~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 }"
" ssl = {\n" "\n ssl = {"
" enable = ~w\n" "\n enable = ~w"
" }\n" "\n }"
"}", "\n }",
[ [
BridgeType, BridgeType,
Name, Name,
Server, Server,
?PGSQL_DATABASE, ?PGSQL_DATABASE,
?PGSQL_USERNAME, ?PGSQL_USERNAME,
?PGSQL_PASSWORD, Password,
?SQL_BRIDGE, ?SQL_BRIDGE,
BatchSize, BatchSize,
QueryMode, QueryMode,
@ -216,6 +218,12 @@ pgsql_config(BridgeType, Config) ->
), ),
{Name, parse_and_check(ConfigString, BridgeType, Name)}. {Name, parse_and_check(ConfigString, BridgeType, Name)}.
create_passfile(BridgeType, Config) ->
Filename = binary_to_list(BridgeType) ++ ".passfile",
Filepath = filename:join(?config(priv_dir, Config), Filename),
ok = file:write_file(Filepath, ?PGSQL_PASSWORD),
"file://" ++ Filepath.
parse_and_check(ConfigString, BridgeType, Name) -> parse_and_check(ConfigString, BridgeType, Name) ->
{ok, RawConf} = hocon:binary(ConfigString, #{format => map}), {ok, RawConf} = hocon:binary(ConfigString, #{format => map}),
hocon_tconf:check_plain(emqx_bridge_schema, RawConf, #{required => false, atom_key => false}), hocon_tconf:check_plain(emqx_bridge_schema, RawConf, #{required => false, atom_key => false}),
@ -379,7 +387,9 @@ t_setup_via_http_api_and_publish(Config) ->
QueryMode = ?config(query_mode, Config), QueryMode = ?config(query_mode, Config),
PgsqlConfig = PgsqlConfig0#{ PgsqlConfig = PgsqlConfig0#{
<<"name">> => Name, <<"name">> => Name,
<<"type">> => BridgeType <<"type">> => BridgeType,
%% NOTE: using literal passwords with HTTP API requests.
<<"password">> => <<?PGSQL_PASSWORD>>
}, },
?assertMatch( ?assertMatch(
{ok, _}, {ok, _},

View File

@ -1,6 +1,6 @@
{application, emqx_bridge_tdengine, [ {application, emqx_bridge_tdengine, [
{description, "EMQX Enterprise TDEngine Bridge"}, {description, "EMQX Enterprise TDEngine Bridge"},
{vsn, "0.1.5"}, {vsn, "0.1.6"},
{registered, []}, {registered, []},
{applications, [ {applications, [
kernel, kernel,

View File

@ -6,7 +6,6 @@
-behaviour(emqx_resource). -behaviour(emqx_resource).
-include_lib("emqx_resource/include/emqx_resource.hrl").
-include_lib("typerefl/include/types.hrl"). -include_lib("typerefl/include/types.hrl").
-include_lib("emqx/include/logger.hrl"). -include_lib("emqx/include/logger.hrl").
-include_lib("snabbkaffe/include/snabbkaffe.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl").
@ -48,8 +47,8 @@ adjust_fields(Fields) ->
fun fun
({username, OrigUsernameFn}) -> ({username, OrigUsernameFn}) ->
{username, add_default_fn(OrigUsernameFn, <<"root">>)}; {username, add_default_fn(OrigUsernameFn, <<"root">>)};
({password, OrigPasswordFn}) -> ({password, _}) ->
{password, make_required_fn(OrigPasswordFn)}; {password, emqx_connector_schema_lib:password_field(#{required => true})};
(Field) -> (Field) ->
Field Field
end, end,
@ -62,12 +61,6 @@ add_default_fn(OrigFn, Default) ->
(Field) -> OrigFn(Field) (Field) -> OrigFn(Field)
end. end.
make_required_fn(OrigFn) ->
fun
(required) -> true;
(Field) -> OrigFn(Field)
end.
server() -> server() ->
Meta = #{desc => ?DESC("server")}, Meta = #{desc => ?DESC("server")},
emqx_schema:servers_sc(Meta, ?TD_HOST_OPTIONS). emqx_schema:servers_sc(Meta, ?TD_HOST_OPTIONS).
@ -223,7 +216,10 @@ aggregate_query(BatchTks, BatchReqs, Acc) ->
). ).
connect(Opts) -> connect(Opts) ->
tdengine:start_link(Opts). %% TODO: teach `tdengine` to accept 0-arity closures as passwords.
{value, {password, Secret}, OptsRest} = lists:keytake(password, 1, Opts),
NOpts = [{password, emqx_secret:unwrap(Secret)} | OptsRest],
tdengine:start_link(NOpts).
query_opts(#{database := Database} = _Opts) -> query_opts(#{database := Database} = _Opts) ->
[{db_name, Database}]. [{db_name, Database}].

View File

@ -131,7 +131,7 @@ on_start(
{host, Host}, {host, Host},
{port, Port}, {port, Port},
{username, User}, {username, User},
{password, emqx_secret:wrap(maps:get(password, Config, ""))}, {password, maps:get(password, Config, "")},
{database, DB}, {database, DB},
{auto_reconnect, ?AUTO_RECONNECT_INTERVAL}, {auto_reconnect, ?AUTO_RECONNECT_INTERVAL},
{pool_size, PoolSize} {pool_size, PoolSize}
@ -357,6 +357,7 @@ validate_table_existence([], _SQL) ->
connect(Opts) -> connect(Opts) ->
Host = proplists:get_value(host, Opts), Host = proplists:get_value(host, Opts),
Username = proplists:get_value(username, Opts), Username = proplists:get_value(username, Opts),
%% TODO: teach `epgsql` to accept 0-arity closures as passwords.
Password = emqx_secret:unwrap(proplists:get_value(password, Opts)), Password = emqx_secret:unwrap(proplists:get_value(password, Opts)),
case epgsql:connect(Host, Username, Password, conn_opts(Opts)) of case epgsql:connect(Host, Username, Password, conn_opts(Opts)) of
{ok, _Conn} = Ok -> {ok, _Conn} = Ok ->