예제 #1
0
    def test_catlessPassword(self):
        'Saving and loading password without category'

        tree = self.pdb.getTree()
        pwd = FigaroPassword(title='CLHost', password='******')
        tree.addNode(pwd)
        self.pdb.save(fname='fpm.saved')
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='fpm.saved')
        tlnodes = self.pdb.getTree().getNodes()
        self.assertEqual(len(tlnodes), 1)
        self.assertEqual(tlnodes[0].title, 'CLHost')
        self.assertEqual(tlnodes[0]['password'], 'CLPass')
        self.assertEqual(tlnodes[0]['url'], '')
예제 #2
0
 def test_tooLongPassword(self):
     pwd = FigaroPassword()
     longpswd = "1234567890" * 3
     self.assertRaises(FigaroPasswordTooLongError, pwd.__setitem__,
                       'password', longpswd)
     self.assertRaises(FigaroPasswordTooLongError, pwd.update,
                       {'password': longpswd})
     pwd.store_long_password = 1
     pwd['title'] = "Long password"
     pwd['password'] = longpswd
     self.pdb.getTree()['Test'].addNode(pwd)
     self.pdb.save(fname="fpm.saved")
     self.pdb = PDBFigaro()
     self.pdb.open(self.password, fname='fpm.saved')
     saved_pwd = self.pdb.getTree()['Test'].locate('Long password')[0]
     self.assertEqual(saved_pwd['password'], longpswd)
예제 #3
0
파일: app.py 프로젝트: a-d-j-i/kedpm
 def openDatabase(self):
     self.pdb = PDBFigaro(
         filename=expanduser(self.conf.options['fpm-database']))
     dlg = LoginDialog(pdb=self.pdb)
     password = dlg['password']
     try:
         res = dlg.run()
         if res != Gtk.ResponseType.OK:
             print("Good bye.")
             sys.exit(1)
     except DatabaseNotExist:
         dlg.destroyDialog()
         newpass = self.createNewDatabase()
         if newpass is None:
             sys.exit(1)
         self.pdb.open(newpass)
예제 #4
0
파일: cli.py 프로젝트: chutzimir/kedpm
    def openDatabase(self):
        ''' Open database amd prompt for password if necessary '''
        
        self.verbose = self.conf.options['verbose']
        self.force_single = self.conf.options['force-single']
        self.confirm_deletes = self.conf.options['confirm-deletes']

        self.pdb = PDBFigaro(filename = expanduser(self.conf.options['fpm-database']))
        password = ""
        self.printMessage(_("Ked Password Manager (version %s)"), __version__)

        while 1:
            try:
                self.pdb.open(password)
                break
            except WrongPassword:
                if password:
                    print _("Error! Wrong password.")
                else:
                    print _("Provide password to access the database (Ctrl-C to exit)")
                password = getpass(_("Password: "******"Password accepted."))
        print
예제 #5
0
파일: app.py 프로젝트: chutzimir/kedpm
class Application(object, Frontend):
    pdb = None
    wnd_main = None
    
    def openDatabase(self):
        self.pdb = PDBFigaro(filename = expanduser(self.conf.options['fpm-database']))
        dlg = LoginDialog(pdb = self.pdb)
        password = dlg['password']
        try:
            res = dlg.run()
            if res != gtk.RESPONSE_OK:
                print "Good bye."
                sys.exit(1)
        except DatabaseNotExist:
            dlg.destroyDialog()
            newpass = self.createNewDatabase()
            if newpass is None:
                sys.exit(1)
            self.pdb.open(newpass)

    def createNewDatabase(self):
        """Create new password database and return password for created
        database"""
        dlg = NewDatabaseDialog()
        newpass = dlg.run()
        self.pdb.create(newpass, expanduser(self.conf.options['fpm-database']))
        return newpass

    def mainLoop(self):
        globals.app = self # Make application instance available to all modules
        self.wnd_main = MainWindow()
        gtk.main()

    def showMessage(self, message):
        dialog = gtk.MessageDialog(None,
                                    gtk.DIALOG_DESTROY_WITH_PARENT,
                                    gtk.MESSAGE_INFO,
                                    gtk.BUTTONS_CLOSE,
                                    message);
        dialog.run();
        dialog.destroy();
                
    def _run(self):
        globals.app = self # Make application instance available to all modules
        self.openDatabase()
        self.wnd_main = MainWindow()
        gtk.main()
예제 #6
0
파일: app.py 프로젝트: widgital/kedpm
class Application(object, Frontend):
    pdb = None
    wnd_main = None

    def openDatabase(self):
        self.pdb = PDBFigaro(
            filename=expanduser(self.conf.options['fpm-database']))
        dlg = LoginDialog(pdb=self.pdb)
        password = dlg['password']
        try:
            res = dlg.run()
            if res != gtk.RESPONSE_OK:
                print "Good bye."
                sys.exit(1)
        except DatabaseNotExist:
            dlg.destroyDialog()
            newpass = self.createNewDatabase()
            if newpass is None:
                sys.exit(1)
            self.pdb.open(newpass)

    def createNewDatabase(self):
        """Create new password database and return password for created
        database"""
        dlg = NewDatabaseDialog()
        newpass = dlg.run()
        self.pdb.create(newpass, expanduser(self.conf.options['fpm-database']))
        return newpass

    def mainLoop(self):
        globals.app = self  # Make application instance available to all modules
        self.wnd_main = MainWindow()
        gtk.main()

    def showMessage(self, message):
        dialog = gtk.MessageDialog(None, gtk.DIALOG_DESTROY_WITH_PARENT,
                                   gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE,
                                   message)
        dialog.run()
        dialog.destroy()

    def _run(self):
        globals.app = self  # Make application instance available to all modules
        self.openDatabase()
        self.wnd_main = MainWindow()
        gtk.main()
예제 #7
0
파일: app.py 프로젝트: a-d-j-i/kedpm
class Application(Frontend):
    pdb = None
    wnd_main = None

    def openDatabase(self):
        self.pdb = PDBFigaro(
            filename=expanduser(self.conf.options['fpm-database']))
        dlg = LoginDialog(pdb=self.pdb)
        password = dlg['password']
        try:
            res = dlg.run()
            if res != Gtk.ResponseType.OK:
                print("Good bye.")
                sys.exit(1)
        except DatabaseNotExist:
            dlg.destroyDialog()
            newpass = self.createNewDatabase()
            if newpass is None:
                sys.exit(1)
            self.pdb.open(newpass)

    def createNewDatabase(self):
        """Create new password database and return password for created
        database"""
        dlg = NewDatabaseDialog()
        newpass = dlg.run()
        self.pdb.create(newpass, expanduser(self.conf.options['fpm-database']))
        return newpass

    def mainLoop(self):
        #globals.app = self # Make application instance available to all modules
        self.wnd_main = MainWindow(self)
        Gtk.main()

    def showMessage(self, message):
        dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                   Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE,
                                   message)
        dialog.run()
        dialog.destroy()

    def _run(self):
        globals.app = self  # Make application instance available to all modules
        self.openDatabase()
        self.wnd_main = MainWindow()
        Gtk.main()
예제 #8
0
 def openDatabase(self):
     ''' Open database amd prompt for password if nesessary '''
     self.pdb = PDBFigaro(
         filename=expanduser(self.conf.options['fpm-database']))
     password = ""
     print "Ked Password Manager (version %s)" % __version__
     while 1:
         try:
             self.pdb.open(password)
             break
         except WrongPassword:
             if password:
                 print "Error! Wrong password."
             else:
                 print "Provide password to access the database (Ctrl-C to exit)"
             password = getpass("Password: "******"Password accepted."
     print
예제 #9
0
    def test_catlessPassword(self):
        'Saving and loading password without category'

        tree = self.pdb.getTree()
        pwd = FigaroPassword(title='CLHost', password='******')
        tree.addNode(pwd)
        self.pdb.save(fname='fpm.saved')
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='fpm.saved')
        tlnodes = self.pdb.getTree().getNodes()
        self.assertEqual(len(tlnodes), 1)
        self.assertEqual(tlnodes[0].title, 'CLHost')
        self.assertEqual(tlnodes[0]['password'], 'CLPass')
        self.assertEqual(tlnodes[0]['url'], '')
예제 #10
0
파일: app.py 프로젝트: chutzimir/kedpm
 def openDatabase(self):
     self.pdb = PDBFigaro(filename = expanduser(self.conf.options['fpm-database']))
     dlg = LoginDialog(pdb = self.pdb)
     password = dlg['password']
     try:
         res = dlg.run()
         if res != gtk.RESPONSE_OK:
             print "Good bye."
             sys.exit(1)
     except DatabaseNotExist:
         dlg.destroyDialog()
         newpass = self.createNewDatabase()
         if newpass is None:
             sys.exit(1)
         self.pdb.open(newpass)
