]> granicus.if.org Git - ejabberd/commitdiff
Add tests for external component
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Wed, 21 Sep 2016 07:45:11 +0000 (10:45 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Wed, 21 Sep 2016 07:45:11 +0000 (10:45 +0300)
src/ejabberd_c2s.erl
src/ejabberd_service.erl
test/ejabberd_SUITE.erl
test/ejabberd_SUITE_data/ejabberd.yml
test/suite.erl

index e98a9eb8fc758cf4ae407407f4352c1a73c9baae..7dc9960e643d5e61249f4447519ec6fb32228591 100644 (file)
@@ -494,7 +494,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
     catch _:{xmpp_codec, Why} ->
            Txt = xmpp:format_error(Why),
            send_header(StateData, ?MYNAME, <<"1.0">>, ?MYLANG),
-           send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)),
+           send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)),
            {stop, normal, StateData}
     end;
 wait_for_stream(timeout, StateData) ->
index 002d74949496c5075bf65cb9a4621481de95f8d5..f4338593d65e03af513e94d7cb40ceae7dd0b587 100644 (file)
@@ -125,7 +125,12 @@ init([{SockMod, Socket}, Opts]) ->
 
 wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
     try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of
-       #stream_start{xmlns = ?NS_COMPONENT, to = To} when is_record(To, jid) ->
+       #stream_start{xmlns = NS_COMPONENT, stream_xmlns = NS_STREAM}
+          when NS_COMPONENT /= ?NS_COMPONENT; NS_STREAM /= ?NS_STREAM ->
+            send_header(StateData, ?MYNAME),
+            send_element(StateData, xmpp:serr_invalid_namespace()),
+            {stop, normal, StateData};
+       #stream_start{to = To} when is_record(To, jid) ->
            Host = To#jid.lserver,
            send_header(StateData, Host),
            HostOpts = case dict:is_key(Host, StateData#state.host_opts) of
@@ -141,13 +146,9 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
                       end,
            {next_state, wait_for_handshake,
             StateData#state{host = Host, host_opts = HostOpts}};
-       #stream_start{xmlns = ?NS_COMPONENT} ->
-           send_header(StateData, ?MYNAME),
-           send_element(StateData, xmpp:serr_improper_addressing()),
-           {stop, normal, StateData};
        #stream_start{} ->
            send_header(StateData, ?MYNAME),
-           send_element(StateData, xmpp:serr_invalid_namespace()),
+           send_element(StateData, xmpp:serr_improper_addressing()),
            {stop, normal, StateData}
     catch _:{xmpp_codec, Why} ->
            Txt = xmpp:format_error(Why),
@@ -203,7 +204,8 @@ stream_established(El, StateData) when ?is_stanza(El) ->
     To = xmpp:get_to(El),
     Lang = xmpp:get_lang(El),
     if From == undefined orelse To == undefined ->
-           send_error(StateData, El, xmpp:err_jid_malformed());
+           Txt = <<"Missing 'from' or 'to' attribute">>,
+           send_error(StateData, El, xmpp:err_jid_malformed(Txt, Lang));
        true ->
            FromJID = case StateData#state.check_from of
                          false ->
@@ -214,19 +216,16 @@ stream_established(El, StateData) when ?is_stanza(El) ->
                              From;
                          _ ->
                              %% The default is the standard behaviour in XEP-0114
-                             case From of
-                                 #jid{lserver = Server} ->
-                                     case dict:is_key(Server, StateData#state.host_opts) of
-                                         true -> From;
-                                         false -> error
-                                     end;
-                                 _ -> error
+                             Server = From#jid.lserver,
+                             case dict:is_key(Server, StateData#state.host_opts) of
+                                 true -> From;
+                                 false -> error
                              end
                      end,
            if FromJID /= error ->
                    ejabberd_router:route(FromJID, To, El);
               true ->
-                   Txt = <<"Incorrect value of 'from' or 'to' attribute">>,
+                   Txt = <<"Improper domain part of 'from' attribute">>,
                    send_error(StateData, El, xmpp:err_not_allowed(Txt, Lang))
            end
     end,
index 0af81dd33cc89d8c855b89e8eedded43760a2646..dbcd9a905fc9c688f1f263ac2c574d949869ebc8 100644 (file)
@@ -125,6 +125,15 @@ do_init_per_group(riak, Config) ->
        Err ->
            {skip, {riak_not_available, Err}}
     end;
+do_init_per_group(component, Config) ->
+    Server = ?config(server, Config),
+    Port = ?config(component_port, Config),
+    set_opt(xmlns, ?NS_COMPONENT,
+            set_opt(server, <<"component.", Server/binary>>,
+                    set_opt(type, component,
+                            set_opt(server_port, Port,
+                                    set_opt(stream_version, <<"">>,
+                                            set_opt(lang, <<"">>, Config))))));
 do_init_per_group(_GroupName, Config) ->
     Pid = start_event_relay(),
     set_opt(event_relay, Pid, Config).
@@ -147,6 +156,8 @@ end_per_group(extauth, _Config) ->
     ok;
 end_per_group(riak, _Config) ->
     ok;
+end_per_group(component, _Config) ->
+    ok;
 end_per_group(_GroupName, Config) ->
     stop_event_relay(Config),
     ok.
@@ -252,7 +263,7 @@ no_db_tests() ->
     [{generic, [parallel],
       [test_connect_bad_xml,
        test_connect_unknown_ns,
-       test_connect_bad_ns_client,
+       test_connect_bad_xmlns,
        test_connect_bad_ns_stream,
        test_connect_bad_lang,
        test_connect_bad_to,
@@ -421,10 +432,27 @@ extauth_tests() ->
        test_auth_fail,
        test_unregister]}].
 
+component_tests() ->
+    [{component_tests, [sequence],
+      [test_connect_bad_xml,
+       test_connect_unknown_ns,
+       test_connect_bad_xmlns,
+       test_connect_bad_ns_stream,
+       test_connect_missing_to,
+       test_connect,
+       test_auth,
+       test_auth_fail,
+       component_missing_address,
+       component_invalid_from,
+       component_send,
+       bad_nonza,
+       codec_failure]}].
+
 groups() ->
     [{ldap, [sequence], ldap_tests()},
      {extauth, [sequence], extauth_tests()},
      {no_db, [sequence], no_db_tests()},
+     {component, [sequence], component_tests()},
      {mnesia, [sequence], db_tests(mnesia)},
      {redis, [sequence], db_tests(redis)},
      {mysql, [sequence], db_tests(mysql)},
@@ -433,7 +461,8 @@ groups() ->
      {riak, [sequence], db_tests(riak)}].
 
 all() ->
-    [%%{group, ldap},
+    [{group, component},
+     %%{group, ldap},
      {group, no_db},
      {group, mnesia},
      %%{group, redis},
@@ -451,19 +480,19 @@ stop_ejabberd(Config) ->
     Config.
 
 test_connect_bad_xml(Config) ->
-    Config0 = init_stream(set_opt(ns_client, <<"'">>, Config)),
+    Config0 = init_stream(set_opt(xmlns, <<"'">>, Config)),
     ?recv1(#stream_error{reason = 'not-well-formed'}),
     ?recv1({xmlstreamend, <<"stream:stream">>}),
     close_socket(Config0).
 
 test_connect_unknown_ns(Config) ->
-    Config0 = init_stream(set_opt(ns_client, <<"wrong">>, Config)),
-    ?recv1(#stream_error{reason = 'not-well-formed'}),
+    Config0 = init_stream(set_opt(xmlns, <<"wrong">>, Config)),
+    ?recv1(#stream_error{reason = 'invalid-xml'}),
     ?recv1({xmlstreamend, <<"stream:stream">>}),
     close_socket(Config0).
 
-test_connect_bad_ns_client(Config) ->
-    Config0 = init_stream(set_opt(ns_client, ?NS_SERVER, Config)),
+test_connect_bad_xmlns(Config) ->
+    Config0 = init_stream(set_opt(xmlns, ?NS_SERVER, Config)),
     ?recv1(#stream_error{reason = 'invalid-namespace'}),
     ?recv1({xmlstreamend, <<"stream:stream">>}),
     close_socket(Config0).
@@ -495,6 +524,12 @@ test_connect_missing_to(Config) ->
 test_connect(Config) ->
     disconnect(connect(Config)).
 
+test_component_connect(Config) ->
+    disconnect(component_connect(Config)).
+
+component_connect(Config) ->
+    init_stream(Config).
+
 test_starttls(Config) ->
     case ?config(starttls, Config) of
         true ->
@@ -578,6 +613,25 @@ invalid_from(Config) ->
     ?recv1({xmlstreamend, <<"stream:stream">>}),
     close_socket(Config).
 
+component_missing_address(Config) ->
+    Server = server_jid(Config),
+    #iq{type = error} = send_recv(Config, #iq{type = get, from = Server}),
+    #iq{type = error} = send_recv(Config, #iq{type = get, to = Server}),
+    disconnect(Config).
+
+component_invalid_from(Config) ->
+    From = jid:make(randoms:get_string()),
+    To = jid:make(randoms:get_string()),
+    #iq{type = error} =
+       send_recv(Config, #iq{type = get, from = From, to = To}),
+    disconnect(Config).
+
+component_send(Config) ->
+    JID = my_jid(Config),
+    send(Config, #message{from = JID, to = JID}),
+    #message{from = JID, to = JID} = recv(),
+    disconnect(Config).
+
 auth_md5(Config) ->
     Mechs = ?config(mechs, Config),
     case lists:member(<<"DIGEST-MD5">>, Mechs) of
@@ -621,7 +675,8 @@ test_auth(Config) ->
     disconnect(auth(Config)).
 
 test_auth_fail(Config0) ->
-    Config = set_opt(user, <<"wrong">>, Config0),
+    Config = set_opt(user, <<"wrong">>,
+                    set_opt(password, <<"wrong">>, Config0)),
     disconnect(auth(Config, _ShouldFail = true)).
 
 test_bind(Config) ->
@@ -659,7 +714,9 @@ roster_ver(Config) ->
     disconnect(Config).
 
 codec_failure(Config) ->
-    #iq{type = error} = send_recv(Config, #iq{type = wrong}),
+    JID = my_jid(Config),
+    #iq{type = error} =
+       send_recv(Config, #iq{type = wrong, from = JID, to = JID}),
     disconnect(Config).
 
 unsupported_query(Config) ->
@@ -829,13 +886,14 @@ private(Config) ->
                                               <<>>)},
     Storage = #bookmark_storage{conference = [Conference]},
     StorageXMLOut = xmpp_codec:encode(Storage),
+    WrongEl = #xmlel{name = <<"wrong">>},
     #iq{type = error} =
-        send_recv(Config, #iq{type = get, sub_els = [#private{}],
-                              to = server_jid(Config)}),
+        send_recv(Config, #iq{type = get,
+                             sub_els = [#private{xml_els = [WrongEl]}]}),
     #iq{type = result, sub_els = []} =
         send_recv(
           Config, #iq{type = set,
-                      sub_els = [#private{xml_els = [StorageXMLOut]}]}),
+                      sub_els = [#private{xml_els = [WrongEl, StorageXMLOut]}]}),
     #iq{type = result,
         sub_els = [#private{xml_els = [StorageXMLIn]}]} =
         send_recv(
index b2a0cd5accc7ff940acae19369b702b9c608cd35..99af18bbf79c3caa98a712efc389a22992756012 100644 (file)
@@ -428,6 +428,11 @@ listen:
     port: @@web_port@@
     module: ejabberd_http
     captcha: true
+  - 
+    port: @@component_port@@
+    module: ejabberd_service
+    password: >-
+      @@password@@
 loglevel: @@loglevel@@
 max_fsm_queue: 1000
 modules: 
index 93141d8460235abbc040e3e087941da4656f4643..f78b7bd1a905e5e1ddce1d055428ff3714521485 100644 (file)
@@ -30,11 +30,14 @@ init_config(Config) ->
     {ok, CWD} = file:get_cwd(),
     {ok, _} = file:copy(CertFile, filename:join([CWD, "cert.pem"])),
     {ok, CfgContentTpl} = file:read_file(ConfigPathTpl),
+    Password = <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>,
     CfgContent = process_config_tpl(CfgContentTpl, [
                                                     {c2s_port, 5222},
                                                     {loglevel, 5},
                                                     {s2s_port, 5269},
+                                                   {component_port, 5270},
                                                     {web_port, 5280},
+                                                   {password, Password},
                                                     {mysql_server, <<"localhost">>},
                                                     {mysql_port, 3306},
                                                     {mysql_db, <<"ejabberd_test">>},
@@ -58,13 +61,15 @@ init_config(Config) ->
     application:set_env(mnesia, dir, MnesiaDir),
     [{server_port, ct:get_config(c2s_port, 5222)},
      {server_host, "localhost"},
+     {component_port, ct:get_config(component_port, 5270)},
      {server, ?COMMON_VHOST},
      {user, <<"test_single!#$%^*()`~+-;_=[]{}|\\">>},
      {master_nick, <<"master_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
      {slave_nick, <<"slave_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
      {room_subject, <<"hello, world!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
      {certfile, CertFile},
-     {ns_client, ?NS_CLIENT},
+     {type, client},
+     {xmlns, ?NS_CLIENT},
      {ns_stream, ?NS_STREAM},
      {stream_version, <<"1.0">>},
      {stream_id, <<"">>},
@@ -74,7 +79,7 @@ init_config(Config) ->
      {resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
      {master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
      {slave_resource, <<"slave_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
-     {password, <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
+     {password, Password},
      {backends, get_config_backends()}
      |Config].
 
@@ -126,20 +131,29 @@ process_config_tpl(Content, [{Name, DefaultValue} | Rest]) ->
 
 stream_header(Config) ->
     NSStream = ?config(ns_stream, Config),
-    NSClient = ?config(ns_client, Config),
-    Lang = ?config(lang, Config),
+    XMLNS = ?config(xmlns, Config),
+    Lang = case ?config(lang, Config) of
+              <<"">> -> <<"">>;
+              L -> iolist_to_binary(["xml:lang='", L, "'"])
+          end,
     To = case ?config(server, Config) of
             <<"">> -> <<"">>;
             Server -> <<"to='", Server/binary, "'">>
         end,
-    Version = ?config(stream_version, Config),
+    Version = case ?config(stream_version, Config) of
+                 <<"">> -> <<"">>;
+                 V -> <<"version='", V/binary, "'">>
+             end,
     io_lib:format("<?xml version='1.0'?><stream:stream "
-                 "xmlns:stream='~s' xmlns='~s' ~s "
-                 "version='~s' xml:lang='~s'>",
-                 [NSStream, NSClient, To, Version, Lang]).
+                 "xmlns:stream='~s' xmlns='~s' ~s ~s ~s>",
+                 [NSStream, XMLNS, To, Version, Lang]).
 
 connect(Config) ->
-    process_stream_features(init_stream(Config)).
+    NewConfig = init_stream(Config),
+    case ?config(type, NewConfig) of
+       client -> process_stream_features(NewConfig);
+       component -> NewConfig
+    end.
 
 init_stream(Config) ->
     Version = ?config(stream_version, Config),
@@ -149,8 +163,11 @@ init_stream(Config) ->
                    [binary, {packet, 0}, {active, false}]),
     NewConfig = set_opt(socket, Sock, Config),
     ok = send_text(NewConfig, stream_header(NewConfig)),
-    #stream_start{id = ID, xmlns = ?NS_CLIENT,
-                 version = Version} = recv(),
+    XMLNS = case ?config(type, Config) of
+               client -> ?NS_CLIENT;
+               component -> ?NS_COMPONENT
+           end,
+    #stream_start{id = ID, xmlns = XMLNS, version = Version} = recv(),
     set_opt(stream_id, ID, NewConfig).
 
 process_stream_features(Config) ->
@@ -174,7 +191,11 @@ process_stream_features(Config) ->
 
 disconnect(Config) ->
     Socket = ?config(socket, Config),
-    ok = ejabberd_socket:send(Socket, ?STREAM_TRAILER),
+    try
+       ok = send_text(Config, ?STREAM_TRAILER)
+    catch exit:normal ->
+           ok
+    end,
     {xmlstreamend, <<"stream:stream">>} = recv(),
     ejabberd_socket:close(Socket),
     Config.
@@ -203,6 +224,7 @@ auth(Config) ->
     auth(Config, false).
 
 auth(Config, ShouldFail) ->
+    Type = ?config(type, Config),
     Mechs = ?config(mechs, Config),
     HaveMD5 = lists:member(<<"DIGEST-MD5">>, Mechs),
     HavePLAIN = lists:member(<<"PLAIN">>, Mechs),
@@ -210,16 +232,23 @@ auth(Config, ShouldFail) ->
             auth_SASL(<<"PLAIN">>, Config, ShouldFail);
        HaveMD5 ->
             auth_SASL(<<"DIGEST-MD5">>, Config, ShouldFail);
-       true ->
-           auth_legacy(Config, false, ShouldFail)
+       Type == client ->
+           auth_legacy(Config, false, ShouldFail);
+       Type == component ->
+           auth_component(Config, ShouldFail)
     end.
 
 bind(Config) ->
-    #iq{type = result, sub_els = [#bind{}]} =
-        send_recv(
-          Config,
-          #iq{type = set,
-              sub_els = [#bind{resource = ?config(resource, Config)}]}),
+    case ?config(type, Config) of
+       client ->
+           #iq{type = result, sub_els = [#bind{}]} =
+               send_recv(
+                 Config,
+                 #iq{type = set,
+                     sub_els = [#bind{resource = ?config(resource, Config)}]});
+       component ->
+           ok
+    end,
     Config.
 
 open_session(Config) ->
@@ -279,6 +308,22 @@ auth_legacy(Config, IsDigest, ShouldFail) ->
            end
     end.
 
+auth_component(Config, ShouldFail) ->
+    StreamID = ?config(stream_id, Config),
+    Password = ?config(password, Config),
+    Digest = p1_sha:sha(<<StreamID/binary, Password/binary>>),
+    send(Config, #handshake{data = Digest}),
+    case recv() of
+       #handshake{} when ShouldFail ->
+           ct:fail(component_auth_should_have_failed);
+       #handshake{} ->
+           Config;
+       #stream_error{reason = 'not-authorized'} when ShouldFail ->
+           Config;
+       #stream_error{reason = 'not-authorized'} ->
+           ct:fail(component_auth_failed)
+    end.
+
 auth_SASL(Mech, Config) ->
     auth_SASL(Mech, Config, false).