Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow JIDs from all MUC light domains in HTTP room API #1622

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 103 additions & 9 deletions big_tests/tests/rest_client_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,13 @@ muc_test_cases() ->
room_is_not_created_with_jid_not_matching_hostname,
room_can_be_fetched_by_jid,
messages_can_be_sent_and_fetched_by_room_jid,
user_can_be_added_and_removed_by_room_jid
user_can_be_added_and_removed_by_room_jid,
room_cant_be_created_given_jid_in_a_non_muc_light_domain,
user_cant_be_invited_given_room_jid_in_a_non_muc_light_domain,
user_cant_be_removed_given_room_jid_in_a_non_muc_light_domain,
message_cant_be_sent_given_room_jid_in_a_non_muc_light_domain,
messages_cant_be_read_given_room_jid_in_a_non_muc_light_domain,
room_can_be_created_in_a_secondary_muc_light_domain
].

roster_test_cases() ->
Expand All @@ -83,16 +89,29 @@ init_per_suite(C) ->
dynamic_modules:start(Host, mod_muc_light,
[{host, binary_to_list(MUCLightHost)},
{rooms_in_rosters, true}]),
[{muc_light_host, MUCLightHost} | escalus:init_per_suite(C1)].
SecondaryMUCLightHost = <<"othermuclight.", Host/binary>>,
register_secondary_muc_light_service(SecondaryMUCLightHost),
NonMUCLightHost = <<"notamuclight.", Host/binary>>, % register!
[{muc_light_host, MUCLightHost},
{secondary_muc_light_host, SecondaryMUCLightHost},
{non_muc_light_host, NonMUCLightHost} | escalus:init_per_suite(C1)].

end_per_suite(Config) ->
escalus_fresh:clean(),
Host = ct:get_config({hosts, mim, domain}),
rest_helper:maybe_disable_mam(mam_helper:backend(), Host),
dynamic_modules:stop(Host, mod_muc_light),
unregister_secondary_muc_light_service(?config(secondary_muc_light_host, Config)),
application:stop(shotgun),
escalus:end_per_suite(Config).

register_secondary_muc_light_service(MUCLightHost) ->
PacketHandler = escalus_ejabberd:rpc(mongoose_packet_handler, new, [mod_muc_light]),
escalus_ejabberd:rpc(ejabberd_router, register_route, [MUCLightHost, PacketHandler]).

unregister_secondary_muc_light_service(MUCLightHost) ->
escalus_ejabberd:rpc(ejabberd_router, unregister_route, [MUCLightHost]).

init_per_group(_GN, C) ->
C.

Expand Down Expand Up @@ -387,6 +406,76 @@ user_can_be_added_and_removed_by_room_jid(Config) ->
?assertEqual(<<"204">>, Status)
end).

room_cant_be_created_given_jid_in_a_non_muc_light_domain(Config) ->
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
RoomID = <<"some_id">>,
RoomJID = <<RoomID/binary, "@", (?config(non_muc_light_host, Config))/binary>>,
Creds = credentials({alice, Alice}),

{{Status, _}, _} = create_room_with_id_request(Creds,
<<"some_name">>,
<<"some subject">>,
RoomJID),
?assertEqual(<<"400">>, Status)
end).

user_cant_be_invited_given_room_jid_in_a_non_muc_light_domain(Config) ->
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
RoomID = given_new_room({alice, Alice}),
RoomJID = <<RoomID/binary, "@", (?config(non_muc_light_host, Config))/binary>>,
Creds = credentials({alice, Alice}),

{{Status, _}, _} = invite_to_room({alice, Alice}, RoomJID, <<"[email protected]">>),

?assertEqual(<<"400">>, Status)
end).

user_cant_be_removed_given_room_jid_in_a_non_muc_light_domain(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
RoomID = given_new_room({alice, Alice}),
RoomJID = <<RoomID/binary, "@", (?config(non_muc_light_host, Config))/binary>>,
Creds = credentials({alice, Alice}),

{{Status, _}, _} = remove_user_from_a_room({alice, Alice}, RoomJID, Bob),

?assertEqual(<<"400">>, Status)
end).

