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