예제 #11
0
 def test_tooLongPassword(self):
     pwd = FigaroPassword()
     longpswd = "1234567890"*3;
     self.assertRaises(FigaroPasswordTooLongError, pwd.__setitem__,
         'password', longpswd)
     self.assertRaises(FigaroPasswordTooLongError, pwd.update,
         {'password': longpswd})
     pwd.store_long_password = 1
     pwd['title'] = "Long password"
     pwd['password'] = longpswd
     self.pdb.getTree()['Test'].addNode(pwd)
     self.pdb.save(fname="fpm.saved")
     self.pdb = PDBFigaro()
     self.pdb.open(self.password, fname='fpm.saved')
     saved_pwd = self.pdb.getTree()['Test'].locate('Long password')[0]
     self.assertEqual(saved_pwd['password'], longpswd)
예제 #12
0
 def test_unrotate(self):
     pdb = PDBFigaro()
     unrotated = pdb._unrotate('FIGARO\x00\xe3')
     self.assertEqual(unrotated, 'FIGARO')
예제 #13
0
 def setUp(self):
     self.pdb = PDBFigaro()
     self.pdb.open(self.password, fname='test/fpm.sample')
예제 #14
0
class PDBFigaroTestCase(unittest.TestCase):
    password = '******'

    def setUp(self):
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='test/fpm.sample')

    def test_create(self):
        assert isinstance(self.pdb, PDBFigaro)

    def test_tree(self):
        'Tree should be formed correctly'
        ptree = self.pdb.getTree()
        branches = ptree.getBranches()
        bkeys = branches.keys()
        bkeys.sort()
        self.assertEqual(bkeys, ['Kedder', 'Test'])
        tree_test = ptree['Test']
        assert tree_test
        self.assertEqual(len(tree_test.getNodes()), 2)
        tree_kedder = ptree['Kedder']
        assert tree_kedder
        self.assertEqual(len(tree_kedder.getNodes()), 1)

    def test_fpmCompatibilty(self):
        ptree = self.pdb.getTree()
        pwd = ptree['Test'].locate('test2')[0]
        self.assertEqual(pwd.default, 1)
        pwd = ptree['Test'].locate('test1')[0]
        self.assertEqual(pwd.default, 0)
        pwd = ptree['Kedder'].locate('kedder1')[0]
        self.assertEqual(pwd.launcher, 'ssh')

    def test_decryption(self):
        'Test how passwords are decrypted'
        tree_test = self.pdb.getTree()['Test']
        pwds = tree_test.locate('url')
        self.assertEqual(len(pwds), 2)
        pwd_test2 = tree_test.locate('test2')
        self.assertEqual(pwd_test2[0].password, 'test2 password')

    def test_encriptionUtils(self):
        str = "FIGARO ENCRYPTION TEST"
        noised = self.pdb._addNoise(str)
        self.assertEqual(len(noised) % Blowfish.block_size, 0)
        rotated = self.pdb._rotate(noised)
        self.assertEqual(len(rotated) % Blowfish.block_size, 0)

        hex = self.pdb._bin_to_hex(rotated)
        bin = self.pdb._hex_to_bin(hex)
        self.assertEqual(bin, rotated)

        unrotated = self.pdb._unrotate(bin)
        self.assertEqual(str, unrotated)

    def test_encription(self):
        strings = ["FIGARO ENCRYPTION TEST", "small"]
        for str in strings:
            encrypted = self.pdb.encrypt(str)
            decrypted = self.pdb.decrypt(encrypted)
            self.assertEqual(str, decrypted)

    def test_passwordEncryption(self):
        'Encrypted password should be 48 character long'
        pwdstr = 'shortpass'
        encrypted = self.pdb.encrypt(pwdstr, 1)
        self.assertEqual(len(encrypted), FPM_PASSWORD_LEN * 2)
        decrypted = self.pdb.decrypt(encrypted)
        self.assertEqual(pwdstr, decrypted)

    def test_native(self):
        self.assertEqual(self.pdb.native, 0)

    def test_versions(self):
        self.assertEqual(self.pdb.FULL_VERSION, '00.53.00')
        self.assertEqual(self.pdb.MIN_VERSION, '00.50.00')
        self.assertEqual(self.pdb.DISPLAY_VERSION, '0.53')
예제 #15
0
 def test_unrotate(self):
     pdb = PDBFigaro()
     unrotated = pdb._unrotate('FIGARO\x00\xe3')
     self.assertEqual(unrotated, 'FIGARO')
예제 #16
0
#! /usr/bin/python2
from kedpm.plugins.pdb_figaro import PDBFigaro, FigaroPassword
import getpass
import sys
import os
import json

f = os.path.join(os.environ["HOME"],".fpm/fpm")

if len(sys.argv) > 1:
    f = sys.argv[1]

db = PDBFigaro(filename=f)

pw = getpass.getpass('Please give password : '******'title']] = { 'user': n['user'],
                          'password' : n['password'],
                          'url' : n['url'],
                          'notes' : n['notes']}

    branches = t.getBranches()
    folders = {}
예제 #17
0
 def setUp(self):
     self.pdb = PDBFigaro()
     self.pdb.open(self.password, fname='test/fpm.sample')
예제 #18
0
 def setUp(self):
     pdb = PDBFigaro()
     pdb.open(self.password, fname='test/fpm.sample')
     pdb.save(fname="fpm.saved")
     self.pdb = PDBFigaro()
     self.pdb.open(self.password, fname='fpm.saved')
예제 #19
0
class SavedFigaroTestCase(PDBFigaroTestCase):
    def setUp(self):
        pdb = PDBFigaro()
        pdb.open(self.password, fname='test/fpm.sample')
        pdb.save(fname="fpm.saved")
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='fpm.saved')

    def tearDown(self):
        os.remove('fpm.saved')

    def test_catlessPassword(self):
        'Saving and loading password without category'

        tree = self.pdb.getTree()
        pwd = FigaroPassword(title='CLHost', password='******')
        tree.addNode(pwd)
        self.pdb.save(fname='fpm.saved')
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='fpm.saved')
        tlnodes = self.pdb.getTree().getNodes()
        self.assertEqual(len(tlnodes), 1)
        self.assertEqual(tlnodes[0].title, 'CLHost')
        self.assertEqual(tlnodes[0]['password'], 'CLPass')
        self.assertEqual(tlnodes[0]['url'], '')

    def test_native(self):
        self.assertEqual(self.pdb.native, 1)

    def test_tooLongPassword(self):
        pwd = FigaroPassword()
        longpswd = "1234567890" * 3
        self.assertRaises(FigaroPasswordTooLongError, pwd.__setitem__,
                          'password', longpswd)
        self.assertRaises(FigaroPasswordTooLongError, pwd.update,
                          {'password': longpswd})
        pwd.store_long_password = 1
        pwd['title'] = "Long password"
        pwd['password'] = longpswd
        self.pdb.getTree()['Test'].addNode(pwd)
        self.pdb.save(fname="fpm.saved")
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='fpm.saved')
        saved_pwd = self.pdb.getTree()['Test'].locate('Long password')[0]
        self.assertEqual(saved_pwd['password'], longpswd)
예제 #20
0
 def setUp(self):
     pdb = PDBFigaro()
     pdb.open(self.password, fname='test/fpm.sample')
     pdb.save(fname="fpm.saved")
     self.pdb = PDBFigaro()
     self.pdb.open(self.password, fname='fpm.saved')
