./ct_report/coverage/mongoose_api_client.COVER.html

1 %%%-------------------------------------------------------------------
2 %%% @author ludwikbukowski
3 %%% @copyright (C) 2016, Erlang Solutions Ltd.
4 %%%
5 %%% @end
6 %%% Created : 19. Jul 2016 17:55
7 %%%-------------------------------------------------------------------
8 %% @doc MongooseIM REST HTTP API for clients.
9 %% This module implements cowboy REST callbacks and
10 %% passes the requests on to the http api backend module.
11 %% It provides also client authorization mechanism
12
13 -module(mongoose_api_client).
14 -author("ludwikbukowski").
15 -include("mongoose_api.hrl").
16 -include("jlib.hrl").
17 -include("mongoose.hrl").
18
19 %% ejabberd_cowboy exports
20 -export([cowboy_router_paths/2, to_json/2, from_json/2]).
21
22 %% API
23 -export([is_authorized/2,
24 init/2,
25 allowed_methods/2,
26 content_types_provided/2,
27 content_types_accepted/2,
28 rest_terminate/2,
29 delete_resource/2]).
30
31 -ignore_xref([allowed_methods/2, content_types_accepted/2, content_types_provided/2,
32 cowboy_router_paths/2, delete_resource/2, from_json/2, init/2,
33 is_authorized/2, rest_terminate/2, to_json/2]).
34
35 -import(mongoose_api_common, [action_to_method/1,
36 method_to_action/1,
37 process_request/4,
38 error_response/4,
39 parse_request_body/1]).
40
41 %%--------------------------------------------------------------------
42 %% ejabberd_cowboy callbacks
43 %%--------------------------------------------------------------------
44
45 %% @doc This is implementation of ejabberd_cowboy callback.
46 %% Returns list of all available http paths.
47 -spec cowboy_router_paths(ejabberd_cowboy:path(), ejabberd_cowboy:options()) ->
48 ejabberd_cowboy:implemented_result().
49 cowboy_router_paths(Base, _Opts) ->
50
:-(
ejabberd_hooks:add(register_command, global, mongoose_api_common, reload_dispatches, 50),
51
:-(
ejabberd_hooks:add(unregister_command, global, mongoose_api_common, reload_dispatches, 50),
52
:-(
try
53
:-(
Commands = mongoose_commands:list(user),
54
:-(
[handler_path(Base, Command) || Command <- Commands]
55 catch
56 Class:Err:Stacktrace ->
57
:-(
?LOG_ERROR(#{what => rest_getting_command_list_failed,
58
:-(
class => Class, reason => Err, stacktrace => Stacktrace}),
59
:-(
[]
60 end.
61
62
63 %%--------------------------------------------------------------------
64 %% cowboy_rest callbacks
65 %%--------------------------------------------------------------------
66
67
68 init(Req, Opts) ->
69
:-(
Bindings = maps:to_list(cowboy_req:bindings(Req)),
70
:-(
CommandCategory =
71 case lists:keytake(command_category, 1, Opts) of
72 {value, {command_category, Name}, _Opts1} ->
73
:-(
Name;
74 false ->
75
:-(
undefined
76 end,
77
:-(
State = #http_api_state{allowed_methods = mongoose_api_common:get_allowed_methods(user),
78 bindings = Bindings, command_category = CommandCategory},
79
:-(
{cowboy_rest, Req, State}.
80
81 allowed_methods(Req, #http_api_state{command_category = Name} = State) ->
82
:-(
CommandList = mongoose_commands:list(user, Name),
83
:-(
AllowedMethods = [action_to_method(mongoose_commands:action(Command))
84
:-(
|| Command <- CommandList],
85
:-(
{AllowedMethods, Req, State}.
86
87 content_types_provided(Req, State) ->
88
:-(
CTP = [{{<<"application">>, <<"json">>, '*'}, to_json}],
89
:-(
{CTP, Req, State}.
90
91 content_types_accepted(Req, State) ->
92
:-(
CTA = [{{<<"application">>, <<"json">>, '*'}, from_json}],
93
:-(
{CTA, Req, State}.
94
95 rest_terminate(_Req, _State) ->
96
:-(
ok.
97
98 is_authorized(Req, State) ->
99
:-(
AuthDetails = cowboy_req:parse_header(<<"authorization">>, Req),
100
:-(
do_authorize(AuthDetails, Req, State).
101
102 %% @doc Called for a method of type "DELETE"
103 delete_resource(Req, #http_api_state{command_category = Category,
104 command_subcategory = SubCategory,
105 bindings = B} = State) ->
106
:-(
Arity = length(B),
107
:-(
Cmds = mongoose_commands:list(user, Category, method_to_action(<<"DELETE">>), SubCategory),
108
:-(
[Command] = [C || C <- Cmds, arity(C) == Arity],
109
:-(
mongoose_api_common:process_request(<<"DELETE">>, Command, Req, State).
110
111 %%--------------------------------------------------------------------
112 %% internal funs
113 %%--------------------------------------------------------------------
114
115 %% @doc Called for a method of type "GET"
116 to_json(Req, #http_api_state{command_category = Category,
117 command_subcategory = SubCategory,
118 bindings = B} = State) ->
119
:-(
Arity = length(B),
120
:-(
Cmds = mongoose_commands:list(user, Category, method_to_action(<<"GET">>), SubCategory),
121
:-(
[Command] = [C || C <- Cmds, arity(C) == Arity],
122
:-(
mongoose_api_common:process_request(<<"GET">>, Command, Req, State).
123
124
125 %% @doc Called for a method of type "POST" and "PUT"
126 from_json(Req, #http_api_state{command_category = Category,
127 command_subcategory = SubCategory,
128 bindings = B} = State) ->
129
:-(
Method = cowboy_req:method(Req),
130
:-(
case parse_request_body(Req) of
131 {error, _R}->
132
:-(
error_response(bad_request, ?BODY_MALFORMED, Req, State);
133 {Params, _} ->
134
:-(
Arity = length(B) + length(Params),
135
:-(
Cmds = mongoose_commands:list(user, Category, method_to_action(Method), SubCategory),
136
:-(
case [C || C <- Cmds, arity(C) == Arity] of
137 [Command] ->
138
:-(
process_request(Method, Command, {Params, Req}, State);
139 [] ->
140
:-(
error_response(not_found, ?ARGS_LEN_ERROR, Req, State)
141 end
142 end.
143
144 arity(C) ->
145 % we don't have caller in bindings (we know it from authorisation),
146 % so it doesn't count when checking arity
147
:-(
Args = mongoose_commands:args(C),
148
:-(
length([N || {N, _} <- Args, N =/= caller]).
149
150 do_authorize({basic, User, Password}, Req, State) ->
151
:-(
case jid:from_binary(User) of
152 error ->
153
:-(
make_unauthorized_response(Req, State);
154 JID ->
155
:-(
do_check_password(JID, Password, Req, State)
156 end;
157 do_authorize(_, Req, State) ->
158
:-(
make_unauthorized_response(Req, State).
159
160 do_check_password(#jid{} = JID, Password, Req, State) ->
161
:-(
case ejabberd_auth:check_password(JID, Password) of
162 true ->
163
:-(
{true, Req, State#http_api_state{entity = jid:to_binary(JID)}};
164 _ ->
165
:-(
make_unauthorized_response(Req, State)
166 end.
167
168 make_unauthorized_response(Req, State) ->
169
:-(
{{false, <<"Basic realm=\"mongooseim\"">>}, Req, State}.
170
171 -spec handler_path(ejabberd_cowboy:path(), mongoose_commands:t()) -> ejabberd_cowboy:route().
172 handler_path(Base, Command) ->
173
:-(
{[Base, mongoose_api_common:create_user_url_path(Command)],
174 ?MODULE, [{command_category, mongoose_commands:category(Command)}]}.
175
Line Hits Source