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

fix(secret): dedicate a specific loader module for file secrets

To make code employing `emqx_secret` easier to follow.
Andrew Mayorov 2 лет назад
Родитель
Сommit
d278486416

+ 7 - 26
apps/emqx/src/emqx_schema_secret.erl

@@ -25,9 +25,6 @@
 %% HOCON Schema API
 -export([convert_secret/2]).
 
-%% Target of `emqx_secret:wrap/3`
--export([load/1]).
-
 %% @doc Secret value.
 -type t() :: binary().
 
@@ -74,31 +71,15 @@ convert_secret(Secret, #{}) ->
     end.
 
 -spec wrap(source()) -> emqx_secret:t(t()).
-wrap(Source) ->
-    emqx_secret:wrap(?MODULE, load, Source).
+wrap(<<"file://", Filename/binary>>) ->
+    emqx_secret:wrap_load({file, Filename});
+wrap(Secret) ->
+    emqx_secret:wrap(Secret).
 
 -spec source(emqx_secret:t(t())) -> source().
 source(Secret) when is_function(Secret) ->
-    emqx_secret:term(Secret);
+    source(emqx_secret:term(Secret));
+source({file, Filename}) ->
+    <<"file://", Filename/binary>>;
 source(Secret) ->
     Secret.
-
-%%
-
--spec load(source()) -> t().
-load(<<"file://", Filename/binary>>) ->
-    load_file(Filename);
-load(Secret) ->
-    Secret.
-
-load_file(Filename) ->
-    case file:read_file(Filename) of
-        {ok, Secret} ->
-            string:trim(Secret, trailing, [$\n]);
-        {error, Reason} ->
-            throw(#{
-                msg => failed_to_read_secret_file,
-                path => Filename,
-                reason => emqx_utils:explain_posix(Reason)
-            })
-    end.

+ 9 - 5
apps/emqx/src/emqx_secret.erl

@@ -19,12 +19,16 @@
 -module(emqx_secret).
 
 %% API:
--export([wrap/1, wrap/3, unwrap/1, term/1]).
+-export([wrap/1, wrap_load/1, unwrap/1, term/1]).
 
 -export_type([t/1]).
 
 -opaque t(T) :: T | fun(() -> t(T)).
 
+%% Secret loader module.
+%% Any changes related to processing of secrets should be made there.
+-define(LOADER, emqx_secret_loader).
+
 %%================================================================================
 %% API funcions
 %%================================================================================
@@ -37,12 +41,12 @@ wrap(Term) ->
         Term
     end.
 
-%% @doc Wrap a function call over a term in a secret closure.
+%% @doc Wrap a loader function call over a term in a secret closure.
 %% This is slightly more flexible form of `wrap/1` with the same basic purpose.
--spec wrap(module(), atom(), _Term) -> t(_).
-wrap(Module, Function, Term) ->
+-spec wrap_load(emqx_secret_loader:source()) -> t(_).
+wrap_load(Source) ->
     fun() ->
-        apply(Module, Function, [Term])
+        apply(?LOADER, load, [Source])
     end.
 
 %% @doc Unwrap a secret closure, revealing the secret.

+ 42 - 0
apps/emqx/src/emqx_secret_loader.erl

@@ -0,0 +1,42 @@
+%%--------------------------------------------------------------------
+%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%%     http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%--------------------------------------------------------------------
+
+-module(emqx_secret_loader).
+
+%% API
+-export([load/1]).
+-export([file/1]).
+
+-export_type([source/0]).
+
+-type source() :: {file, file:filename_all()}.
+
+-spec load(source()) -> binary() | no_return().
+load({file, Filename}) ->
+    file(Filename).
+
+-spec file(file:filename_all()) -> binary() | no_return().
+file(Filename) ->
+    case file:read_file(Filename) of
+        {ok, Secret} ->
+            string:trim(Secret, trailing);
+        {error, Reason} ->
+            throw(#{
+                msg => failed_to_read_secret_file,
+                path => Filename,
+                reason => emqx_utils:explain_posix(Reason)
+            })
+    end.

+ 26 - 19
apps/emqx/test/emqx_secret_tests.erl

@@ -16,8 +16,6 @@
 
 -module(emqx_secret_tests).
 
--export([ident/1]).
-
 -include_lib("eunit/include/eunit.hrl").
 
 wrap_unwrap_test() ->
@@ -32,16 +30,30 @@ unwrap_immediate_test() ->
         emqx_secret:unwrap(42)
     ).
 
-wrap_unwrap_external_test() ->
+wrap_unwrap_load_test_() ->
+    Secret = <<"foobaz">>,
+    {
+        setup,
+        fun() -> write_temp_file(Secret) end,
+        fun(Filename) -> file:delete(Filename) end,
+        fun(Filename) ->
+            ?_assertEqual(
+                Secret,
+                emqx_secret:unwrap(emqx_secret:wrap_load({file, Filename}))
+            )
+        end
+    }.
+
+wrap_load_term_test() ->
     ?assertEqual(
-        ident({foo, bar}),
-        emqx_secret:unwrap(emqx_secret:wrap(?MODULE, ident, {foo, bar}))
+        {file, "no/such/file/i/swear"},
+        emqx_secret:term(emqx_secret:wrap_load({file, "no/such/file/i/swear"}))
     ).
 
-wrap_unwrap_transform_test() ->
-    ?assertEqual(
-        <<"this_was_an_atom">>,
-        emqx_secret:unwrap(emqx_secret:wrap(erlang, atom_to_binary, this_was_an_atom))
+wrap_unwrap_missing_file_test() ->
+    ?assertThrow(
+        #{msg := failed_to_read_secret_file, reason := "No such file or directory"},
+        emqx_secret:unwrap(emqx_secret:wrap_load({file, "no/such/file/i/swear"}))
     ).
 
 wrap_term_test() ->
@@ -50,12 +62,6 @@ wrap_term_test() ->
         emqx_secret:term(emqx_secret:wrap(42))
     ).
 
-wrap_external_term_test() ->
-    ?assertEqual(
-        this_was_an_atom,
-        emqx_secret:term(emqx_secret:wrap(erlang, atom_to_binary, this_was_an_atom))
-    ).
-
 external_fun_term_error_test() ->
     Term = {foo, bar},
     ?assertError(
@@ -63,7 +69,8 @@ external_fun_term_error_test() ->
         emqx_secret:term(fun() -> Term end)
     ).
 
-%%
-
-ident(X) ->
-    X.
+write_temp_file(Bytes) ->
+    Ts = erlang:system_time(millisecond),
+    Filename = filename:join("/tmp", ?MODULE_STRING ++ integer_to_list(-Ts)),
+    ok = file:write_file(Filename, Bytes),
+    Filename.