Try to limit serial access when checking api permissions

This commit is contained in:
Paweł Chmielowski 2021-06-24 15:15:13 +02:00
parent 774de2bdc5
commit 795addca7d
1 changed files with 35 additions and 33 deletions

View File

@ -46,6 +46,7 @@
code_change/3]). code_change/3]).
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
-define(CACHE_TAB, access_permissions_cache).
-record(state, -record(state,
{definitions = none :: none | [definition()]}). {definitions = none :: none | [definition()]}).
@ -71,17 +72,45 @@
%%%=================================================================== %%%===================================================================
-spec can_access(atom(), caller_info()) -> allow | deny. -spec can_access(atom(), caller_info()) -> allow | deny.
can_access(Cmd, CallerInfo) -> can_access(Cmd, CallerInfo) ->
gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}). Defs0 = show_current_definitions(),
CallerModule = maps:get(caller_module, CallerInfo, none),
Host = maps:get(caller_host, CallerInfo, global),
Tag = maps:get(tag, CallerInfo, none),
Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0,
Res = lists:foldl(
fun({Name, _} = Def, none) ->
case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
true ->
?DEBUG("Command '~p' execution allowed by rule "
"'~ts' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
allow;
_ ->
none
end;
(_, Val) ->
Val
end, none, Defs),
case Res of
allow -> allow;
_ ->
?DEBUG("Command '~p' execution denied "
"(CallerInfo=~p)", [Cmd, CallerInfo]),
deny
end.
-spec invalidate() -> ok. -spec invalidate() -> ok.
invalidate() -> invalidate() ->
gen_server:cast(?MODULE, invalidate). gen_server:cast(?MODULE, invalidate),
ets_cache:delete(?CACHE_TAB, definitions).
-spec show_current_definitions() -> [definition()]. -spec show_current_definitions() -> [definition()].
show_current_definitions() -> show_current_definitions() ->
gen_server:call(?MODULE, show_current_definitions). ets_cache:lookup(?CACHE_TAB, definitions,
fun() ->
{cache, gen_server:call(?MODULE, show_current_definitions)}
end).
start_link() -> start_link() ->
ets_cache:new(?CACHE_TAB, [{max_size, 2}]),
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%%=================================================================== %%%===================================================================
@ -90,38 +119,11 @@ start_link() ->
-spec init([]) -> {ok, state()}. -spec init([]) -> {ok, state()}.
init([]) -> init([]) ->
ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90), ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90),
ets_cache:new(access_permissions),
{ok, #state{}}. {ok, #state{}}.
-spec handle_call({can_access, atom(), caller_info()} | -spec handle_call(show_current_definitions | term(),
show_current_definitions | term(),
term(), state()) -> {reply, term(), state()}. term(), state()) -> {reply, term(), state()}.
handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
CallerModule = maps:get(caller_module, CallerInfo, none),
Host = maps:get(caller_host, CallerInfo, global),
Tag = maps:get(tag, CallerInfo, none),
{State2, Defs0} = get_definitions(State),
Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0,
Res = lists:foldl(
fun({Name, _} = Def, none) ->
case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
true ->
?DEBUG("Command '~p' execution allowed by rule "
"'~ts' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
allow;
_ ->
none
end;
(_, Val) ->
Val
end, none, Defs),
Res2 = case Res of
allow -> allow;
_ ->
?DEBUG("Command '~p' execution denied "
"(CallerInfo=~p)", [Cmd, CallerInfo]),
deny
end,
{reply, Res2, State2};
handle_call(show_current_definitions, _From, State) -> handle_call(show_current_definitions, _From, State) ->
{State2, Defs} = get_definitions(State), {State2, Defs} = get_definitions(State),
{reply, Defs, State2}; {reply, Defs, State2};