#!/usr/bin/env python # # userdb.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 plain text password files. """ __all__ = ['UserDB'] __author__ = "Christopher Arndt <chris.arndt@web.de>" __version__ = "1.0b" import fileinput import authlib from getpass import getpass class UserDB: """Represents a user database and provides authentication methods. """ def __init__(self, dbname=None, crypt_type='des', dbtype='flat'): if not dbname: if dbtype == 'unix': self.dbname = '/etc/passwd' else: raise TypeError, \ "Please specify a database filename or set dbtype='unix'" else: self.dbname = dbname if self.dbname == '/etc/passwd' or self.dbname == '/etc/shadow': self.dbtype = 'unix' else: self.dbtype = dbtype self.crypt_type = crypt_type self.user_prompt = 'Login: ' self.pass_prompt = 'Password: ' def add_user(self, user, passwd=None): """Add a new user to the database. """ try: self.get_user(user) raise ValueError, "User '%s' already exists." % user except KeyError: pass if self.dbtype != 'flat': raise NotImplementedError, \ "Can't add a new user to database of type '%s'." % self.dbtype if passwd == None: passwd = '!!' elif passwd != '': passwd = passcrypt(passwd, method=self.crypt_type) if self.dbtype == 'flat': f = open(self.dbname, 'r+b') try: f.seek(-1,2) except IOError: pass if f.tell() != 0 and f.read(1) != '\n': f.write('\n') f.write('%s:%s\n' % (user, passwd)) f.close() def del_user(self, user): """Delete a user from the database. """ try: self.get_user(user) except KeyError: raise ValueError, "User '%s' does not exist." % user if self.dbtype not in ['flat', 'unix']: raise NotImplementedError, \ "Can't delete a user from database of type '%s'." % self.dbtype if self.dbtype == 'flat': for line in fileinput.input(self.dbname, inplace=1): if line: if not line.strip().split(':', 1)[0] == user: print line, 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: self.get_user(user) except KeyError: raise ValueError, "User '%s' does not exist." % user if self.dbtype != 'flat': raise NotImplementedError, \ "Can't change password in database of type '%s'." % self.dbtype if passwd == None: passwd = '!!' elif passwd != '': passwd = passcrypt(passwd, method=self.crypt_type) if self.dbtype == 'flat': for line in fileinput.input(self.dbname, inplace=1): if line: entry = line.strip().split(':', 1) if entry[0] == user: print "%s:%s\n" % (entry[0], passwd), else: print line, def get_passwd(self, user): """Return encrypted password for the given user. """ return self.get_user()[1] def get_user(self, user): """Return database entry for the given user name. Returns a tuple of at most 7 items starting with username and encrypted password. """ return authlib.getpwnam(user, self.dbname) def check_passwd(self, user, passwd): """Validate given user, passwd pair against database. """ return authlib.check_passwd(user, passwd, self.dbname) 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__': import os db = UserDB(dbtype='unix') if os.path.exists('/etc/shadow'): if os.getuid() != 0: print "Your system uses shadow password files!" print "Login will probably fail because you are not root." db.dbname = '/etc/shadow' if db.login(max_tries=1): print "Access granted" else: print "Access denied"