1
0
mirror of https://github.com/progval/irctest.git synced 2025-04-07 07:49:52 +00:00

1 Commits

Author SHA1 Message Date
77c3c10847 [WIP] Add test for CHGHOST using services
Only Insp + Anope for now

TODO: cleanup code
2022-04-03 20:42:13 +02:00
5 changed files with 121 additions and 76 deletions

@ -210,9 +210,10 @@ class BaseServerController(_BaseController):
case: irctest.cases.BaseServerTestCase, # type: ignore
username: str,
password: Optional[str] = None,
**kwargs: Any,
) -> None:
if self.services_controller is not None:
self.services_controller.registerUser(case, username, password)
self.services_controller.registerUser(case, username, password, **kwargs)
else:
raise NotImplementedByController("account registration")
@ -293,7 +294,7 @@ class BaseServicesController(_BaseController):
timeout = time.time() + 5
while True:
c.sendLine(f"PRIVMSG {self.server_controller.nickserv} :HELP")
msgs = self.getNickServResponse(c)
msgs = self.getServiceResponse(c)
for msg in msgs:
if msg.command == "401":
# NickServ not available yet
@ -319,7 +320,7 @@ class BaseServicesController(_BaseController):
c.disconnect()
self.services_up = True
def getNickServResponse(self, client: Any) -> List[Message]:
def getServiceResponse(self, client: Any) -> List[Message]:
"""Wrapper aroung getMessages() that waits longer, because NickServ
is queried asynchronously."""
msgs: List[Message] = []
@ -333,11 +334,14 @@ class BaseServicesController(_BaseController):
case: irctest.cases.BaseServerTestCase, # type: ignore
username: str,
password: Optional[str] = None,
**kwargs: Any,
) -> None:
if not case.run_services:
raise ValueError(
"Attempted to register a nick, but `run_services` it not True."
)
if kwargs:
raise NotImplementedByController(", ".join(kwargs))
assert password
client = case.addClient(show_io=True)
case.sendLine(client, "NICK " + username)
@ -350,7 +354,7 @@ class BaseServicesController(_BaseController):
f"PRIVMSG {self.server_controller.nickserv} "
f":REGISTER {password} foo@example.org",
)
msgs = self.getNickServResponse(case.clients[client])
msgs = self.getServiceResponse(case.clients[client])
if self.server_controller.software_name == "inspircd":
assert "900" in {msg.command for msg in msgs}, msgs
assert "NOTICE" in {msg.command for msg in msgs}, msgs

@ -1,9 +1,12 @@
import os
import shutil
import subprocess
from typing import Type
from typing import Any, Optional, Type
from irctest import cases, runner
from irctest.basecontrollers import BaseServicesController, DirectoryBasedController
from irctest.client_mock import ClientMock
from irctest.irc_utils.sasl import sasl_plain_blob
TEMPLATE_CONFIG = """
serverinfo {{
@ -30,12 +33,17 @@ networkinfo {{
userlen = 10
hostlen = 64
chanlen = 32
vhost_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-"
}}
mail {{
usemail = no
}}
/************************
* NickServ:
*/
service {{
nick = "NickServ"
user = "services"
@ -57,6 +65,29 @@ module {{
}}
command {{ service = "NickServ"; name = "REGISTER"; command = "nickserv/register"; }}
/************************
* HostServ:
*/
service {{
nick = "HostServ"
user = "services"
host = "services.host"
gecos = "vHost Service"
}}
module {{
name = "hostserv"
client = "HostServ"
}}
module {{ name = "hs_set" }}
command {{ service = "HostServ"; name = "SET"; command = "hostserv/set"; }}
/************************
* Misc:
*/
options {{
casemap = "ascii"
readtimeout = 5s
@ -66,7 +97,6 @@ options {{
module {{ name = "m_sasl" }}
module {{ name = "enc_sha256" }}
module {{ name = "ns_cert" }}
"""
@ -121,6 +151,39 @@ class AnopeController(BaseServicesController, DirectoryBasedController):
# stderr=subprocess.DEVNULL,
)
def registerUser(
self,
case: cases.BaseServerTestCase, # type: ignore
username: str,
password: Optional[str] = None,
vhost: Optional[str] = None,
**kwargs: Any,
) -> None:
super().registerUser(case, username, password)
if vhost:
if not password:
raise runner.NotImplementedByController(
"vHost for users with no password"
)
c = ClientMock(name="setVhost", show_io=True)
c.connect(self.server_controller.hostname, self.server_controller.port)
c.sendLine("CAP REQ :sasl")
c.sendLine("NICK " + username)
c.sendLine("USER r e g :user")
while c.getMessage(synchronize=False).command != "CAP":
pass
c.sendLine("AUTHENTICATE PLAIN")
while c.getMessage(synchronize=False).command != "AUTHENTICATE":
pass
c.sendLine(sasl_plain_blob(username, password))
c.sendLine("CAP END")
while c.getMessage(synchronize=False).command != "001":
pass
c.getMessages()
c.sendLine(f"PRIVMSG HostServ :SET {username} {vhost}")
self.getServiceResponse(c)
def get_irctest_controller_class() -> Type[AnopeController]:
return AnopeController

