fix(secret): dedicate a specific loader module for file secrets
To make code employing `emqx_secret` easier to follow.
This commit is contained in:
parent
44b4205561
commit
d278486416
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue