]> granicus.if.org Git - ejabberd/commitdiff
Add support for checking access rules conformance for commands
authorMickael Remond <mremond@process-one.net>
Sat, 23 Jul 2016 15:57:44 +0000 (17:57 +0200)
committerMickael Remond <mremond@process-one.net>
Sat, 23 Jul 2016 16:21:45 +0000 (18:21 +0200)
include/ejabberd_commands.hrl
src/ejabberd_admin.erl
src/ejabberd_commands.erl
src/mod_http_api.erl

index 2b4eca581b6d0af5c9a6f09cf103977d983916bc..bafd93a4d8b450080117d441553fe069eea90f5c 100644 (file)
          function                :: atom() | '_',
          args = []               :: [aterm()] | '_' | '$1' | '$2',
          policy = restricted     :: open | restricted | admin | user,
+         access_rules = []       :: [atom()],
          result = {res, rescode} :: rterm() | '_' | '$2',
          args_desc = none        :: none | [string()] | '_',
          result_desc = none      :: none | string() | '_',
          args_example = none     :: none | [any()] | '_',
          result_example = none   :: any()}).
 
+%% TODO Fix me: Type is not up to date
 -type ejabberd_commands() :: #ejabberd_commands{name :: atom(),
                                                 tags :: [atom()],
                                                 desc :: string(),
                                                 longdesc :: string(),
+                                                version :: integer(),
                                                 module :: atom(),
                                                 function :: atom(),
                                                 args :: [aterm()],
+                                                policy :: open | restricted | admin | user,
+                                                access_rules :: [atom()],
                                                 result :: rterm()}.
 
 %% @type ejabberd_commands() = #ejabberd_commands{
index 87ac76875b38f5d6998ea858316faa2eb56a2014..68aaf9be6e1e895df1eacda8bf29d7545ff38bda 100644 (file)
@@ -129,6 +129,8 @@ get_commands_spec() ->
 
      #ejabberd_commands{name = register, tags = [accounts],
                        desc = "Register a user",
+                       policy = admin,
+                       access_rules = [configure],
                        module = ?MODULE, function = register,
                        args = [{user, binary}, {host, binary}, {password, binary}],
                        result = {res, restuple}},
@@ -166,7 +168,7 @@ get_commands_spec() ->
      #ejabberd_commands{name = list_cluster, tags = [cluster],
                        desc = "List nodes that are part of the cluster handled by Node",
                        module = ?MODULE, function = list_cluster,
-                       args = [], 
+                       args = [],
                        result = {nodes, {list, {node, atom}}}},
 
      #ejabberd_commands{name = import_file, tags = [mnesia],
@@ -220,7 +222,7 @@ get_commands_spec() ->
                        desc = "Delete offline messages older than DAYS",
                        module = ?MODULE, function = delete_old_messages,
                        args = [{days, integer}], result = {res, rescode}},
-        
+
      #ejabberd_commands{name = export2sql, tags = [mnesia],
                        desc = "Export virtual host information from Mnesia tables to SQL files",
                        module = ejd2sql, function = export,
index 075ff35cfc267dfe86647acc0aa852edb1752d8b..0f8cd2e0a81932056b239a2fc368afadda55d488 100644 (file)
@@ -494,7 +494,7 @@ execute_command(AccessCommands, Auth, Name, Arguments) ->
 %%
 %% @doc Execute a command in a given API version
 %% Can return the following exceptions:
