Allow specifying tag for listener for api_permission purposes

This commit will allow adding tag to http listeners:

listener:
  - port: 4000
  - module: ejabberd_http
  - tag: "magic_listener"

that later can be used to have special api_permission just for it:

api_permissions:
  "magic_access":
    from:
      - tag: "magic_listener"
    who: all
    what: "*"
This commit is contained in:
Paweł Chmielowski 2019-01-30 12:56:52 +01:00
parent 23e5b3756c
commit 62ad1e5e4f
3 changed files with 22 additions and 14 deletions

View File

@ -130,11 +130,12 @@ init([]) ->
handle_call({can_access, Cmd, CallerInfo}, _From, State) -> handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
CallerModule = maps:get(caller_module, CallerInfo, none), CallerModule = maps:get(caller_module, CallerInfo, none),
Host = maps:get(caller_host, CallerInfo, global), Host = maps:get(caller_host, CallerInfo, global),
Tag = maps:get(tag, CallerInfo, none),
{State2, Defs0} = get_definitions(State), {State2, Defs0} = get_definitions(State),
Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0, Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0,
Res = lists:foldl( Res = lists:foldl(
fun({Name, _} = Def, none) -> fun({Name, _} = Def, none) ->
case matches_definition(Def, Cmd, CallerModule, Host, CallerInfo) of case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
true -> true ->
?DEBUG("Command '~p' execution allowed by rule '~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]), ?DEBUG("Command '~p' execution allowed by rule '~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
allow; allow;
@ -261,10 +262,10 @@ get_definitions(#state{definitions = none, fragments_generators = Gens} = State)
end, end,
{State#state{definitions = NDefs}, NDefs}. {State#state{definitions = NDefs}, NDefs}.
matches_definition({_Name, {From, Who, What}}, Cmd, Module, Host, CallerInfo) -> matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInfo) ->
case What == all orelse lists:member(Cmd, What) of case What == all orelse lists:member(Cmd, What) of
true -> true ->
case From == [] orelse lists:member(Module, From) of case From == [] orelse lists:member(Module, From) orelse lists:member({tag, Tag}, From) of
true -> true ->
Scope = maps:get(oauth_scope, CallerInfo, none), Scope = maps:get(oauth_scope, CallerInfo, none),
lists:any( lists:any(
@ -347,13 +348,15 @@ parse_api_permission(Name, Args0) ->
parse_from(_Name, Module) when is_atom(Module) -> parse_from(_Name, Module) when is_atom(Module) ->
[Module]; [Module];
parse_from(Name, Modules) when is_list(Modules) -> parse_from(Name, Modules) when is_list(Modules) ->
lists:foreach(fun(Module) when is_atom(Module) -> lists:map(
ok; fun(Module) when is_atom(Module) ->
(Val) -> Module;
report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>, ([{tag, Tag}]) when is_binary(Tag) ->
[Val, Name]) {tag, Tag};
end, Modules), (Val) ->
Modules; report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>,
[Val, Name])
end, Modules);
parse_from(Name, Val) -> parse_from(Name, Val) ->
report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>, report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>,
[Val, Name]). [Val, Name]).

View File

@ -992,6 +992,8 @@ listen_opt_type(http_bind) ->
fun(B) when is_boolean(B) -> B end; fun(B) when is_boolean(B) -> B end;
listen_opt_type(xmlrpc) -> listen_opt_type(xmlrpc) ->
fun(B) when is_boolean(B) -> B end; fun(B) when is_boolean(B) -> B end;
listen_opt_type(tag) ->
fun(B) when is_binary(B) -> B end;
listen_opt_type(request_handlers) -> listen_opt_type(request_handlers) ->
fun(Hs) -> fun(Hs) ->
Hs1 = lists:map(fun Hs1 = lists:map(fun
@ -1026,5 +1028,6 @@ listen_options() ->
{http_bind, false}, {http_bind, false},
{xmlrpc, false}, {xmlrpc, false},
{request_handlers, []}, {request_handlers, []},
{tag, <<>>},
{default_host, undefined}, {default_host, undefined},
{custom_headers, []}]. {custom_headers, []}].

View File

@ -137,7 +137,7 @@ depends(_Host, _Opts) ->
%% basic auth %% basic auth
%% ---------- %% ----------
extract_auth(#request{auth = HTTPAuth, ip = {IP, _}}) -> extract_auth(#request{auth = HTTPAuth, ip = {IP, _}, opts = Opts}) ->
Info = case HTTPAuth of Info = case HTTPAuth of
{SJID, Pass} -> {SJID, Pass} ->
try jid:decode(SJID) of try jid:decode(SJID) of
@ -163,13 +163,15 @@ extract_auth(#request{auth = HTTPAuth, ip = {IP, _}}) ->
end, end,
case Info of case Info of
Map when is_map(Map) -> Map when is_map(Map) ->
Map#{caller_module => ?MODULE, ip => IP}; Tag = proplists:get_value(tag, Opts, <<>>),
Map#{caller_module => ?MODULE, ip => IP, tag => Tag};
_ -> _ ->
?DEBUG("Invalid auth data: ~p", [Info]), ?DEBUG("Invalid auth data: ~p", [Info]),
Info Info
end; end;
extract_auth(#request{ip = IP}) -> extract_auth(#request{ip = IP, opts = Opts}) ->
#{ip => IP, caller_module => ?MODULE}. Tag = proplists:get_value(tag, Opts, <<>>),
#{ip => IP, caller_module => ?MODULE, tag => Tag}.
%% ------------------ %% ------------------
%% command processing %% command processing