24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-12 21:52:07 +02:00

Allow passing username and ip to ejabberd_comamnds, and use it in mod_http_api

This commit is contained in:
Paweł Chmielowski 2016-05-25 13:01:07 +02:00
parent fffae97940
commit 1981e13326
2 changed files with 45 additions and 36 deletions

View File

@ -230,6 +230,7 @@
execute_command/3, execute_command/3,
execute_command/4, execute_command/4,
execute_command/5, execute_command/5,
execute_command/6,
opt_type/1, opt_type/1,
get_commands_spec/0 get_commands_spec/0
]). ]).
@ -352,7 +353,7 @@ get_command_format(Name, Auth) ->
{[aterm()], rterm()}. {[aterm()], rterm()}.
get_command_format(Name, Auth, Version) -> get_command_format(Name, Auth, Version) ->
Admin = is_admin(Name, Auth), Admin = is_admin(Name, Auth, #{}),
#ejabberd_commands{args = Args, #ejabberd_commands{args = Args,
result = Result, result = Result,
policy = Policy} = policy = Policy} =
@ -489,13 +490,16 @@ execute_command(AccessCommands, Auth, Name, Arguments) ->
%% 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
execute_command(AccessCommands1, Auth1, Name, Arguments, Version) -> execute_command(AccessCommands1, Auth1, Name, Arguments, Version) ->
Auth = case is_admin(Name, Auth1) of execute_command(AccessCommands1, Auth1, Name, Arguments, Version, #{}).
execute_command(AccessCommands1, Auth1, Name, Arguments, Version, CallerInfo) ->
Auth = case is_admin(Name, Auth1, CallerInfo) of
true -> admin; true -> admin;
false -> Auth1 false -> Auth1
end, end,
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) of case check_access_commands(AccessCommands, Auth, Name, Command, Arguments, CallerInfo) of
ok -> execute_command2(Auth, Command, Arguments) ok -> execute_command2(Auth, Command, Arguments)
end. end.
@ -573,9 +577,9 @@ get_tags_commands(Version) ->
%% At least one AccessCommand must be satisfied. %% At least one AccessCommand must be satisfied.
%% It may throw {error, Error} where: %% It may throw {error, Error} where:
%% Error = account_unprivileged | invalid_account_data %% Error = account_unprivileged | invalid_account_data
check_access_commands([], _Auth, _Method, _Command, _Arguments) -> check_access_commands([], _Auth, _Method, _Command, _Arguments, _CallerInfo) ->
ok; ok;
check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) -> check_access_commands(AccessCommands, Auth, Method, Command1, Arguments, CallerInfo) ->
Command = Command =
case {Command1#ejabberd_commands.policy, Auth} of case {Command1#ejabberd_commands.policy, Auth} of
{user, {_, _, _, _}} -> {user, {_, _, _, _}} ->
@ -590,7 +594,7 @@ check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) ->
AccessCommandsAllowed = AccessCommandsAllowed =
lists:filter( lists:filter(
fun({Access, Commands, ArgumentRestrictions}) -> fun({Access, Commands, ArgumentRestrictions}) ->
case check_access(Command, Access, Auth) of case check_access(Command, Access, Auth, CallerInfo) of
true -> true ->
check_access_command(Commands, Command, check_access_command(Commands, Command,
ArgumentRestrictions, ArgumentRestrictions,
@ -600,7 +604,7 @@ check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) ->
end; end;
({Access, Commands}) -> ({Access, Commands}) ->
ArgumentRestrictions = [], ArgumentRestrictions = [],
case check_access(Command, Access, Auth) of case check_access(Command, Access, Auth, CallerInfo) of
true -> true ->
check_access_command(Commands, Command, check_access_command(Commands, Command,
ArgumentRestrictions, ArgumentRestrictions,
@ -637,31 +641,33 @@ check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
_ -> throw({error, invalid_account_data}) _ -> throw({error, invalid_account_data})
end. end.
check_access(Command, ?POLICY_ACCESS, _) check_access(Command, ?POLICY_ACCESS, _, _)
when Command#ejabberd_commands.policy == open -> when Command#ejabberd_commands.policy == open ->
true; true;
check_access(_Command, _Access, admin) -> check_access(_Command, _Access, admin, _) ->
true; true;
check_access(_Command, _Access, {_User, _Server, _, true}) -> check_access(_Command, _Access, {_User, _Server, _, true}, _) ->
false; false;
check_access(Command, Access, Auth) check_access(Command, Access, Auth, CallerInfo)
when Access =/= ?POLICY_ACCESS; when Access =/= ?POLICY_ACCESS;
Command#ejabberd_commands.policy == open; Command#ejabberd_commands.policy == open;
Command#ejabberd_commands.policy == user -> Command#ejabberd_commands.policy == user ->
case check_auth(Command, Auth) of case check_auth(Command, Auth) of
{ok, User, Server} -> {ok, User, Server} ->
check_access2(Access, User, Server); check_access2(Access, CallerInfo#{usr => jid:split(jid:make(User, Server, <<>>))}, Server);
no_auth_provided ->
check_access2(Access, CallerInfo, global);
_ -> _ ->
false false
end; end;
check_access(_Command, _Access, _Auth) -> check_access(_Command, _Access, _Auth, _CallerInfo) ->
false. false.
check_access2(?POLICY_ACCESS, _User, _Server) -> check_access2(?POLICY_ACCESS, _CallerInfo, _Server) ->
true; true;
check_access2(Access, User, Server) -> check_access2(Access, AccessInfo, Server) ->
%% Check this user has access permission %% Check this user has access permission
case acl:match_rule(Server, Access, jid:make(User, Server, <<"">>)) of case acl:access_matches(Access, AccessInfo, Server) of
allow -> true; allow -> true;
deny -> false deny -> false
end. end.
@ -737,22 +743,26 @@ get_commands(Version) ->
end, AdminCmds ++ UserCmds, Opts), end, AdminCmds ++ UserCmds, Opts),
Cmds. Cmds.
is_admin(_Name, noauth) -> is_admin(_Name, admin, _Extra) ->
false;
is_admin(_Name, admin) ->
true; true;
is_admin(_Name, {_User, _Server, _, false}) -> is_admin(_Name, {_User, _Server, _, false}, _Extra) ->
false; false;
is_admin(Name, {User, Server, _, true} = Auth) -> is_admin(Name, Auth, Extra) ->
{ACLInfo, Server} = case Auth of
{U, S, _, _} ->
{Extra#{usr=>jid:split(jid:make(U, S, <<>>))}, S};
_ ->
{Extra, global}
end,
AdminAccess = ejabberd_config:get_option( AdminAccess = ejabberd_config:get_option(
commands_admin_access, commands_admin_access,
fun(A) when is_atom(A) -> A end, fun(A) when is_atom(A) -> A end,
none), none),
case acl:match_rule(Server, AdminAccess, case acl:access_matches(AdminAccess, ACLInfo, Server) of
jid:make(User, Server, <<"">>)) of
allow -> allow ->
case catch check_auth(get_command_definition(Name), Auth) of case catch check_auth(get_command_definition(Name), Auth) of
{ok, _, _} -> true; {ok, _, _} -> true;
no_auth_provided -> true;
_ -> false _ -> false
end; end;
deny -> false deny -> false

View File

@ -188,9 +188,8 @@ check_permissions2(#request{ip={IP, _Port}}, Call, _Policy) ->
true -> {allowed, Call, admin}; true -> {allowed, Call, admin};
_ -> unauthorized_response() _ -> unauthorized_response()
end; end;
E -> _E ->
?DEBUG("Unauthorized: ~p", [E]), {allowed, Call, noauth}
unauthorized_response()
end; end;
check_permissions2(_Request, _Call, _Policy) -> check_permissions2(_Request, _Call, _Policy) ->
unauthorized_response(). unauthorized_response().
@ -209,7 +208,7 @@ oauth_check_token(Scope, Token) ->
process(_, #request{method = 'POST', data = <<>>}) -> process(_, #request{method = 'POST', data = <<>>}) ->
?DEBUG("Bad Request: no data", []), ?DEBUG("Bad Request: no data", []),
badrequest_response(<<"Missing POST data">>); badrequest_response(<<"Missing POST data">>);
process([Call], #request{method = 'POST', data = Data, ip = IP} = Req) -> process([Call], #request{method = 'POST', data = Data, ip = {IP, _} = IPPort} = Req) ->
Version = get_api_version(Req), Version = get_api_version(Req),
try try
Args = case jiffy:decode(Data) of Args = case jiffy:decode(Data) of
@ -217,10 +216,10 @@ process([Call], #request{method = 'POST', data = Data, ip = IP} = Req) ->
{List} when is_list(List) -> List; {List} when is_list(List) -> List;
Other -> [Other] Other -> [Other]
end, end,
log(Call, Args, IP), log(Call, Args, IPPort),
case check_permissions(Req, Call) of case check_permissions(Req, Call) of
{allowed, Cmd, Auth} -> {allowed, Cmd, Auth} ->
{Code, Result} = handle(Cmd, Auth, Args, Version), {Code, Result} = handle(Cmd, Auth, Args, Version, IP),
json_response(Code, jiffy:encode(Result)); json_response(Code, jiffy:encode(Result));
%% Warning: check_permission direcly formats 401 reply if not authorized %% Warning: check_permission direcly formats 401 reply if not authorized
ErrorResponse -> ErrorResponse ->
@ -243,7 +242,7 @@ process([Call], #request{method = 'GET', q = Data, ip = IP} = Req) ->
log(Call, Args, IP), log(Call, Args, IP),
case check_permissions(Req, Call) of case check_permissions(Req, Call) of
{allowed, Cmd, Auth} -> {allowed, Cmd, Auth} ->
{Code, Result} = handle(Cmd, Auth, Args, Version), {Code, Result} = handle(Cmd, Auth, Args, Version, IP),
json_response(Code, jiffy:encode(Result)); json_response(Code, jiffy:encode(Result));
%% Warning: check_permission direcly formats 401 reply if not authorized %% Warning: check_permission direcly formats 401 reply if not authorized
ErrorResponse -> ErrorResponse ->
@ -279,7 +278,7 @@ get_api_version([]) ->
%% ---------------- %% ----------------
% generic ejabberd command handler % generic ejabberd command handler
handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) -> handle(Call, Auth, Args, Version, IP) when is_atom(Call), is_list(Args) ->
case ejabberd_commands:get_command_format(Call, Auth, Version) of case ejabberd_commands:get_command_format(Call, Auth, Version) of
{ArgsSpec, _} when is_list(ArgsSpec) -> {ArgsSpec, _} when is_list(ArgsSpec) ->
Args2 = [{jlib:binary_to_atom(Key), Value} || {Key, Value} <- Args], Args2 = [{jlib:binary_to_atom(Key), Value} || {Key, Value} <- Args],
@ -296,7 +295,7 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
[{Key, undefined}|Acc] [{Key, undefined}|Acc]
end, [], ArgsSpec), end, [], ArgsSpec),
try try
handle2(Call, Auth, match(Args2, Spec), Version) handle2(Call, Auth, match(Args2, Spec), Version, IP)
catch throw:not_found -> catch throw:not_found ->
{404, <<"not_found">>}; {404, <<"not_found">>};
throw:{not_found, Why} when is_atom(Why) -> throw:{not_found, Why} when is_atom(Why) ->
@ -333,10 +332,10 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
{400, <<"Error">>} {400, <<"Error">>}
end. end.
handle2(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) -> handle2(Call, Auth, Args, Version, IP) when is_atom(Call), is_list(Args) ->
{ArgsF, _ResultF} = ejabberd_commands:get_command_format(Call, Auth, Version), {ArgsF, _ResultF} = ejabberd_commands:get_command_format(Call, Auth, Version),
ArgsFormatted = format_args(Args, ArgsF), ArgsFormatted = format_args(Args, ArgsF),
ejabberd_command(Auth, Call, ArgsFormatted, Version). ejabberd_command(Auth, Call, ArgsFormatted, Version, IP).
get_elem_delete(A, L) -> get_elem_delete(A, L) ->
case proplists:get_all_values(A, L) of case proplists:get_all_values(A, L) of
@ -416,12 +415,12 @@ process_unicode_codepoints(Str) ->
match(Args, Spec) -> match(Args, Spec) ->
[{Key, proplists:get_value(Key, Args, Default)} || {Key, Default} <- Spec]. [{Key, proplists:get_value(Key, Args, Default)} || {Key, Default} <- Spec].
ejabberd_command(Auth, Cmd, Args, Version) -> ejabberd_command(Auth, Cmd, Args, Version, IP) ->
Access = case Auth of Access = case Auth of
admin -> []; admin -> [];
_ -> undefined _ -> undefined
end, end,
case ejabberd_commands:execute_command(Access, Auth, Cmd, Args, Version) of case ejabberd_commands:execute_command(Access, Auth, Cmd, Args, Version, #{ip => IP}) of
{error, Error} -> {error, Error} ->
throw(Error); throw(Error);
Res -> Res ->