1
0
Fork 0
mirror of https://github.com/processone/ejabberd synced 2025-10-06 12:00:15 +02:00

Add support for checking access rules conformance for commands

This commit is contained in:
Mickael Remond 2016-07-23 17:57:44 +02:00
parent caf2c20210
commit 68555ff466
No known key found for this signature in database
GPG key ID: E6F6045D79965AA3
4 changed files with 51 additions and 23 deletions

View file

@ -38,19 +38,24 @@
function :: atom() | '_', function :: atom() | '_',
args = [] :: [aterm()] | '_' | '$1' | '$2', args = [] :: [aterm()] | '_' | '$1' | '$2',
policy = restricted :: open | restricted | admin | user, policy = restricted :: open | restricted | admin | user,
access_rules = [] :: [atom()],
result = {res, rescode} :: rterm() | '_' | '$2', result = {res, rescode} :: rterm() | '_' | '$2',
args_desc = none :: none | [string()] | '_', args_desc = none :: none | [string()] | '_',
result_desc = none :: none | string() | '_', result_desc = none :: none | string() | '_',
args_example = none :: none | [any()] | '_', args_example = none :: none | [any()] | '_',
result_example = none :: any()}). result_example = none :: any()}).
%% TODO Fix me: Type is not up to date
-type ejabberd_commands() :: #ejabberd_commands{name :: atom(), -type ejabberd_commands() :: #ejabberd_commands{name :: atom(),
tags :: [atom()], tags :: [atom()],
desc :: string(), desc :: string(),
longdesc :: string(), longdesc :: string(),
version :: integer(),
module :: atom(), module :: atom(),
function :: atom(), function :: atom(),
args :: [aterm()], args :: [aterm()],
policy :: open | restricted | admin | user,
access_rules :: [atom()],
result :: rterm()}. result :: rterm()}.
%% @type ejabberd_commands() = #ejabberd_commands{ %% @type ejabberd_commands() = #ejabberd_commands{

View file

@ -129,6 +129,8 @@ get_commands_spec() ->
#ejabberd_commands{name = register, tags = [accounts], #ejabberd_commands{name = register, tags = [accounts],
desc = "Register a user", desc = "Register a user",
policy = admin,
access_rules = [configure],
module = ?MODULE, function = register, module = ?MODULE, function = register,
args = [{user, binary}, {host, binary}, {password, binary}], args = [{user, binary}, {host, binary}, {password, binary}],
result = {res, restuple}}, result = {res, restuple}},

View file

@ -494,7 +494,7 @@ execute_command(AccessCommands, Auth, Name, Arguments) ->
%% %%
%% @doc Execute a command in a given API version %% @doc Execute a command in a given API version
%% Can return the following exceptions: %% 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) ->
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; true -> admin;
false -> Auth1 false -> Auth1
end, end,
TokenJID = oauth_token_user(Auth1),
Command = get_command_definition(Name, Version), Command = get_command_definition(Name, Version),
AccessCommands = get_access_commands(AccessCommands1, Version), AccessCommands = get_access_commands(AccessCommands1, Version),
case check_access_commands(AccessCommands, Auth, Name, Command, Arguments, CallerInfo) of 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. 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, Module = Command#ejabberd_commands.module,
Function = Command#ejabberd_commands.function, Function = Command#ejabberd_commands.function,
?DEBUG("Executing command ~p:~p with Args=~p", [Module, Function, Arguments]), ?DEBUG("Executing command ~p:~p with Args=~p", [Module, Function, Arguments]),
@ -754,6 +767,13 @@ get_commands(Version) ->
end, AdminCmds ++ UserCmds, Opts), end, AdminCmds ++ UserCmds, Opts),
Cmds. 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) -> is_admin(_Name, admin, _Extra) ->
true; true;
is_admin(_Name, {_User, _Server, _, false}, _Extra) -> is_admin(_Name, {_User, _Server, _, false}, _Extra) ->

View file

@ -136,6 +136,7 @@ check_permissions(Request, Command) ->
{ok, CommandPolicy, Scope} = ejabberd_commands:get_command_policy_and_scope(Call), {ok, CommandPolicy, Scope} = ejabberd_commands:get_command_policy_and_scope(Call),
check_permissions2(Request, Call, CommandPolicy, Scope); check_permissions2(Request, Call, CommandPolicy, Scope);
_ -> _ ->
%% TODO Should this be a 404 or 400 instead of 401 ?
unauthorized_response() unauthorized_response()
end. end.