Services: Add support for network-specific password.

This commit is contained in:
Valentin Lorentz 2019-08-25 14:00:11 +02:00
parent c07086d7dc
commit b65d78518c
5 changed files with 112 additions and 54 deletions

View File

@ -205,14 +205,24 @@ class Config(callbacks.Plugin):
network=network.network, channel=channel, check=False) network=network.network, channel=channel, check=False)
value = str(group) or ' ' value = str(group) or ' '
if addGlobal and not irc.nested: if addGlobal and not irc.nested:
value = _( if global_group._channelValue and channel:
'Global: %(global_value)s; ' # TODO: also show the network value when relevant
'%(channel_name)s @ %(network_name)s: %(channel_value)s') % { value = _(
'global_value': global_value, 'Global: %(global_value)s; '
'channel_name': msg.channel, '%(channel_name)s @ %(network_name)s: %(channel_value)s') % {
'network_name': irc.network, 'global_value': global_value,
'channel_value': value, 'channel_name': msg.channel,
} 'network_name': irc.network,
'channel_value': value,
}
elif global_group._networkValue and network:
value = _(
'Global: %(global_value)s; '
'%(network_name)s: %(network_value)s') % {
'global_value': global_value,
'network_name': irc.network,
'network_value': value,
}
if hasattr(global_group, 'value'): if hasattr(global_group, 'value'):
if not global_group._private: if not global_group._private:
return (value, None) return (value, None)
@ -330,7 +340,7 @@ class Config(callbacks.Plugin):
(value, private) = self._getValue( (value, private) = self._getValue(
irc, msg, group, network=irc, irc, msg, group, network=irc,
channel=msg.channel, channel=msg.channel,
addGlobal=group._channelValue) addGlobal=group._channelValue or group._networkValue)
irc.reply(value, private=private) irc.reply(value, private=private)
config = wrap(config, ['settableConfigVar', additional('text')]) config = wrap(config, ['settableConfigVar', additional('text')])

View File

