Skip to content

Commit

Permalink
Use maps for mod_keystore config
Browse files Browse the repository at this point in the history
Because the validation of different keys has been moved to parsing the config,
the test is also moved to the config_parser_SUITE.
  • Loading branch information
gustawlippa committed Mar 18, 2022
1 parent d50f62c commit af191c9
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 98 deletions.
17 changes: 8 additions & 9 deletions big_tests/tests/oauth_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -461,15 +461,14 @@ to_lower(B) when is_binary(B) ->
list_to_binary(string:to_lower(binary_to_list(B))).

required_modules() ->
KeyStoreOpts = [{keys, [
{token_secret, ram},
%% This is a hack for tests! As the name implies,
%% a pre-shared key should be read from a file stored
%% on disk. This way it can be shared with trusted 3rd
%% parties who can use it to sign tokens for users
%% to authenticate with and MongooseIM to verify.
{provision_pre_shared, ram}
]}],
KeyOpts = #{keys => [{token_secret, ram},
%% This is a hack for tests! As the name implies,
%% a pre-shared key should be read from a file stored
%% on disk. This way it can be shared with trusted 3rd
%% parties who can use it to sign tokens for users
%% to authenticate with and MongooseIM to verify.
{provision_pre_shared, ram}]},
KeyStoreOpts = config_parser_helper:mod_config(mod_keystore, KeyOpts),
[{mod_last, stopped},
{mod_keystore, KeyStoreOpts},
{mod_auth_token, auth_token_opts()}].
Expand Down
57 changes: 22 additions & 35 deletions src/mod_keystore.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@
%% Hook handlers
-export([get_key/2]).

%% Tests only!
-export([validate_opts/1]).

-export([config_metrics/1]).
-export([process_keys/1]).
-export([process_key/1]).

%% Public types
-export_type([key/0,
Expand All @@ -25,9 +22,7 @@
key_name/0,
raw_key/0]).

-ignore_xref([
behaviour_info/1, get_key/2, validate_opts/1
]).
-ignore_xref([get_key/2]).

-include("mod_keystore.hrl").
-include("mongoose.hrl").
Expand All @@ -54,9 +49,8 @@
%% gen_mod callbacks
%%

-spec start(mongooseim:host_type(), list()) -> ok.
-spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
start(HostType, Opts) ->
validate_opts(Opts),
create_keystore_ets(),
mod_keystore_backend:init(HostType, Opts),
init_keys(HostType, Opts),
Expand Down Expand Up @@ -84,7 +78,11 @@ config_spec() ->
items = #{<<"ram_key_size">> => #option{type = integer,
validate = non_negative},
<<"keys">> => #list{items = keys_spec()}
}
},
defaults = #{<<"ram_key_size">> => ?DEFAULT_RAM_KEY_SIZE,
<<"keys">> => []},
format_items = map,
process = fun validate_key_ids/1
}.

keys_spec() ->
Expand All @@ -97,15 +95,14 @@ keys_spec() ->
validate = filename}
},
required = [<<"name">>, <<"type">>],
process = fun ?MODULE:process_keys/1
format_items = map,
process = fun ?MODULE:process_key/1
}.

process_keys(KVs) ->
{[[{name, Name}], [{type, Type}]], PathOpts} = proplists:split(KVs, [name, type]),
process_key_opts(Name, Type, PathOpts).