예제 #21
0
class Application(Cmd, Frontend):
    PS1 = "kedpm:%s> "  # prompt template
    cwd = []  # Current working directory
    intro = """Ked Password Manager is ready for operation.
try 'help' for brief description of available commands
"""

    modified = 0
    histfile = os.getenv("HOME") + '/.kedpm/history'

    def __init__(self):
        Cmd.__init__(self)
        if sys.stdout.isatty():
            self.PS1 = "\x1b[1m" + self.PS1 + "\x1b[0m"  # colored prompt template

    def openDatabase(self):
        ''' Open database amd prompt for password if nesessary '''
        self.pdb = PDBFigaro(
            filename=expanduser(self.conf.options['fpm-database']))
        password = ""
        print "Ked Password Manager (version %s)" % __version__
        while 1:
            try:
                self.pdb.open(password)
                break
            except WrongPassword:
                if password:
                    print "Error! Wrong password."
                else:
                    print "Provide password to access the database (Ctrl-C to exit)"
                password = getpass("Password: "******"Password accepted."
        print

    def createNewDatabase(self):
        'Create new password database and return password for created database'
        print "Creating new password database."
        pass1 = pass2 = ""
        while pass1 != pass2 or pass1 == "":
            pass1 = getpass("Provide password: "******"Repeat password: "******"Empty passwords are really insecure. You shoud create one."
            if pass1 != pass2:
                print "Passwords don't match! Please repeat."

        self.pdb.create(pass1)
        return pass1

    def updatePrompt(self):
        self.prompt = self.PS1 % ('/' + '/'.join(self.cwd))

    def getCwd(self):
        'Return current working password tree instance'
        return self.pdb.getTree().getTreeFromPath(self.cwd)

    def listPasswords(self, passwords, show_numbers=0):
        '''display given passwords in nicely formed table'''
        # we assume that all passwords in list have the same fields
        # the same as in first one
        if not passwords:
            return
        prot = passwords[0]
        lengths = show_numbers and {'nr': 3} or {}
        headers = show_numbers and ['Nr'] or []
        fstr = show_numbers and "%%%ds " or ""
        listable = prot.getFieldsOfType([password.TYPE_STRING])
        # determine maximum space needed by each column
        for fld in listable:
            ftitle = prot.getFieldTitle(fld)
            lengths[fld] = len(ftitle)
            fstr = fstr + "%%%ds "
            headers.append(ftitle)

        ptuples = []
        num = 1
        for pwd in passwords:
            ptup = []
            if show_numbers:
                ptup.append("%d)" % num)
            for fld in listable:
                ptup.append(getattr(pwd, fld))
                newlen = len(getattr(pwd, fld))
                if newlen > lengths[fld]:
                    lengths[fld] = newlen
            ptuples.append(tuple(ptup))
            num = num + 1
        # form format string
        if show_numbers:
            listable = ['nr'] + listable
        fstr = fstr % tuple([lengths[x] + 1 for x in listable])
        print fstr % tuple(headers)
        print fstr % tuple(["=" * lengths[x] for x in listable])
        for ptup in ptuples:
            print fstr % ptup

    def pickPassword(self, regexp, tree=None):
        '''Prompt user to pick one password from given password tree. Password
        tree, provided by "tree" argument filtered using "regexp".

        If resulted list contains only one password, return it without
        prompting. If no passwords were located, or user desides to cancel
        operation, return None'''

        if tree is None:
            tree = self.getCwd()
        passwords = tree.locate(regexp)
        if not passwords:
            print "No passwords matching \"%s\" were found" % regexp
            return None
        if len(passwords) > 1:
            self.listPasswords(passwords, 1)
            print "Enter number. Enter 0 to cancel."
            try:
                showstr = raw_input('show: ')
            except (KeyboardInterrupt, EOFError):
                # user has cancelled selection
                showstr = "0"
            try:
                shownr = int(showstr)
                if not shownr:
                    return None
                selected_password = passwords[shownr - 1]
            except (ValueError, IndexError):
                return None
        else:
            selected_password = passwords[0]
        return selected_password

    def inputString(self, prompt):
        '''Input string from user'''
        input = raw_input(prompt)
        return input

    def inputText(self, prompt):
        '''Input long text from user'''
        return self.inputString(prompt)

    def inputPassword(self, prompt):
        '''Input long text from user'''
        pwd = None
        while pwd is None:
            pass1 = getpass(prompt)
            if pass1 == '':
                return ''
            pass2 = getpass('Repeat: ')
            if pass1 == pass2:
                pwd = pass1
            else:
                print "Passwords don't match. Try again."
        return pwd

    def editPassword(self, pwd):
        '''Prompt user for each field of the password. Respect fields' type.'''

        input = {}

        for field, fieldinfo in pwd.fields_type_info:
            field_type = fieldinfo['type']

            new_value = ""
            if field_type == password.TYPE_STRING:
                new_value = self.inputString(
                    "Enter %s (\"%s\"): " %
                    (pwd.getFieldTitle(field), pwd[field]))
            elif field_type == password.TYPE_TEXT:
                new_value = self.inputText(
                    "Enter %s (\"%s\"): " %
                    (pwd.getFieldTitle(field), pwd[field]))
            elif field_type == password.TYPE_PASSWORD:
                new_value = self.inputPassword("Enter %s: " %
                                               pwd.getFieldTitle(field))
            else:
                print """Error. Type %s is unsupported yet. This field will retain an old value.""" % field_type

            if new_value != "":
                input[field] = new_value

        try:
            pwd.update(input)
        except FigaroPasswordTooLongError:
            print "WARNING! Your password is too long for Figaro Password Manager."
            print "Figaro Password Manager can handle only passwords shorter than 24 characters."
            print """However, KedPM can store this password for you, but it
will break fpm compatibility. fpm will not be able to handle such
long password correctly."""
            answer = raw_input(
                "Do you still want to save your password? [Y/n]: ")
            if answer.lower().startswith('n'):
                raise KeyboardInterrupt
            pwd.store_long_password = 1
            pwd.update(input)

        #return pwd

    def tryToSave(self):

        self.modified = 1
        savemode = self.conf.options["save-mode"]
        if savemode == 'no':
            return
        answer = 'y'
        if self.conf.options["save-mode"] == "ask":
            answer = raw_input(
                "Database was modified. Do you want to save it now? [Y/n]: ")
        if answer == '' or answer.lower().startswith('y'):
            self.do_save('')

    def complete_dirs(self, text, line, begidx, endidx):
        completing = line[:endidx].split(' ')[-1]
        base = completing[:completing.rfind('/') + 1]
        abspath = self.getAbsolutePath(base)
        dirs = self.pdb.getTree().getTreeFromPath(abspath).getBranches()
        compl = []
        for dir in dirs.keys():
            if dir.startswith(text):
                compl.append(dir + '/')
        return compl

    def getEditorInput(self, content=''):
        """Fire up default editor and read user input from temporary file"""
        default_editor = "vi"
        if os.environ.has_key('VISUAL'):
            editor = os.environ['VISUAL']
        elif os.environ.has_key('EDITOR'):
            editor = os.environ['EDITOR']
        else:
            editor = default_editor
        print "running %s" % editor
        # create temporary file
        handle, tmpfname = tempfile.mkstemp(prefix="kedpm_")
        tmpfile = open(tmpfname, 'w')
        tmpfile.write(content)
        tmpfile.close()

        os.system(editor + " " + tmpfname)

        tmpfile = open(tmpfname, 'r')
        text = tmpfile.read()
        tmpfile.close()
        os.remove(tmpfname)
        return text

    def getAbsolutePath(self, arg):
        """Return absolute path from potentially relative (cat)"""
        root = self.pdb.getTree()
        if not arg:
            return self.cwd
        if (arg[0] == '/'):
            path = root.normalizePath(arg.split('/'))
        else:
            path = root.normalizePath(self.cwd + arg.split('/'))
        return path

    def getTreeFromRelativePath(self, path):
        """Get tree object from given relative path and current working
        directory"""
        root = self.pdb.getTree()
        abspath = self.getAbsolutePath(path)
        return root.getTreeFromPath(abspath)

    ##########################################
    # Command implementations below.         #

    def emptyline(self):
        pass

    def do_exit(self, arg):
        '''Quit KED Password Manager'''
        readline.write_history_file(self.histfile)
        if self.modified:
            self.tryToSave()
        print "Exiting."
        sys.exit(0)

    def do_EOF(self, arg):
        '''The same as 'exit' command'''
        print
        self.do_exit(arg)

    def do_help(self, arg):
        '''Print help message'''
        Cmd.do_help(self, arg)

    def do_ls(self, arg):
        '''List available catalogs and passwords
Syntax:
    ls [<category>]
'''
        try:
            tree = self.getTreeFromRelativePath(arg)
        except KeyError:
            print "ls: %s:  No such catalog" % arg
            return

        print "=== Directories ==="
        for bname in tree.getBranches().keys():
            print bname + "/"
        print "==== Passwords ===="
        self.listPasswords(tree.getNodes())

    def complete_ls(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_cd(self, arg):
        '''change directory (category)

Syntax:
    cd <category>
'''
        root = self.pdb.getTree()
        cdpath = self.getAbsolutePath(arg)
        try:
            newpath = root.getTreeFromPath(cdpath)
        except KeyError:
            print "cd: %s: No such catalog" % arg
        else:
            self.cwd = cdpath
            self.updatePrompt()

    def complete_cd(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_pwd(self, arg):
        '''print name of current/working directory'''
        print '/' + '/'.join(self.cwd)

    def do_show(self, arg):
        '''display password information.

Syntax:
    show [-r] <regexp>

    -r - recursive search. search all subtree for matching passwords

This will display contents of a password item in current category or whole
subtree, if -r flag was specified. If several items matched by <regexp>, list
of them will be printed and you will be prompted to enter a number, pointing to
password you want to look at.  After receiving that number, KedPM will show you
the password.'''

        argv = arg.split()
        tree = None
        if argv and argv[0] == '-r':
            tree = self.getCwd().flatten()
            if len(argv) > 1:
                arg = " ".join(argv[1:])
            else:
                arg = ""

        selected_password = self.pickPassword(arg, tree)
        if selected_password:
            print "---------------------------------------"
            print selected_password.asText()
            print "---------------------------------------"

    def do_edit(self, arg):
        '''edit password information.

Syntax:
    edit <regexp>

This will prompt you for editing of a password item in current category. If
several items matched by <regexp>, list of them will be printed and you will be
prompted to enter a number, pointing to password you want to edit.  After
receiving that number, you will be able to edit picked password.

'''

        selected_password = self.pickPassword(arg)
        if selected_password:
            try:
                self.editPassword(selected_password)
                #self.modified = 1
                self.tryToSave()
            except (KeyboardInterrupt, EOFError):
                print "Cancelled"
        else:
            print "No password selected"

    def do_new(self, arg):
        '''Add new password to current category. You will be prompted to enter
fields.

Syntax:
    new [-p]

    -p - Get properties by parsing provided text. Will open default text editor
         for you to paste text in.
'''
        new_pass = FigaroPassword(
        )  # FIXME: Password type shouldn't be hardcoded.
        argv = arg.split()

        if "-p" in argv:
            text = self.getEditorInput()
            choosendict = parser.parseMessage(text, self.conf.patterns)
            new_pass.update(choosendict)

        try:
            self.editPassword(new_pass)
        except (KeyboardInterrupt, EOFError):
            print "Cancelled"
        else:
            tree = self.getCwd()
            tree.addNode(new_pass)
            self.tryToSave()

    def do_save(self, arg):
        '''Save current password tree'''
        sys.stdout.write("Saving...")
        sys.stdout.flush()
        self.pdb.save()
        print "OK"
        self.modified = 0

    def do_mkdir(self, arg):
        '''create new category (directory)

Syntax:
    mkdir <category>

Creates new password category in current one.
'''
        if not arg:
            print "mkdir: too few arguments"
            print "try 'help mkdir' for more information"
            return

        pwd = self.getCwd()
        pwd.addBranch(arg.strip())

    def do_rename(self, arg):
        '''rename category

Syntax:
    rename <category> <new_name>
'''
        args = arg.split()
        if len(args) != 2:
            print '''Syntax:
    rename <category> <new_name>
'''
            return
        oldname = args[0]
        newname = args[1]
        try:
            self.pdb.getTree().renameBranch(self.cwd + [oldname], newname)
        except RenameError:
            print "rename: category %s already exists" % newname
            return
        except KeyError:
            print "rename: %s: no such category" % oldname
            return
        self.tryToSave()

    def complete_rename(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_help(self, arg):
        """Print help topic"""
        argv = arg.split()
        if argv and argv[0] in ['set']:
            # Provide extended help
            help_def = getattr(self, "help_" + argv[0])
            if help_def:
                help_def(' '.join(argv[1:]))
            else:
                Cmd.do_help(self, arg)
        else:
            Cmd.do_help(self, arg)

    def do_set(self, arg):
        """Set KedPM options

Syntax:
    set                     -- show all options
    set <option>            -- show value of option
    set <option> = <value>  -- set value to option

for boolean values 1, 'on' or 'true' are considered as True; 0, 'off' or 'false' are
considered as False.

enter help set <option> for more info on particular option."""

        opts = self.conf.options
        if not arg:
            # show all options
            for opt, value in opts.items():
                print "%s = %s" % (opt, value)
            return
        tokens = arg.split('=')
        opt_name = tokens[0]
        try:
            opt_value = opts[opt_name]
        except KeyError:
            print "set: no such option: %s" % arg
            return
        if len(tokens) == 1:
            # show value of option
            print "%s = %s" % (opt_name, opt_value)
        else:
            # set the value
            try:
                opts[opt_name] = ' '.join(tokens[1:])
            except OptionError, e:
                print "set: %s" % e
        # save confuguration file
        self.conf.save()
예제 #22
0
파일: cli.py 프로젝트: chutzimir/kedpm
class Application (Cmd, Frontend):
    PS1 = "kedpm:%s> " # prompt template
    cwd = []  # Current working directory
    intro = _("""Ked Password Manager is ready for operation.
try 'help' for brief description of available commands.
""")

    modified = 0
    histfile = os.path.join(expanduser('~'), '.kedpm', 'history')

    def __init__(self):
        Cmd.__init__(self)
        
        # This method of setting colored prompt doesn't work on windows. We
        # need to figure out how to put color in cross-platform way and
        # re-enable it here.
        #if sys.stdout.isatty():
        #    self.PS1 = "\x1b[1m"+self.PS1+"\x1b[0m" # colored prompt template


    def printMessage(self, message, *vars):
        if self.verbose:
            print (message) % vars

    def openDatabase(self):
        ''' Open database amd prompt for password if necessary '''
        
        self.verbose = self.conf.options['verbose']
        self.force_single = self.conf.options['force-single']
        self.confirm_deletes = self.conf.options['confirm-deletes']

        self.pdb = PDBFigaro(filename = expanduser(self.conf.options['fpm-database']))
        password = ""
        self.printMessage(_("Ked Password Manager (version %s)"), __version__)

        while 1:
            try:
                self.pdb.open(password)
                break
            except WrongPassword:
                if password:
                    print _("Error! Wrong password.")
                else:
                    print _("Provide password to access the database (Ctrl-C to exit)")
                password = getpass(_("Password: "******"Password accepted."))
        print

    def createNewDatabase(self):
        'Create new password database and return password for created database'
        print _("Creating new password database.")
        pass1 = pass2 = ""
        while pass1 != pass2 or pass1 == "":
            pass1 = getpass(_("Provide password: "******"Repeat password: "******"Empty passwords are really insecure. You should " \
                        "create one.")
            if pass1!=pass2:
                print _("Passwords don't match! Please repeat.")

        self.pdb.create(pass1)
        return pass1

    def updatePrompt(self):
        self.prompt = self.PS1 % ('/'+'/'.join(self.cwd))

    def getCwd(self):
        'Return current working password tree instance'
        return self.pdb.getTree().getTreeFromPath(self.cwd)

    def listPasswords(self, passwords, show_numbers=0):
        '''display given passwords in nicely formed table'''
        # we assume that all passwords in list have the same fields
        # the same as in first one
        if not passwords:
            return
        prot = passwords[0]
        lengths = show_numbers and {'nr': 3} or {}
        headers = show_numbers and ['Nr'] or []
        fstr = show_numbers and "%%%ds " or ""
        listable = prot.getFieldsOfType([password.TYPE_STRING])
        # determine maximum space needed by each column
        for fld in listable:
            ftitle = prot.getFieldTitle(fld)
            lengths[fld] = len(ftitle)
            fstr = fstr + "%%%ds "
            headers.append(ftitle)

        ptuples = []
        num = 1
        for pwd in passwords:
            ptup = []
            if show_numbers:
                ptup.append("%d)" %num)
            for fld in listable:
                ptup.append(getattr(pwd, fld))
                newlen = len(getattr(pwd, fld))
                if  newlen > lengths[fld]:
                    lengths[fld] = newlen
            ptuples.append(tuple(ptup))
            num = num + 1
        # form format string
        if show_numbers:
            listable = ['nr'] + listable
        fstr = fstr % tuple([lengths[x]+1 for x in listable])
        print fstr % tuple(headers)
        print fstr % tuple(["="*lengths[x]for x in listable])
        for ptup in ptuples:
            print fstr % ptup

    def filterPasswords(self, regexp, tree = None):
        '''Returns a list of passwords, filtered by REGEXP'''
        if tree is None:
            tree = self.getCwd()
      
        return(tree.locate(regexp))


    def getPasswords(self, regexp, tree = None):
        '''Returns a list of passwords, filtered by REGEXP.
        Calls pickPassword if program has been configured to force
        single selection'''
      
        if self.force_single:
            return [self.pickPassword(regexp, tree)] 
        else:
            return(self.filterPasswords(regexp, tree))
         
    def pickPassword(self, regexp, tree = None):
        '''Prompt user to pick one password from given password tree. Password
        tree, provided by "tree" argument filtered using "regexp".

        If resulted list contains only one password, return it without
        prompting. If no passwords were located, or user desides to cancel
        operation, return None'''

        passwords = self.filterPasswords(regexp, tree)
        
        if not passwords:
            self.printMessage(_("No passwords matching \"%s\" were found"), regexp)
            return None
          
         
        if len(passwords) > 1:
            self.listPasswords(passwords, 1)
            print _("Enter number. Enter 0 to cancel.")
            try:
                showstr = raw_input(_('show: '))
            except (KeyboardInterrupt, EOFError):
                # user has cancelled selection
                showstr = "0"
            try:
                shownr = int(showstr)
                if not shownr:
                    return None
                selected_password = passwords[shownr-1]
            except (ValueError, IndexError):
                return None
        else:
            selected_password = passwords[0]
        return selected_password

    def inputString(self, prompt):
        '''Input string from user'''
        input = raw_input(prompt)
        return input

    def inputText(self, prompt):
        '''Input long text from user'''
        return self.inputString(prompt)

    def inputPassword(self, prompt):
        '''Input long text from user'''
        pwd = None
        while pwd is None:
            pass1 = getpass(prompt)
            if pass1=='':
                return ''
            pass2 = getpass('Repeat: ')
            if pass1==pass2:
                pwd = pass1
            else:
                print _("Passwords don't match. Try again.")
        return pwd

    def editPassword(self, pwd):
        '''Prompt user for each field of the password. Respect fields\' type.'''

        input = {}

        for field, fieldinfo in pwd.fields_type_info:
            field_type = fieldinfo['type']

            new_value = ""
            if field_type == password.TYPE_STRING:
                new_value = self.inputString(_("Enter %s (\"%s\"): ") % (pwd.getFieldTitle(field), pwd[field]))
            elif field_type == password.TYPE_TEXT:
                new_value = self.inputText(_("Enter %s (\"%s\"): ") % (pwd.getFieldTitle(field), pwd[field]))
            elif field_type == password.TYPE_PASSWORD:
                new_value = self.inputPassword(_("Enter %s: ") % pwd.getFieldTitle(field))
            else:
                print _("Error. Type %s is unsupported yet. " \
                        "This field will retain an old value.") % field_type

            if new_value!="":
                input[field] = new_value

        try:
            pwd.update(input)
        except FigaroPasswordTooLongError:
            print _("WARNING! Your password is too long for Figaro Password Manager.")
            print _("Figaro Password Manager can handle only passwords shorter than 24 characters.")
            print _("""However, KedPM can store this password for you, but it
will break fpm compatibility. fpm will not be able to handle such
long password correctly.""")
            answer = raw_input(_("Do you still want to save your password? [Y/n]: "))
            if answer.lower().startswith('n'):
                raise KeyboardInterrupt
            pwd.store_long_password = 1
            pwd.update(input)

        #return pwd

    def tryToSave(self):

        self.modified = 1
        savemode = self.conf.options["save-mode"]
        if savemode == 'no':
            return
        answer = 'y'
        if self.conf.options["save-mode"] == "ask":
            answer = raw_input(_("Database was modified. Do you want to save it now? [Y/n]: "))
        if answer=='' or answer.lower().startswith('y'):
            self.do_save('')

    def complete_dirs(self, text, line, begidx, endidx):
        completing = line[:endidx].split(' ')[-1]
        base = completing[:completing.rfind('/')+1]
        abspath = self.getAbsolutePath(base)
        dirs = self.pdb.getTree().getTreeFromPath(abspath).getBranches()
        compl = []
        for dir in dirs.keys():
            if dir.startswith(text):
                compl.append(dir+'/')
        return compl

    def getEditorInput(self, content=''):
        """Fire up an editor and read user input from temporary file"""

        for name in ('VISUAL', 'EDITOR'):
            editor = os.environ.get(name)
            if editor:
                break
            else:
                if editor is None:
                    editor = 'vi'

        self.printMessage(_("running %s"), editor)
        # create temporary file
        handle, tmpfname = tempfile.mkstemp(prefix="kedpm_")
        tmpfile = open(tmpfname, 'w')
        tmpfile.write(content)
        tmpfile.close()
        
        os.system(editor + " " + tmpfname)

        tmpfile = open(tmpfname, 'r')
        text = tmpfile.read()
        tmpfile.close()
        os.remove(tmpfname)
        return text

    def getAbsolutePath(self, arg):
        """Return absolute path from potentially relative (cat)"""
        root = self.pdb.getTree()
        if not arg:
            return self.cwd
        if(arg[0] == '/'):
            path = root.normalizePath(arg.split('/'))
        else:
            path = root.normalizePath(self.cwd + arg.split('/'))
        return path

    def getTreeFromRelativePath(self, path):
        """Get tree object from given relative path and current working
        directory"""
        root = self.pdb.getTree()
        abspath = self.getAbsolutePath(path)
        return root.getTreeFromPath(abspath)

    def shiftArgv(self, argv, count = 1):
        if len(argv) > count:
            arg = " ".join(argv[count:])
        else:
            arg = ""
        
        return arg

    ##########################################
    # Command implementations below.         #

    def emptyline(self):
        pass

    def do_exit(self, arg):
        '''Quit KED Password Manager'''
        readline.write_history_file(self.histfile)
        if self.modified:
            self.tryToSave()
        self.printMessage(_("Exiting."))
        sys.exit(0)

    def do_EOF(self, arg):
        '''The same as 'exit' command'''
        print
        self.do_exit(arg)

    def do_quit(self, arg):
        '''The same as 'exit' command'''
        print
        self.do_exit(arg)


    def do_help(self, arg):
        '''Print help message'''
        Cmd.do_help(self, arg)

    def do_ls(self, arg):
        '''List available catalogs and passwords
Syntax:
    ls [<category>]
'''
        try:
            tree = self.getTreeFromRelativePath(arg)
        except KeyError:
            print _("ls: %s:  No such catalog") % arg
            return
        
        print _("=== Directories ===")
        for bname in tree.getBranches().keys():
            print bname+"/"
        print _("==== Passwords ====")
        self.listPasswords(tree.getNodes())

    def complete_ls(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_cd(self, arg):
        '''change directory (category)

Syntax:
    cd <category>
'''
        root = self.pdb.getTree()
        cdpath = self.getAbsolutePath(arg)
        try:
            newpath = root.getTreeFromPath(cdpath)
        except KeyError:
            print _("cd: %s: No such catalog") % arg
        else:
            self.cwd = cdpath
            self.updatePrompt()

    def complete_cd(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_pwd(self, arg):
        '''print name of current/working directory'''
        print '/'+'/'.join(self.cwd)

    def do_show(self, arg):
        '''display password information.

Syntax:
    show [-r] <regexp>

    -r - recursive search. search all subtree for matching passwords

This will display contents of a password item in current category or whole
subtree, if -r flag was specified. If several items matched by <regexp> and the
program has been configured to prompt for a single entry, a list
of them will be printed and you will be prompted to enter a number, pointing to
password you want to look at.  After receiving that number, KedPM will show you
the password. Otherwise all matching entries will be displayed'''

        argv = arg.split()
        tree = None
        if argv and argv[0] == '-r':
            tree = self.getCwd().flatten()
            arg = self.shiftArgv(argv)

        selected_passwords = self.getPasswords(arg, tree)
        for record in selected_passwords:
            if record:
                print "---------------------------------------"
                for key, fieldinfo in record.fields_type_info:
                    if record[key] == '':
                        continue
                    if key == 'password':
                        # this is a password, hide it with colors
                        hide = "\x1b[%02im\x1b[%02im" % (31, 41)
                        reset = "\x1b[%02im" % 0
                        print "%s: %s" % (fieldinfo['title'], hide + record[key] + reset)
                    else:
                        print "%s: %s" % (fieldinfo['title'], record[key])
                print "---------------------------------------"

    def do_edit(self, arg):
        '''edit password information.

Syntax:
    edit [-p] <regexp>

Prompts the user to edit a password item in the current category.
If <regexp> matches multiple items, the list of matches will be printed
and user is prompted to select one.

If the optional '-p' flag is specified, the password will be edited using the
the editor specified in the VISUAL or EDITOR environment variables, defaulting
to "vi" if neither is found. Otherwise the user is prompted to
edit each entry of the password entry on the command line.

'''
        argv = arg.split()

        if argv and argv[0] == '-p':
            use_editor = True 
            arg = self.shiftArgv(argv)
        else:
            use_editor = False

        selected_password = self.pickPassword(arg)
        
        if selected_password:
            try:
                if use_editor:
                    text = self.getEditorInput(selected_password.asEditText())
                    patterns = [selected_password.getEditPattern()]
                    chosendict = parser.parseMessage(text, patterns)
                    selected_password.update(chosendict)
                else:
                    self.editPassword(selected_password)

                self.tryToSave()
            except (KeyboardInterrupt, EOFError):
                self.printMessage(_("Cancelled"))
        else:
            self.printMessage(_("No password selected"))

    def do_new(self, arg):
        '''Add new password to current category. You will be prompted to enter
fields.

Syntax:
    new [-p | -t]

    -p - Get properties by parsing provided text. Will open default text editor
         for you to paste text in. Mutually exclusive with -t option. 
    -t - Display editor template in default text editor. Mutually exclusive with -p option.
'''
        new_pass = FigaroPassword() # FIXME: Password type shouldn't be hardcoded.
        argv = arg.split()

        if   "-p" in argv and "-t" in argv:
            print _("new: -p and -t arguments are mutually exclusive.")          
            print _("try 'help new' for more information")
        elif "-p" in argv:
            text = self.getEditorInput()
            choosendict = parser.parseMessage(text, self.conf.patterns)
            new_pass.update(choosendict)
        elif "-t" in argv:
            text = self.getEditorInput(new_pass.asEditText())
            choosendict = parser.parseMessage(text, [new_pass.getEditPattern()])
            new_pass.update(choosendict)

        try:
            self.editPassword(new_pass)
        except (KeyboardInterrupt, EOFError):
            self.printMessage(_("Cancelled"))
        else:
            tree = self.getCwd()
            tree.addNode(new_pass)
            self.tryToSave()

    def do_import(self, arg):
        '''Imports new password records into current category.
Syntax:
    import
    
    Get properties by parsing provided text. Will open default text editor
    for you to paste text in.
'''
        argv = arg.split()
        tree = self.getCwd()

        text = self.getEditorInput()
        for line in text.split("\n"):
            new_pass = FigaroPassword() # FIXME: Password type shouldn't be hardcoded.
            choosendict = parser.parseMessage(line, self.conf.patterns)
            new_pass.update(choosendict)
            tree.addNode(new_pass)

        self.tryToSave()

    def do_save(self, arg):
        '''Save current password tree'''
        sys.stdout.write(_("Saving..."))
        sys.stdout.flush()
        self.pdb.save()
        print "OK"
        self.modified = 0

    def do_mkdir(self, arg):
        '''create new category (directory)

Syntax:
    mkdir <category>

Creates new password category in current one.
'''
        if not arg:
            print _("mkdir: too few arguments")
            print _("try 'help mkdir' for more information")
            return

        pwd = self.getCwd()
        pwd.addBranch(arg.strip())

    def do_rename(self, arg):
        '''rename category

Syntax:
    rename <category> <new_name>
'''
        args = arg.split()
        if len(args) != 2:
            print '''Syntax:
    rename <category> <new_name>
'''
            return
        oldname = args[0]
        newname = args[1]
        try:
            self.pdb.getTree().renameBranch(self.cwd+[oldname], newname)
        except RenameError:
            print _("rename: category %s already exists") % newname
            return
        except KeyError:
            print _("rename: %s: no such category") % oldname
            return
        self.tryToSave()

    def complete_rename(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_passwd(self, arg):
        """Change master password for opened database
        
Syntax:
    password [new password]

If new password is not provided with command, you will be promted to enter new
one.
"""

        if not arg:
            # Password is not provided with command. Ask user for it
            pass1 = getpass(_("New password: "******"Repeat password: "******"Empty passwords are really insecure. You should " \
                        "create one.")
                return
            if pass1!=pass2:
                print _("Passwords don't match! Please repeat.")
                return
            new_pass = pass1
        else:
            new_pass = arg

        self.pdb.changePassword(new_pass)
        self.printMessage(_("Password changed."))
        

    def do_help(self, arg):
        """Print help topic"""
        argv = arg.split()
        if argv and argv[0] in ['set']:
            # Provide extended help
            help_def = getattr(self, "help_"+argv[0])
            if help_def:
                help_def(' '.join(argv[1:]))
            else:
                Cmd.do_help(self, arg)
        else:
            Cmd.do_help(self, arg)

    def do_set(self, arg):
        """Set KedPM options

Syntax:
    set                     -- show all options
    set <option>            -- show value of option
    set <option> = <value>  -- set value to option

For boolean values: 
1, 'on' or 'true' are synonyms for True and
0, 'off' or 'false' are synonyms for False.

Enter help set <option> for more info on a particular option.
"""

        opts = self.conf.options
        if not arg:
            # show all options
            for opt, value in opts.items():
                print "%s = %s" % (opt, value)
            return
        tokens = arg.split('=')
        opt_name = tokens[0].strip()
        try:
            opt_value = opts[opt_name]
        except KeyError:
            print _("set: no such option: [%s]") % arg
            return
        if len(tokens) == 1:
            # show value of option
            print "%s = %s" % (opt_name, opt_value)
        else:
            # set the value
            try:
                opt_value = ' '.join(tokens[1:])
                opts[opt_name] = opt_value.strip() 
            except OptionError, e:
                print "set: %s" % e
        # save confuguration file
        self.conf.save()
예제 #23
0
class PDBFigaroTestCase(unittest.TestCase):
    password = '******'
    def setUp(self):
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='test/fpm.sample')

    def test_create(self):
        assert isinstance(self.pdb, PDBFigaro)

    def test_tree(self):
        'Tree should be formed correctly'
        ptree = self.pdb.getTree()
        branches = ptree.getBranches()
        bkeys = branches.keys()
        bkeys.sort()
        self.assertEqual(bkeys, ['Kedder', 'Test'])
        tree_test = ptree['Test']
        assert tree_test
        self.assertEqual(len(tree_test.getNodes()), 2)
        tree_kedder = ptree['Kedder']
        assert tree_kedder
        self.assertEqual(len(tree_kedder.getNodes()), 1)

    def test_fpmCompatibilty(self):
        ptree = self.pdb.getTree()
        pwd = ptree['Test'].locate('test2')[0]
        self.assertEqual(pwd.default, 1)
        pwd = ptree['Test'].locate('test1')[0]
        self.assertEqual(pwd.default, 0)
        pwd = ptree['Kedder'].locate('kedder1')[0]
        self.assertEqual(pwd.launcher, 'ssh')
        
    def test_decryption(self):
        'Test how passwords are decrypted'
        tree_test = self.pdb.getTree()['Test']
        pwds = tree_test.locate('url')
        self.assertEqual(len(pwds), 2)
        pwd_test2 = tree_test.locate('test2')
        self.assertEqual(pwd_test2[0].password, 'test2 password')

    def test_encriptionUtils(self):
        str = "FIGARO ENCRYPTION TEST"
        noised = self.pdb._addNoise(str)
        self.assertEqual(len(noised) % Blowfish.block_size, 0)
        rotated = self.pdb._rotate(noised)
        self.assertEqual(len(rotated) % Blowfish.block_size, 0)

        hex = self.pdb._bin_to_hex(rotated)
        bin = self.pdb._hex_to_bin(hex)
        self.assertEqual(bin, rotated)
        
        unrotated = self.pdb._unrotate(bin)
        self.assertEqual(str, unrotated)

    def test_encription(self):
        strings = ["FIGARO ENCRYPTION TEST", "small"]
        for str in strings:
            encrypted = self.pdb.encrypt(str)
            decrypted = self.pdb.decrypt(encrypted)
            self.assertEqual(str, decrypted)

    def test_passwordEncryption(self):
        'Encrypted password should be 48 character long'
        pwdstr = 'shortpass'
        encrypted = self.pdb.encrypt(pwdstr, 1)
        self.assertEqual(len(encrypted), FPM_PASSWORD_LEN*2)
        decrypted = self.pdb.decrypt(encrypted)
        self.assertEqual(pwdstr, decrypted)

    def test_native(self):
        self.assertEqual(self.pdb.native, 0)

    def test_versions(self):
        self.assertEqual(self.pdb.FULL_VERSION, '00.53.00')
        self.assertEqual(self.pdb.MIN_VERSION, '00.50.00')
        self.assertEqual(self.pdb.DISPLAY_VERSION, '0.53')

    def test_changePassword(self):
        new_pwd = 'new_password'
        # Copy test database to new location
        shutil.copyfile('test/fpm.sample', 'test/fpm.passwdtest')
        try:
            self.pdb.open(self.password, fname='test/fpm.passwdtest')
            
            self.pdb.changePassword(new_pwd)

            # Try to open database with old password
            self.assertRaises(WrongPassword, self.pdb.open, self.password,
                              fname='test/fpm.passwdtest')

            self.pdb.open(new_pwd, fname='test/fpm.passwdtest')
        finally:
            os.unlink('test/fpm.passwdtest')
예제 #24
0
class SavedFigaroTestCase(PDBFigaroTestCase):
    def setUp(self):
        pdb = PDBFigaro()
        pdb.open(self.password, fname='test/fpm.sample')
        pdb.save(fname="fpm.saved")
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='fpm.saved')

    def tearDown(self):
        os.remove('fpm.saved')

    def test_catlessPassword(self):
        'Saving and loading password without category'

        tree = self.pdb.getTree()
        pwd = FigaroPassword(title='CLHost', password='******')
        tree.addNode(pwd)
        self.pdb.save(fname='fpm.saved')
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='fpm.saved')
        tlnodes = self.pdb.getTree().getNodes()
        self.assertEqual(len(tlnodes), 1)
        self.assertEqual(tlnodes[0].title, 'CLHost')
        self.assertEqual(tlnodes[0]['password'], 'CLPass')
        self.assertEqual(tlnodes[0]['url'], '')

    def test_native(self):
        self.assertEqual(self.pdb.native, 1)

    def test_tooLongPassword(self):
        pwd = FigaroPassword()
        longpswd = "1234567890"*3;
        self.assertRaises(FigaroPasswordTooLongError, pwd.__setitem__,
            'password', longpswd)
        self.assertRaises(FigaroPasswordTooLongError, pwd.update,
            {'password': longpswd})
        pwd.store_long_password = 1
        pwd['title'] = "Long password"
        pwd['password'] = longpswd
        self.pdb.getTree()['Test'].addNode(pwd)
        self.pdb.save(fname="fpm.saved")
        self.pdb = PDBFigaro()
        self.pdb.open(self.password, fname='fpm.saved')
        saved_pwd = self.pdb.getTree()['Test'].locate('Long password')[0]
        self.assertEqual(saved_pwd['password'], longpswd)
예제 #25
0
class Application(Cmd, Frontend):
    PS1 = "kedpm:%s> "  # prompt template
    cwd = []  # Current working directory
    intro = _("""Ked Password Manager is ready for operation.
try 'help' for brief description of available commands.
""")

    modified = 0
    histfile = os.path.join(expanduser('~'), '.kedpm', 'history')

    # console supports escape sequences?
    use_console_escapes = False

    def __init__(self):
        Cmd.__init__(self)
        if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
            if hasattr(ctypes, 'windll'):
                # skip windows for now, but see
                # http://www.burgaud.com/bring-colors-to-the-windows-console-with-python/
                # with a possible solution here: http://pypi.python.org/pypi/colorama
                pass
            else:
                self.use_console_escapes = True

        if self.use_console_escapes:
            self.PS1 = "\x1b[1m" + self.PS1 + "\x1b[0m"  # bold prompt

    def printMessage(self, message, *vars):
        if self.conf.options['verbose']:
            print(message) % vars

    def openDatabase(self):
        ''' Open database amd prompt for password if necessary '''

        self.pdb = PDBFigaro(
            filename=expanduser(self.conf.options['fpm-database']))
        password = ""
        self.printMessage(_("Ked Password Manager (version %s)"), __version__)

        while 1:
            try:
                self.pdb.open(password)
                break
            except WrongPassword:
                if password:
                    print _("Error! Wrong password.")
                else:
                    print _(
                        "Provide password to access the database (Ctrl-C to exit)"
                    )
                password = getpass(_("Password: "******"Password accepted."))
        print

    def createNewDatabase(self):
        'Create new password database and return password for created database'
        print _("Creating new password database.")
        pass1 = pass2 = ""
        while pass1 != pass2 or pass1 == "":
            pass1 = getpass(_("Provide password: "******"Repeat password: "******"Empty passwords are really insecure. You should " \
                        "create one.")
            if pass1 != pass2:
                print _("Passwords don't match! Please repeat.")

        self.pdb.create(pass1)
        return pass1

    def updatePrompt(self):
        self.prompt = self.PS1 % ('/' + '/'.join(self.cwd))

    def getCwd(self):
        'Return current working password tree instance'
        return self.pdb.getTree().getTreeFromPath(self.cwd)

    def listPasswords(self, passwords, show_numbers=0):
        '''display given passwords in nicely formed table'''
        # we assume that all passwords in list have the same fields
        # the same as in first one
        if not passwords:
            return
        prot = passwords[0]
        lengths = show_numbers and {'nr': 3} or {}
        headers = show_numbers and ['Nr'] or []
        fstr = show_numbers and "%%%ds " or ""
        listable = prot.getFieldsOfType([password.TYPE_STRING])
        # determine maximum space needed by each column
        for fld in listable:
            ftitle = prot.getFieldTitle(fld)
            lengths[fld] = len(ftitle)
            fstr = fstr + "%%%ds "
            headers.append(ftitle)

        ptuples = []
        num = 1
        for pwd in passwords:
            ptup = []
            if show_numbers:
                ptup.append("%d)" % num)
            for fld in listable:
                ptup.append(getattr(pwd, fld))
                newlen = len(getattr(pwd, fld))
                if newlen > lengths[fld]:
                    lengths[fld] = newlen
            ptuples.append(tuple(ptup))
            num = num + 1
        # form format string
        if show_numbers:
            listable = ['nr'] + listable
        fstr = fstr % tuple([lengths[x] + 1 for x in listable])
        print fstr % tuple(headers)
        print fstr % tuple(["=" * lengths[x] for x in listable])
        for ptup in ptuples:
            print fstr % ptup

    def filterPasswords(self, regexp, tree=None):
        '''Returns a list of passwords, filtered by REGEXP'''
        if tree is None:
            tree = self.getCwd()

        return (tree.locate(regexp))

    def getPasswords(self, regexp, tree=None):
        '''Returns a list of passwords, filtered by REGEXP.
        Calls pickPassword if program has been configured to force
        single selection'''

        if self.conf.options['force-single']:
            return [self.pickPassword(regexp, tree)]
        else:
            return (self.filterPasswords(regexp, tree))

    def pickPassword(self, regexp, tree=None):
        '''Prompt user to pick one password from given password tree. Password
        tree, provided by "tree" argument filtered using "regexp".

        If resulted list contains only one password, return it without
        prompting. If no passwords were located, or user desides to cancel
        operation, return None'''

        passwords = self.filterPasswords(regexp, tree)

        if not passwords:
            self.printMessage(_("No passwords matching \"%s\" were found"),
                              regexp)
            return None

        if len(passwords) > 1:
            self.listPasswords(passwords, 1)
            print _("Enter number. Enter 0 to cancel.")
            try:
                showstr = raw_input(_('show: '))
            except (KeyboardInterrupt, EOFError):
                # user has cancelled selection
                showstr = "0"
            try:
                shownr = int(showstr)
                if not shownr:
                    return None
                selected_password = passwords[shownr - 1]
            except (ValueError, IndexError):
                return None
        else:
            selected_password = passwords[0]
        return selected_password

    def inputString(self, prompt):
        '''Input string from user'''
        input = raw_input(prompt)
        return input

    def inputText(self, prompt):
        '''Input long text from user'''
        return self.inputString(prompt)

    def inputPassword(self, prompt):
        '''Input long text from user'''
        pwd = None
        while pwd is None:
            pass1 = getpass(prompt)
            if pass1 == '':
                return ''
            pass2 = getpass('Repeat: ')
            if pass1 == pass2:
                pwd = pass1
            else:
                print _("Passwords don't match. Try again.")
        return pwd

    def editPassword(self, pwd):
        '''Prompt user for each field of the password. Respect fields\' type.'''

        input = {}

        for field, fieldinfo in pwd.fields_type_info:
            field_type = fieldinfo['type']

            new_value = ""
            if field_type == password.TYPE_STRING:
                new_value = self.inputString(
                    _("Enter %s (\"%s\"): ") %
                    (pwd.getFieldTitle(field), pwd[field]))
            elif field_type == password.TYPE_TEXT:
                new_value = self.inputText(
                    _("Enter %s (\"%s\"): ") %
                    (pwd.getFieldTitle(field), pwd[field]))
            elif field_type == password.TYPE_PASSWORD:
                new_value = self.inputPassword(
                    _("Enter %s: ") % pwd.getFieldTitle(field))
            else:
                print _("Error. Type %s is unsupported yet. " \
                        "This field will retain an old value.") % field_type

            if new_value != "":
                input[field] = new_value

        try:
            pwd.update(input)
        except FigaroPasswordTooLongError:
            print _(
                "WARNING! Your password is too long for Figaro Password Manager."
            )
            print _(
                "Figaro Password Manager can handle only passwords shorter than 24 characters."
            )
            print _("""However, KedPM can store this password for you, but it
will break fpm compatibility. fpm will not be able to handle such
long password correctly.""")
            answer = raw_input(
                _("Do you still want to save your password? [Y/n]: "))
            if answer.lower().startswith('n'):
                raise KeyboardInterrupt
            pwd.store_long_password = 1
            pwd.update(input)

        #return pwd

    def tryToSave(self):

        self.modified = 1
        savemode = self.conf.options["save-mode"]
        if savemode == 'no':
            return
        answer = 'y'
        if self.conf.options["save-mode"] == "ask":
            answer = raw_input(
                _("Database was modified. Do you want to save it now? [Y/n]: ")
            )
        if answer == '' or answer.lower().startswith('y'):
            self.do_save('')

    def complete_dirs(self, text, line, begidx, endidx):
        completing = line[:endidx].split(' ')[-1]
        base = completing[:completing.rfind('/') + 1]
        abspath = self.getAbsolutePath(base)
        dirs = self.pdb.getTree().getTreeFromPath(abspath).getBranches()
        compl = []
        for dir in dirs.keys():
            if dir.startswith(text):
                compl.append(dir + '/')
        return compl

    def getEditorInput(self, content=''):
        """Fire up an editor and read user input from temporary file"""

        for name in ('VISUAL', 'EDITOR'):
            editor = os.environ.get(name)
            if editor:
                break
            else:
                if editor is None:
                    editor = 'vi'

        self.printMessage(_("running %s"), editor)
        # create temporary file
        handle, tmpfname = tempfile.mkstemp(prefix="kedpm_")
        tmpfile = open(tmpfname, 'w')
        tmpfile.write(content)
        tmpfile.close()

        os.system(editor + " " + tmpfname)

        tmpfile = open(tmpfname, 'r')
        text = tmpfile.read()
        tmpfile.close()
        os.remove(tmpfname)
        return text

    def getAbsolutePath(self, arg):
        """Return absolute path from potentially relative (cat)"""
        root = self.pdb.getTree()
        if not arg:
            return self.cwd
        if (arg[0] == '/'):
            path = root.normalizePath(arg.split('/'))
        else:
            path = root.normalizePath(self.cwd + arg.split('/'))
        return path

    def getTreeFromRelativePath(self, path):
        """Get tree object from given relative path and current working
        directory"""
        root = self.pdb.getTree()
        abspath = self.getAbsolutePath(path)
        return root.getTreeFromPath(abspath)

    def shiftArgv(self, argv, count=1):
        if len(argv) > count:
            arg = " ".join(argv[count:])
        else:
            arg = ""

        return arg

    def printRecord(self, record):
        obscure_passwords = self.conf.options['obscure-passwords']
        for key, fieldinfo in record.fields_type_info:
            if record[key] == '':
                continue
            if fieldinfo[
                    'type'] == password.TYPE_PASSWORD and obscure_passwords and self.use_console_escapes:
                print "%s: \x1b[31m\x1b[41m%s\x1b[00m" % (fieldinfo['title'],
                                                          record[key])
            else:
                print "%s: %s" % (fieldinfo['title'], record[key])

    ##########################################
    # Command implementations below.         #

    def emptyline(self):
        pass

    def do_exit(self, arg):
        '''Quit KED Password Manager'''
        readline.write_history_file(self.histfile)
        if self.modified:
            self.tryToSave()
        self.printMessage(_("Exiting."))
        sys.exit(0)

    def do_EOF(self, arg):
        '''The same as 'exit' command'''
        print
        self.do_exit(arg)

    def do_quit(self, arg):
        '''The same as 'exit' command'''
        print
        self.do_exit(arg)

    def do_help(self, arg):
        '''Print help message'''
        Cmd.do_help(self, arg)

    def do_ls(self, arg):
        '''List available catalogs and passwords
Syntax:
    ls [<category>]
'''
        try:
            tree = self.getTreeFromRelativePath(arg)
        except KeyError:
            print _("ls: %s:  No such catalog") % arg
            return

        print _("=== Directories ===")
        for bname in tree.getBranches().keys():
            print bname + "/"
        print _("==== Passwords ====")
        self.listPasswords(tree.getNodes())

    def complete_ls(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_cd(self, arg):
        '''change directory (category)

Syntax:
    cd <category>
'''
        root = self.pdb.getTree()
        cdpath = self.getAbsolutePath(arg)
        try:
            newpath = root.getTreeFromPath(cdpath)
        except KeyError:
            print _("cd: %s: No such catalog") % arg
        else:
            self.cwd = cdpath
            self.updatePrompt()

    def complete_cd(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_pwd(self, arg):
        '''print name of current/working directory'''
        print '/' + '/'.join(self.cwd)

    def do_show(self, arg):
        '''display password information.

Syntax:
    show [-r] <regexp>

    -r - recursive search. search all subtree for matching passwords

This will display contents of a password item in current category or whole
subtree, if -r flag was specified. If several items matched by <regexp> and the
program has been configured to prompt for a single entry, a list
of them will be printed and you will be prompted to enter a number, pointing to
password you want to look at.  After receiving that number, KedPM will show you
the password. Otherwise all matching entries will be displayed'''

        argv = arg.split()
        tree = None
        if argv and argv[0] == '-r':
            tree = self.getCwd().flatten()
            arg = self.shiftArgv(argv)

        selected_passwords = self.getPasswords(arg, tree)

        for record in selected_passwords:
            if record:
                print "---------------------------------------"
                self.printRecord(record)
                print "---------------------------------------"

    def do_edit(self, arg):
        '''edit password information.

Syntax:
    edit [-p] <regexp>

Prompts the user to edit a password item in the current category.
If <regexp> matches multiple items, the list of matches will be printed
and user is prompted to select one.

If the optional '-p' flag is specified or the 'force-editor' config option is set,
the password will be edited using the editor specified in the VISUAL or EDITOR
environment variables, defaulting to "vi" if neither is found.
Otherwise the user is prompted to edit each entry of the password entry on the command line.

'''
        argv = arg.split()

        if argv and argv[0] == '-p':
            use_editor = True
            arg = self.shiftArgv(argv)
        elif self.conf.options['force-editor']:
            use_editor = True
        else:
            use_editor = False

        selected_password = self.pickPassword(arg)

        if selected_password:
            try:
                if use_editor:
                    text = self.getEditorInput(selected_password.asEditText())
                    patterns = [selected_password.getEditPattern()]
                    chosendict = parser.parseMessage(text, patterns)
                    selected_password.update(chosendict)
                else:
                    self.editPassword(selected_password)

                self.tryToSave()
            except (KeyboardInterrupt, EOFError):
                self.printMessage(_("Cancelled"))
        else:
            self.printMessage(_("No password selected"))

    def do_new(self, arg):
        '''Add new password to current category. You will be prompted to enter
fields.

Syntax:
    new [-p | -t]

    -p - Get properties by parsing provided text. Will open default text editor
         for you to paste text in. Mutually exclusive with -t option. 
    -t - Display editor template in default text editor. Mutually exclusive with -p option.

    If the config option 'force-editor is set, this command defaults to the -t option when no options are provided.
'''
        new_pass = FigaroPassword(
        )  # FIXME: Password type shouldn't be hardcoded.

        argv = arg.split()

        use_visual_editor = len(
            argv) == 0 and self.conf.options["force-editor"]

        if "-p" in argv and "-t" in argv:
            print _("new: -p and -t arguments are mutually exclusive.")
            print _("try 'help new' for more information")
        elif "-p" in argv:
            text = self.getEditorInput()
            choosendict = parser.parseMessage(text, self.conf.patterns)
            new_pass.update(choosendict)
        elif "-t" in argv or use_visual_editor:
            text = self.getEditorInput(new_pass.asEditText())
            choosendict = parser.parseMessage(text,
                                              [new_pass.getEditPattern()])
            new_pass.update(choosendict)

        try:
            if not use_visual_editor:
                self.editPassword(new_pass)
        except (KeyboardInterrupt, EOFError):
            self.printMessage(_("Cancelled"))
        else:
            tree = self.getCwd()
            tree.addNode(new_pass)
            self.tryToSave()

    def do_import(self, arg):
        '''Imports new password records into current category.
Syntax:
    import
    
    Get properties by parsing provided text. Will open default text editor
    for you to paste text in.
'''
        argv = arg.split()
        tree = self.getCwd()

        text = self.getEditorInput()
        for line in [x for x in text.splitlines() if x]:
            new_pass = FigaroPassword(
            )  # FIXME: Password type shouldn't be hardcoded.
            choosendict = parser.parseMessage(line, self.conf.patterns)
            new_pass.update(choosendict)
            tree.addNode(new_pass)

        self.tryToSave()

    def do_save(self, arg):
        '''Save current password tree'''
        sys.stdout.write(_("Saving..."))
        sys.stdout.flush()
        self.pdb.save()
        print "OK"
        self.modified = 0

    def do_mkdir(self, arg):
        '''create new category (directory)

Syntax:
    mkdir <category>

Creates new password category in current one.
'''
        if not arg:
            print _("mkdir: too few arguments")
            print _("try 'help mkdir' for more information")
            return

        pwd = self.getCwd()
        pwd.addBranch(arg.strip())

    def do_rename(self, arg):
        '''rename category

Syntax:
    rename <category> <new_name>
'''
        args = arg.split()
        if len(args) != 2:
            print '''Syntax:
    rename <category> <new_name>
'''
            return
        oldname = args[0]
        newname = args[1]
        try:
            self.pdb.getTree().renameBranch(self.cwd + [oldname], newname)
        except RenameError:
            print _("rename: category %s already exists") % newname
            return
        except KeyError:
            print _("rename: %s: no such category") % oldname
            return
        self.tryToSave()

    def complete_rename(self, text, line, begidx, endidx):
        return self.complete_dirs(text, line, begidx, endidx)

    def do_passwd(self, arg):
        """Change master password for opened database
        
Syntax:
    password [new password]

If new password is not provided with command, you will be promted to enter new
one.
"""

        if not arg:
            # Password is not provided with command. Ask user for it
            pass1 = getpass(_("New password: "******"Repeat password: "******"Empty passwords are really insecure. You should " \
                        "create one.")
                return
            if pass1 != pass2:
                print _("Passwords don't match! Please repeat.")
                return
            new_pass = pass1
        else:
            new_pass = arg

        self.pdb.changePassword(new_pass)
        self.printMessage(_("Password changed."))

    def do_help(self, arg):
        """Print help topic"""
        argv = arg.split()
        if argv and argv[0] in ['set']:
            # Provide extended help
            help_def = getattr(self, "help_" + argv[0])
            if help_def:
                help_def(' '.join(argv[1:]))
            else:
                Cmd.do_help(self, arg)
        else:
            Cmd.do_help(self, arg)

    def do_set(self, arg):
        """Set KedPM options

Syntax:
    set                     -- show all options
    set <option>            -- show value of option
    set <option> = <value>  -- set value to option

For boolean values: 
1, 'on' or 'true' are synonyms for True and
0, 'off' or 'false' are synonyms for False.

Enter help set <option> for more info on a particular option.
"""

        opts = self.conf.options
        if not arg:
            # show all options
            for opt, value in opts.items():
                print "%s = %s" % (opt, value)
            return
        tokens = arg.split('=')
        opt_name = tokens[0].strip()
        try:
            opt_value = opts[opt_name]
        except KeyError:
            print _("set: no such option: [%s]") % arg
            return
        if len(tokens) == 1:
            # show value of option
            print "%s = %s" % (opt_name, opt_value)
        else:
            # set the value
            try:
                opt_value = ' '.join(tokens[1:])
                opts[opt_name] = opt_value.strip()
            except OptionError, e:
                print "set: %s" % e
        # save configuration file
        self.conf.save()