Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
chrzaszcz committed Feb 7, 2022
1 parent 6a008ef commit d0415c9
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 249 deletions.
41 changes: 20 additions & 21 deletions big_tests/tests/extdisco_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ all() ->
{group, extdisco_required_elements_configured}].

groups() ->
G = [{extdisco_not_configured, [sequence], extdisco_not_configured_tests()},
{extdisco_configured, [sequence], extdisco_configured_tests()},
{multiple_extdisco_configured, [sequence], multiple_extdisco_configured_tests()},
{extdisco_required_elements_configured, [sequence], extdisco_required_elements_configured_tests()}],
ct_helper:repeat_all_until_all_ok(G).
[{extdisco_not_configured, [sequence], extdisco_not_configured_tests()},
{extdisco_configured, [sequence], extdisco_configured_tests()},
{multiple_extdisco_configured, [sequence], multiple_extdisco_configured_tests()},
{extdisco_required_elements_configured, [sequence], extdisco_required_elements_configured_tests()}].

extdisco_not_configured_tests() ->
[external_services_discovery_not_supported,
Expand Down Expand Up @@ -75,7 +74,7 @@ init_per_group(multiple_extdisco_configured, Config) ->
ExternalServices = [stun_service(), stun_service(), turn_service()],
set_external_services(ExternalServices, Config);
init_per_group(extdisco_required_elements_configured, Config) ->
ExternalServices = [[{type, ftp},{host, "3.3.3.3"}]],
ExternalServices = [#{type => ftp, host => <<"3.3.3.3">>}],
set_external_services(ExternalServices, Config);
init_per_group(_GroupName, Config) ->
Config.
Expand Down Expand Up @@ -299,23 +298,23 @@ external_service_required_elements_configured(Config) ->
%%-----------------------------------------------------------------

stun_service() ->
[{type, stun},
{host, "1.1.1.1"},
{port, 3478},
{transport, "udp"},
{username, "username"},
{password, "secret"}].
#{type => stun,
host => <<"1.1.1.1">>,
port => 3478,
transport => <<"udp">>,
username => <<"username">>,
password => <<"secret">>}.

turn_service() ->
[{type, turn},
{host, "2.2.2.2"},
{port, 3478},
{transport, "tcp"},
{username, "username"},
{password, "secret"}].

set_external_services(Opts, Config) ->
Module = [{mod_extdisco, Opts}],
#{type => turn,
host => <<"2.2.2.2">>,
port => 3478,
transport => <<"tcp">>,
username => <<"username">>,
password => <<"secret">>}.