@ -38,8 +38,8 @@ def registerNick(nick, password=''):
p = conf.supybot.plugins.Services.Nickserv.get('password') p = conf.supybot.plugins.Services.Nickserv.get('password')
h = _('Determines what password the bot will use with NickServ when ' \ h = _('Determines what password the bot will use with NickServ when ' \
'identifying as %s.') % nick 'identifying as %s.') % nick
v = conf.registerGlobalValue(p, nick, v = conf.registerNetworkValue(p, nick,
registry.String(password, h, private=True)) registry.String(password, h, private=True))
if password: if password:
v.setValue(password) v.setValue(password)
@ -65,7 +65,7 @@ class ValidNickSet(conf.ValidNicks):
List = ircutils.IrcSet List = ircutils.IrcSet
Services = conf.registerPlugin('Services') Services = conf.registerPlugin('Services')
conf.registerGlobalValue(Services, 'nicks', conf.registerNetworkValue(Services, 'nicks',
ValidNickSet([], _("""Determines what nicks the bot will use with ValidNickSet([], _("""Determines what nicks the bot will use with
services."""))) services.""")))
@ -76,19 +76,19 @@ conf.registerGlobalValue(Services, 'disabledNetworks',
Networks(_('QuakeNet').split(), _("""Determines what networks this plugin Networks(_('QuakeNet').split(), _("""Determines what networks this plugin
will be disabled on."""))) will be disabled on.""")))
conf.registerGlobalValue(Services, 'noJoinsUntilIdentified', conf.registerNetworkValue(Services, 'noJoinsUntilIdentified',
registry.Boolean(False, _("""Determines whether the bot will not join any registry.Boolean(False, _("""Determines whether the bot will not join any
channels until it is identified. This may be useful, for instances, if channels until it is identified. This may be useful, for instances, if
you have a vhost that isn't set until you're identified, or if you're you have a vhost that isn't set until you're identified, or if you're
joining +r channels that won't allow you to join unless you identify."""))) joining +r channels that won't allow you to join unless you identify.""")))
conf.registerGlobalValue(Services, 'ghostDelay', conf.registerNetworkValue(Services, 'ghostDelay',
registry.NonNegativeInteger(60, _("""Determines how many seconds the bot will registry.NonNegativeInteger(60, _("""Determines how many seconds the bot will
wait between successive GHOST attempts. Set this to 0 to disable GHOST."""))) wait between successive GHOST attempts. Set this to 0 to disable GHOST.""")))
conf.registerGlobalValue(Services, 'NickServ', conf.registerNetworkValue(Services, 'NickServ',
ValidNickOrEmptyString('NickServ', _("""Determines what nick the 'NickServ' service ValidNickOrEmptyString('NickServ', _("""Determines what nick the 'NickServ' service
has."""))) has.""")))
conf.registerGroup(Services.NickServ, 'password') conf.registerGroup(Services.NickServ, 'password')
conf.registerGlobalValue(Services, 'ChanServ', conf.registerNetworkValue(Services, 'ChanServ',
ValidNickOrEmptyString('ChanServ', _("""Determines what nick the 'ChanServ' service ValidNickOrEmptyString('ChanServ', _("""Determines what nick the 'ChanServ' service
has."""))) has.""")))
conf.registerChannelValue(Services.ChanServ, 'password', conf.registerChannelValue(Services.ChanServ, 'password',

View File

@ -55,7 +55,7 @@ class Services(callbacks.Plugin):
def __init__(self, irc): def __init__(self, irc):
self.__parent = super(Services, self) self.__parent = super(Services, self)
self.__parent.__init__(irc) self.__parent.__init__(irc)
for nick in self.registryValue('nicks'): for nick in self.registryValue('nicks', network=irc.network):
config.registerNick(nick) config.registerNick(nick)
self.reset() self.reset()
@ -75,7 +75,7 @@ class Services(callbacks.Plugin):
def outFilter(self, irc, msg): def outFilter(self, irc, msg):
if msg.command == 'JOIN' and not self.disabled(irc): if msg.command == 'JOIN' and not self.disabled(irc):
if not self.identified: if not self.identified:
if self.registryValue('noJoinsUntilIdentified'): if self.registryValue('noJoinsUntilIdentified', network=irc.network):
self.log.info('Holding JOIN to %s @ %s until identified.', self.log.info('Holding JOIN to %s @ %s until identified.',
msg.channel, irc.network) msg.channel, irc.network)
self.waitingJoins.setdefault(irc.network, []) self.waitingJoins.setdefault(irc.network, [])
@ -90,25 +90,25 @@ class Services(callbacks.Plugin):
else: else:
return network_nick return network_nick
def _getNickServPassword(self, nick): def _getNickServPassword(self, nick, network):
# This should later be nick-specific. # This should later be nick-specific.
assert nick in self.registryValue('nicks') assert nick in self.registryValue('nicks', network=network)
return self.registryValue('NickServ.password.%s' % nick) return self.registryValue('NickServ.password.%s' % nick, network=network)
def _setNickServPassword(self, nick, password): def _setNickServPassword(self, nick, password, network):
# This also should be nick-specific. # This also should be nick-specific.
assert nick in self.registryValue('nicks') assert nick in self.registryValue('nicks', network=network)
self.setRegistryValue('NickServ.password.%s' % nick, password) self.setRegistryValue('NickServ.password.%s' % nick, password, network=network)
def _doIdentify(self, irc, nick=None): def _doIdentify(self, irc, nick=None):
if self.disabled(irc): if self.disabled(irc):
return return
if nick is None: if nick is None:
nick = self._getNick(irc.network) nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'): if nick not in self.registryValue('nicks', network=irc.network):
return return
nickserv = self.registryValue('NickServ') nickserv = self.registryValue('NickServ', network=irc.network)
password = self._getNickServPassword(nick) password = self._getNickServPassword(nick, irc.network)
if not nickserv or not password: if not nickserv or not password:
s = 'Tried to identify without a NickServ or password set.' s = 'Tried to identify without a NickServ or password set.'
self.log.warning(s) self.log.warning(s)
@ -127,11 +127,11 @@ class Services(callbacks.Plugin):
return return
if nick is None: if nick is None:
nick = self._getNick(irc.network) nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'): if nick not in self.registryValue('nicks', network=irc.network):
return return
nickserv = self.registryValue('NickServ') nickserv = self.registryValue('NickServ', network=irc.network)
password = self._getNickServPassword(nick) password = self._getNickServPassword(nick, irc.network)
ghostDelay = self.registryValue('ghostDelay') ghostDelay = self.registryValue('ghostDelay', network=irc.network)
if not ghostDelay: if not ghostDelay:
return return
if not nickserv or not password: if not nickserv or not password:
@ -157,11 +157,11 @@ class Services(callbacks.Plugin):
if self.disabled(irc): if self.disabled(irc):
return return
nick = self._getNick(irc.network) nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'): if nick not in self.registryValue('nicks', network=irc.network):
return return
nickserv = self.registryValue('NickServ') nickserv = self.registryValue('NickServ', network=irc.network)
password = self._getNickServPassword(nick) password = self._getNickServPassword(nick, irc.network)
ghostDelay = self.registryValue('ghostDelay') ghostDelay = self.registryValue('ghostDelay', network=irc.network)
if not ghostDelay: if not ghostDelay:
return return
if nick and nickserv and password and \ if nick and nickserv and password and \
@ -181,13 +181,13 @@ class Services(callbacks.Plugin):
if self.disabled(irc): if self.disabled(irc):
return return
nick = self._getNick(irc.network) nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'): if nick not in self.registryValue('nicks', network=irc.network):
return return
nickserv = self.registryValue('NickServ') nickserv = self.registryValue('NickServ', network=irc.network)
if not nickserv: if not nickserv:
self.log.warning('NickServ is unset, cannot identify.') self.log.warning('NickServ is unset, cannot identify.')
return return
password = self._getNickServPassword(nick) password = self._getNickServPassword(nick, irc.network)
if not password: if not password:
self.log.warning('Password for %s is unset, cannot identify.',nick) self.log.warning('Password for %s is unset, cannot identify.',nick)
return return
@ -205,10 +205,10 @@ class Services(callbacks.Plugin):
if self.disabled(irc): if self.disabled(irc):
return return
nick = self._getNick(irc.network) nick = self._getNick(irc.network)
if nick not in self.registryValue('nicks'): if nick not in self.registryValue('nicks', network=irc.network):
return return
if nick and irc.afterConnect: if nick and irc.afterConnect:
password = self._getNickServPassword(nick) password = self._getNickServPassword(nick, irc.network)
if not password: if not password:
return return
self._doGhost(irc) self._doGhost(irc)
@ -232,8 +232,8 @@ class Services(callbacks.Plugin):
def doNotice(self, irc, msg): def doNotice(self, irc, msg):
if irc.afterConnect: if irc.afterConnect:
nickserv = self.registryValue('NickServ') nickserv = self.registryValue('NickServ', network=irc.network)
chanserv = self.registryValue('ChanServ') chanserv = self.registryValue('ChanServ', network=irc.network)
if nickserv and ircutils.strEqual(msg.nick, nickserv): if nickserv and ircutils.strEqual(msg.nick, nickserv):
self.doNickservNotice(irc, msg) self.doNickservNotice(irc, msg)
elif chanserv and ircutils.strEqual(msg.nick, chanserv): elif chanserv and ircutils.strEqual(msg.nick, chanserv):
@ -298,7 +298,7 @@ class Services(callbacks.Plugin):
'Resetting password to empty.' % on 'Resetting password to empty.' % on
self.log.warning(log) self.log.warning(log)
self.sentGhost = time.time() self.sentGhost = time.time()
self._setNickServPassword(nick, '') self._setNickServPassword(nick, '', irc.network)
elif self._ghosted(irc, s): elif self._ghosted(irc, s):
self.log.info('Received "GHOST succeeded" from NickServ %s.', on) self.log.info('Received "GHOST succeeded" from NickServ %s.', on)
self.sentGhost = None self.sentGhost = None
@ -355,7 +355,7 @@ class Services(callbacks.Plugin):
def checkPrivileges(self, irc, channel): def checkPrivileges(self, irc, channel):
if self.disabled(irc): if self.disabled(irc):
return return
chanserv = self.registryValue('ChanServ') chanserv = self.registryValue('ChanServ', network=irc.network)
on = 'on %s' % irc.network on = 'on %s' % irc.network
if chanserv and self.registryValue('ChanServ.op', channel, irc.network): if chanserv and self.registryValue('ChanServ.op', channel, irc.network):
if irc.nick not in irc.state.channels[channel].ops: if irc.nick not in irc.state.channels[channel].ops:
@ -376,7 +376,7 @@ class Services(callbacks.Plugin):
def doMode(self, irc, msg): def doMode(self, irc, msg):
if self.disabled(irc): if self.disabled(irc):
return return
chanserv = self.registryValue('ChanServ') chanserv = self.registryValue('ChanServ', network=irc.network)
on = 'on %s' % irc.network on = 'on %s' % irc.network
if ircutils.strEqual(msg.nick, chanserv): if ircutils.strEqual(msg.nick, chanserv):
channel = msg.args[0] channel = msg.args[0]
@ -406,7 +406,7 @@ class Services(callbacks.Plugin):
self.__parent.callCommand(command, irc, msg, *args, **kwargs) self.__parent.callCommand(command, irc, msg, *args, **kwargs)
def _chanservCommand(self, irc, channel, command, log=False): def _chanservCommand(self, irc, channel, command, log=False):
chanserv = self.registryValue('ChanServ') chanserv = self.registryValue('ChanServ', network=irc.network)
if chanserv: if chanserv:
msg = ircmsgs.privmsg(chanserv, msg = ircmsgs.privmsg(chanserv,
' '.join([command, channel])) ' '.join([command, channel]))
@ -493,7 +493,8 @@ class Services(callbacks.Plugin):
invite = wrap(invite, [('checkChannelCapability', 'op'), 'inChannel']) invite = wrap(invite, [('checkChannelCapability', 'op'), 'inChannel'])
def doInvite(self, irc, msg): def doInvite(self, irc, msg):
if ircutils.strEqual(msg.nick, self.registryValue('ChanServ')): if ircutils.strEqual(
msg.nick, self.registryValue('ChanServ', etwork=irc.network)):
channel = msg.args[1] channel = msg.args[1]
on = 'on %s' % irc.network on = 'on %s' % irc.network
networkGroup = conf.supybot.networks.get(irc.network) networkGroup = conf.supybot.networks.get(irc.network)
@ -506,8 +507,8 @@ class Services(callbacks.Plugin):
Identifies with NickServ using the current nick. Identifies with NickServ using the current nick.
""" """
if self.registryValue('NickServ'): if self.registryValue('NickServ', network=irc.network):
if irc.nick in self.registryValue('nicks'): if irc.nick in self.registryValue('nicks', network=irc.network):
self._doIdentify(irc, irc.nick) self._doIdentify(irc, irc.nick)
irc.replySuccess() irc.replySuccess()
else: else:
@ -525,7 +526,7 @@ class Services(callbacks.Plugin):
Ghosts the bot's given nick and takes it. If no nick is given, Ghosts the bot's given nick and takes it. If no nick is given,
ghosts the bot's configured nick and takes it. ghosts the bot's configured nick and takes it.
""" """
if self.registryValue('NickServ'): if self.registryValue('NickServ', network=irc.network):
if not nick: if not nick:
nick = self._getNick(irc.network) nick = self._getNick(irc.network)
if ircutils.strEqual(nick, irc.nick): if ircutils.strEqual(nick, irc.nick):
@ -547,13 +548,17 @@ class Services(callbacks.Plugin):
""" """
if not password: if not password:
try: try:
self.registryValue('nicks').remove(nick) v = self.registryValue('nicks', network=irc.network).copy()
v.remove(nick)
self.setRegistryValue('nicks', value=v, network=irc.network)
irc.replySuccess() irc.replySuccess()
except KeyError: except KeyError:
irc.error(_('That nick was not configured with a password.')) irc.error(_('That nick was not configured with a password.'))
return return
else: else:
self.registryValue('nicks').add(nick) v = self.registryValue('nicks', network=irc.network).copy()
v.add(nick)
self.setRegistryValue('nicks', value=v, network=irc.network)
config.registerNick(nick, password) config.registerNick(nick, password)
irc.replySuccess() irc.replySuccess()
password = wrap(password, [('checkCapability', 'admin'), password = wrap(password, [('checkCapability', 'admin'),
@ -566,7 +571,7 @@ class Services(callbacks.Plugin):
Returns the nicks that this plugin is configured to identify and ghost Returns the nicks that this plugin is configured to identify and ghost
with. with.
""" """
L = list(self.registryValue('nicks')) L = list(self.registryValue('nicks', network=irc.network))
if L: if L:
utils.sortBy(ircutils.toLower, L) utils.sortBy(ircutils.toLower, L)
irc.reply(format('%L', L)) irc.reply(format('%L', L))

View File

@ -30,7 +30,7 @@
from supybot.test import * from supybot.test import *
class ServicesTestCase(PluginTestCase): class ServicesTestCase(PluginTestCase):
plugins = ('Services',) plugins = ('Services', 'Config')
config = { config = {
'plugins.Services.NickServ': 'NickServ', 'plugins.Services.NickServ': 'NickServ',
'plugins.Services.ChanServ': 'ChanServ', 'plugins.Services.ChanServ': 'ChanServ',
@ -48,6 +48,33 @@ class ServicesTestCase(PluginTestCase):
self.failUnless(m.args[0] == 'NickServ') self.failUnless(m.args[0] == 'NickServ')
self.failUnless(m.args[1].lower() == 'identify biff') self.failUnless(m.args[1].lower() == 'identify biff')
def testPasswordConfg(self):
self.assertNotError('config plugins.Services.nicks ""')
self.assertNotError('config network plugins.Services.nicks ""')
self.assertNotError('services password %s bar' % self.nick)
self.assertResponse(
'config plugins.Services.nicks',
'Global: ; test: %s' % self.nick)
self.assertResponse(
'config plugins.Services.nickserv.password.%s' % self.nick,
'Global: bar; test: bar')
self.assertNotError(
'config network plugins.Services.nickserv.password.%s bar2'
% self.nick)
self.assertResponse(
'config plugins.Services.nickserv.password.%s' % self.nick,
'Global: bar; test: bar2')
self.assertResponse(
'config plugins.Services.nickserv.password.%s' % self.nick,
'Global: bar; test: bar2')
m = self.assertNotError('services identify')
self.failUnless(m.args[0] == 'NickServ')
self.failUnless(m.args[1].lower() == 'identify bar2')
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -87,6 +87,21 @@ def registerGlobalValue(group, name, value):
value._channelValue = False value._channelValue = False
return group.register(name, value) return group.register(name, value)
def registerNetworkValue(group, name, value):
value._supplyDefault = True
value._networkValue = True
value._channelValue = False
g = group.register(name, value)
gname = g._name.lower()
for name in registry._cache.keys():
if name.lower().startswith(gname) and len(gname) < len(name):
name = name[len(gname)+1:] # +1 for .
parts = registry.split(name)
if len(parts) == 1 and parts[0] and ircutils.isChannel(parts[0]):
# This gets the network values so they always persist.
g.get(parts[0])()
return g
def registerChannelValue(group, name, value, opSettable=True): def registerChannelValue(group, name, value, opSettable=True):
value._supplyDefault = True value._supplyDefault = True
value._networkValue = True value._networkValue = True
@ -106,6 +121,7 @@ def registerChannelValue(group, name, value, opSettable=True):
elif len(parts) == 1 and parts[0] and ircutils.isChannel(parts[0]): elif len(parts) == 1 and parts[0] and ircutils.isChannel(parts[0]):
# Old-style variant of the above, without a network # Old-style variant of the above, without a network
g.get(parts[0])() g.get(parts[0])()
return g
def registerPlugin(name, currentValue=None, public=True): def registerPlugin(name, currentValue=None, public=True):
group = registerGlobalValue(supybot.plugins, name, group = registerGlobalValue(supybot.plugins, name,