message_cant_be_sent_given_room_jid_in_a_non_muc_light_domain(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
RoomID = given_new_room({alice, Alice}),
RoomJID = <<RoomID/binary, "@", (?config(non_muc_light_host, Config))/binary>>,
Creds = credentials({alice, Alice}),

{{Status, _}, _} = send_message_to_room({alice, Alice}, RoomJID),

?assertEqual(<<"400">>, Status)
end).

messages_cant_be_read_given_room_jid_in_a_non_muc_light_domain(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
RoomID = given_new_room({alice, Alice}),
RoomJID = <<RoomID/binary, "@", (?config(non_muc_light_host, Config))/binary>>,
Creds = credentials({alice, Alice}),

{{Status, _}, _} = get_room_messages({alice, Alice}, RoomJID),

?assertEqual(<<"400">>, Status)
end).

room_can_be_created_in_a_secondary_muc_light_domain(Config) ->
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
RoomID = <<"some_id_but_different_domain">>,
RoomJID = <<RoomID/binary, "@", (?config(secondary_muc_light_host, Config))/binary>>,
Creds = credentials({alice, Alice}),

{{Status, _}, _} = create_room_with_id_request(Creds,
<<"some_name">>,
<<"some subject">>,
RoomJID),
?assertEqual(<<"201">>, Status)
end).

assert_room_messages(RecvMsg, {_ID, _GenFrom, GenMsg}) ->
escalus:assert(is_chat_message, [maps:get(body, RecvMsg)], GenMsg),
ok.
Expand All @@ -413,15 +502,12 @@ wait_for_room_msg(Msg, User) ->
Stanza = escalus:wait_for_stanza(User),
escalus:assert(is_groupchat_message, [maps:get(body, Msg)], Stanza).

given_message_sent_to_room(RoomID, Sender) ->
{UserJID, _} = Creds = credentials(Sender),
Path = <<"/rooms/", RoomID/binary, "/messages">>,
Body = #{body => <<"Hi all!">>},
{{<<"200">>, <<"OK">>}, {Result}} = rest_helper:post(client, Path, Body, Creds),
given_message_sent_to_room(RoomID, {_, ClientOrSpec} = Sender) ->
Body = <<"Hi all!">>,
{{<<"200">>, <<"OK">>}, {Result}} = send_message_to_room(Sender, RoomID, Body),
MsgId = proplists:get_value(<<"id">>, Result),
true = is_binary(MsgId),

Body#{id => MsgId, from => UserJID}.
#{body => Body, id => MsgId, from => user_jid(ClientOrSpec)}.

given_new_room_with_users(Owner, Users) ->
RoomID = given_new_room(Owner),
Expand Down Expand Up @@ -501,6 +587,14 @@ get_messages(MeCreds, Other, Before, Count) ->
"&limit=", integer_to_list(Count)]),
get_messages(GetPath, MeCreds).

send_message_to_room(Client, RoomID) ->
send_message_to_room(Client, RoomID, <<"Hi all!">>).

send_message_to_room(Client, RoomID, Body) ->
{UserJID, _} = Creds = credentials(Client),
Path = <<"/rooms/", RoomID/binary, "/messages">>,
Payload = #{body => Body},
rest_helper:post(client, Path, Payload, Creds).

