Просмотр исходного кода

feat(cassa): accept wrapped secrets as passwords

Andrew Mayorov 2 лет назад
Родитель
Сommit
9c5856029f

+ 11 - 26
apps/emqx_bridge_cassandra/src/emqx_bridge_cassandra_connector.erl

@@ -70,7 +70,7 @@ cassandra_db_fields() ->
         {keyspace, fun keyspace/1},
         {pool_size, fun emqx_connector_schema_lib:pool_size/1},
         {username, fun emqx_connector_schema_lib:username/1},
-        {password, fun emqx_connector_schema_lib:password/1},
+        {password, emqx_connector_schema_lib:password_field()},
         {auto_reconnect, fun emqx_connector_schema_lib:auto_reconnect/1}
     ].
 
@@ -111,14 +111,14 @@ on_start(
             emqx_schema:parse_servers(Servers0, ?DEFAULT_SERVER_OPTION)
         ),
 
-    Options = [
-        {nodes, Servers},
-        {keyspace, Keyspace},
-        {auto_reconnect, ?AUTO_RECONNECT_INTERVAL},
-        {pool_size, PoolSize}
-    ],
-    Options1 = maybe_add_opt(username, Config, Options),
-    Options2 = maybe_add_opt(password, Config, Options1, _IsSensitive = true),
+    Options =
+        maps:to_list(maps:with([username, password], Config)) ++
+            [
+                {nodes, Servers},
+                {keyspace, Keyspace},
+                {auto_reconnect, ?AUTO_RECONNECT_INTERVAL},
+                {pool_size, PoolSize}
+            ],
 
     SslOpts =
         case maps:get(enable, SSL) of
@@ -131,7 +131,7 @@ on_start(
                 []
         end,
     State = parse_prepare_cql(Config),
-    case emqx_resource_pool:start(InstId, ?MODULE, Options2 ++ SslOpts) of
+    case emqx_resource_pool:start(InstId, ?MODULE, Options ++ SslOpts) of
         ok ->
             {ok, init_prepare(State#{pool_name => InstId, prepare_statement => #{}})};
         {error, Reason} ->
@@ -387,6 +387,7 @@ conn_opts(Opts) ->
 conn_opts([], Acc) ->
     Acc;
 conn_opts([{password, Password} | Opts], Acc) ->
+    %% TODO: teach `ecql` to accept 0-arity closures as passwords.
     conn_opts(Opts, [{password, emqx_secret:unwrap(Password)} | Acc]);
 conn_opts([Opt | Opts], Acc) ->
     conn_opts(Opts, [Opt | Acc]).
@@ -512,19 +513,3 @@ maybe_assign_type(V) when is_integer(V) ->
 maybe_assign_type(V) when is_float(V) -> {double, V};
 maybe_assign_type(V) ->
     V.
-
-maybe_add_opt(Key, Conf, Opts) ->
-    maybe_add_opt(Key, Conf, Opts, _IsSensitive = false).
-
-maybe_add_opt(Key, Conf, Opts, IsSensitive) ->
-    case Conf of
-        #{Key := Val} ->
-            [{Key, maybe_wrap(IsSensitive, Val)} | Opts];
-        _ ->
-            Opts
-    end.
-
-maybe_wrap(false = _IsSensitive, Val) ->
-    Val;
-maybe_wrap(true, Val) ->
-    emqx_secret:wrap(Val).

+ 41 - 22
apps/emqx_bridge_cassandra/test/emqx_bridge_cassandra_connector_SUITE.erl

@@ -40,10 +40,9 @@ all() ->
     ].
 
 groups() ->
-    TCs = emqx_common_test_helpers:all(?MODULE),
     [
-        {auth, TCs},
-        {noauth, TCs}
+        {auth, [t_lifecycle, t_start_passfile]},
+        {noauth, [t_lifecycle]}
     ].
 
 cassandra_servers(CassandraHost) ->
@@ -115,32 +114,37 @@ end_per_testcase(_, _Config) ->
 
 t_lifecycle(Config) ->
     perform_lifecycle_check(
-        <<"emqx_connector_cassandra_SUITE">>,
+        <<?MODULE_STRING>>,
         cassandra_config(Config)
     ).
 
-show(X) ->
-    erlang:display(X),
-    X.
-
-show(Label, What) ->
-    erlang:display({Label, What}),
-    What.
+t_start_passfile(Config) ->
+    ResourceID = atom_to_binary(?FUNCTION_NAME),
+    PasswordFilename = filename:join(?config(priv_dir, Config), "passfile"),
+    ok = file:write_file(PasswordFilename, ?CASSA_PASSWORD),
+    InitialConfig = emqx_utils_maps:deep_merge(
+        cassandra_config(Config),
+        #{
+            <<"config">> => #{
+                password => iolist_to_binary(["file://", PasswordFilename])
+            }
+        }
+    ),
+    ?assertMatch(
+        #{status := connected},
+        create_local_resource(ResourceID, check_config(InitialConfig))
+    ),
+    ?assertEqual(
+        ok,
+        emqx_resource:remove_local(ResourceID)
+    ).
 
 perform_lifecycle_check(ResourceId, InitialConfig) ->
-    {ok, #{config := CheckedConfig}} =
-        emqx_resource:check_config(?CASSANDRA_RESOURCE_MOD, InitialConfig),
-    {ok, #{
+    CheckedConfig = check_config(InitialConfig),
+    #{
         state := #{pool_name := PoolName} = State,
         status := InitialStatus
-    }} =
-        emqx_resource:create_local(
-            ResourceId,
-            ?CONNECTOR_RESOURCE_GROUP,
-            ?CASSANDRA_RESOURCE_MOD,
-            CheckedConfig,
-            #{}
-        ),
+    } = create_local_resource(ResourceId, CheckedConfig),
     ?assertEqual(InitialStatus, connected),
     % Instance should match the state and status of the just started resource
     {ok, ?CONNECTOR_RESOURCE_GROUP, #{
@@ -191,6 +195,21 @@ perform_lifecycle_check(ResourceId, InitialConfig) ->
 %% utils
 %%--------------------------------------------------------------------
 
+check_config(Config) ->
+    {ok, #{config := CheckedConfig}} = emqx_resource:check_config(?CASSANDRA_RESOURCE_MOD, Config),
+    CheckedConfig.
+
+create_local_resource(ResourceId, CheckedConfig) ->
+    {ok, Bridge} =
+        emqx_resource:create_local(
+            ResourceId,
+            ?CONNECTOR_RESOURCE_GROUP,
+            ?CASSANDRA_RESOURCE_MOD,
+            CheckedConfig,
+            #{}
+        ),
+    Bridge.
+
 cassandra_config(Config) ->
     Host = ?config(cassa_host, Config),
     AuthOpts = maps:from_list(?config(cassa_auth_opts, Config)),