set_external_services(Services, Config) ->
Module = [{mod_extdisco, #{service => Services}}],
ok = dynamic_modules:ensure_modules(domain(), Module),
Config.

Expand Down
5 changes: 3 additions & 2 deletions big_tests/tests/oauth_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -475,5 +475,6 @@ required_modules() ->
{mod_auth_token, auth_token_opts()}].

auth_token_opts() ->
[{ {validity_period, access}, {60, minutes} },
{ {validity_period, refresh}, {1, days} }].
#{iqdisc => no_queue,
validity_period => #{access => #{value => 60, unit => minutes},
refresh => #{value => 1, unit => days}}}.
22 changes: 14 additions & 8 deletions doc/modules/mod_auth_token.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ Generation of keys necessary to sign binary tokens is delegated to module `mod_k

## Options

### `modules.mod_auth_token.validity_period`
* **Syntax:** Array of TOML tables with the following keys: `token`, `value`, `unit` and following values: {token = `values: "access", "refresh", "provision"`, value = `non-negative integer`, unit = `values: "days", "hours", "minutes", "seconds"`}.
* **Default:** `[{token = "access", value = 1, unit = "hours"}, {token = "refresh", value = 25, unit = "days"}]`
* **Example:** `[{token = "access", value = 13, unit = "minutes"}, {token = "refresh", value = 13, unit = "days"}]`
### `modules.mod_private.iqdisc.type`
* **Syntax:** string, one of `"one_queue"`, `"no_queue"`, `"queues"`, `"parallel"`
* **Default:** `"no_queue"`

Strategy to handle incoming stanzas. For details, please refer to
[IQ processing policies](../configuration/Modules.md#iq-processing-policies).

### `modules.mod_auth_token.access.validity_period`
### `modules.mod_auth_token.refresh.validity_period`
* **Syntax:** TOML table with the following mandatory keys: `value` (non-negative integer) and `unit` (`"days"`, `"hours"`, `"minutes"` or `"seconds"`.
* **Default:** `{value = 1, unit = "hours"}` for `access` and `{value = 1, unit = "hours"}` for `refresh`
* **Example:** `access.validity_period = {value = 30, unit = "minutes"}`

Validity periods of access and refresh tokens can be defined independently.
Validity period configuration for provision tokens happens outside the module since the server does not generate provision tokens - it only validates them.
Expand Down Expand Up @@ -156,8 +164,6 @@ Access token validity can't be sidestepped right now.

```toml
[modules.mod_auth_token]
validity_period = [
{token = "access", value = 13, unit = "minutes"},
{token = "refresh", value = 13, unit = "days"}
]
validity_period.access = {value = 13, unit = "minutes"}
validity_period.refresh = {value = 13, unit = "days"}
```
2 changes: 1 addition & 1 deletion src/config/mongoose_config_parser.erl
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ post_process_modules_opt(Other) ->
Other.

unfold_opts(Modules) ->
maps:map(fun(_Mod, Opts) -> proplists:unfold(Opts) end, Modules).
maps:map(fun(_Mod, Opts) -> mongoose_modules:unfold_opts(Opts) end, Modules).

%% local functions

Expand Down
43 changes: 29 additions & 14 deletions src/gen_mod.erl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
get_opt/4,
set_opt/3,
get_module_opt/4,
get_module_opt/3,
get_module_opts/2,
get_loaded_module_opts/2,
get_opt_subhost/3,
Expand All @@ -82,7 +83,11 @@
-type module_feature() :: atom().
-type domain_name() :: mongooseim:domain_name().
-type host_type() :: mongooseim:host_type().
-type module_opts() :: list().
-type key_path() :: mongoose_config:key_path().
-type opt_key() :: term(). %% top-level module options have atoms as keys
-type opt_value() :: mongoose_config:value().
-type module_opts() :: [{opt_key(), opt_value()}] % deprecated, will be removed
| #{opt_key() => opt_value()}. % new format

%% -export([behaviour_info/1]).
%% behaviour_info(callbacks) ->
Expand Down Expand Up @@ -265,6 +270,11 @@ wait_for_stop(MonitorReference) ->
timeout
end.

-spec get_opt(opt_key() | key_path(), module_opts()) -> opt_value().
get_opt(Path, Opts) when is_list(Path), is_map(Opts) ->
lists:foldl(fun maps:get/2, Opts, Path);
get_opt(Opt, Opts) when is_map(Opts) ->
maps:get(Opt, Opts);
get_opt(Opt, Opts) ->
case lists:keysearch(Opt, 1, Opts) of
false ->
Expand All @@ -273,14 +283,16 @@ get_opt(Opt, Opts) ->
Val
end.

get_opt(Opt, Opts, Default) ->
case lists:keysearch(Opt, 1, Opts) of
false ->
Default;
{value, {_, Val}} ->
Val
-spec get_opt(opt_key() | key_path(), module_opts(), opt_value()) -> opt_value().
get_opt(Path, Opts, Default) ->
try
get_opt(Path, Opts)
catch
error:{badkey, _} -> Default;
throw:{undefined_option, _} -> Default
end.

%% @deprecated Processing should be done in the config spec
get_opt(Opt, Opts, F, Default) ->
case lists:keysearch(Opt, 1, Opts) of
false ->
Expand All @@ -289,16 +301,12 @@ get_opt(Opt, Opts, F, Default) ->
F(Val)
end.

-spec set_opt(_, [tuple()], _) -> [tuple(), ...].
%% @deprecated Options shouldn't be modified
set_opt(Opt, Opts, Value) ->
lists:keystore(Opt, 1, Opts, {Opt, Value}).


%%% TODO Make Opt an atom. Fix in mod_auth_token:
%%% 374: The call gen_mod:get_module_opt(Domain::any(), 'mod_auth_token',
%%% {'validity_period','access' | 'refresh'}, {1 | 25,'days' | 'hours'})
%%% breaks the contract (mongooseim:host_type(), module(), atom(), term()) -> term()
-spec get_module_opt(mongooseim:host_type(), module(), term(), term()) -> term().
-spec get_module_opt(mongooseim:host_type(), module(), opt_key() | key_path(), opt_value()) ->
opt_value().
get_module_opt(HostType, Module, Opt, Default) ->
%% Fail in dev builds.
%% It protects against passing something weird as a Module argument
Expand All @@ -307,10 +315,17 @@ get_module_opt(HostType, Module, Opt, Default) ->
ModuleOpts = get_module_opts(HostType, Module),
get_opt(Opt, ModuleOpts, Default).

-spec get_module_opt(mongooseim:host_type(), module(), opt_key() | key_path()) -> opt_value().
get_module_opt(HostType, Module, Opt) ->
?ASSERT_MODULE(Module),
ModuleOpts = get_loaded_module_opts(HostType, Module),
get_opt(Opt, ModuleOpts).

-spec get_module_opts(mongooseim:host_type(), module()) -> module_opts().
get_module_opts(HostType, Module) ->
mongoose_config:get_opt([{modules, HostType}, Module], []).

-spec get_loaded_module_opts(mongooseim:host_type(), module()) -> module_opts().
get_loaded_module_opts(HostType, Module) ->
mongoose_config:get_opt([{modules, HostType}, Module]).

Expand Down
20 changes: 10 additions & 10 deletions src/mam/mod_mam_rdbms_arch_async.erl
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,20 @@ supported_features() ->
-spec start_pool(mongooseim:host_type(), {writer_type(), gen_mod:module_opts()}) ->
supervisor:startchild_ret().
start_pool(HostType, {Type, Opts}) ->
{Opts1, Extra} = extend_opts(Type, Opts),
{PoolOpts, Extra} = make_pool_opts(Type, Opts),
prepare_insert_queries(Type, Extra),
ensure_metrics(Type, HostType),
register_hooks(Type, HostType),
start_pool(Type, HostType, Opts1).
start_pool(Type, HostType, PoolOpts).

extend_opts(Type, Opts) ->
-spec make_pool_opts(writer_type(), gen_mod:module_opts()) ->
{mongoose_async_pools:pool_opts(), mongoose_async_pools:pool_extra()}.
make_pool_opts(Type, Opts) ->
Merge = maps:merge(defaults(), maps:from_list(Opts)),
Extra = add_batch_name(Type, Merge),
Opts1 = maps:to_list(Extra),
Opts2 = gen_mod:set_opt(flush_extra, Opts1, Extra),
{add_callback(Type, Opts2), Extra}.
PoolOpts = Extra#{flush_callback => flush_callback(Type),
flush_extra => Extra},
{PoolOpts, Extra}.

%% Put batch_size into a statement name, so we could survive the module restarts
%% with different batch sizes
Expand All @@ -84,10 +86,8 @@ add_batch_name(pm, #{batch_size := MaxSize} = Opts) ->
add_batch_name(muc, #{batch_size := MaxSize} = Opts) ->
Opts#{batch_name => multi_name(insert_mam_muc_messages, MaxSize)}.

add_callback(pm, Opts) ->
gen_mod:set_opt(flush_callback, Opts, fun ?MODULE:flush_pm/2);
add_callback(muc, Opts) ->
gen_mod:set_opt(flush_callback, Opts, fun ?MODULE:flush_muc/2).
flush_callback(pm) -> fun ?MODULE:flush_pm/2;
flush_callback(muc) -> fun ?MODULE:flush_muc/2.

defaults() ->
#{flush_interval => 2000,
Expand Down
65 changes: 27 additions & 38 deletions src/mod_auth_token.erl
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
-export([supported_features/0]).
-export([config_spec/0]).

%% Config spec callbacks
-export([process_validity_period/1]).

%% Hook handlers
-export([clean_tokens/3,
disco_local_features/1]).
Expand Down Expand Up @@ -59,8 +56,8 @@
]).

-type error() :: error | {error, any()}.
-type period() :: {Count :: non_neg_integer(),
Unit :: 'days' | 'hours' | 'minutes' | 'seconds'}.
-type period() :: #{value := non_neg_integer(),
unit := days | hours | minutes | seconds}.
-type sequence_no() :: integer().
-type serialized() :: binary().
-type token() :: #token{}.
Expand All @@ -79,9 +76,8 @@
%%

-spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
start(HostType, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue),
mod_auth_token_backend:start(HostType, Opts),
start(HostType, #{iqdisc := IQDisc}) ->
mod_auth_token_backend:start(HostType),
ejabberd_hooks:add(hooks(HostType)),
gen_iq_handler:add_iq_handler_for_domain(
HostType, ?NS_ESL_TOKEN_AUTH, ejabberd_sm,
Expand All @@ -106,30 +102,33 @@ supported_features() ->
-spec config_spec() -> mongoose_config_spec:config_section().
config_spec() ->
#section{
items = #{<<"validity_period">> => #list{items = validity_period_spec(),
wrap = none},
<<"iqdisc">> => mongoose_config_spec:iqdisc()
}
items = #{<<"validity_period">> => validity_periods_spec(),
<<"iqdisc">> => mongoose_config_spec:iqdisc()},
defaults = #{<<"iqdisc">> => no_queue},
format_items = map
}.

validity_periods_spec() ->
#section{
items = #{<<"access">> => validity_period_spec(),
<<"refresh">> => validity_period_spec()},
defaults = #{<<"access">> => #{value => 1, unit => hours},
<<"refresh">> => #{value => 25, unit => days}},
format_items = map,
include = always
}.

validity_period_spec() ->
#section{
items = #{<<"token">> => #option{type = atom,
validate = {enum, [access, refresh, provision]}},
<<"value">> => #option{type = integer,
items = #{<<"value">> => #option{type = integer,
validate = non_negative},
<<"unit">> => #option{type = atom,
validate = {enum, [days, hours, minutes, seconds]}}
},
required = all,
process = fun ?MODULE:process_validity_period/1
format_items = map
}.

process_validity_period(KVs) ->
{[[{token, Token}], [{value, Value}], [{unit, Unit}]], []} =
proplists:split(KVs, [token, value, unit]),
{{validity_period, Token}, {Value, Unit}}.

-spec commands() -> [ejabberd_commands:cmd()].
commands() ->
[#ejabberd_commands{ name = revoke_token, tags = [tokens],
Expand Down Expand Up @@ -350,27 +349,20 @@ token(HostType, User, Type) ->
{error, {Class, Reason}}
end.

%% {modules, [
%% {mod_auth_token, [{{validity_period, access}, {13, minutes}},
%% {{validity_period, refresh}, {13, days}}]}
%% ]}.
-spec expiry_datetime(mongooseim:host_type(), token_type(), non_neg_integer()) ->
calendar:datetime().
expiry_datetime(HostType, Type, UTCSeconds) ->
Period = get_validity_period(HostType, Type),
seconds_to_datetime(UTCSeconds + period_to_seconds(Period)).
#{value := Value, unit := Unit} = get_validity_period(HostType, Type),
seconds_to_datetime(UTCSeconds + period_to_seconds(Value, Unit)).

-spec get_validity_period(mongooseim:host_type(), token_type()) -> period().
get_validity_period(HostType, Type) ->
gen_mod:get_module_opt(HostType, ?MODULE, {validity_period, Type},
default_validity_period(Type)).

period_to_seconds({Days, days}) -> milliseconds_to_seconds(timer:hours(24 * Days));
period_to_seconds({Hours, hours}) -> milliseconds_to_seconds(timer:hours(Hours));
period_to_seconds({Minutes, minutes}) -> milliseconds_to_seconds(timer:minutes(Minutes));
period_to_seconds({Seconds, seconds}) -> milliseconds_to_seconds(timer:seconds(Seconds)).
gen_mod:get_module_opt(HostType, ?MODULE, [validity_period, Type]).

milliseconds_to_seconds(Millis) -> erlang:round(Millis / 1000).
period_to_seconds(Days, days) -> 24 * 3600 * Days;
period_to_seconds(Hours, hours) -> 3600 * Hours;
period_to_seconds(Minutes, minutes) -> 60 * Minutes;
period_to_seconds(Seconds, seconds) -> Seconds.

token_to_xmlel(#token{type = Type} = T) ->
#xmlel{name = case Type of
Expand All @@ -380,9 +372,6 @@ token_to_xmlel(#token{type = Type} = T) ->
attrs = [{<<"xmlns">>, ?NS_ESL_TOKEN_AUTH}],
children = [#xmlcdata{content = jlib:encode_base64(serialize(T))}]}.

default_validity_period(access) -> {1, hours};
default_validity_period(refresh) -> {25, days}.

%% args: Token with Mac decoded from transport, #token
%% is shared between tokens. Introduce other container types if
%% they start to differ more than a few fields.
Expand Down
8 changes: 4 additions & 4 deletions src/mod_auth_token_backend.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
%%% @end
%%%----------------------------------------------------------------------
-module(mod_auth_token_backend).
-export([start/2,
-export([start/1,
revoke/2,
get_valid_sequence_number/2,
clean_tokens/2]).
Expand All @@ -26,9 +26,9 @@
%% ----------------------------------------------------------------------
%% API Functions

-spec start(HostType :: mongooseim:host_type(), Opts :: gen_mod:module_opts()) -> ok.
start(HostType, Opts) ->
mongoose_backend:init(HostType, ?MAIN_MODULE, [], [{backend, rdbms} | Opts]),
-spec start(HostType :: mongooseim:host_type()) -> ok.
start(HostType) ->
mongoose_backend:init(HostType, ?MAIN_MODULE, [], #{backend => rdbms}),
Args = [HostType],
mongoose_backend:call(HostType, ?MAIN_MODULE, ?FUNCTION_NAME, Args).

Expand Down
Loading

0 comments on commit d0415c9

Please sign in to comment.