1: -module(domain_isolation_SUITE).
    2: 
    3: -include_lib("common_test/include/ct.hrl").
    4: -include_lib("eunit/include/eunit.hrl").
    5: 
    6: -compile([export_all, nowarn_export_all]).
    7: -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4, subhost_pattern/1]).
    8: -import(domain_helper, [host_type/0, secondary_host_type/0]).
    9: 
   10: suite() ->
   11:     require_rpc_nodes([mim]).
   12: 
   13: all() ->
   14:     [{group, two_domains}].
   15: 
   16: groups() ->
   17:     [{two_domains, [parallel], cases()}].
   18: 
   19: cases() ->
   20:     [routing_one2one_message_inside_one_domain_works,
   21:      routing_one2one_message_to_another_domain_gets_dropped,
   22:      routing_one2one_message_to_another_domain_results_in_service_unavailable,
   23:      routing_to_yours_subdomain_gets_passed_to_muc_module,
   24:      routing_to_foreign_subdomain_results_in_service_unavailable].
   25: 
   26: host_types() ->
   27:     %% This suite tests domain isolation.
   28:     %% But two domains could be on the same host type, and still should be isolated.
   29:     %% So, we could need to init modules only once.
   30:     lists:usort([host_type(), secondary_host_type()]).
   31: 
   32: %%--------------------------------------------------------------------
   33: %% Init & teardown
   34: %%--------------------------------------------------------------------
   35: 
   36: init_per_suite(Config) ->
   37:     escalus:init_per_suite(Config).
   38: 
   39: end_per_suite(Config) ->
   40:     escalus:end_per_suite(Config).
   41: 
   42: modules() ->
   43:     MucHost = subhost_pattern(muc_helper:muc_host_pattern()),
   44:     [{mod_domain_isolation, []},
   45:      {mod_muc_light, [{host, MucHost}]}].
   46: 
   47: init_per_group(two_domains, Config) ->
   48:     Config2 = dynamic_modules:save_modules(host_types(), Config),
   49:     [dynamic_modules:ensure_modules(HostType, modules()) || HostType <- host_types()],
   50:     Config2.
   51: 
   52: end_per_group(two_domains, Config) ->
   53:     escalus_fresh:clean(),
   54:     dynamic_modules:restore_modules(Config),
   55:     Config.
   56: 
   57: init_per_testcase(Testcase, Config) ->
   58:     escalus:init_per_testcase(Testcase, Config).
   59: 
   60: end_per_testcase(Testcase, Config) ->
   61:     escalus:end_per_testcase(Testcase, Config).
   62: 
   63: %%--------------------------------------------------------------------
   64: %% Tests
   65: %%--------------------------------------------------------------------
   66: 
   67: routing_one2one_message_inside_one_domain_works(Config) ->
   68:     F = fun(Alice, Bob) ->
   69:           %% WHEN Routed inside the same domain
   70:           escalus_client:send(Bob, escalus_stanza:chat_to(Alice, <<"Hello">>)),
   71:           %% THEN Message gets delivered
   72:           Stanza = escalus:wait_for_stanza(Alice),
   73:           escalus:assert(is_chat_message, [<<"Hello">>], Stanza)
   74:         end,
   75:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], F).
   76: 
   77: routing_one2one_message_to_another_domain_gets_dropped(Config) ->
   78:     F = fun(Alice, Bob, Bis) ->
   79:           %% GIVEN Alice and Bis are on different domains
   80:           %% WHEN A stanza is sent to another domain
   81:           escalus_client:send(Bis, escalus_stanza:chat_to(Alice, <<"Hello">>)),
   82:           %% THEN Receiver does not receive a message
   83:           verify_alice_has_no_pending_messages(Alice, Bob)
   84:         end,
   85:     escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {alice_bis, 1}], F).
   86: 
   87: routing_one2one_message_to_another_domain_results_in_service_unavailable(Config) ->
   88:     F = fun(Alice, Bis) ->
   89:           %% GIVEN Alice and Bis are on different domains
   90:           %% WHEN A stanza is sent to another domain
   91:           escalus_client:send(Bis, escalus_stanza:chat_to(Alice, <<"Hello">>)),
   92:           %% THEN Sender receives an error
   93:           receives_service_unavailable(Bis)
   94:         end,
   95:     escalus:fresh_story(Config, [{alice, 1}, {alice_bis, 1}], F).
   96: 
   97: routing_to_yours_subdomain_gets_passed_to_muc_module(Config) ->
   98:     F = fun(Alice) ->
   99:           %% GIVEN Alice is on the same domain
  100:           %% WHEN Alice routes a stanza
  101:           escalus_client:send(Alice, invalid_muc_stanza()),
  102:           %% THEN Alice receives an error from mod_muc,
  103:           %%      like if there is no mod_domain_isolation.
  104:           receives_muc_bad_request(Alice)
  105:         end,
  106:     escalus:fresh_story(Config, [{alice, 1}], F).
  107: 
  108: routing_to_foreign_subdomain_results_in_service_unavailable(Config) ->
  109:     F = fun(Alice) ->
  110:           %% GIVEN Alice is on another domain
  111:           %% WHEN Alice routes a stanza
  112:           escalus_client:send(Alice, invalid_muc_stanza()),
  113:           %% THEN Sender receives an error about the drop
  114:           receives_service_unavailable(Alice)
  115:         end,
  116:     escalus:fresh_story(Config, [{alice_bis, 1}], F).
  117: 
  118: %%--------------------------------------------------------------------
  119: %% Helpers
  120: %%--------------------------------------------------------------------
  121: 
  122: get_error_text(Err) ->
  123:     exml_query:path(Err, [{element, <<"error">>}, {element, <<"text">>}, cdata]).
  124: 
  125: invalid_muc_address() ->
  126:     MucHost = muc_helper:muc_host(),
  127:     <<MucHost/binary, "/wow_resource_not_so_empty">>.
  128: 
  129: invalid_muc_stanza() ->
  130:     escalus_stanza:chat_to(invalid_muc_address(), <<"Hi muc!">>).
  131: 
  132: receives_service_unavailable(Alice) ->
  133:     Err = escalus:wait_for_stanza(Alice),
  134:     escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Err),
  135:     <<"Filtered by the domain isolation">> = get_error_text(Err).
  136: 
  137: receives_muc_bad_request(Alice) ->
  138:     Err = escalus:wait_for_stanza(Alice),
  139:     escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], Err),
  140:     %% This error is generated by mod_muc:
  141:     <<"Resource expected to be empty">> = get_error_text(Err).
  142: 
  143: %% Verify than there is no unreceived messages by Alice by routing a message from Bob.
  144: %% Bob should be able to send messages to Alice.
  145: %% If the Bob's message gets received - there is no pending messages.
  146: verify_alice_has_no_pending_messages(Alice, Bob) ->
  147:     escalus_client:send(Bob, escalus_stanza:chat_to(Alice, <<"Forces to flush">>)),
  148:     Stanza = escalus:wait_for_stanza(Alice),
  149:     escalus:assert(is_chat_message, [<<"Forces to flush">>], Stanza).