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:
parent
fffae97940
commit
1981e13326
|
@ -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
|
||||||
|
|
|
@ -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 ->
|
||||||
|
|
Loading…
Reference in New Issue
Block a user