get_room_messages(Client, RoomID, Count) ->
get_room_messages(Client, RoomID, Count, undefined).
Expand Down
2 changes: 1 addition & 1 deletion doc/rest-api/Client-frontend_swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ info:
This is to ensure integration between the **REST API** users and regular **XMPP** users.
* All requests requiring a room ID (i.e. most of the requests fired at `/room` endpoint) accept either
a bare room ID (e.g. `656c6f656c6f`) or a room JID (e.g. `[email protected]`).
The host part of the room JID must be the host name of a MUC light service running in user's domain.
The host part of the room JID must be the host name of a registered MUC light service.
* All requests require authentication.
This is to make sure the server can identify who sent the request and if it comes from an authorized user.
Currently the only supported method is **Basic Auth**.
Expand Down
2 changes: 1 addition & 1 deletion rel/files/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{log_root, "{{mongooseim_log_dir}}"},
{crash_log, "crash.log"},
{handlers, [
{lager_console_backend, [info, {lager_default_formatter,[{eol, "\r\n"}]}]},
{lager_console_backend, [{level, info}]},
%% use below line to add syslog backend for Lager
% {lager_syslog_backend, [ "mongooseim", local0, info]},
{lager_file_backend, [{file, "ejabberd.log"}, {level, info}, {size, 2097152}, {date, "$D0"}, {count, 5}]}
Expand Down
10 changes: 10 additions & 0 deletions src/ejabberd_router.erl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
dirty_get_all_domains/0,
dirty_get_all_routes/1,
dirty_get_all_domains/1,
dirty_get_route_handler_module/1,
register_components/2,
register_components/3,
register_components/4,
Expand Down Expand Up @@ -374,6 +375,15 @@ all_routes(only_public) ->
++
mnesia:dirty_select(external_component_global, [MatchNonHidden]).

-spec dirty_get_route_handler_module(Domain :: binary()) -> {ok, module()} | error.
dirty_get_route_handler_module(Domain) ->
Routes = mnesia:dirty_match_object(#route{domain = Domain, handler = '_'}),
case Routes of
[#route{handler = {packet_handler, Module, _}}] ->
{ok, Module};
_ ->
error
end.

%%====================================================================
%% gen_server callbacks
Expand Down
33 changes: 23 additions & 10 deletions src/mongoose_client_api_rooms.erl
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ allowed_methods(Req, State) ->

resource_exists(Req, #{jid := #jid{lserver = Server}} = State) ->
{RoomIDOrJID, Req2} = cowboy_req:binding(id, Req),
MUCLightDomain = muc_light_domain(Server),
case RoomIDOrJID of
undefined ->
{Method, Req3} = cowboy_req:method(Req2),
Expand All @@ -53,8 +52,8 @@ resource_exists(Req, #{jid := #jid{lserver = Server}} = State) ->
end;
_ ->
case validate_room_id(RoomIDOrJID, Server) of
{ok, RoomID} ->
does_room_exist(RoomID, MUCLightDomain, Req2, State);
{ok, RoomID, RoomHost} ->
does_room_exist(RoomID, RoomHost, Req2, State);
_ ->
bad_request(Req2, State)
end
Expand Down Expand Up @@ -150,15 +149,29 @@ determine_role(US, Users) ->
Role
end.

-spec validate_room_id(RoomIDOrJID :: binary(), Server :: binary()) ->
{ok, RoomID :: binary()} | error.
validate_room_id(RoomIDOrJID, Server) ->
MUCLightDomain = muc_light_domain(Server),
-spec validate_room_id(RoomIDOrJID :: binary() | term(), Server :: binary()) ->
{ok, RoomID :: binary(), RoomHost :: binary()} | error.
validate_room_id(RoomIDOrJID, Server) when is_binary(RoomIDOrJID) ->
case jid:from_binary(RoomIDOrJID) of
#jid{luser = <<>>, lserver = RoomID, lresource = <<>>} ->
{ok, RoomID};
#jid{luser = RoomID, lserver = MUCLightDomain, lresource = <<>>} ->
{ok, RoomID};
DefaultMucLightDomain = muc_light_domain(Server),
{ok, RoomID, DefaultMucLightDomain};
#jid{luser = RoomID, lserver = RoomHost, lresource = <<>>} ->
case validate_room_host(RoomHost) of
ok ->
{ok, RoomID, RoomHost};
error ->
error
end;
_ ->
error
end.

-spec validate_room_host(binary()) -> ok | error.
validate_room_host(RoomHost) ->
case ejabberd_router:dirty_get_route_handler_module(RoomHost) of
{ok, mod_muc_light} ->
ok;
_ ->
error
end.