mirror of
https://github.com/processone/ejabberd
synced 2025-10-06 03:50:15 +02:00
Add support for checking access rules conformance for commands
This commit is contained in:
parent
caf2c20210
commit
68555ff466
4 changed files with 51 additions and 23 deletions
|
@ -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{
|
||||||
|
|
|
@ -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}},
|
||||||
|
@ -166,7 +168,7 @@ get_commands_spec() ->
|
||||||
#ejabberd_commands{name = list_cluster, tags = [cluster],
|
#ejabberd_commands{name = list_cluster, tags = [cluster],
|
||||||
desc = "List nodes that are part of the cluster handled by Node",
|
desc = "List nodes that are part of the cluster handled by Node",
|
||||||
module = ?MODULE, function = list_cluster,
|
module = ?MODULE, function = list_cluster,
|
||||||
args = [],
|
args = [],
|
||||||
result = {nodes, {list, {node, atom}}}},
|
result = {nodes, {list, {node, atom}}}},
|
||||||
|
|
||||||
#ejabberd_commands{name = import_file, tags = [mnesia],
|
#ejabberd_commands{name = import_file, tags = [mnesia],
|
||||||
|
@ -220,7 +222,7 @@ get_commands_spec() ->
|
||||||
desc = "Delete offline messages older than DAYS",
|
desc = "Delete offline messages older than DAYS",
|
||||||
module = ?MODULE, function = delete_old_messages,
|
module = ?MODULE, function = delete_old_messages,
|
||||||
args = [{days, integer}], result = {res, rescode}},
|
args = [{days, integer}], result = {res, rescode}},
|
||||||
|
|
||||||
#ejabberd_commands{name = export2sql, tags = [mnesia],
|
#ejabberd_commands{name = export2sql, tags = [mnesia],
|
||||||
desc = "Export virtual host information from Mnesia tables to SQL files",
|
desc = "Export virtual host information from Mnesia tables to SQL files",
|
||||||
module = ejd2sql, function = export,
|
module = ejd2sql, function = export,
|
||||||
|
|
|
@ -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) ->
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue