summaryrefslogtreecommitdiff
path: root/src/mod_http_api.erl
diff options
context:
space:
mode:
authorMickael Remond <mremond@process-one.net>2016-03-29 19:40:20 +0200
committerMickael Remond <mremond@process-one.net>2016-03-29 19:40:20 +0200
commit82cf7f7ca804744fb820593fcc5084bf34922f4f (patch)
treef3161893b64ac0782473496af29b72b3fc0aad2c /src/mod_http_api.erl
parentMerge branch 'master' of github.com:processone/ejabberd (diff)
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)
Diffstat (limited to 'src/mod_http_api.erl')
-rw-r--r--src/mod_http_api.erl119
1 files changed, 84 insertions, 35 deletions
diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl
index f46860a5..15fe3636 100644
--- a/src/mod_http_api.erl
+++ b/src/mod_http_api.erl
@@ -43,6 +43,25 @@
%%
%% Then to perform an action, send a POST request to the following URL:
%% 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).
@@ -102,46 +121,72 @@ stop(_Host) ->
%% basic auth
%% ----------
-check_permissions(#request{auth = HTTPAuth, headers = Headers}, Command)
- when HTTPAuth /= undefined ->
+check_permissions(Request, Command) ->
case catch binary_to_existing_atom(Command, utf8) of
Call when is_atom(Call) ->
- Admin =
- case lists:keysearch(<<"X-Admin">>, 1, Headers) of
- {value, {_, <<"true">>}} -> true;
- _ -> false
- end,
- Auth =
- case HTTPAuth of
- {SJID, Pass} ->
- case jid:from_string(SJID) of
- #jid{user = User, server = Server} ->
- case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
- true -> {ok, {User, Server, Pass, Admin}};
- false -> false
- end;
- _ ->
- false
- end;
- {oauth, Token, _} ->
- case ejabberd_oauth:check_token(Command, Token) of
- {ok, User, Server} ->
- {ok, {User, Server, {oauth, Token}, Admin}};
- false ->
- false
+ check_permissions2(Request, Call);
+ _ ->
+ unauthorized_response()
+ end.
+
+check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call)
+ when HTTPAuth /= undefined ->
+ Admin =
+ case lists:keysearch(<<"X-Admin">>, 1, Headers) of
+ {value, {_, <<"true">>}} -> true;
+ _ -> false
+ end,
+ Auth =
+ case HTTPAuth of
+ {SJID, Pass} ->
+ case jid:from_string(SJID) of
+ #jid{user = User, server = Server} ->
+ case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
+ true -> {ok, {User, Server, Pass, Admin}};
+ false -> false
end;
_ ->
false
- end,
- case Auth of
- {ok, A} -> {allowed, Call, A};
+ end;
+ {oauth, Token, _} ->
+ case oauth_check_token(Call, Token) of
+ {ok, User, Server} ->
+ {ok, {User, Server, {oauth, Token}, Admin}};
+ false ->
+ false
+ end;
+ _ ->
+ false
+ end,
+ case Auth of
+ {ok, A} -> {allowed, Call, A};
+ _ -> unauthorized_response()
+ 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()
- end;
-check_permissions(_, _Command) ->
- unauthorized_response().
+ end.
+
+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
@@ -162,7 +207,7 @@ process([Call], #request{method = 'POST', data = Data, ip = IP} = Req) ->
{allowed, Cmd, Auth} ->
{Code, Result} = handle(Cmd, Auth, Args),
json_response(Code, jiffy:encode(Result));
- ErrorResponse ->
+ ErrorResponse -> %% Should we reply 403 ?
ErrorResponse
end
catch _:Error ->
@@ -309,7 +354,11 @@ match(Args, Spec) ->
[{Key, proplists:get_value(Key, Args, Default)} || {Key, Default} <- Spec].
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;
{error, _} -> Default;
Result -> Result
@@ -387,6 +436,6 @@ log(Call, Args, {Addr, Port}) ->
AddrS = jlib:ip_to_list({Addr, 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;
-mod_opt_type(_) -> [access].
+mod_opt_type(_) -> [admin_ip_access].