1: %%==============================================================================
    2: %% Copyright 2016 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: 
   17: -module(sasl_SUITE).
   18: -compile([export_all, nowarn_export_all]).
   19: 
   20: -include_lib("escalus/include/escalus.hrl").
   21: -include_lib("common_test/include/ct.hrl").
   22: -include_lib("exml/include/exml.hrl").
   23: 
   24: -define(TEST_MECHANISM, <<"TEST-MECHANISM">>).
   25: -define(TEXT_CONTENT, <<"Call 555-123-1234 for assistance">>).
   26: 
   27: -behaviour(cyrsasl).
   28: 
   29: -import(distributed_helper, [mim/0,
   30:                              require_rpc_nodes/1,
   31:                              rpc/4]).
   32: -import(domain_helper, [host_type/0]).
   33: 
   34: %%--------------------------------------------------------------------
   35: %% Suite configuration
   36: %%--------------------------------------------------------------------
   37: 
   38: all() ->
   39:     [{group, host_type_config}].
   40: 
   41: groups() ->
   42:     [{host_type_config, [sequence], all_tests()}].
   43: 
   44: all_tests() ->
   45:     [text_response].
   46: 
   47: suite() ->
   48:     require_rpc_nodes([mim]) ++ escalus:suite().
   49: 
   50: %%--------------------------------------------------------------------
   51: %% Init & teardown
   52: %%--------------------------------------------------------------------
   53: init_per_suite(Config) ->
   54:     escalus:init_per_suite(Config).
   55: 
   56: end_per_suite(Config) ->
   57:     escalus:end_per_suite(Config).
   58: 
   59: init_per_group(_GroupName, Config) ->
   60:     Config1 = set_sasl_mechanisms(Config),
   61:     escalus:create_users(Config1, escalus:get_users([alice])).
   62: 
   63: end_per_group(_GroupName, Config) ->
   64:     reset_sasl_mechanisms(Config),
   65:     escalus:delete_users(Config, escalus:get_users([alice])).
   66: 
   67: init_per_testcase(CaseName, Config) ->
   68:     escalus:init_per_testcase(CaseName, Config).
   69: 
   70: end_per_testcase(CaseName, Config) ->
   71:     escalus:end_per_testcase(CaseName, Config).
   72: 
   73: %%--------------------------------------------------------------------
   74: %% cyrsasl tests
   75: %%--------------------------------------------------------------------
   76: 
   77: text_response(Config) ->
   78:     mongoose_helper:inject_module(?MODULE),
   79:     AliceSpec = escalus_users:get_options(Config, alice),
   80:     {ok, Client, _} = escalus_connection:start(AliceSpec, [start_stream, stream_features]),
   81:     Stanza = escalus_stanza:auth(?TEST_MECHANISM,
   82:                                  [#xmlcdata{content = base64:encode(<<"alice">>)}]),
   83:     Result = escalus:send_and_wait(Client, Stanza),
   84:     assert_is_failure_with_text(Result).
   85: 
   86: %%--------------------------------------------------------------------
   87: %% Helpers
   88: %%--------------------------------------------------------------------
   89: 
   90: set_sasl_mechanisms(Config) ->
   91:     %% pretend that an auth module is set for this mechanism
   92:     rpc(mim(), meck, new, [ejabberd_auth, [no_link, passthrough]]),
   93:     rpc(mim(), meck, expect, [ejabberd_auth, supports_sasl_module,
   94:                               fun(_, M) -> M =:= ?MODULE end]),
   95: 
   96:     %% configure the mechanism
   97:     Key = {auth, host_type()},
   98:     AuthOpts = rpc(mim(), mongoose_config, get_opt, [Key]),
   99:     NewAuthOpts = AuthOpts#{sasl_mechanisms => [?MODULE]},
  100:     mongoose_helper:backup_and_set_config_option(Config, Key, NewAuthOpts).
  101: 
  102: reset_sasl_mechanisms(Config) ->
  103:     mongoose_helper:restore_config_option(Config, {auth, host_type()}),
  104:     rpc(mim(), meck, unload, [ejabberd_auth]).
  105: 
  106: assert_is_failure_with_text(#xmlel{name = <<"failure">>,
  107:                                    children = Children}) ->
  108:     assert_has_text(Children);
  109: assert_is_failure_with_text(Result) ->
  110:     ct:fail("Result is not a failure stanza: ~p", [Result]).
  111: 
  112: assert_has_text([]) ->
  113:     ct:fail("Result has no or incorrect text field");
  114: assert_has_text([#xmlel{name = <<"text">>,
  115:                         children = [#xmlcdata{content = ?TEXT_CONTENT}]}
  116:                  | _]) ->
  117:     ok;
  118: assert_has_text([_ | Tail]) ->
  119:     assert_has_text(Tail).
  120: 
  121: %%--------------------------------------------------------------------
  122: %% cyrsasl test callback functions
  123: %%--------------------------------------------------------------------
  124: 
  125: mechanism() ->
  126:     ?TEST_MECHANISM.
  127: 
  128: mech_new(_Host, _Creds, _Socket) ->
  129:     {ok, state}.
  130: 
  131: mech_step(_State, _ClientIn) ->
  132:     {error, {<<"not-authorized">>, ?TEXT_CONTENT}, <<>>}.