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: %%--------------------------------------------------------------------
   24: %% Suite configuration
   25: %%--------------------------------------------------------------------
   26: all() ->
   27:     [{group, private_positive},
   28:      {group, private_negative}].
   29: 
   30: groups() ->
   31:     G = [{private_positive, [sequence], positive_test_cases()},
   32:          {private_negative, [sequence], negative_test_cases()}],
   33:     ct_helper:repeat_all_until_all_ok(G).
   34:     %% FIXME: broken exmpp prevents us from sending
   35:     %% out elements without NS set missing_ns]}].
   36: 
   37: positive_test_cases() ->
   38:     [store_retrieve].
   39: 
   40: negative_test_cases() ->
   41:     [get_other_user,
   42:      set_other_user].
   43: 
   44: suite() ->
   45:     escalus:suite().
   46: 
   47: init_per_suite(Config0) ->
   48:     HostType = domain_helper:host_type(),
   49:     Config1 = dynamic_modules:save_modules(HostType, Config0),
   50:     Backend = mongoose_helper:get_backend_mnesia_rdbms(HostType),
   51:     ModConfig = create_config(Backend),
   52:     dynamic_modules:ensure_modules(HostType, ModConfig),
   53:     escalus:init_per_suite([{backend, Backend} | Config1]).
   54: 
   55: create_config(Backend) ->
   56:     [{mod_private, #{backend => Backend, iqdisc => one_queue}}].
   57: 
   58: end_per_suite(Config) ->
   59:     dynamic_modules:restore_modules(Config),
   60:     escalus:end_per_suite(Config).
   61: 
   62: init_per_group(_GroupName, Config) ->
   63:     escalus:create_users(Config, escalus:get_users([alice, bob])).
   64: 
   65: end_per_group(_GroupName, Config) ->
   66:     escalus:delete_users(Config, escalus:get_users([alice, bob])).
   67: 
   68: init_per_testcase(CaseName, Config) ->
   69:     escalus:init_per_testcase(CaseName, Config).
   70: 
   71: end_per_testcase(CaseName, Config) ->
   72:     escalus:end_per_testcase(CaseName, Config).
   73: 
   74: %%--------------------------------------------------------------------
   75: %% Private storage tests
   76: %%--------------------------------------------------------------------
   77: store_retrieve(Config) ->
   78:     escalus:story(Config, [{alice, 1}],
   79:                   fun(Alice) ->
   80:                           NS = <<"alice:private:ns">>,
   81: 
   82:                           %% Alice stores some data in her private storage
   83:                           PrivateStanza = escalus_stanza:private_set(my_banana(NS)),
   84:                           escalus_client:send(Alice, PrivateStanza),
   85: 
   86:                           %% Alice receives store confirmation
   87:                           escalus:assert(
   88:                             is_iq_result,
   89:                             [PrivateStanza],
   90:                             escalus_client:wait_for_stanza(Alice)),
   91: 
   92:                           %% Alice asks for the data
   93:                           escalus_client:send(Alice, escalus_stanza:private_get(NS, <<"my_element">>)),
   94: 
   95:                           %% Alice ensures data has not been changed
   96:                           Stanza = escalus_client:wait_for_stanza(Alice),
   97:                           escalus:assert(is_private_result, Stanza),
   98:                           check_body(Stanza, [<<"my_element">>, <<"banana">>]),
   99: 
  100:                           %% Alice asks for non-existing data
  101:                           escalus_client:send(Alice, escalus_stanza:private_get(<<"non_existing_ns">>,
  102:                                                                                 <<"my_element">>)),
  103: 
  104:                           %% Alice receives an empty response
  105:                           Stanza2 = escalus_client:wait_for_stanza(Alice),
  106:                           escalus:assert(is_private_result, Stanza2),
  107:                           check_body(Stanza, [<<"my_element">>])
  108:                   end).
  109: 
  110: get_other_user(Config) ->
  111:     escalus:story(Config, [{alice, 1}, {bob, 1}],
  112:                   fun(Alice, _Bob) ->
  113:                           NS = <<"bob:private:ns">>,
  114: 
  115:                           %% Alice asks for Bob's private data
  116:                           GetIQ = escalus_stanza:private_get(NS, <<"my_element">>),
  117:                           IQ = escalus_stanza:to(GetIQ, escalus_users:get_jid(Config, bob)),
  118:                           escalus_client:send(Alice, IQ),
  119: 
  120:                           %% Alice gets an error
  121:                           Stanza = escalus_client:wait_for_stanza(Alice),
  122:                           escalus:assert(is_private_error, Stanza),
  123:                           escalus_pred:is_error(<<"cancel">>, forbidden, Stanza)
  124:                   end).
  125: 
  126: set_other_user(Config) ->
  127:     escalus:story(Config, [{alice, 1}, {bob, 1}],
  128:                   fun(Alice, _Bob) ->
  129:                           NS = <<"bob:private:ns">>,
  130: 
  131:                           %% Alice asks for Bob's private data
  132:                           IQ = escalus_stanza:to(escalus_stanza:private_set(my_banana(NS)),
  133:                                                  escalus_users:get_jid(Config, bob)),
  134:                           escalus_client:send(Alice, IQ),
  135: 
  136:                           %% Alice gets a forbidden error
  137:                           Stanza = escalus_client:wait_for_stanza(Alice),
  138:                           escalus:assert(is_private_error, Stanza),
  139:                           escalus_pred:is_error(<<"cancel">>, forbidden, Stanza)
  140:                   end).
  141: 
  142: missing_ns(Config) ->
  143:     escalus:story(Config, [{alice, 1}],
  144:                   fun(Alice) ->
  145:                           %% Alice asks for her own private storage, without
  146:                           %% providing a namespace for a child
  147:                           MyBanana = #xmlel{name = <<"my_element">>,
  148:                                                  children = [#xmlel{name = <<"banana">>}]},
  149:                           IQ = escalus_stanza:private_get(MyBanana),
  150:                           escalus_client:send(Alice, IQ),
  151: 
  152:                           %% Alice gets a bad-format error
  153:                           Stanza = escalus_client:wait_for_stanza(Alice),
  154:                           escalus:assert(is_private_error, Stanza),
  155:                           escalus_pred:is_error(<<"modify">>, 'bad-format', Stanza)
  156:                   end).
  157: 
  158: %%-----------------------------------------------------------------
  159: %% Helpers
  160: %%-----------------------------------------------------------------
  161: 
  162: my_banana(NS) ->
  163:     #xmlel{
  164:         name = <<"my_element">>,
  165:         attrs = [{<<"xmlns">>, NS}],
  166:         children = [#xmlel{name = <<"banana">>}]}.
  167: 
  168: check_body(Stanza, Names) ->
  169:     Query = exml_query:subelement(Stanza, <<"query">>),
  170:     check_body_rec(Query, Names).
  171: 
  172: check_body_rec(_, []) ->
  173:     ok;
  174: check_body_rec(Element, [Name | Names]) ->
  175:     [Child] = Element#xmlel.children,
  176:     Name = Child#xmlel.name,
  177:     check_body_rec(Child, Names).
  178: 
  179: required_modules(Backend) ->
  180:     [{mod_private, [{backend, Backend}]}].