NickAuth: First commit.

This commit is contained in:
Valentin Lorentz 2012-11-03 23:06:57 +01:00
parent 6ea2d062b7
commit 659f6ebceb
6 changed files with 383 additions and 0 deletions

View File

@ -0,0 +1,6 @@
This plugin allows users to use their network services account to
authenticate to the bot.
They first have to use @nickauth nick add <the nick>, then use @auth
every time they want to be authenticated.

View File

@ -0,0 +1,66 @@
###
# Copyright (c) 2012, Valentin Lorentz
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
"""
Add a description of the plugin (to be presented to the user inside the wizard)
here. This should describe *what* the plugin does.
"""
import supybot
import supybot.world as world
# Use this for the version of this plugin. You may wish to put a CVS keyword
# in here if you're keeping the plugin in CVS or some similar system.
__version__ = ""
# XXX Replace this with an appropriate author or supybot.Author instance.
__author__ = supybot.authors.unknown
# This is a dictionary mapping supybot.Author instances to lists of
# contributions.
__contributors__ = {}
# This is a url where the most recent plugin package can be downloaded.
__url__ = '' # 'http://supybot.com/Members/yourname/NickAuth/download'
import config
import plugin
reload(plugin) # In case we're being reloaded.
# Add more reloads here if you add third-party modules and want them to be
# reloaded when this plugin is reloaded. Don't forget to import them as well!
if world.testing:
import test
Class = plugin.Class
configure = config.configure
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -0,0 +1,52 @@
###
# Copyright (c) 2012, Valentin Lorentz
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot.conf as conf
import supybot.registry as registry
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('NickAuth')
def configure(advanced):
# This will be called by supybot to configure this module. advanced is
# a bool that specifies whether the user identified himself as an advanced
# user or not. You should effect your configuration by manipulating the
# registry as appropriate.
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('NickAuth', True)
NickAuth = conf.registerPlugin('NickAuth')
# This is where your configuration variables (if any) should go. For example:
# conf.registerGlobalValue(NickAuth, 'someConfigVariableName',
# registry.Boolean(False, _("""Help for someConfigVariableName.""")))
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -0,0 +1 @@
# Stub so local is a module, used for third-party modules

166
plugins/NickAuth/plugin.py Normal file
View File

@ -0,0 +1,166 @@
###
# Copyright (c) 2012, Valentin Lorentz
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import time
import supybot.conf as conf
import supybot.utils as utils
import supybot.ircdb as ircdb
from supybot.commands import *
import supybot.ircmsgs as ircmsgs
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('NickAuth')
@internationalizeDocstring
class NickAuth(callbacks.Plugin):
"""Support authentication based on nicks and network services."""
def __init__(self, irc):
super(NickAuth, self).__init__(irc)
self._requests = {}
class nick(callbacks.Commands):
def _check_auth(self, irc, msg, user):
if user is None:
irc.error(_('You are not authenticated.'), Raise=True)
if not user.checkHostmask(msg.prefix):
try:
u = ircdb.users.getUser(msg.prefix)
except KeyError:
irc.error(_('You are not authenticated.'),
Raise=True)
if not u._checkCapability('owner'):
irc.error(_('You must be owner to do that.'),
Raise=True)
@internationalizeDocstring
def add(self, irc, msg, args, network, user, nick):
"""[<network>] <user> <nick>
Add <nick> to the list of nicks owned by the <user> on the
<network>. You have to register this nick to the network
services to be authenticated.
<network> defaults to the current network.
"""
network = network.network or irc.network
user = user or ircdb.users.getUser(msg.prefix)
self._check_auth(irc, msg, user)
try:
user.addNick(network, nick)
except KeyError:
irc.error(_('This nick is already used by someone on this '
'network.'), Raise=True)
irc.replySuccess()
add = wrap(add, [optional('networkIrc'),
optional('otherUser'),
'nick'])
@internationalizeDocstring
def remove(self, irc, msg, args, network, user, nick):
"""[<network>] <user> <nick>
Remove <nick> from the list of nicks owned by the <user> on the
<network>.
<network> defaults to the current network.
"""
network = network.network or irc.network
user = user or ircdb.users.getUser(msg.prefix)
self._check_auth(user, irc, msg)
try:
user.removeNick(network, nick)
except KeyError:
irc.error(_('This nick is not registered to you on this '
'network.'), Raise=True)
irc.replySuccess()
remove = wrap(remove, [optional('networkIrc'),
optional('otherUser'),
'nick'])
@internationalizeDocstring
def list(self, irc, msg, args, network, user):
"""[<network>] [<user>]
Lists nicks of the <user> on the network.
<network> defaults to the current network.
"""
network = network.network or irc.network
user = user or ircdb.users.getUser(msg.prefix)
self._check_auth(irc, msg, user)
try:
list_ = user.nicks[network]
if list_:
irc.reply(format('%L', list_))
else:
raise KeyError
except KeyError:
irc.error(_('You have no recognized nick on this '
'network.'), Raise=True)
list = wrap(list, [optional('networkIrc'),
optional('otherUser')])
@internationalizeDocstring
def auth(self, irc, msg, args):
"""takes no argument
Tries to authenticate you using network services.
If you get no reply, it means you are not authenticated to the
network services."""
nick = ircutils.toLower(msg.nick)
self._requests[(irc.network, msg.nick)] = (time.time(), msg.prefix, irc)
irc.queueMsg(ircmsgs.whois(nick, nick))
auth = wrap(auth, [])
def do330(self, irc, msg):
mynick, theirnick, theiraccount, garbage = msg.args
# I would like to use a dict comprehension, but we have to support
# Python 2.6 :(
self._requests = dict([(x,y) for x,y in self._requests.items()
if y[0]+60>time.time()])
try:
(timestamp, prefix, irc) = self._requests.pop((irc.network, theirnick))
except KeyError:
return
user = ircdb.users.getUserFromNick(irc.network, theiraccount)
if not user:
user = ircdb.users.getUserFromNick(irc.network, theirnick)
if user:
user.addAuth(prefix)
ircdb.users.setUser(user, flush=False)
irc.reply(_('You are now authenticated as %s.') % user.name)
else:
irc.error(_('No user has this nick on this network.'))
Class = NickAuth
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

