]> granicus.if.org Git - ejabberd/commitdiff
Adds support for option admin_ip_access on mod_http_api
authorMickael Remond <mremond@process-one.net>
Tue, 29 Mar 2016 17:40:20 +0000 (19:40 +0200)
committerMickael Remond <mremond@process-one.net>
Tue, 29 Mar 2016 17:40:20 +0000 (19:40 +0200)
This allows granting access to admin commands to backend, by using IP address restrictions.
(Pawel Chmielowski)

src/mod_http_api.erl

index f46860a56b623ba526fe674b7bebbdabe44fda17..15fe363642ffc972d071cb42adc327e3d3bbc8d1 100644 (file)
 %%
 %% 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].