Added the ability to do hashed passwords.

This commit is contained in:
Jeremy Fincher 2003-10-28 15:13:53 +00:00
parent 8ae59844bf
commit d174226c5c
6 changed files with 58 additions and 17 deletions

View File

@ -33,6 +33,7 @@
Provides commands useful to users in general. This plugin is loaded by default. Provides commands useful to users in general. This plugin is loaded by default.
""" """
import getopt
import string import string
import conf import conf
@ -50,14 +51,20 @@ class User(callbacks.Privmsg):
return True return True
def register(self, irc, msg, args): def register(self, irc, msg, args):
"""<name> <password> """[--hashed] <name> <password>
Registers <name> with the given password <password> and the current Registers <name> with the given password <password> and the current
hostmask of the person registering. This command (and all other hostmask of the person registering. This command (and all other
commands that include a password) must be sent to the bot privately, commands that include a password) must be sent to the bot privately,
not in a channel. not in a channel. If --hashed is given, the password will be hashed
on disk, rather than being stored in plaintext.
""" """
(name, password) = privmsgs.getArgs(args, needed=2) (optlist, rest) = getopt.getopt(args, '', ['hashed'])
(name, password) = privmsgs.getArgs(rest, needed=2)
hashed = False
for (option, arg) in optlist:
if option == '--hashed':
hashed = True
if not self._checkNotChannel(irc, msg, password): if not self._checkNotChannel(irc, msg, password):
return return
try: try:
@ -77,7 +84,7 @@ class User(callbacks.Privmsg):
pass pass
(id, user) = ircdb.users.newUser() (id, user) = ircdb.users.newUser()
user.name = name user.name = name
user.setPassword(password) user.setPassword(password, hashed=hashed)
user.addHostmask(msg.prefix) user.addHostmask(msg.prefix)
ircdb.users.setUser(id, user) ircdb.users.setUser(id, user)
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
@ -200,13 +207,19 @@ class User(callbacks.Privmsg):
return return
def setpassword(self, irc, msg, args): def setpassword(self, irc, msg, args):
"""<name> <old password> <new password> """[--hashed] <name> <old password> <new password>
Sets the new password for the user specified by <name> to Sets the new password for the user specified by <name> to
<new password>. Obviously this message must be sent to the bot <new password>. Obviously this message must be sent to the bot
privately (not on a channel). privately (not in a channel). If --hashed is given, the password will
be hashed on disk (rather than being stored in plaintext.
""" """
(name, oldpassword, newpassword) = privmsgs.getArgs(args, 3) (optlist, rest) = getopt.getopt(args, '', ['hashed'])
(name, oldpassword, newpassword) = privmsgs.getArgs(rest, 3)
hashed = False
for (option, arg) in optlist:
if option == '--hashed':
hashed = True
if not self._checkNotChannel(irc, msg, oldpassword+newpassword): if not self._checkNotChannel(irc, msg, oldpassword+newpassword):
return return
try: try:
@ -216,7 +229,7 @@ class User(callbacks.Privmsg):
irc.error(msg, conf.replyNoUser) irc.error(msg, conf.replyNoUser)
return return
if user.checkPassword(oldpassword): if user.checkPassword(oldpassword):
user.setPassword(newpassword) user.setPassword(newpassword, hashed=hashed)
ircdb.users.setUser(id, user) ircdb.users.setUser(id, user)
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
else: else:

View File

@ -186,11 +186,12 @@ class UserCapabilitySet(CapabilitySet):
class IrcUser(object): class IrcUser(object):
"""This class holds the capabilities and authentications for a user.""" """This class holds the capabilities and authentications for a user."""
def __init__(self, ignore=False, password='', name='', def __init__(self, ignore=False, password='', name='',
capabilities=(), hostmasks=None, secure=False): capabilities=(), hostmasks=None, secure=False, hashed=False):
self.auth = None # The (time, hostmask) a user authenticated under self.auth = None # The (time, hostmask) a user authenticated under
self.name = name # The name of the user. self.name = name # The name of the user.
self.ignore = ignore # A boolean deciding if the person is ignored. self.ignore = ignore # A boolean deciding if the person is ignored.
self.secure = secure # A boolean describing if hostmasks *must* match. self.secure = secure # A boolean describing if hostmasks *must* match.
self.hashed = hashed # True if the password is hashed on disk.
self.password = password # password (plaintext? hashed?) self.password = password # password (plaintext? hashed?)
self.capabilities = UserCapabilitySet() self.capabilities = UserCapabilitySet()
for capability in capabilities: for capability in capabilities:
@ -201,10 +202,11 @@ class IrcUser(object):
self.hostmasks = hostmasks self.hostmasks = hostmasks
def __repr__(self): def __repr__(self):
return '%s(ignore=%s, password=%r, name=%r, '\ return '%s(ignore=%s, password=%r, name=%r, hashed=%r, '\
'capabilities=%r, hostmasks=%r, secure=%r)\n' %\ 'capabilities=%r, hostmasks=%r, secure=%r)\n' %\
(self.__class__.__name__, self.ignore, self.password, (self.__class__.__name__,
self.name, self.capabilities, self.hostmasks, self.secure) self.ignore, self.password, self.name, self.hashed,
self.capabilities, self.hostmasks, self.secure)
def addCapability(self, capability): def addCapability(self, capability):
"""Gives the user the given capability.""" """Gives the user the given capability."""
@ -224,13 +226,21 @@ class IrcUser(object):
else: else:
return self.capabilities.check(capability) return self.capabilities.check(capability)
def setPassword(self, password): def setPassword(self, password, hashed=False):
"""Sets the user's password.""" """Sets the user's password."""
self.password = password if hashed or self.hashed:
self.hashed = True
self.password = utils.saltHash(password)
else:
self.password = password
def checkPassword(self, password): def checkPassword(self, password):
"""Checks the user's password.""" """Checks the user's password."""
return (self.password == password) if self.hashed:
(salt, _) = self.password.split('|')
return (self.password == utils.saltHash(password, salt=salt))
else:
return (self.password == password)
def checkHostmask(self, hostmask, useAuth=True): def checkHostmask(self, hostmask, useAuth=True):
"""Checks a given hostmask against the user's hostmasks or current """Checks a given hostmask against the user's hostmasks or current

View File

@ -412,7 +412,7 @@ def saltHash(password, salt=None, hash='sha'):
hasher = md5.md5 hasher = md5.md5
elif hash == 'sha': elif hash == 'sha':
hasher = sha.sha hasher = sha.sha
return salt + hasher(salt + password).hexdigest() return '|'.join([salt, hasher(salt + password).hexdigest()])
class IterableMap(object): class IterableMap(object):
"""Define .iteritems() in a class and subclass this to get the other iters. """Define .iteritems() in a class and subclass this to get the other iters.

View File

@ -54,5 +54,15 @@ class UserTestCase(PluginTestCase, PluginDocumentation):
self.assertError('changeusername foo bar') self.assertError('changeusername foo bar')
self.assertNotError('changeusername foo baz') self.assertNotError('changeusername foo baz')
def testSetpassword(self):
self.prefix = self.prefix1
self.assertNotError('register foo bar')
self.assertEqual(ircdb.users.getUser(self.prefix).password, 'bar')
self.assertNotError('setpassword foo bar baz')
self.assertEqual(ircdb.users.getUser(self.prefix).password, 'baz')
self.assertNotError('setpassword --hashed foo baz biff')
self.assertNotEqual(ircdb.users.getUser(self.prefix).password, 'biff')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -212,6 +212,13 @@ class IrcUserTestCase(unittest.TestCase):
self.failUnless(u.checkPassword('foobar')) self.failUnless(u.checkPassword('foobar'))
self.failIf(u.checkPassword('somethingelse')) self.failIf(u.checkPassword('somethingelse'))
def testHashedPassword(self):
u = ircdb.IrcUser()
u.setPassword('foobar', hashed=True)
self.failUnless(u.checkPassword('foobar'))
self.failIf(u.checkPassword('somethingelse'))
self.assertNotEqual(u.password, 'foobar')
def testHostmasks(self): def testHostmasks(self):
prefix = 'foo!bar@baz' prefix = 'foo!bar@baz'
hostmasks = ['*!bar@baz', 'foo!*@*'] hostmasks = ['*!bar@baz', 'foo!*@*']

View File

@ -277,7 +277,8 @@ class UtilsTest(unittest.TestCase):
def testSaltHash(self): def testSaltHash(self):
s = utils.saltHash('jemfinch') s = utils.saltHash('jemfinch')
self.assertEqual(utils.saltHash('jemfinch', salt=s[:8]), s) (salt, hash) = s.split('|')
self.assertEqual(utils.saltHash('jemfinch', salt=salt), s)