diff --git a/src/UserCommands.py b/src/UserCommands.py index 37fbd119f..e1f555f31 100755 --- a/src/UserCommands.py +++ b/src/UserCommands.py @@ -315,6 +315,39 @@ class UserCommands(callbacks.Privmsg): except KeyError: irc.error(msg, conf.replyNotRegistered) + def setsecure(self, irc, msg, args): + """ [] + + Sets the secure flag on the user of the person sending the message. + Requires that the person's hostmask be in the list of hostmasks for + that user in addition to the password being correct. When the secure + flag is set, the user *must* identify before he can be recognized. + If a specific True/False value is not given, it inverts the current + value. + """ + if not self._checkNotChannel(irc, msg, password): + return + try: + id = ircdb.users.getUserId(msg.prefix) + user = ircdb.users.getUser(id) + except KeyError: + irc.error(msg, conf.replyNotRegistered) + (password, value) = privmsgs.getArgs(args, optional=1) + if value == '': + value = not user.secure + elif value.lower() in ('true', 'false'): + value = eval(value.capitalize()) + else: + irc.error(msg, '%s is not a valid boolean value.' % value) + return + if user.checkPassword(password) and \ + user.checkHostmask(msg.prefix, useAuth=False): + user.secure = value + ircdb.users.setUser(id, user) + irc.reply(msg, 'Secure flag set to %s' % value) + else: + irc.error(msg, conf.replyIncorrectAuth) + Class = UserCommands diff --git a/src/ircdb.py b/src/ircdb.py index 8d703bad1..266100986 100644 --- a/src/ircdb.py +++ b/src/ircdb.py @@ -171,10 +171,11 @@ class IrcUser(object): """This class holds the capabilities and authentications for a user. """ def __init__(self, ignore=False, password='', name='', - capabilities=(), hostmasks=None): + capabilities=(), hostmasks=None, secure=False): self.auth = None # The (time, hostmask) a user authenticated under self.name = name # The name of the user. self.ignore = ignore # A boolean deciding if the person is ignored. + self.secure = secure # A boolean describing if hostmasks *must* match. self.password = password # password (plaintext? hashed?) self.capabilities = UserCapabilitySet() for capability in capabilities: @@ -186,9 +187,9 @@ class IrcUser(object): def __repr__(self): return '%s(ignore=%s, password=%r, name=%r, '\ - 'capabilities=%r, hostmasks=%r)\n' %\ + 'capabilities=%r, hostmasks=%r, secure=%r)\n' %\ (self.__class__.__name__, self.ignore, self.password, - self.name, self.capabilities, self.hostmasks) + self.name, self.capabilities, self.hostmasks, self.secure) def addCapability(self, capability): self.capabilities.add(capability) @@ -211,8 +212,8 @@ class IrcUser(object): def checkPassword(self, password): return (self.password == password) - def checkHostmask(self, hostmask): - if self.auth and (hostmask == self.auth[1]): + def checkHostmask(self, hostmask, useAuth=True): + if useAuth and self.auth and (hostmask == self.auth[1]): return True for pat in self.hostmasks: if ircutils.hostmaskPatternEqual(pat, hostmask): @@ -558,6 +559,9 @@ def checkCapability(hostmask, capability, users=users, channels=channels): return _x(capability, True) try: u = users.getUser(hostmask) + if u.secure and not u.checkHostmask(hostmask, useAuth=False): + debug.printf('Secure user with non-matching hostmask.') + raise KeyError except KeyError: #debug.printf('user could not be found.') if isChannelCapability(capability): diff --git a/test/test_ircdb.py b/test/test_ircdb.py index cea6bbb1c..48d383971 100644 --- a/test/test_ircdb.py +++ b/test/test_ircdb.py @@ -307,6 +307,7 @@ class CheckCapabilityTestCase(unittest.TestCase): antifoo = 'antifoo!antifoo@antifoo' justchanfoo = 'justchanfoo!justchanfoo@justchanfoo' antichanfoo = 'antichanfoo!antichanfoo@antichanfoo' + securefoo = 'securefoo!securefoo@securefoo' channel = '#channel' cap = 'foo' anticap = ircdb.makeAntiCapability(cap) @@ -325,35 +326,49 @@ class CheckCapabilityTestCase(unittest.TestCase): pass self.users = ircdb.UsersDB(self.filename) self.channels = ircdb.ChannelsDictionary(self.filename) + (id, owner) = self.users.newUser() owner.name = 'owner' owner.addCapability('owner') owner.addHostmask(self.owner) self.users.setUser(id, owner) + (id, nothing) = self.users.newUser() nothing.name = 'nothing' nothing.addHostmask(self.nothing) self.users.setUser(id, nothing) + (id, justfoo) = self.users.newUser() justfoo.name = 'justfoo' justfoo.addCapability(self.cap) justfoo.addHostmask(self.justfoo) self.users.setUser(id, justfoo) + (id, antifoo) = self.users.newUser() antifoo.name = 'antifoo' antifoo.addCapability(self.anticap) antifoo.addHostmask(self.antifoo) self.users.setUser(id, antifoo) + (id, justchanfoo) = self.users.newUser() justchanfoo.name = 'justchanfoo' justchanfoo.addCapability(self.chancap) justchanfoo.addHostmask(self.justchanfoo) self.users.setUser(id, justchanfoo) + (id, antichanfoo) = self.users.newUser() antichanfoo.name = 'antichanfoo' antichanfoo.addCapability(self.antichancap) antichanfoo.addHostmask(self.antichanfoo) self.users.setUser(id, antichanfoo) + + (id, securefoo) = self.users.newUser() + securefoo.name = 'securefoo' + securefoo.addCapability(self.cap) + securefoo.secure = True + securefoo.addHostmask(self.securefoo) + self.users.setUser(id, securefoo) + channel = ircdb.IrcChannel() self.channels.setChannel(self.channel, channel) @@ -428,6 +443,19 @@ class CheckCapabilityTestCase(unittest.TestCase): self.failUnless(self.checkCapability(self.antichanfoo, self.antichancap)) + def testSecurefoo(self): + self.failUnless(self.checkCapability(self.securefoo, self.cap)) + id = self.users.getUserId(self.securefoo) + u = self.users.getUser(id) + u.setAuth(self.securefoo) + self.users.setUser(id, u) + try: + originalConfDefaultAllow = conf.defaultAllow + conf.defaultAllow = False + self.failIf(self.checkCapability('a' + self.securefoo, self.cap)) + finally: + conf.defaultAllow = originalConfDefaultAllow + # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: