mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Adds support for option admin_ip_access on mod_http_api
This allows granting access to admin commands to backend, by using IP address restrictions. (Pawel Chmielowski)
This commit is contained in:
parent
6d7891ed16
commit
82cf7f7ca8
@ -43,6 +43,25 @@
|
|||||||
%%
|
%%
|
||||||
%% Then to perform an action, send a POST request to the following URL:
|
%% Then to perform an action, send a POST request to the following URL:
|
||||||
%% http://localhost:5280/api/<call_name>
|
%% http://localhost:5280/api/<call_name>
|
||||||
|
%%
|
||||||
|
%% It's also possible to enable unrestricted access to some commands from group
|
||||||
|
%% of IP addresses by using option `admin_ip_access` by having fragment like
|
||||||
|
%% this in configuration file:
|
||||||
|
%% modules:
|
||||||
|
%% mod_http_api:
|
||||||
|
%% admin_ip_access: admin_ip_access_rule
|
||||||
|
%%...
|
||||||
|
%% access:
|
||||||
|
%% admin_ip_access_rule:
|
||||||
|
%% admin_ip_acl:
|
||||||
|
%% - command1
|
||||||
|
%% - command2
|
||||||
|
%% %% use `all` to give access to all commands
|
||||||
|
%%...
|
||||||
|
%% acl:
|
||||||
|
%% admin_ip_acl:
|
||||||
|
%% ip:
|
||||||
|
%% - "127.0.0.1/8"
|
||||||
|
|
||||||
-module(mod_http_api).
|
-module(mod_http_api).
|
||||||
|
|
||||||
@ -102,10 +121,16 @@ stop(_Host) ->
|
|||||||
%% basic auth
|
%% basic auth
|
||||||
%% ----------
|
%% ----------
|
||||||
|
|
||||||
check_permissions(#request{auth = HTTPAuth, headers = Headers}, Command)
|
check_permissions(Request, Command) ->
|
||||||
when HTTPAuth /= undefined ->
|
|
||||||
case catch binary_to_existing_atom(Command, utf8) of
|
case catch binary_to_existing_atom(Command, utf8) of
|
||||||
Call when is_atom(Call) ->
|
Call when is_atom(Call) ->
|
||||||
|
check_permissions2(Request, Call);
|
||||||
|
_ ->
|
||||||
|
unauthorized_response()
|
||||||
|
end.
|
||||||
|
|
||||||
|
check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call)
|
||||||
|
when HTTPAuth /= undefined ->
|
||||||
Admin =
|
Admin =
|
||||||
case lists:keysearch(<<"X-Admin">>, 1, Headers) of
|
case lists:keysearch(<<"X-Admin">>, 1, Headers) of
|
||||||
{value, {_, <<"true">>}} -> true;
|
{value, {_, <<"true">>}} -> true;
|
||||||
@ -124,7 +149,7 @@ check_permissions(#request{auth = HTTPAuth, headers = Headers}, Command)
|
|||||||
false
|
false
|
||||||
end;
|
end;
|
||||||
{oauth, Token, _} ->
|
{oauth, Token, _} ->
|
||||||
case ejabberd_oauth:check_token(Command, Token) of
|
case oauth_check_token(Call, Token) of
|
||||||
{ok, User, Server} ->
|
{ok, User, Server} ->
|
||||||
{ok, {User, Server, {oauth, Token}, Admin}};
|
{ok, {User, Server, {oauth, Token}, Admin}};
|
||||||
false ->
|
false ->
|
||||||
@ -137,11 +162,31 @@ check_permissions(#request{auth = HTTPAuth, headers = Headers}, Command)
|
|||||||
{ok, A} -> {allowed, Call, A};
|
{ok, A} -> {allowed, Call, A};
|
||||||
_ -> unauthorized_response()
|
_ -> unauthorized_response()
|
||||||
end;
|
end;
|
||||||
|
check_permissions2(#request{ip={IP, _Port}}, Call) ->
|
||||||
|
Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access,
|
||||||
|
mod_opt_type(admin_ip_access),
|
||||||
|
none),
|
||||||
|
Res = acl:match_rule(global, Access, IP),
|
||||||
|
case Res of
|
||||||
|
all ->
|
||||||
|
{allowed, Call, admin};
|
||||||
|
[all] ->
|
||||||
|
{allowed, Call, admin};
|
||||||
|
allow ->
|
||||||
|
{allowed, Call, admin};
|
||||||
|
Commands when is_list(Commands) ->
|
||||||
|
case lists:member(Call, Commands) of
|
||||||
|
true -> {allowed, Call, admin};
|
||||||
|
_ -> unauthorized_response()
|
||||||
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
unauthorized_response()
|
unauthorized_response()
|
||||||
end;
|
end.
|
||||||
check_permissions(_, _Command) ->
|
|
||||||
unauthorized_response().
|
oauth_check_token(Scope, Token) when is_atom(Scope) ->
|
||||||
|
oauth_check_token(atom_to_binary(Scope, utf8), Token);
|
||||||
|
oauth_check_token(Scope, Token) ->
|
||||||
|
ejabberd_oauth:check_token(Scope, Token).
|
||||||
|
|
||||||
%% ------------------
|
%% ------------------
|
||||||
%% command processing
|
%% command processing
|
||||||
@ -162,7 +207,7 @@ process([Call], #request{method = 'POST', data = Data, ip = IP} = Req) ->
|
|||||||
{allowed, Cmd, Auth} ->
|
{allowed, Cmd, Auth} ->
|
||||||
{Code, Result} = handle(Cmd, Auth, Args),
|
{Code, Result} = handle(Cmd, Auth, Args),
|
||||||
json_response(Code, jiffy:encode(Result));
|
json_response(Code, jiffy:encode(Result));
|
||||||
ErrorResponse ->
|
ErrorResponse -> %% Should we reply 403 ?
|
||||||
ErrorResponse
|
ErrorResponse
|
||||||
end
|
end
|
||||||
catch _:Error ->
|
catch _:Error ->
|
||||||
@ -309,7 +354,11 @@ match(Args, Spec) ->
|
|||||||
[{Key, proplists:get_value(Key, Args, Default)} || {Key, Default} <- Spec].
|
[{Key, proplists:get_value(Key, Args, Default)} || {Key, Default} <- Spec].
|
||||||
|
|
||||||
ejabberd_command(Auth, Cmd, Args, Default) ->
|
ejabberd_command(Auth, Cmd, Args, Default) ->
|
||||||
case catch ejabberd_commands:execute_command(undefined, Auth, Cmd, Args) of
|
Access = case Auth of
|
||||||
|
admin -> [];
|
||||||
|
_ -> undefined
|
||||||
|
end,
|
||||||
|
case catch ejabberd_commands:execute_command(Access, Auth, Cmd, Args) of
|
||||||
{'EXIT', _} -> Default;
|
{'EXIT', _} -> Default;
|
||||||
{error, _} -> Default;
|
{error, _} -> Default;
|
||||||
Result -> Result
|
Result -> Result
|
||||||
@ -387,6 +436,6 @@ log(Call, Args, {Addr, Port}) ->
|
|||||||
AddrS = jlib:ip_to_list({Addr, Port}),
|
AddrS = jlib:ip_to_list({Addr, Port}),
|
||||||
?INFO_MSG("API call ~s ~p from ~s:~p", [Call, Args, AddrS, Port]).
|
?INFO_MSG("API call ~s ~p from ~s:~p", [Call, Args, AddrS, Port]).
|
||||||
|
|
||||||
mod_opt_type(access) ->
|
mod_opt_type(admin_ip_access) ->
|
||||||
fun(Access) when is_atom(Access) -> Access end;
|
fun(Access) when is_atom(Access) -> Access end;
|
||||||
mod_opt_type(_) -> [access].
|
mod_opt_type(_) -> [admin_ip_access].
|
||||||
|
Loading…
Reference in New Issue
Block a user