1: %%==============================================================================
    2: %% Copyright 2011 Erlang Solutions Ltd.
    3: %%
    4: %% Licensed under the Apache License, Version 2.0 (the "License");
    5: %% you may not use this file except in compliance with the License.
    6: %% You may obtain a copy of the License at
    7: %%
    8: %% http://www.apache.org/licenses/LICENSE-2.0
    9: %%
   10: %% Unless required by applicable law or agreed to in writing, software
   11: %% distributed under the License is distributed on an "AS IS" BASIS,
   12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13: %% See the License for the specific language governing permissions and
   14: %% limitations under the License.
   15: %%==============================================================================
   16: -module(private_SUITE).
   17: -compile([export_all, nowarn_export_all]).
   18: 
   19: -include_lib("exml/include/exml.hrl").
   20: -include_lib("escalus/include/escalus.hrl").
   21: -include_lib("common_test/include/ct.hrl").
   22: 
   23: -import(config_parser_helper, [mod_config_with_auto_backend/2]).
   24: 
   25: %%--------------------------------------------------------------------
   26: %% Suite configuration
   27: %%--------------------------------------------------------------------
   28: all() ->
   29:     [{group, private_positive},
   30:      {group, private_negative}].
   31: 
   32: groups() ->
   33:     G = [{private_positive, [sequence], positive_test_cases()},
   34:          {private_negative, [sequence], negative_test_cases()}],
   35:     ct_helper:repeat_all_until_all_ok(G).
   36:     %% FIXME: broken exmpp prevents us from sending
   37:     %% out elements without NS set missing_ns]}].
   38: 
   39: positive_test_cases() ->
   40:     [store_retrieve].
   41: 
   42: negative_test_cases() ->
   43:     [get_other_user,
   44:      set_other_user].
   45: 
   46: suite() ->
   47:     escalus:suite().
   48: 
   49: init_per_suite(Config0) ->
   50:     HostType = domain_helper:host_type(),
   51:     Config1 = dynamic_modules:save_modules(HostType, Config0),
   52:     dynamic_modules:ensure_modules(HostType, create_config()),
   53:     escalus:init_per_suite(Config1).
   54: 
   55: create_config() ->
   56:     [{mod_private,
   57:       mod_config_with_auto_backend(mod_private, #{iqdisc => one_queue})}].
   58: 
   59: end_per_suite(Config) ->
   60:     dynamic_modules:restore_modules(Config),
   61:     escalus:end_per_suite(Config).
   62: 
   63: init_per_group(_GroupName, Config) ->
   64:     escalus:create_users(Config, escalus:get_users([alice, bob])).
   65: 
   66: end_per_group(_GroupName, Config) ->
   67:     escalus:delete_users(Config, escalus:get_users([alice, bob])).
   68: 
   69: init_per_testcase(CaseName, Config) ->
   70:     escalus:init_per_testcase(CaseName, Config).
   71: 
   72: end_per_testcase(CaseName, Config) ->
   73:     escalus:end_per_testcase(CaseName, Config).
   74: 
   75: %%--------------------------------------------------------------------
   76: %% Private storage tests
   77: %%--------------------------------------------------------------------
   78: store_retrieve(Config) ->
   79:     escalus:story(Config, [{alice, 1}],
   80:                   fun(Alice) ->
   81:                           NS = <<"alice:private:ns">>,
   82: 
   83:                           %% Alice stores some data in her private storage
   84:                           PrivateStanza = escalus_stanza:private_set(my_banana(NS)),
   85:                           escalus_client:send(Alice, PrivateStanza),
   86: 
   87:                           %% Alice receives store confirmation
   88:                           escalus:assert(
   89:                             is_iq_result,
   90:                             [PrivateStanza],
   91:                             escalus_client:wait_for_stanza(Alice)),
   92: 
   93:                           %% Alice asks for the data
   94:                           escalus_client:send(Alice, escalus_stanza:private_get(NS, <<"my_element">>)),
   95: 
   96:                           %% Alice ensures data has not been changed
   97:                           Stanza = escalus_client:wait_for_stanza(Alice),
   98:                           escalus:assert(is_private_result, Stanza),
   99:                           check_body(Stanza, [<<"my_element">>, <<"banana">>]),
  100: 
  101:                           %% Alice asks for non-existing data
  102:                           escalus_client:send(Alice, escalus_stanza:private_get(<<"non_existing_ns">>,
  103:                                                                                 <<"my_element">>)),
  104: 
  105:                           %% Alice receives an empty response
  106:                           Stanza2 = escalus_client:wait_for_stanza(Alice),
  107:                           escalus:assert(is_private_result, Stanza2),
  108:                           check_body(Stanza, [<<"my_element">>])
  109:                   end).
  110: 
  111: get_other_user(Config) ->
  112:     escalus:story(Config, [{alice, 1}, {bob, 1}],
  113:                   fun(Alice, _Bob) ->
  114:                           NS = <<"bob:private:ns">>,
  115: 
  116:                           %% Alice asks for Bob's private data
  117:                           GetIQ = escalus_stanza:private_get(NS, <<"my_element">>),
  118:                           IQ = escalus_stanza:to(GetIQ, escalus_users:get_jid(Config, bob)),
  119:                           escalus_client:send(Alice, IQ),
  120: 
  121:                           %% Alice gets an error
  122:                           Stanza = escalus_client:wait_for_stanza(Alice),
  123:                           escalus:assert(is_private_error, Stanza),
  124:                           escalus_pred:is_error(<<"cancel">>, forbidden, Stanza)
  125:                   end).
  126: 
  127: set_other_user(Config) ->
  128:     escalus:story(Config, [{alice, 1}, {bob, 1}],
  129:                   fun(Alice, _Bob) ->
  130:                           NS = <<"bob:private:ns">>,
  131: 
  132:                           %% Alice asks for Bob's private data
  133:                           IQ = escalus_stanza:to(escalus_stanza:private_set(my_banana(NS)),
  134:                                                  escalus_users:get_jid(Config, bob)),
  135:                           escalus_client:send(Alice, IQ),
  136: 
  137:                           %% Alice gets a forbidden error
  138:                           Stanza = escalus_client:wait_for_stanza(Alice),
  139:                           escalus:assert(is_private_error, Stanza),
  140:                           escalus_pred:is_error(<<"cancel">>, forbidden, Stanza)
  141:                   end).
  142: 
  143: missing_ns(Config) ->
  144:     escalus:story(Config, [{alice, 1}],
  145:                   fun(Alice) ->
  146:                           %% Alice asks for her own private storage, without
  147:                           %% providing a namespace for a child
  148:                           MyBanana = #xmlel{name = <<"my_element">>,
  149:                                                  children = [#xmlel{name = <<"banana">>}]},
  150:                           IQ = escalus_stanza:private_get(MyBanana),
  151:                           escalus_client:send(Alice, IQ),
  152: 
  153:                           %% Alice gets a bad-format error
  154:                           Stanza = escalus_client:wait_for_stanza(Alice),
  155:                           escalus:assert(is_private_error, Stanza),
  156:                           escalus_pred:is_error(<<"modify">>, 'bad-format', Stanza)
  157:                   end).
  158: 
  159: %%-----------------------------------------------------------------
  160: %% Helpers
  161: %%-----------------------------------------------------------------
  162: 
  163: my_banana(NS) ->
  164:     #xmlel{
  165:         name = <<"my_element">>,
  166:         attrs = [{<<"xmlns">>, NS}],
  167:         children = [#xmlel{name = <<"banana">>}]}.
  168: 
  169: check_body(Stanza, Names) ->
  170:     Query = exml_query:subelement(Stanza, <<"query">>),
  171:     check_body_rec(Query, Names).
  172: 
  173: check_body_rec(_, []) ->
  174:     ok;
  175: check_body_rec(Element, [Name | Names]) ->
  176:     [Child] = Element#xmlel.children,
  177:     Name = Child#xmlel.name,
  178:     check_body_rec(Child, Names).