process_key_opts(Name, ram, []) -> {Name, ram};
process_key_opts(Name, file, [{path, Path}]) -> {Name, {file, Path}}.
process_key(#{name := Name, type := file, path := Path}) ->
{Name, {file, Path}};
process_key(#{name := Name, type := ram}) ->
{Name, ram}.

%%
%% Hook handlers
Expand Down Expand Up @@ -165,16 +162,16 @@ clear_keystore_ets(HostType) ->
does_table_exist(NameOrTID) ->
ets:info(NameOrTID, name) /= undefined.

init_keys(HostType, Opts) ->
[ init_key(K, HostType, Opts) || K <- proplists:get_value(keys, Opts, []) ].
init_keys(HostType, Opts = #{keys := Keys}) ->
[ init_key(K, HostType, Opts) || K <- Keys ].

-spec init_key({key_name(), key_type()}, mongooseim:host_type(), list()) -> ok.
-spec init_key({key_name(), key_type()}, mongooseim:host_type(), gen_mod:module_opts()) -> ok.
init_key({KeyName, {file, Path}}, HostType, _Opts) ->
{ok, Data} = file:read_file(Path),
true = ets_store_key({KeyName, HostType}, Data),
ok;
init_key({KeyName, ram}, HostType, Opts) ->
ProposedKey = crypto:strong_rand_bytes(get_key_size(Opts)),
init_key({KeyName, ram}, HostType, #{ram_key_size := KeySize}) ->
ProposedKey = crypto:strong_rand_bytes(KeySize),
KeyRecord = #key{id = {KeyName, HostType},
key = ProposedKey},
{ok, _ActualKey} = mod_keystore_backend:init_ram_key(HostType, KeyRecord),
Expand All @@ -187,23 +184,13 @@ ets_get_key(KeyID) ->
ets_store_key(KeyID, RawKey) ->
ets:insert(keystore, {KeyID, RawKey}).

get_key_size(Opts) ->
case lists:keyfind(ram_key_size, 1, Opts) of
false -> ?DEFAULT_RAM_KEY_SIZE;
{ram_key_size, KeySize} -> KeySize
end.

validate_opts(Opts) ->
validate_key_ids(proplists:get_value(keys, Opts, [])).

validate_key_ids(KeySpecs) ->
validate_key_ids(Opts = #{keys := KeySpecs}) ->
KeyIDs = [ KeyID || {KeyID, _} <- KeySpecs ],
SortedAndUniqueKeyIDs = lists:usort(KeyIDs),
case KeyIDs -- SortedAndUniqueKeyIDs of
[] -> ok;
[] -> Opts;
[_|_] -> error(non_unique_key_ids, KeySpecs)
end.

config_metrics(Host) ->
OptsToReport = [{backend, mnesia}], %list of tuples {option, defualt_value}
mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport).
mongoose_module_metrics:opts_for_module(Host, ?MODULE, [backend]).
11 changes: 6 additions & 5 deletions test/common/config_parser_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -453,11 +453,10 @@ all_modules() ->
mod_adhoc => #{iqdisc => one_queue, report_commands_node => true},
mod_mam_rdbms_arch_async => default_config([modules, mod_mam_meta, async_writer]),
mod_keystore =>
[{keys,
[{access_secret, ram},
{access_psk, {file, "priv/access_psk"}},
{provision_psk, {file, "priv/provision_psk"}}]},
{ram_key_size, 1000}],
mod_config(mod_keystore, #{keys => [{access_secret, ram},
{access_psk, {file, "priv/access_psk"}},
{provision_psk, {file, "priv/provision_psk"}}],
ram_key_size => 1000}),
mod_global_distrib =>
mod_config(mod_global_distrib,
#{global_host => <<"example.com">>,
Expand Down Expand Up @@ -866,6 +865,8 @@ default_mod_config(mod_global_distrib) ->
redis => default_config([modules, mod_global_distrib, redis]),
cache => default_config([modules, mod_global_distrib, cache]),
bounce => default_config([modules, mod_global_distrib, bounce])};
default_mod_config(mod_keystore) ->
#{ram_key_size => 2048, keys => []};
default_mod_config(mod_last) ->
#{iqdisc => one_queue, backend => mnesia};
default_mod_config(mod_inbox) ->
Expand Down
19 changes: 13 additions & 6 deletions test/config_parser_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2101,30 +2101,37 @@ mod_jingle_sip(_Config) ->
?errh(T(#{<<"sdp_origin">> => <<"abc">>})).

mod_keystore(_Config) ->
check_module_defaults(mod_keystore),
T = fun(Opts) -> #{<<"modules">> => #{<<"mod_keystore">> => Opts}} end,
M = fun(Cfg) -> modopts(mod_keystore, Cfg) end,
?cfgh(M([{ram_key_size, 1024}]),
P = [modules, mod_keystore],
?cfgh(P ++ [ram_key_size], 1024,
T(#{<<"ram_key_size">> => 1024})),
?errh(T(#{<<"ram_key_size">> => -1})).

mod_keystore_keys(_Config) ->
T = fun(Opts) -> #{<<"modules">> => #{<<"mod_keystore">> =>
#{<<"keys">> => Opts}}}
end,
M = fun(Cfg) -> modopts(mod_keystore, [{keys, Cfg}]) end,
P = [modules, mod_keystore, keys],
RequiredOpts = #{<<"name">> => <<"access_secret">>,
<<"type">> => <<"ram">>},
?cfgh(M([{access_secret, ram}]),
?cfgh(P, [{access_secret, ram}],
T([RequiredOpts])),
?cfgh(M([{access_secret, {file, "priv/access_psk"}}]),
?cfgh(P, [{access_secret, {file, "priv/access_psk"}}],
T([RequiredOpts#{<<"type">> => <<"file">>,
<<"path">> => <<"priv/access_psk">>}])),
[?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)],
?errh(T([RequiredOpts#{<<"name">> => <<>>}])),
?errh(T([RequiredOpts#{<<"type">> => <<"rampampam">>}])),
?errh(T([RequiredOpts#{<<"type">> => <<"file">>}])),
?errh(T([RequiredOpts#{<<"type">> => <<"file">>,
<<"path">> => <<"does/not/exists">>}])).
<<"path">> => <<"does/not/exists">>}])),
?errh([#{reason := non_unique_key_ids}],
T([#{<<"name">> => <<"same_name_twice">>,
<<"type">> => <<"ram">>},
#{<<"name">> => <<"same_name_twice">>,
<<"type">> => <<"file">>,
<<"path">> => <<"priv/access_psk">>}])).

mod_last(_Config) ->
check_iqdisc_map(mod_last),
Expand Down
49 changes: 6 additions & 43 deletions test/keystore_SUITE.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-module(keystore_SUITE).
-include_lib("eunit/include/eunit.hrl").
-compile([export_all, nowarn_export_all]).
-import(config_parser_helper, [default_mod_config/1, mod_config/2]).

-define(ae(Expected, Actual), ?assertEqual(Expected, Actual)).

Expand All @@ -11,7 +12,6 @@ all() ->
module_startup_create_ram_key,
module_startup_create_ram_key_of_given_size,
module_startup_for_multiple_domains,
module_startup_non_unique_key_ids,
multiple_domains_one_stopped
].

Expand Down Expand Up @@ -54,7 +54,7 @@ clean_after_testcase(C) ->
%%

module_startup_no_opts(_) ->
ok = mod_keystore:start(<<"localhost">>, []).
ok = mod_keystore:start(<<"localhost">>, default_mod_config(mod_keystore)).

module_startup_read_key_from_file(_) ->
%% given
Expand Down Expand Up @@ -103,18 +103,6 @@ module_startup_for_multiple_domains(_Config) ->
?ae([{{key_from_file, <<"second.com">>}, SecondKey}],
get_key(<<"second.com">>, key_from_file)).

module_startup_non_unique_key_ids(_) ->
%% given
NonUniqueKeyIDsOpts = [{keys, [{some_key, ram},
{some_key, {file, "some_key.dat"}}]}],
%% when
try
mod_keystore:start(<<"localhost">>, NonUniqueKeyIDsOpts)
%% then
catch
error:non_unique_key_ids -> ok
end.

multiple_domains_one_stopped(_Config) ->
% given
[] = get_key(<<"first.com">>, key_from_file),
Expand All @@ -135,40 +123,19 @@ multiple_domains_one_stopped(_Config) ->
%% Helpers
%%

start_async(M, F, A) ->
Self = self(),
P = spawn(fun () ->
erlang:apply(M, F, A),
Self ! started,
helper_loop()
end),
receive
started ->
%ct:pal("started", []),
{ok, P}
after timer:seconds(1) ->
ct:fail("async start timeout")
end.

helper_loop() ->
receive
stop -> exit(normal);
_ -> helper_loop()
end.

key_at(Path, Data) ->
ok = file:write_file(Path, Data),
{ok, Path}.

key_from_file(KeyFile) ->
[{keys, [{key_from_file, {file, KeyFile}}]}].
mod_config(mod_keystore, #{keys => [{key_from_file, {file, KeyFile}}]}).

ram_key() ->
[{keys, [{ram_key, ram}]}].
mod_config(mod_keystore, #{keys => [{ram_key, ram}]}).

sized_ram_key(Size) ->
[{keys, [{ram_key, ram}]},
{ram_key_size, Size}].
mod_config(mod_keystore, #{keys => [{ram_key, ram}],
ram_key_size => Size}).

mock_mongoose_metrics() ->
meck:new(mongoose_metrics, []),
Expand All @@ -183,7 +150,3 @@ mock_mongoose_metrics() ->
Result :: mod_keystore:key_list().
get_key(HostType, KeyName) ->
mongoose_hooks:get_key(HostType, KeyName).

%%{mod_keystore, [{keys, [{asdqwe_access_secret, ram},
%% {asdqwe_access_psk, {file, "priv/asdqwe_access_psk"}},
%% {asdqwe_provision_psk, {file, "priv/asdqwe_access_psk"}}]}]},

0 comments on commit af191c9

Please sign in to comment.