From 73237237ef8a14bb68f1c98f8826ebd67b023fee Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 17:27:32 -0500 Subject: [PATCH 1/9] test incorrect PASS passwords --- irctest/server_tests/test_connection_registration.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/irctest/server_tests/test_connection_registration.py b/irctest/server_tests/test_connection_registration.py index a719768..e26b08c 100644 --- a/irctest/server_tests/test_connection_registration.py +++ b/irctest/server_tests/test_connection_registration.py @@ -31,6 +31,16 @@ class PasswordedConnectionRegistrationTestCase(cases.BaseServerTestCase): self.assertNotEqual(m.command, '001', msg='Got 001 after NICK+USER but missing PASS') + @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812') + def testWrongPassword(self): + self.addClient() + self.sendLine(1, 'PASS {}'.format(self.password + "garbage")) + self.sendLine(1, 'NICK foo') + self.sendLine(1, 'USER username * * :Realname') + m = self.getRegistrationMessage(1) + self.assertNotEqual(m.command, '001', + msg='Got 001 after NICK+USER but incorrect PASS') + @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812') def testPassAfterNickuser(self): """“The password can and must be set before any attempt to register From ecfb0e327fd179cff73f5fafe0d1078c3263b6ce Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 17:35:26 -0500 Subject: [PATCH 2/9] channel quit test case --- .../server_tests/test_channel_operations.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/irctest/server_tests/test_channel_operations.py b/irctest/server_tests/test_channel_operations.py index 74c5566..57191c2 100644 --- a/irctest/server_tests/test_channel_operations.py +++ b/irctest/server_tests/test_channel_operations.py @@ -7,7 +7,6 @@ from irctest import cases from irctest import client_mock from irctest import runner from irctest.irc_utils import ambiguities -from irctest.irc_utils.message_parser import Message class JoinTestCase(cases.BaseServerTestCase): @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812', @@ -519,3 +518,26 @@ class InviteTestCase(cases.BaseServerTestCase): # we were invited, so join should succeed now self.joinChannel(2, '#chan') + + +class ChannelQuitTestCase(cases.BaseServerTestCase): + + @cases.SpecificationSelector.requiredBySpecification('RFC2812') + def testQuit(self): + """“Once a user has joined a channel, he receives information about + all commands his server receives affecting the channel. This + includes [...] QUIT” + + """ + self.connectClient('bar') + self.joinChannel(1, '#chan') + self.connectClient('qux') + self.sendLine(2, 'JOIN #chan') + + self.getMessages(1) + self.sendLine(2, 'QUIT :qux out') + self.getMessages(2) + m = self.getMessage(1) + self.assertEqual(m.command, 'QUIT') + self.assertTrue(m.prefix.startswith('qux')) # nickmask of quitter + self.assertIn('qux out', m.params[0]) From 9497bc72a9867f5a6dbc9a7c75d802ffb90745e9 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 19:01:47 -0500 Subject: [PATCH 3/9] basic whois test --- irctest/server_tests/test_user_commands.py | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/irctest/server_tests/test_user_commands.py b/irctest/server_tests/test_user_commands.py index c9bc668..063ef87 100644 --- a/irctest/server_tests/test_user_commands.py +++ b/irctest/server_tests/test_user_commands.py @@ -5,8 +5,36 @@ User commands as specified in Section 3.6 of RFC 2812: from irctest import cases +RPL_WHOISUSER = '311' RPL_WHOISCHANNELS = '319' +class WhoisTestCase(cases.BaseServerTestCase): + + @cases.SpecificationSelector.requiredBySpecification('RFC2812') + def testWhoisUser(self): + """Test basic WHOIS behavior""" + nick = 'myCoolNickname' + username = 'myCoolUsername' + realname = 'My Real Name' + self.addClient() + self.sendLine(1, f'NICK {nick}') + self.sendLine(1, f'USER {username} 0 * :{realname}') + self.skipToWelcome(1) + + self.connectClient('otherNickname') + self.getMessages(2) + self.sendLine(2, 'WHOIS mycoolnickname') + messages = self.getMessages(2) + whois_user = messages[0] + self.assertEqual(whois_user.command, RPL_WHOISUSER) + # " * :" + self.assertEqual(whois_user.params[1], nick) + self.assertEqual(whois_user.params[2], '~' + username) + # dumb regression test for oragono/oragono#355: + self.assertNotIn(whois_user.params[3], [nick, username, '~' + username, realname]) + self.assertEqual(whois_user.params[5], realname) + + class InvisibleTestCase(cases.BaseServerTestCase): @cases.SpecificationSelector.requiredBySpecification('Oragono') From 83f5c924f0f109b3081feffdd5a549a4fd68f122 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 19:14:03 -0500 Subject: [PATCH 4/9] tests for RPL_NOTOPIC --- .../server_tests/test_channel_operations.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/irctest/server_tests/test_channel_operations.py b/irctest/server_tests/test_channel_operations.py index 57191c2..78d10ca 100644 --- a/irctest/server_tests/test_channel_operations.py +++ b/irctest/server_tests/test_channel_operations.py @@ -8,6 +8,8 @@ from irctest import client_mock from irctest import runner from irctest.irc_utils import ambiguities +RPL_NOTOPIC = '331' + class JoinTestCase(cases.BaseServerTestCase): @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812', strict=True) @@ -227,6 +229,42 @@ class JoinTestCase(cases.BaseServerTestCase): # either 403 ERR_NOSUCHCHANNEL or 443 ERR_NOTONCHANNEL self.assertIn(m.command, ('403', '443')) + @cases.SpecificationSelector.requiredBySpecification('RFC2812') + def testUnsetTopicResponses(self): + """Test various cases related to RPL_NOTOPIC with set and unset topics.""" + self.connectClient('bar') + self.sendLine(1, 'JOIN #test') + messages = self.getMessages(1) + # shouldn't send RPL_NOTOPIC for a new channel + self.assertNotIn(RPL_NOTOPIC, [m.command for m in messages]) + + self.connectClient('baz') + self.sendLine(2, 'JOIN #test') + messages = self.getMessages(2) + # topic is still unset, shouldn't send RPL_NOTOPIC on initial join + self.assertNotIn(RPL_NOTOPIC, [m.command for m in messages]) + + self.sendLine(2, 'TOPIC #test') + messages = self.getMessages(2) + # explicit TOPIC should receive RPL_NOTOPIC + self.assertIn(RPL_NOTOPIC, [m.command for m in messages]) + + self.sendLine(1, 'TOPIC #test :new topic') + self.getMessages(1) + # client 2 should get the new TOPIC line + messages = [message for message in self.getMessages(2) if message.command == 'TOPIC'] + self.assertEqual(len(messages), 1) + self.assertEqual(messages[0].params, ['#test', 'new topic']) + + # unset the topic: + self.sendLine(1, 'TOPIC #test :') + self.getMessages(1) + self.connectClient('qux') + self.sendLine(3, 'join #test') + messages = self.getMessages(3) + # topic is once again unset, shouldn't send RPL_NOTOPIC on initial join + self.assertNotIn(RPL_NOTOPIC, [m.command for m in messages]) + @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812') def testListEmpty(self): """ From a3ad8a103857f41aa736228c083a08953ed3261c Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 19:20:46 -0500 Subject: [PATCH 5/9] fix lots of pyflakes3 failures --- irctest/controllers/oragono.py | 1 - irctest/server_tests/test_account_tag.py | 2 -- irctest/server_tests/test_cap.py | 1 - irctest/server_tests/test_connection_registration.py | 3 --- irctest/server_tests/test_echo_message.py | 2 +- irctest/server_tests/test_extended_join.py | 3 +-- irctest/server_tests/test_messages.py | 4 ---- irctest/server_tests/test_metadata.py | 1 - irctest/server_tests/test_monitor.py | 2 +- irctest/server_tests/test_multi_prefix.py | 4 ---- irctest/server_tests/test_sasl.py | 1 - 11 files changed, 3 insertions(+), 21 deletions(-) diff --git a/irctest/controllers/oragono.py b/irctest/controllers/oragono.py index 4b26f03..6b32a3b 100644 --- a/irctest/controllers/oragono.py +++ b/irctest/controllers/oragono.py @@ -1,5 +1,4 @@ import os -import time import subprocess from irctest.basecontrollers import NotImplementedByController diff --git a/irctest/server_tests/test_account_tag.py b/irctest/server_tests/test_account_tag.py index 40e86c4..844672b 100644 --- a/irctest/server_tests/test_account_tag.py +++ b/irctest/server_tests/test_account_tag.py @@ -3,8 +3,6 @@ """ from irctest import cases -from irctest.client_mock import NoMessageException -from irctest.basecontrollers import NotImplementedByController class AccountTagTestCase(cases.BaseServerTestCase, cases.OptionalityHelper): def connectRegisteredClient(self, nick): diff --git a/irctest/server_tests/test_cap.py b/irctest/server_tests/test_cap.py index 0de4e1a..fc5c858 100644 --- a/irctest/server_tests/test_cap.py +++ b/irctest/server_tests/test_cap.py @@ -1,5 +1,4 @@ from irctest import cases -from irctest.irc_utils.message_parser import Message class CapTestCase(cases.BaseServerTestCase): @cases.SpecificationSelector.requiredBySpecification('IRCv3.1') diff --git a/irctest/server_tests/test_connection_registration.py b/irctest/server_tests/test_connection_registration.py index e26b08c..501014d 100644 --- a/irctest/server_tests/test_connection_registration.py +++ b/irctest/server_tests/test_connection_registration.py @@ -4,9 +4,6 @@ Tests section 4.1 of RFC 1459. """ from irctest import cases -from irctest import authentication -from irctest.irc_utils.message_parser import Message -from irctest.basecontrollers import NotImplementedByController from irctest.client_mock import ConnectionClosed class PasswordedConnectionRegistrationTestCase(cases.BaseServerTestCase): diff --git a/irctest/server_tests/test_echo_message.py b/irctest/server_tests/test_echo_message.py index ad2040d..a583a90 100644 --- a/irctest/server_tests/test_echo_message.py +++ b/irctest/server_tests/test_echo_message.py @@ -22,7 +22,7 @@ class EchoMessageTestCase(cases.BaseServerTestCase): # TODO: check also without this self.sendLine(1, 'CAP REQ :echo-message{}'.format( ' server-time' if server_time else '')) - m = self.getRegistrationMessage(1) + self.getRegistrationMessage(1) # TODO: Remove this one the trailing space issue is fixed in Charybdis # and Mammon: #self.assertMessageEqual(m, command='CAP', diff --git a/irctest/server_tests/test_extended_join.py b/irctest/server_tests/test_extended_join.py index 6e3e8fa..8f13d0d 100644 --- a/irctest/server_tests/test_extended_join.py +++ b/irctest/server_tests/test_extended_join.py @@ -3,7 +3,6 @@ """ from irctest import cases -from irctest.irc_utils.message_parser import Message class MetadataTestCase(cases.BaseServerTestCase, cases.OptionalityHelper): def connectRegisteredClient(self, nick): @@ -41,7 +40,7 @@ class MetadataTestCase(cases.BaseServerTestCase, cases.OptionalityHelper): @cases.SpecificationSelector.requiredBySpecification('IRCv3.1') @cases.OptionalityHelper.skipUnlessHasMechanism('PLAIN') - def testNotLoggedIn(self): + def testLoggedIn(self): self.connectClient('foo', capabilities=['extended-join'], skip_if_cap_nak=True) self.joinChannel(1, '#chan') diff --git a/irctest/server_tests/test_messages.py b/irctest/server_tests/test_messages.py index 90bec42..6ec232d 100644 --- a/irctest/server_tests/test_messages.py +++ b/irctest/server_tests/test_messages.py @@ -4,10 +4,6 @@ Section 3.2 of RFC 2812 """ from irctest import cases -from irctest import client_mock -from irctest import runner -from irctest.irc_utils import ambiguities -from irctest.irc_utils.message_parser import Message class PrivmsgTestCase(cases.BaseServerTestCase): @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812') diff --git a/irctest/server_tests/test_metadata.py b/irctest/server_tests/test_metadata.py index 76a3026..776119b 100644 --- a/irctest/server_tests/test_metadata.py +++ b/irctest/server_tests/test_metadata.py @@ -4,7 +4,6 @@ Tests METADATA features. """ from irctest import cases -from irctest.irc_utils.message_parser import Message class MetadataTestCase(cases.BaseServerTestCase): valid_metadata_keys = {'valid_key1', 'valid_key2'} diff --git a/irctest/server_tests/test_monitor.py b/irctest/server_tests/test_monitor.py index 819c92e..eb9315a 100644 --- a/irctest/server_tests/test_monitor.py +++ b/irctest/server_tests/test_monitor.py @@ -88,7 +88,7 @@ class EchoMessageTestCase(cases.BaseServerTestCase): self.assertMonoffline(1, 'bar') @cases.SpecificationSelector.requiredBySpecification('IRCv3.2') - def testMonitorOneConnection(self): + def testMonitorOneConnectionWithQuit(self): self.connectClient('foo') self.check_server_support() self.connectClient('bar') diff --git a/irctest/server_tests/test_multi_prefix.py b/irctest/server_tests/test_multi_prefix.py index 8b691f4..ef9a1f2 100644 --- a/irctest/server_tests/test_multi_prefix.py +++ b/irctest/server_tests/test_multi_prefix.py @@ -4,10 +4,6 @@ Tests multi-prefix. """ from irctest import cases -from irctest import client_mock -from irctest import runner -from irctest.irc_utils import ambiguities -from irctest.irc_utils.message_parser import Message class MultiPrefixTestCase(cases.BaseServerTestCase): @cases.SpecificationSelector.requiredBySpecification('IRCv3.1') diff --git a/irctest/server_tests/test_sasl.py b/irctest/server_tests/test_sasl.py index c5047e8..a1ac3dc 100644 --- a/irctest/server_tests/test_sasl.py +++ b/irctest/server_tests/test_sasl.py @@ -1,7 +1,6 @@ import base64 from irctest import cases -from irctest.irc_utils.message_parser import Message class RegistrationTestCase(cases.BaseServerTestCase): def testRegistration(self): From dc3ac21f752f80a145b95a6be107f0adbc8ebd27 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 19:32:57 -0500 Subject: [PATCH 6/9] test cap removal with, e.g., CAP REQ :-server-time --- irctest/server_tests/test_cap.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/irctest/server_tests/test_cap.py b/irctest/server_tests/test_cap.py index fc5c858..2bb7b98 100644 --- a/irctest/server_tests/test_cap.py +++ b/irctest/server_tests/test_cap.py @@ -95,3 +95,33 @@ class CapTestCase(cases.BaseServerTestCase): subcommand='ACK', subparams=['multi-prefix'], fail_msg='Expected “CAP ACK :multi-prefix” after ' 'sending “CAP REQ :multi-prefix”, but got {msg}.') + + @cases.SpecificationSelector.requiredBySpecification('Oragono') + def testCapRemovalByClient(self): + """Test CAP LIST and removal of caps via CAP REQ :-tagname.""" + self.addClient(1) + self.sendLine(1, 'CAP LS 302') + self.assertIn('multi-prefix', self.getCapLs(1)) + self.sendLine(1, 'CAP REQ :echo-message server-time') + self.sendLine(1, 'nick bar') + self.sendLine(1, 'user user 0 * realname') + self.sendLine(1, 'CAP END') + self.skipToWelcome(1) + self.getMessages(1) + + self.sendLine(1, 'CAP LIST') + messages = self.getMessages(1) + cap_list = [m for m in messages if m.command == 'CAP'][0] + self.assertEqual(set(cap_list.params[2].split()), {'echo-message', 'server-time'}) + self.assertIn('time', cap_list.tags) + + # remove the server-time cap + self.sendLine(1, 'CAP REQ :-server-time') + self.getMessages(1) + + # server-time should be disabled + self.sendLine(1, 'CAP LIST') + messages = self.getMessages(1) + cap_list = [m for m in messages if m.command == 'CAP'][0] + self.assertEqual(set(cap_list.params[2].split()), {'echo-message'}) + self.assertNotIn('time', cap_list.tags) From 7f5a489cae8409da171b6afd26a406d632456bc5 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 19:52:10 -0500 Subject: [PATCH 7/9] remove voodoo sleep --- irctest/server_tests/test_channel_operations.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/irctest/server_tests/test_channel_operations.py b/irctest/server_tests/test_channel_operations.py index 78d10ca..f95ab4c 100644 --- a/irctest/server_tests/test_channel_operations.py +++ b/irctest/server_tests/test_channel_operations.py @@ -333,8 +333,6 @@ class JoinTestCase(cases.BaseServerTestCase): # TODO: check foo is an operator - import time - time.sleep(0.1) self.getMessages(1) self.getMessages(2) self.getMessages(3) From b9872018adbb5bfe564f23e0176f692ada53c36d Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 13 Feb 2019 20:06:41 -0500 Subject: [PATCH 8/9] kick privileges test --- .../server_tests/test_channel_operations.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/irctest/server_tests/test_channel_operations.py b/irctest/server_tests/test_channel_operations.py index f95ab4c..f28b495 100644 --- a/irctest/server_tests/test_channel_operations.py +++ b/irctest/server_tests/test_channel_operations.py @@ -9,6 +9,11 @@ from irctest import runner from irctest.irc_utils import ambiguities RPL_NOTOPIC = '331' +RPL_NAMREPLY = '353' + +ERR_NOSUCHCHANNEL = '403' +ERR_NOTONCHANNEL = '442' +ERR_CHANOPRIVSNEEDED = '482' class JoinTestCase(cases.BaseServerTestCase): @cases.SpecificationSelector.requiredBySpecification('RFC1459', 'RFC2812', @@ -353,6 +358,52 @@ class JoinTestCase(cases.BaseServerTestCase): self.assertMessageEqual(m, command='KICK', params=['#chan', 'bar', 'bye']) + @cases.SpecificationSelector.requiredBySpecification('RFC2812') + def testKickPrivileges(self): + """Test who has the ability to kick / what error codes are sent for invalid kicks.""" + self.connectClient('foo') + self.sendLine(1, 'JOIN #chan') + self.getMessages(1) + + self.connectClient('bar') + self.sendLine(2, 'JOIN #chan') + + messages = self.getMessages(2) + names = set() + for message in messages: + if message.command == RPL_NAMREPLY: + names.update(set(message.params[-1].split())) + # assert foo is opped + self.assertIn('@foo', names, f'unexpected names: {names}') + + self.connectClient('baz') + + self.sendLine(3, 'KICK #chan bar') + replies = set(m.command for m in self.getMessages(3)) + self.assertTrue( + ERR_NOTONCHANNEL in replies or ERR_CHANOPRIVSNEEDED in replies or ERR_NOSUCHCHANNEL in replies, + f'did not receive acceptable error code for kick from outside channel: {replies}') + + self.joinChannel(3, '#chan') + self.getMessages(3) + self.sendLine(3, 'KICK #chan bar') + replies = set(m.command for m in self.getMessages(3)) + # now we're a channel member so we should receive ERR_CHANOPRIVSNEEDED + self.assertIn(ERR_CHANOPRIVSNEEDED, replies) + + self.sendLine(1, 'MODE #chan +o baz') + self.getMessages(1) + # should be able to kick an unprivileged user: + self.sendLine(3, 'KICK #chan bar') + # should be able to kick an operator: + self.sendLine(3, 'KICK #chan foo') + baz_replies = set(m.command for m in self.getMessages(3)) + self.assertNotIn(ERR_CHANOPRIVSNEEDED, baz_replies) + kick_targets = [m.params[1] for m in self.getMessages(1) if m.command == 'KICK'] + # foo should see bar and foo being kicked + self.assertTrue(any(target.startswith('foo') for target in kick_targets), f'unexpected kick targets: {kick_targets}') + self.assertTrue(any(target.startswith('bar') for target in kick_targets), f'unexpected kick targets: {kick_targets}') + @cases.SpecificationSelector.requiredBySpecification('RFC2812') def testKickNonexistentChannel(self): """“Kick command [...] Numeric replies: [...] ERR_NOSUCHCHANNEL.""" From 1463d4b2c4114e0285aaabb6a58605789709f87f Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 14 Feb 2019 20:26:17 -0500 Subject: [PATCH 9/9] shave a few seconds off the test suite --- irctest/controllers/oragono.py | 1 + 1 file changed, 1 insertion(+) diff --git a/irctest/controllers/oragono.py b/irctest/controllers/oragono.py index 6b32a3b..54e435b 100644 --- a/irctest/controllers/oragono.py +++ b/irctest/controllers/oragono.py @@ -51,6 +51,7 @@ accounts: enabled-callbacks: - none # no verification needed, will instantly register successfully allow-multiple-per-connection: true + bcrypt-cost: 4 authentication-enabled: true