#!/usr/bin/env python # # userdbm.py: OO-user database with authentication # # Author: Christopher Arndt <chris.arndt@web.de> # Version: 1.1 # Date: 26.04.2002 # Copyright: LGPL """OO-user database using dbm password files. """ __all__ = ['UserDB'] __author__ = "Christopher Arndt <chris.arndt@web.de>" __version__ = "1.0b" import anydbm, cPickle from getpass import getpass import authlib class UserDB: """Represents a user database and provides authentication methods. """ def __init__(self, dbname, crypt_type='md5', failsafe=0): self.dbname = dbname self.crypt_type = crypt_type self.failsafe = failsafe self.user_prompt = 'Login: ' self.pass_prompt = 'Password: ' # database query methods def get_user(self, user): """Return database entry for the given user name. Returns a tuple (encrypted password, extra info). """ try: db = anydbm.open(self.dbname) except anydbm.error: if self.failsafe: raise KeyError, "User '%s' does not exists." % user raise IOError, "Could not open database file '%s'" % self.dbname entry = db[user].split(':', 1) try: info = cPickle.loads(entry[1]) except IndexError: info = {} db.close() return entry[0], info def get_passwd(self, user): """Return encrypted password for the given user. """ return self.get_user(user)[0] def get_info(self, user): """Return extra info for the given user as a dictionary. """ return self.get_user(user)[1] # pseudo dictionary methods __getitem__ = get_user def keys(self): try: db = anydbm.open(self.dbname) except anydbm.error: if self.failsafe: return [] raise IOError, "Could not open database file '%s'" % self.dbname keys = db.keys() db.close() return keys def has_key(self, key): try: db = anydbm.open(self.dbname) except anydbm.error: if self.failsafe: return 0 raise IOError, "Could not open database file '%s'" % self.dbname ret = db.has_key(key) db.close() return ret # database manipulation methods def add_user(self, user, passwd=None, **args): """Add a new user to the database. """ try: db = anydbm.open(self.dbname, 'w') except anydbm.error: import sys sys.stderr.write("Notice: creating new database!\n") db = anydbm.open(self.dbname, 'c') if db.has_key(user): db.close() raise ValueError, "User '%s' already exists." % user if passwd == None: passwd = '!!' elif passwd != '': passwd = authlib.passcrypt(passwd, method=self.crypt_type) if args: info = ":" + cPickle.dumps(args, 1) else: info = '' db[user] = passwd + info db.close() def del_user(self, user): """Delete a user from the database. """ try: db = anydbm.open(self.dbname, 'w') except anydbm.error: if self.failsafe: return raise IOError, "Could not open database file '%s'" % self.dbname try: del db[user] except KeyError: db.close() raise KeyError, "User '%s' does not exist." % (user) def set_passwd(self, user, passwd=None): """Change the passwd of a user. Setting password to an empty string deletes the password, setting it to None disables login. """ try: db = anydbm.open(self.dbname, 'w') except anydbm.error: if self.failsafe: return raise IOError, "Could not open database file '%s'" % self.dbname if not db.has_key(user): db.close() raise KeyError, "User '%s' does not exists." % user if passwd == None: passwd = '!!' elif passwd != '': passwd = authlib.passcrypt(passwd, method=self.crypt_type) try: info = ":" + db[user].split(':', 1)[1] except IndexError: info = '' db[user] = passwd + info db.close() def set_info(self, user, **args): """Set extra info for user to given keyword args. """ try: db = anydbm.open(self.dbname, 'w') except anydbm.error: if self.failsafe: return raise IOError, "Could not open database file '%s'" % self.dbname if not db.has_key(user): db.close() raise KeyError, "User '%s' does not exists." % user passwd = self.get_passwd(user) if args: info = ":" + cPickle.dumps(args, 1) else: _info = '' db[user] = passwd + info db.close() def update_info(self, user, **args): """Updates extra info for user with given keyword args. """ info = self.get_info(user) info.update(args) apply(self.set_info, [user], info) # authentication methods def check_passwd(self, user, passwd): """Validate given user, passwd pair against database. """ return authlib.check_passwd(user, passwd, self) def login(self, user=None, user_prompt=None, pass_prompt=None, max_tries=3): """Generate a login screen and validate the login. Returns user name or None on failure. """ return authlib.login(user, self, user_prompt, pass_prompt, max_tries) if __name__ == '__main__': # test suite import sys if len(sys.argv) > 1: dbname = sys.argv[1] else: dbname = 'test.db' db = UserDB(dbname) user = db.login(max_tries=1) if user: print "Access granted" print "Info: " + repr(db.get_info(user)) else: print "Access denied"