From 562fa147065e2e357924e56dcf1259d62e1539aa Mon Sep 17 00:00:00 2001 From: James Lu Date: Wed, 15 Apr 2015 18:01:06 -0700 Subject: [PATCH 1/4] ChannelIdDatabasePlugin: allow unregistered users to use commands This adds a new configuration variable, supybot.databases.plugins.requireRegistration, which defaults to True for maximum security. --- plugins/__init__.py | 28 ++++++++++++++++++++-------- src/conf.py | 5 +++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/plugins/__init__.py b/plugins/__init__.py index 07f6abfc7..b6b2a15a1 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -330,7 +330,7 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): (self.name(), id, channel)) def checkChangeAllowed(self, irc, msg, channel, user, record): - if user.id == record.by: + if (hasattr(user, 'id') and user.id == record.by) or user == record.by: return True cap = ircdb.makeChannelCapability(channel, 'op') if ircdb.checkCapability(msg.prefix, cap): @@ -341,27 +341,38 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): """This should irc.error or raise an exception if text is invalid.""" pass - def add(self, irc, msg, args, user, channel, text): + def getUserId(self, irc, prefix): + try: + user = ircdb.users.getUser(prefix) + return user.id + except KeyError: + if conf.supybot.databases.plugins.requireRegistration(): + irc.errorNotRegistered(Raise=True) + return + + def add(self, irc, msg, args, channel, text): """[] Adds to the $type database for . is only necessary if the message isn't sent in the channel itself. """ + user = self.getUserId(irc, msg.prefix) or msg.prefix at = time.time() self.addValidator(irc, text) if text is not None: - id = self.db.add(channel, at, user.id, text) + id = self.db.add(channel, at, user, text) irc.replySuccess('%s #%s added.' % (self.name(), id)) - add = wrap(add, ['user', 'channeldb', 'text']) + add = wrap(add, ['channeldb', 'text']) - def remove(self, irc, msg, args, user, channel, id): + def remove(self, irc, msg, args, channel, id): """[] Removes the $type with id from the $type database for . is only necessary if the message isn't sent in the channel itself. """ + user = self.getUserId(irc, msg.prefix) or msg.prefix try: record = self.db.get(channel, id) self.checkChangeAllowed(irc, msg, channel, user, record) @@ -369,7 +380,7 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): irc.replySuccess() except KeyError: self.noSuchRecord(irc, channel, id) - remove = wrap(remove, ['user', 'channeldb', 'id']) + remove = wrap(remove, ['channeldb', 'id']) def searchSerializeRecord(self, record): text = utils.str.ellipsisify(record.text, 50) @@ -430,13 +441,14 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): self.noSuchRecord(irc, channel, id) get = wrap(get, ['channeldb', 'id']) - def change(self, irc, msg, args, user, channel, id, replacer): + def change(self, irc, msg, args, channel, id, replacer): """[] Changes the $type with id according to the regular expression . is only necessary if the message isn't sent in the channel itself. """ + user = self.getUserId(irc, msg.prefix) or msg.prefix try: record = self.db.get(channel, id) self.checkChangeAllowed(irc, msg, channel, user, record) @@ -445,7 +457,7 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): irc.replySuccess() except KeyError: self.noSuchRecord(irc, channel, id) - change = wrap(change, ['user', 'channeldb', 'id', 'regexpReplacer']) + change = wrap(change, ['channeldb', 'id', 'regexpReplacer']) def stats(self, irc, msg, args, channel): """[] diff --git a/src/conf.py b/src/conf.py index 92385713f..ec0c0b207 100644 --- a/src/conf.py +++ b/src/conf.py @@ -931,6 +931,11 @@ class ChannelSpecific(registry.Boolean): return lchannel registerGroup(supybot.databases, 'plugins') + +registerGlobalValue(supybot.databases.plugins, 'requireRegistration', + registry.Boolean(True, _("""Determines whether the bot will require user + registration to use 'add' commands in database-based Supybot + plugins."""))) registerChannelValue(supybot.databases.plugins, 'channelSpecific', ChannelSpecific(True, _("""Determines whether database-based plugins that can be channel-specific will be so. This can be overridden by individual From 24c1caac439306f2aec03818477327cc5efe155e Mon Sep 17 00:00:00 2001 From: James Lu Date: Sat, 9 May 2015 18:45:01 -0700 Subject: [PATCH 2/4] Praise: make tests channel specific This should fix build errors in Travis CI. (cherry picked from commit 8dc2996e3597e6a641be089188816b44505366c5) --- plugins/Praise/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Praise/test.py b/plugins/Praise/test.py index fc1baa0f1..38f89da68 100644 --- a/plugins/Praise/test.py +++ b/plugins/Praise/test.py @@ -29,7 +29,7 @@ from supybot.test import * -class PraiseTestCase(PluginTestCase): +class PraiseTestCase(ChannelPluginTestCase): plugins = ('Praise',) def testAdd(self): From 1fe663ddb266f44063ecd5e11136214f270f4d12 Mon Sep 17 00:00:00 2001 From: James Lu Date: Sun, 28 Jun 2015 11:24:42 -0700 Subject: [PATCH 3/4] Quote: test unauthenticated DB 'add' --- plugins/Quote/test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugins/Quote/test.py b/plugins/Quote/test.py index fb0728471..d3a1d6c4f 100644 --- a/plugins/Quote/test.py +++ b/plugins/Quote/test.py @@ -40,4 +40,16 @@ class QuoteTestCase(ChannelPluginTestCase): self.assertRegexp("quote get 1", "goodbye") self.assertError("quote replace 5 afsdafas") # non-existant + def testUnauthenticatedAdd(self): + # This should fail because the user isn't registered + self.assertError('quote add hello world!') + original_value = conf.supybot.databases.plugins.requireRegistration() + conf.supybot.databases.plugins.requireRegistration.setValue(False) + try: + self.assertNotError('quote add hello world!') + self.assertRegexp('quote get 1', 'hello') + self.assertNotError('quote remove 1') + finally: + conf.supybot.databases.plugins.requireRegistration.setValue(original_value) + # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: From 8e51209c3f5f62d9002dcc548364e5e415309e15 Mon Sep 17 00:00:00 2001 From: James Lu Date: Sun, 28 Jun 2015 11:51:58 -0700 Subject: [PATCH 4/4] Fixes to ChannelIdDatabasePlugin as pointed out by @ProgVal - Quote: factorize tests - Make supybot.databases.plugins channel specific --- plugins/Quote/test.py | 6 +----- plugins/__init__.py | 12 +++++++----- src/conf.py | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/plugins/Quote/test.py b/plugins/Quote/test.py index d3a1d6c4f..f8a069491 100644 --- a/plugins/Quote/test.py +++ b/plugins/Quote/test.py @@ -43,13 +43,9 @@ class QuoteTestCase(ChannelPluginTestCase): def testUnauthenticatedAdd(self): # This should fail because the user isn't registered self.assertError('quote add hello world!') - original_value = conf.supybot.databases.plugins.requireRegistration() - conf.supybot.databases.plugins.requireRegistration.setValue(False) - try: + with conf.supybot.databases.plugins.requireRegistration.context(False): self.assertNotError('quote add hello world!') self.assertRegexp('quote get 1', 'hello') self.assertNotError('quote remove 1') - finally: - conf.supybot.databases.plugins.requireRegistration.setValue(original_value) # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/plugins/__init__.py b/plugins/__init__.py index b6b2a15a1..1dd6d728d 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -330,6 +330,8 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): (self.name(), id, channel)) def checkChangeAllowed(self, irc, msg, channel, user, record): + # Checks and returns True if either the user ID (integer) + # or the hostmask of the caller match. if (hasattr(user, 'id') and user.id == record.by) or user == record.by: return True cap = ircdb.makeChannelCapability(channel, 'op') @@ -341,12 +343,12 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): """This should irc.error or raise an exception if text is invalid.""" pass - def getUserId(self, irc, prefix): + def getUserId(self, irc, prefix, channel=None): try: user = ircdb.users.getUser(prefix) return user.id except KeyError: - if conf.supybot.databases.plugins.requireRegistration(): + if conf.get(conf.supybot.databases.plugins.requireRegistration, channel): irc.errorNotRegistered(Raise=True) return @@ -357,7 +359,7 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): is only necessary if the message isn't sent in the channel itself. """ - user = self.getUserId(irc, msg.prefix) or msg.prefix + user = self.getUserId(irc, msg.prefix, channel) or msg.prefix at = time.time() self.addValidator(irc, text) if text is not None: @@ -372,7 +374,7 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): is only necessary if the message isn't sent in the channel itself. """ - user = self.getUserId(irc, msg.prefix) or msg.prefix + user = self.getUserId(irc, msg.prefix, channel) or msg.prefix try: record = self.db.get(channel, id) self.checkChangeAllowed(irc, msg, channel, user, record) @@ -448,7 +450,7 @@ class ChannelIdDatabasePlugin(callbacks.Plugin): . is only necessary if the message isn't sent in the channel itself. """ - user = self.getUserId(irc, msg.prefix) or msg.prefix + user = self.getUserId(irc, msg.prefix, channel) or msg.prefix try: record = self.db.get(channel, id) self.checkChangeAllowed(irc, msg, channel, user, record) diff --git a/src/conf.py b/src/conf.py index ec0c0b207..ba2eb89d5 100644 --- a/src/conf.py +++ b/src/conf.py @@ -932,7 +932,7 @@ class ChannelSpecific(registry.Boolean): registerGroup(supybot.databases, 'plugins') -registerGlobalValue(supybot.databases.plugins, 'requireRegistration', +registerChannelValue(supybot.databases.plugins, 'requireRegistration', registry.Boolean(True, _("""Determines whether the bot will require user registration to use 'add' commands in database-based Supybot plugins.""")))