@ -61,10 +61,12 @@ TEMPLATE_CONFIG = """
# Protocol:
<module name="botmode">
<module name="cap">
<module name="chghost">
<module name="ircv3">
<module name="ircv3_accounttag">
<module name="ircv3_batch">
<module name="ircv3_capnotify">
<module name="ircv3_chghost">
<module name="ircv3_ctctags">
<module name="ircv3_echomessage">
<module name="ircv3_invitenotify">
@ -74,7 +76,6 @@ TEMPLATE_CONFIG = """
<module name="monitor">
<module name="m_muteban"> # for testing mute extbans
<module name="namesx"> # For multi-prefix
<module name="noctcp">
<module name="sasl">
# HELP/HELPOP

@ -0,0 +1,46 @@
"""
<http://ircv3.net/specs/extensions/chghost.html>
"""
from irctest import cases
from irctest.irc_utils.sasl import sasl_plain_blob
from irctest.patma import ANYSTR, StrRe
@cases.mark_services
class ChghostServicesTestCase(cases.BaseServerTestCase, cases.OptionalityHelper):
def testChghostFromServices(self):
self.connectClient("observer", capabilities=["chghost"], skip_if_cap_nak=True)
self.connectClient("oldclient")
self.controller.registerUser(
self, "vhostuser", "sesame", vhost="vhost.example.com"
)
self.connectClient("vhost-user", capabilities=["sasl"], skip_if_cap_nak=True)
for i in (1, 2, 3):
self.sendLine(i, "JOIN #chan")
self.getMessages(i)
for i in (1, 2, 3):
self.getMessages(i)
self.sendLine(3, "AUTHENTICATE PLAIN")
self.assertMessageMatch(
self.getRegistrationMessage(3),
command="AUTHENTICATE",
params=["+"],
)
self.sendLine(3, sasl_plain_blob("vhostuser", "sesame"))
self.assertMessageMatch(
self.getRegistrationMessage(3),
command="900",
)
self.assertMessageMatch(
self.getMessage(1),
prefix=StrRe("vhost-user!.*@(?!vhost-user.example)"),
command="CHGHOST",
params=[ANYSTR, "vhost.example.com"],
)
self.assertEqual(self.getMessages(2), []) # cycle?

@ -1,69 +0,0 @@
from irctest import cases, runner
from irctest.numerics import ERR_CANNOTSENDTOCHAN
from irctest.patma import ANYSTR
class NoctcpModeTestCase(cases.BaseServerTestCase):
@cases.mark_specifications("Modern")
def testNoctcpMode(self):
"""
"This mode is used in almost all IRC software today. The standard mode letter
used for it is `"+C"`.
When this mode is set, should not send [CTCP](/ctcp.html) messages, except
CTCP Action (also known as `/me`) to the channel.
When blocking a message because of this mode, servers SHOULD use
ERR_CANNOTSENDTOCHAN"
-- TODO add link
"""
self.connectClient("chanop")
if "C" not in self.server_support.get("CHANMODES", ""):
raise runner.NotImplementedByController("+C (noctcp) channel mode")
# Both users join:
self.sendLine(1, "JOIN #chan")
self.getMessages(1) # synchronize
self.connectClient("user")
self.sendLine(2, "JOIN #chan")
self.getMessages(2)
self.getMessages(1)
# Send ACTION and PING, both should go through:
self.sendLine(2, "PRIVMSG #chan :\x01ACTION is testing\x01")
self.sendLine(2, "PRIVMSG #chan :\x01PING 12345\x01")
self.assertEqual(self.getMessages(2), [])
self.assertEqual(
[(m.command, m.params[1]) for m in self.getMessages(1)],
[
("PRIVMSG", "\x01ACTION is testing\x01"),
("PRIVMSG", "\x01PING 12345\x01"),
],
)
# Set mode +C:
self.sendLine(1, "MODE #chan +C")
self.getMessages(1)
self.getMessages(2)
# Send ACTION and PING, only ACTION should go through:
self.sendLine(2, "PRIVMSG #chan :\x01ACTION is testing\x01")
self.assertEqual(self.getMessages(2), [])
self.sendLine(2, "PRIVMSG #chan :\x01PING 12345\x01")
self.assertMessageMatch(
self.getMessage(2),
command=ERR_CANNOTSENDTOCHAN,
params=["user", "#chan", ANYSTR],
)
self.assertEqual(
[(m.command, m.params[1]) for m in self.getMessages(1)],
[
("PRIVMSG", "\x01ACTION is testing\x01"),
],
)