92
plugins/NickAuth/test.py Normal file
View File

@ -0,0 +1,92 @@
###
# Copyright (c) 2012, Valentin Lorentz
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot.ircdb as ircdb
from supybot.test import *
class NickAuthTestCase(PluginTestCase):
plugins = ('NickAuth', 'User')
prefix1 = 'something!user@host.tld'
def _procedure(self, nickserv_reply):
self.assertNotError('register foobar 123')
self.assertResponse('user list', 'foobar')
self.assertNotError('hostmask remove foobar %s' % self.prefix)
self.assertNotError('identify foobar 123')
self.assertNotError('nick add foobar baz')
self.assertNotError('unidentify')
self.prefix = self.prefix1
self.assertError('nick add foobar qux')
self.nick = self.prefix.split('!')[0]
self.assertError('hostmask list')
self.irc.feedMsg(ircmsgs.privmsg(self.irc.nick,
'auth',
prefix=self.prefix))
self.assertIs(self.irc.takeMsg().command, 'WHOIS')
self.assertError('hostmask list')
self.irc.feedMsg(ircmsgs.privmsg(self.irc.nick,
'auth',
prefix=self.prefix))
self.assertEqual(self.irc.takeMsg().command, 'WHOIS')
if nickserv_reply:
self.irc.feedMsg(ircmsgs.IrcMsg(':leguin.freenode.net 330 pgjrgrg '
'%s baz :is logged in as' % self.nick))
msg = self.irc.takeMsg()
self.assertNotEqual(msg, None)
self.assertEqual(msg.args[1], 'You are now authenticated as foobar.')
self.assertResponse('hostmask list',
'foobar has no registered hostmasks.')
else:
msg = self.irc.takeMsg()
self.assertEqual(msg, None)
self.assertError('hostmask list')
def testAuth(self):
self._procedure(True)
def testNoAuth(self):
self._procedure(False)
def testList(self):
self.assertNotError('register foobar 123')
self.assertRegexp('nick list', 'You have no recognized nick')
self.assertNotError('nick add foo')
self.assertRegexp('nick list', 'foo')
self.assertNotError('nick add %s bar' % self.nick)
self.assertRegexp('nick list', 'foo and bar')
self.assertNotError('nick add %s %s baz' % (self.irc.network, self.nick))
self.assertRegexp('nick list', 'foo, bar, and baz')
self.assertRegexp('nick list %s' % self.irc.network, 'foo, bar, and baz')
self.assertRegexp('nick list %s foobar' % self.irc.network,
'foo, bar, and baz')
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: