Merge de fichiers KeepassX ou Keepass 1.x (.kdb)

Posted by & filed under Système.

J’ai eu le besoin de merger plusieurs fichiers base de données de Keepass. Si vous ne connaissez pas cet excellent gestionnaire de mot de passe allez faire un tour ici ! Son pendant Linux/Mac OS X est KeepassX compatible avec la branche 1.x de Keepass.

Ce script (rapidement fait) prend plusieurs fichiers d’export XML Keepass (Menu -> Exporter sous -> XML)
et les regroupent sous un fichier. Il prend l’entrée la plus récente en cas de conflit. Il affichera les conflits en sortie console.

La source est disponible ici pour ceux qui veulent la regarder et je l’ai également compilé ici pour ceux qui veulent faire un merge rapide.

Télécharger KeeMerge

Pour l’exécutable, le mettre dans le même dossier que les exports xml puis le lancer.

Il faut ensuite importer le merge.xml dans Keepass. Créer une nouvelle base puis Menu-> Importer -> XML Keepass 1.x.

from lxml import etree
import datetime, glob

class Password(object):
    def __init__(self, node):
        date= node.find("lastmodtime").text
        self.lastmodtime= datetime.datetime.strptime(date,"%Y-%m-%dT%H:%M:%S")
        self.uuid= node.find("uuid").text
        self.username= node.find("username").text
        self.password= node.find("password").text
        self.title= node.find("title").text
        self.node= node

    def __hash__(self):
        return hash(str(self.title)+str(self.lastmodtime)+str(self.uuid))

    def __eq__(self,other):
        return self.uuid==other.uuid and self.username==other.username and self.password==other.password and self.title==self.title


    def __str__(self):

        s = "%s (%s): [%s|%s] - %s" % (self.title,self.uuid,self.username,self.password,self.lastmodtime)
        return s

    def __repr__(self):

        s = "%s (%s): [%s|%s] - %s" % (self.title,self.uuid,self.username,self.password,self.lastmodtime)
        return s


def getTree(file):
    """
    Give the root and tree of the XML file
    """

    parser = etree.XMLParser(strip_cdata=False)
    tree = etree.parse(file, parser)
    root= tree.getroot()
    return tree ,root

def getpasswords(file):
    """
    Parse and return all the password of an xml exported by Keepass
    """

    tree, root = getTree(file)
    elements= []
    for e in root:
        elements.append(Password(e))
    return elements

def main():
    # give me your xmls
    xmls= glob.glob("*.xml")

    # a dict uniq by the uuid of the node
    uniquepasswords= dict()

    # all your passwords are belong to us
    passwords=[pwd for f in xmls for pwd in getpasswords(f)]

    # let's go marco
    for pwd in passwords:
        # do I know you ?
        if pwd.uuid in uniquepasswords:
            challenger= pwd
            champion= uniquepasswords[pwd.uuid]
            # If exact match, skips
            if challenger != champion:
                # challenger modified recently ?
                if challenger.lastmodtime > champion.lastmodtime:
                    print "####"
                    print "%s" % (challenger)
                    print "more recent than"
                    print "%s" % (champion)
                    print "Challenger wins"
                    uniquepasswords[pwd.uuid]= challenger
                elif challenger.lastmodtime < champion.lastmodtime:
                    print "####"
                    print "%s" % (challenger)
                    print "older than"
                    print "%s" % (champion)
                    print "Garbage"
        else:
            # hello you
            uniquepasswords[pwd.uuid]= pwd

    # Append xml nodes to root
    root= etree.Element("pwlist")
    for pwd in uniquepasswords.values():
        root.append(pwd.node)

    # Write to file
    et = etree.ElementTree(root)
    et.write("merge.xml", pretty_print=True, xml_declaration=True, encoding='utf-8', standalone='yes')

if __name__ == "__main__":
    main()

Leave a Reply