diff --git a/src/irclib.py b/src/irclib.py index eed5931d8..a31d265e3 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -966,13 +966,6 @@ class Irc(IrcCommandDispatcher, log.Firewalled): self.ident = get_value('ident') self.alternateNicks = conf.supybot.nick.alternates()[:] self.password = conf.supybot.networks.get(self.network).password() - self.sasl_username = \ - conf.supybot.networks.get(self.network).sasl.username() - self.sasl_password = \ - conf.supybot.networks.get(self.network).sasl.password() - self.sasl_ecdsa_key = \ - conf.supybot.networks.get(self.network).sasl.ecdsa_key() - self.authenticate_decoder = None self.prefix = '%s!%s@%s' % (self.nick, self.ident, 'unset.domain') # The rest. self.lastTake = 0 @@ -981,8 +974,14 @@ class Irc(IrcCommandDispatcher, log.Firewalled): self.startedAt = time.time() self.lastping = time.time() self.outstandingPing = False + self.resetSasl() + def resetSasl(self): network_config = conf.supybot.networks.get(self.network) + self.sasl_username = network_config.sasl.username() + self.sasl_password = network_config.sasl.password() + self.sasl_ecdsa_key = network_config.sasl.ecdsa_key() + self.authenticate_decoder = None self.sasl_next_mechanisms = [] self.sasl_current_mechanism = None @@ -1049,6 +1048,11 @@ class Irc(IrcCommandDispatcher, log.Firewalled): self.sasl_current_mechanism = None self.sendMsg(ircmsgs.IrcMsg(command='CAP', args=('END',))) + def filterSaslMechanisms(self, available): + self.sasl_next_mechanisms = [ + x for x in self.sasl_next_mechanisms + if x in available] + def doAuthenticate(self, msg): if not self.authenticate_decoder: self.authenticate_decoder = ircutils.AuthenticateDecoder() @@ -1107,7 +1111,7 @@ class Irc(IrcCommandDispatcher, log.Firewalled): def do908(self, msg): log.info('%s: Supported SASL mechanisms: %s', self.network, msg.args[1]) - # TODO: filter self.sasl_next_mechanisms + self.filterSaslMechanisms(set(msg.args[1].split(','))) def doCap(self, msg): subcommand = msg.args[1] @@ -1153,7 +1157,6 @@ class Irc(IrcCommandDispatcher, log.Firewalled): else: self.state.capabilities_ls[item] = None def doCapLs(self, msg): - # TODO: filter self.sasl_next_mechanisms if len(msg.args) == 4: # Multi-line LS if msg.args[2] != '*': @@ -1164,6 +1167,10 @@ class Irc(IrcCommandDispatcher, log.Firewalled): self._addCapabilities(msg.args[2]) common_supported_capabilities = set(self.state.capabilities_ls) & \ self.REQUEST_CAPABILITIES + if 'sasl' in self.state.capabilities_ls: + s = self.state.capabilities_ls['sasl'] + if s is not None: + self.filterSaslMechanisms(set(s.split(','))) # NOTE: Capabilities are requested in alphabetic order, because # sets are unordered, and their "order" is nondeterministic. # This is needed for the tests. diff --git a/test/test_irclib.py b/test/test_irclib.py index c1176af22..963600919 100644 --- a/test/test_irclib.py +++ b/test/test_irclib.py @@ -531,7 +531,7 @@ class SaslTestCase(SupyTestCase): def setUp(self): pass - def startCapNegociation(self): + def startCapNegociation(self, sasl_attributes=None): m = self.irc.takeMsg() self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m) self.failUnless(m.args == ('LS', '302'), 'Expected CAP LS 302, got %r.' % m) @@ -541,9 +541,13 @@ class SaslTestCase(SupyTestCase): m = self.irc.takeMsg() self.failUnless(m.command == 'USER', 'Expected USER, got %r.' % m) - # TODO - self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', - args=('*', 'LS', 'sasl'))) + + if sasl_attributes: + self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', + args=('*', 'LS', 'sasl=%s' % sasl_attributes))) + else: + self.irc.feedMsg(ircmsgs.IrcMsg(command='CAP', + args=('*', 'LS', 'sasl'))) m = self.irc.takeMsg() self.failUnless(m.command == 'CAP', 'Expected CAP, got %r.' % m) @@ -624,6 +628,37 @@ class SaslTestCase(SupyTestCase): self.endCapNegociation() + def testFilter(self): + try: + conf.supybot.networks.test.sasl.username.setValue('jilles') + conf.supybot.networks.test.sasl.password.setValue('sesame') + conf.supybot.networks.test.certfile.setValue('foo') + self.irc = irclib.Irc('test') + finally: + conf.supybot.networks.test.sasl.username.setValue('') + conf.supybot.networks.test.sasl.password.setValue('') + conf.supybot.networks.test.certfile.setValue('') + self.assertEqual(self.irc.sasl_current_mechanism, None) + self.assertEqual(self.irc.sasl_next_mechanisms, + ['external', 'plain']) + + self.startCapNegociation(sasl_attributes='foo,plain,bar') + + m = self.irc.takeMsg() + self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE', + args=('PLAIN',))) + + self.irc.feedMsg(ircmsgs.IrcMsg(command='AUTHENTICATE', args=('+',))) + + m = self.irc.takeMsg() + self.assertEqual(m, ircmsgs.IrcMsg(command='AUTHENTICATE', + args=('amlsbGVzAGppbGxlcwBzZXNhbWU=',))) + + self.irc.feedMsg(ircmsgs.IrcMsg(command='900', args=('jilles',))) + self.irc.feedMsg(ircmsgs.IrcMsg(command='903', args=('jilles',))) + + self.endCapNegociation() + class IrcCallbackTestCase(SupyTestCase):