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.
"""
import getopt
import string
import conf
@ -50,14 +51,20 @@ class User(callbacks.Privmsg):
return True
def register(self, irc, msg, args):
"""<name> <password>
"""[--hashed] <name> <password>
Registers <name> with the given password <password> and the current
hostmask of the person registering. This command (and all other
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):
return
try:
@ -77,7 +84,7 @@ class User(callbacks.Privmsg):
pass
(id, user) = ircdb.users.newUser()
user.name = name
user.setPassword(password)
user.setPassword(password, hashed=hashed)
user.addHostmask(msg.prefix)
ircdb.users.setUser(id, user)
irc.reply(msg, conf.replySuccess)
@ -200,13 +207,19 @@ class User(callbacks.Privmsg):
return
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
<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):
return
try:
@ -216,7 +229,7 @@ class User(callbacks.Privmsg):
irc.error(msg, conf.replyNoUser)
return
if user.checkPassword(oldpassword):
user.setPassword(newpassword)
user.setPassword(newpassword, hashed=hashed)
ircdb.users.setUser(id, user)
irc.reply(msg, conf.replySuccess)
else:

View File

@ -186,11 +186,12 @@ class UserCapabilitySet(CapabilitySet):
class IrcUser(object):
"""This class holds the capabilities and authentications for a user."""
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.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.hashed = hashed # True if the password is hashed on disk.
self.password = password # password (plaintext? hashed?)
self.capabilities = UserCapabilitySet()
for capability in capabilities:
@ -201,10 +202,11 @@ class IrcUser(object):
self.hostmasks = hostmasks
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' %\
(self.__class__.__name__, self.ignore, self.password,
self.name, self.capabilities, self.hostmasks, self.secure)
(self.__class__.__name__,
self.ignore, self.password, self.name, self.hashed,
self.capabilities, self.hostmasks, self.secure)
def addCapability(self, capability):
"""Gives the user the given capability."""
@ -224,12 +226,20 @@ class IrcUser(object):
else:
return self.capabilities.check(capability)
def setPassword(self, password):
def setPassword(self, password, hashed=False):
"""Sets the user's password."""
if hashed or self.hashed:
self.hashed = True
self.password = utils.saltHash(password)
else:
self.password = password
def checkPassword(self, password):
"""Checks the user's 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):

View File

@ -412,7 +412,7 @@ def saltHash(password, salt=None, hash='sha'):
hasher = md5.md5
elif hash == 'sha':
hasher = sha.sha
return salt + hasher(salt + password).hexdigest()
return '|'.join([salt, hasher(salt + password).hexdigest()])
class IterableMap(object):
"""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.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:

View File

@ -212,6 +212,13 @@ class IrcUserTestCase(unittest.TestCase):
self.failUnless(u.checkPassword('foobar'))
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):
prefix = 'foo!bar@baz'
hostmasks = ['*!bar@baz', 'foo!*@*']

View File

@ -277,7 +277,8 @@ class UtilsTest(unittest.TestCase):
def testSaltHash(self):
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)