diff options
Diffstat (limited to 'src/mod_http_api.erl')
-rw-r--r-- | src/mod_http_api.erl | 111 |
1 files changed, 65 insertions, 46 deletions
diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 595c121cd..ba3a14cf8 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -74,7 +74,7 @@ -behaviour(gen_mod). --export([start/2, stop/1, process/2, mod_opt_type/1]). +-export([start/2, stop/1, process/2, mod_opt_type/1, depends/2]). -include("ejabberd.hrl"). -include("jlib.hrl"). @@ -123,6 +123,9 @@ start(_Host, _Opts) -> stop(_Host) -> ok. +depends(_Host, _Opts) -> + []. + %% ---------- %% basic auth %% ---------- @@ -130,13 +133,13 @@ stop(_Host) -> check_permissions(Request, Command) -> case catch binary_to_existing_atom(Command, utf8) of Call when is_atom(Call) -> - {ok, CommandPolicy} = ejabberd_commands:get_command_policy(Call), - check_permissions2(Request, Call, CommandPolicy); + {ok, CommandPolicy, Scope} = ejabberd_commands:get_command_policy_and_scope(Call), + check_permissions2(Request, Call, CommandPolicy, Scope); _ -> - unauthorized_response() + json_error(404, 40, <<"Endpoint not found.">>) end. -check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _) +check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _, ScopeList) when HTTPAuth /= undefined -> Admin = case lists:keysearch(<<"X-Admin">>, 1, Headers) of @@ -156,11 +159,9 @@ check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _) false end; {oauth, Token, _} -> - case oauth_check_token(Call, Token) of + case oauth_check_token(ScopeList, Token) of {ok, user, {User, Server}} -> {ok, {User, Server, {oauth, Token}, Admin}}; - {ok, server_admin} -> %% token whas generated using issue_token command line - {ok, admin}; false -> false end; @@ -171,9 +172,9 @@ check_permissions2(#request{auth = HTTPAuth, headers = Headers}, Call, _) {ok, A} -> {allowed, Call, A}; _ -> unauthorized_response() end; -check_permissions2(_Request, Call, open) -> +check_permissions2(_Request, Call, open, _Scope) -> {allowed, Call, noauth}; -check_permissions2(#request{ip={IP, _Port}}, Call, _Policy) -> +check_permissions2(#request{ip={IP, _Port}}, Call, _Policy, _Scope) -> Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access, fun(V) -> V end, none), @@ -193,13 +194,11 @@ check_permissions2(#request{ip={IP, _Port}}, Call, _Policy) -> _E -> {allowed, Call, noauth} end; -check_permissions2(_Request, _Call, _Policy) -> +check_permissions2(_Request, _Call, _Policy, _Scope) -> unauthorized_response(). -oauth_check_token(Scope, Token) when is_atom(Scope) -> - oauth_check_token(atom_to_binary(Scope, utf8), Token); -oauth_check_token(Scope, Token) -> - ejabberd_oauth:check_token(Scope, Token). +oauth_check_token(ScopeList, Token) when is_list(ScopeList) -> + ejabberd_oauth:check_token(ScopeList, Token). %% ------------------ %% command processing @@ -221,8 +220,12 @@ process([Call], #request{method = 'POST', data = Data, ip = {IP, _} = IPPort} = log(Call, Args, IPPort), case check_permissions(Req, Call) of {allowed, Cmd, Auth} -> - {Code, Result} = handle(Cmd, Auth, Args, Version, IP), - json_response(Code, jiffy:encode(Result)); + case handle(Cmd, Auth, Args, Version, IP) of + {Code, Result} -> + json_response(Code, jiffy:encode(Result)); + {HTMLCode, JSONErrorCode, Message} -> + json_error(HTMLCode, JSONErrorCode, Message) + end; %% Warning: check_permission direcly formats 401 reply if not authorized ErrorResponse -> ErrorResponse @@ -265,10 +268,10 @@ get_api_version(#request{path = Path}) -> get_api_version(lists:reverse(Path)); get_api_version([<<"v", String/binary>> | Tail]) -> case catch jlib:binary_to_integer(String) of - N when is_integer(N) -> - N; - _ -> - get_api_version(Tail) + N when is_integer(N) -> + N; + _ -> + get_api_version(Tail) end; get_api_version([_Head | Tail]) -> get_api_version(Tail); @@ -279,6 +282,8 @@ get_api_version([]) -> %% command handlers %% ---------------- +%% TODO Check accept types of request before decided format of reply. + % generic ejabberd command handler handle(Call, Auth, Args, Version, IP) when is_atom(Call), is_list(Args) -> case ejabberd_commands:get_command_format(Call, Auth, Version) of @@ -310,8 +315,10 @@ handle(Call, Auth, Args, Version, IP) when is_atom(Call), is_list(Args) -> {401, jlib:atom_to_binary(Why)}; throw:{not_allowed, Msg} -> {401, iolist_to_binary(Msg)}; - throw:{error, account_unprivileged} -> - {401, iolist_to_binary(<<"Unauthorized: Account Unpriviledged">>)}; + throw:{error, account_unprivileged} -> + {403, 31, <<"Command need to be run with admin priviledge.">>}; + throw:{error, access_rules_unauthorized} -> + {403, 32, <<"AccessRules: Account associated to token does not have the right to perform the operation.">>}; throw:{invalid_parameter, Msg} -> {400, iolist_to_binary(Msg)}; throw:{error, Why} when is_atom(Why) -> @@ -367,28 +374,33 @@ format_args(Args, ArgsFormat) -> L when is_list(L) -> exit({additional_unused_args, L}) end. -format_arg({array, Elements}, - {list, {ElementDefName, ElementDefFormat}}) - when is_list(Elements) -> - lists:map(fun ({struct, [{ElementName, ElementValue}]}) when - ElementDefName == ElementName -> - format_arg(ElementValue, ElementDefFormat) - end, - Elements); -format_arg({array, [{struct, Elements}]}, - {list, {ElementDefName, ElementDefFormat}}) +format_arg(Elements, + {list, {_ElementDefName, ElementDefFormat}}) when is_list(Elements) -> - lists:map(fun ({ElementName, ElementValue}) -> - true = ElementDefName == ElementName, - format_arg(ElementValue, ElementDefFormat) - end, - Elements); -format_arg({array, [{struct, Elements}]}, + [format_arg(Element, ElementDefFormat) + || Element <- Elements]; +format_arg({[{Name, Value}]}, + {tuple, [{_Tuple1N, Tuple1S}, {_Tuple2N, Tuple2S}]}) + when Tuple1S == binary; + Tuple1S == string -> + {format_arg(Name, Tuple1S), format_arg(Value, Tuple2S)}; +format_arg({Elements}, {tuple, ElementsDef}) when is_list(Elements) -> - FormattedList = format_args(Elements, ElementsDef), - list_to_tuple(FormattedList); -format_arg({array, Elements}, {list, ElementsDef}) + F = lists:map(fun({TElName, TElDef}) -> + case lists:keyfind(atom_to_binary(TElName, latin1), 1, Elements) of + {_, Value} -> + format_arg(Value, TElDef); + _ when TElDef == binary; TElDef == string -> + <<"">>; + _ -> + ?ERROR_MSG("missing field ~p in tuple ~p", [TElName, Elements]), + throw({invalid_parameter, + io_lib:format("Missing field ~w in tuple ~w", [TElName, Elements])}) + end + end, ElementsDef), + list_to_tuple(F); +format_arg(Elements, {list, ElementsDef}) when is_list(Elements) and is_atom(ElementsDef) -> [format_arg(Element, ElementsDef) || Element <- Elements]; @@ -402,7 +414,7 @@ format_arg(undefined, string) -> <<>>; format_arg(Arg, Format) -> ?ERROR_MSG("don't know how to format Arg ~p for format ~p", [Arg, Format]), throw({invalid_parameter, - io_lib:format("Arg ~p is not in format ~p", + io_lib:format("Arg ~w is not in format ~w", [Arg, Format])}). process_unicode_codepoints(Str) -> @@ -486,9 +498,7 @@ format_result(404, {_Name, _}) -> "not_found". unauthorized_response() -> - unauthorized_response(<<"401 Unauthorized">>). -unauthorized_response(Body) -> - json_response(401, jiffy:encode(Body)). + json_error(401, 10, <<"Oauth Token is invalid or expired.">>). badrequest_response() -> badrequest_response(<<"400 Bad Request">>). @@ -498,6 +508,15 @@ badrequest_response(Body) -> json_response(Code, Body) when is_integer(Code) -> {Code, ?HEADER(?CT_JSON), Body}. +%% HTTPCode, JSONCode = integers +%% message is binary +json_error(HTTPCode, JSONCode, Message) -> + {HTTPCode, ?HEADER(?CT_JSON), + jiffy:encode({[{<<"status">>, <<"error">>}, + {<<"code">>, JSONCode}, + {<<"message">>, Message}]}) + }. + log(Call, Args, {Addr, Port}) -> AddrS = jlib:ip_to_list({Addr, Port}), ?INFO_MSG("API call ~s ~p from ~s:~p", [Call, Args, AddrS, Port]); |