-%% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided
+%% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided | access_rules_unauthorized
 execute_command(AccessCommands1, Auth1, Name, Arguments, Version) ->
 execute_command(AccessCommands1, Auth1, Name, Arguments, Version, #{}).
 
@@ -503,32 +503,45 @@ execute_command(AccessCommands1, Auth1, Name, Arguments, Version, CallerInfo) ->
                true -> admin;
                false -> Auth1
            end,
+    TokenJID = oauth_token_user(Auth1),
     Command = get_command_definition(Name, Version),
     AccessCommands = get_access_commands(AccessCommands1, Version),
     case check_access_commands(AccessCommands, Auth, Name, Command, Arguments, CallerInfo) of
-       ok -> execute_command2(Auth, Command, Arguments)
+        ok -> execute_check_policy(Auth, TokenJID, Command, Arguments)
     end.
 
-execute_command2(
-  _Auth, #ejabberd_commands{policy = open} = Command, Arguments) ->
-    execute_command2(Command, Arguments);
-execute_command2(
-  _Auth, #ejabberd_commands{policy = restricted} = Command, Arguments) ->
-    execute_command2(Command, Arguments);
-execute_command2(
-  _Auth, #ejabberd_commands{policy = admin} = Command, Arguments) ->
-    execute_command2(Command, Arguments);
-execute_command2(
-  admin, #ejabberd_commands{policy = user} = Command, Arguments) ->
-    execute_command2(Command, Arguments);
-execute_command2(
-  noauth, #ejabberd_commands{policy = user} = Command, Arguments) ->
-    execute_command2(Command, Arguments);
-execute_command2(
-  {User, Server, _, _}, #ejabberd_commands{policy = user} = Command, Arguments) ->
-    execute_command2(Command, [User, Server | Arguments]).
-
-execute_command2(Command, Arguments) ->
+
+execute_check_policy(
+  _Auth, _JID, #ejabberd_commands{policy = open} = Command, Arguments) ->
+    do_execute_command(Command, Arguments);
+execute_check_policy(
+  _Auth, _JID, #ejabberd_commands{policy = restricted} = Command, Arguments) ->
+    do_execute_command(Command, Arguments);
+execute_check_policy(
+  _Auth, JID, #ejabberd_commands{policy = admin} = Command, Arguments) ->
+    execute_check_access(JID, Command, Arguments);
+execute_check_policy(
+  admin, JID, #ejabberd_commands{policy = user} = Command, Arguments) ->
+    execute_check_access(JID, Command, Arguments);
+execute_check_policy(
+  noauth, _JID, #ejabberd_commands{policy = user} = Command, Arguments) ->
+    do_execute_command(Command, Arguments);
+execute_check_policy(
+  {User, Server, _, _}, JID, #ejabberd_commands{policy = user} = Command, Arguments) ->
+    execute_check_access(JID, Command, [User, Server | Arguments]).
+
+execute_check_access(_FromJID, #ejabberd_commands{access_rules = []} = Command, Arguments) ->
+    do_execute_command(Command, Arguments);
+execute_check_access(FromJID, #ejabberd_commands{access_rules = Rules} = Command, Arguments) ->
+    %% TODO Review: Do we have smarter / better way to check rule on other Host than global ?
+    case acl:any_rules_allowed(global, Rules, FromJID) of
+        true ->
+            do_execute_command(Command, Arguments);
+        false ->
+            {error, access_rules_unauthorized}
+    end.
+
+do_execute_command(Command, Arguments) ->
     Module = Command#ejabberd_commands.module,
     Function = Command#ejabberd_commands.function,
     ?DEBUG("Executing command ~p:~p with Args=~p", [Module, Function, Arguments]),
@@ -754,6 +767,13 @@ get_commands(Version) ->
           end, AdminCmds ++ UserCmds, Opts),
     Cmds.
 
+oauth_token_user(noauth) ->
+    undefined;
+oauth_token_user(admin) ->
+    undefined;
+oauth_token_user({User, Server, _, _}) ->
+    jid:make(User, Server, <<>>).
+
 is_admin(_Name, admin, _Extra) ->
     true;
 is_admin(_Name, {_User, _Server, _, false}, _Extra) ->
index 07a1574e912f34bf87590850846493cfd83b0d70..bc30ee0908466e80188f8fc529fb34d9cc769d08 100644 (file)
@@ -136,6 +136,7 @@ check_permissions(Request, Command) ->
             {ok, CommandPolicy, Scope} = ejabberd_commands:get_command_policy_and_scope(Call),
             check_permissions2(Request, Call, CommandPolicy, Scope);
         _ ->
+            %% TODO Should this be a 404 or 400 instead of 401 ?
             unauthorized_response()
     end.