diff options
author | Paweł Chmielowski <pchmielowski@process-one.net> | 2016-05-25 13:01:07 +0200 |
---|---|---|
committer | Paweł Chmielowski <pchmielowski@process-one.net> | 2016-05-26 11:08:53 +0200 |
commit | 1981e13326f84f8d269d11b304867de3f1dd021c (patch) | |
tree | d15a887e72fce5c640b286aaaef4f01abe0ab141 | |
parent | Use acl:access_matches in c2s (diff) |
Allow passing username and ip to ejabberd_comamnds, and use it in mod_http_api
-rw-r--r-- | src/ejabberd_commands.erl | 56 | ||||
-rw-r--r-- | src/mod_http_api.erl | 25 |
2 files changed, 45 insertions, 36 deletions
diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 55ecba5d..543e27ca 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -230,6 +230,7 @@ execute_command/3, execute_command/4, execute_command/5, + execute_command/6, opt_type/1, get_commands_spec/0 ]). @@ -352,7 +353,7 @@ get_command_format(Name, Auth) -> {[aterm()], rterm()}. get_command_format(Name, Auth, Version) -> - Admin = is_admin(Name, Auth), + Admin = is_admin(Name, Auth, #{}), #ejabberd_commands{args = Args, result = Result, policy = Policy} = @@ -489,13 +490,16 @@ execute_command(AccessCommands, Auth, Name, Arguments) -> %% Can return the following exceptions: %% command_unknown | account_unprivileged | invalid_account_data | no_auth_provided 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; false -> Auth1 end, Command = get_command_definition(Name, 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) end. @@ -573,9 +577,9 @@ get_tags_commands(Version) -> %% At least one AccessCommand must be satisfied. %% It may throw {error, Error} where: %% Error = account_unprivileged | invalid_account_data -check_access_commands([], _Auth, _Method, _Command, _Arguments) -> +check_access_commands([], _Auth, _Method, _Command, _Arguments, _CallerInfo) -> ok; -check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) -> +check_access_commands(AccessCommands, Auth, Method, Command1, Arguments, CallerInfo) -> Command = case {Command1#ejabberd_commands.policy, Auth} of {user, {_, _, _, _}} -> @@ -590,7 +594,7 @@ check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) -> AccessCommandsAllowed = lists:filter( fun({Access, Commands, ArgumentRestrictions}) -> - case check_access(Command, Access, Auth) of + case check_access(Command, Access, Auth, CallerInfo) of true -> check_access_command(Commands, Command, ArgumentRestrictions, @@ -600,7 +604,7 @@ check_access_commands(AccessCommands, Auth, Method, Command1, Arguments) -> end; ({Access, Commands}) -> ArgumentRestrictions = [], - case check_access(Command, Access, Auth) of + case check_access(Command, Access, Auth, CallerInfo) of true -> check_access_command(Commands, Command, ArgumentRestrictions, @@ -637,31 +641,33 @@ check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) -> _ -> throw({error, invalid_account_data}) end. -check_access(Command, ?POLICY_ACCESS, _) +check_access(Command, ?POLICY_ACCESS, _, _) when Command#ejabberd_commands.policy == open -> true; -check_access(_Command, _Access, admin) -> +check_access(_Command, _Access, admin, _) -> true; -check_access(_Command, _Access, {_User, _Server, _, true}) -> +check_access(_Command, _Access, {_User, _Server, _, true}, _) -> false; -check_access(Command, Access, Auth) +check_access(Command, Access, Auth, CallerInfo) when Access =/= ?POLICY_ACCESS; Command#ejabberd_commands.policy == open; Command#ejabberd_commands.policy == user -> case check_auth(Command, Auth) of {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 end; -check_access(_Command, _Access, _Auth) -> +check_access(_Command, _Access, _Auth, _CallerInfo) -> false. -check_access2(?POLICY_ACCESS, _User, _Server) -> +check_access2(?POLICY_ACCESS, _CallerInfo, _Server) -> true; -check_access2(Access, User, Server) -> +check_access2(Access, AccessInfo, Server) -> %% 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; deny -> false end. @@ -737,22 +743,26 @@ get_commands(Version) -> end, AdminCmds ++ UserCmds, Opts), Cmds. -is_admin(_Name, noauth) -> - false; -is_admin(_Name, admin) -> +is_admin(_Name, admin, _Extra) -> true; -is_admin(_Name, {_User, _Server, _, false}) -> +is_admin(_Name, {_User, _Server, _, false}, _Extra) -> 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( commands_admin_access, fun(A) when is_atom(A) -> A end, none), - case acl:match_rule(Server, AdminAccess, - jid:make(User, Server, <<"">>)) of + case acl:access_matches(AdminAccess, ACLInfo, Server) of allow -> case catch check_auth(get_command_definition(Name), Auth) of {ok, _, _} -> true; + no_auth_provided -> true; _ -> false end; deny -> false diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index c4fae202..1962e1d0 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -188,9 +188,8 @@ check_permissions2(#request{ip={IP, _Port}}, Call, _Policy) -> true -> {allowed, Call, admin}; _ -> unauthorized_response() end; - E -> - ?DEBUG("Unauthorized: ~p", [E]), - unauthorized_response() + _E -> + {allowed, Call, noauth} end; check_permissions2(_Request, _Call, _Policy) -> unauthorized_response(). @@ -209,7 +208,7 @@ oauth_check_token(Scope, Token) -> process(_, #request{method = 'POST', data = <<>>}) -> ?DEBUG("Bad Request: no 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), try 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; Other -> [Other] end, - log(Call, Args, IP), + log(Call, Args, IPPort), case check_permissions(Req, Call) of {allowed, Cmd, Auth} -> - {Code, Result} = handle(Cmd, Auth, Args, Version), + {Code, Result} = handle(Cmd, Auth, Args, Version, IP), json_response(Code, jiffy:encode(Result)); %% Warning: check_permission direcly formats 401 reply if not authorized ErrorResponse -> @@ -243,7 +242,7 @@ process([Call], #request{method = 'GET', q = Data, ip = IP} = Req) -> log(Call, Args, IP), case check_permissions(Req, Call) of {allowed, Cmd, Auth} -> - {Code, Result} = handle(Cmd, Auth, Args, Version), + {Code, Result} = handle(Cmd, Auth, Args, Version, IP), json_response(Code, jiffy:encode(Result)); %% Warning: check_permission direcly formats 401 reply if not authorized ErrorResponse -> @@ -279,7 +278,7 @@ get_api_version([]) -> %% ---------------- % 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 {ArgsSpec, _} when is_list(ArgsSpec) -> 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] end, [], ArgsSpec), try - handle2(Call, Auth, match(Args2, Spec), Version) + handle2(Call, Auth, match(Args2, Spec), Version, IP) catch throw:not_found -> {404, <<"not_found">>}; 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">>} 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), ArgsFormatted = format_args(Args, ArgsF), - ejabberd_command(Auth, Call, ArgsFormatted, Version). + ejabberd_command(Auth, Call, ArgsFormatted, Version, IP). get_elem_delete(A, L) -> case proplists:get_all_values(A, L) of @@ -416,12 +415,12 @@ process_unicode_codepoints(Str) -> match(Args, 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 admin -> []; _ -> undefined 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} -> throw(Error); Res -> |