class AlteraKeePass: def __init__(self, arquivo_kdbx, senha): self.kp = PyKeePass(arquivo_kdbx, password=senha) def cria_entrada(self, grupo, nome, usuario, senha, url, icone, ip, porta): self.group = self.kp.find_groups(name=grupo, first=True) self.kp.add_entry(self.group, nome, usuario, senha, url=url, icon=icone) self.entry = self.kp.find_entries(title=nome, first=True) self.entry.set_custom_property('IP', ip) self.entry.set_custom_property('PORT', porta) self.kp.save()
def write_entry(kdbx_file, kdbx_password, group_path, entry_title, entry_username, entry_password, entry_url, entry_notes, entry_tags, kdbx_keyfile=None, force_creation=False, outfile=None): logging.info('Attempt to write entry "{}: {}:{}" to {}'.format( entry_title, entry_username, entry_password, group_path)) samba_db = False if kdbx_file.startswith('smb://'): samba_db = True smb_kdbx_file = smb_retrieve(kdbx_file) kp = PyKeePass(smb_kdbx_file if samba_db else kdbx_file, password=kdbx_password, keyfile=kdbx_keyfile) dest_group = kp.find_groups_by_path(group_path, first=True) kp.add_entry(destination_group=dest_group, title=entry_title, username=entry_username, password=entry_password, url=entry_url, notes=entry_notes, tags=entry_tags, force_creation=force_creation) if outfile: if outfile.startswith('smb://'): file_written = kp.save() smb_send(file_written.name, outfile) logging.info('Sent database file to {}'.format(outfile)) else: file_written = kp.save(kdbx_file) logging.info('KeePass DB written to {}'.format(file_written.name)) else: if samba_db: file_written = kp.save() smb_send(file_written.name, kdbx_file) logging.info('Sent database file to {}'.format(kdbx_file)) else: file_written = kp.save(kdbx_file) logging.info('KeePass DB written to {}'.format(file_written.name))
def keepassxc_new_ssh_key(db_name, db_password, passphrase="", title="", username=""): kp = PyKeePass(db_name, password=db_password) keyname = title.replace(" ", "-") ssh_group = kp.find_groups(name="SSH", first=True) if not ssh_group: ssh_group = kp.add_group(kp.root_group, "SSH") entry = kp.add_entry(ssh_group, title=title, username=username, password=passphrase) pub, priv = generate_keys(passphrase) entry_ssh_settings = f"""\ <?xml version="1.0" encoding="UTF-16"?> <EntrySettings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <AllowUseOfSshKey>true</AllowUseOfSshKey> <AddAtDatabaseOpen>true</AddAtDatabaseOpen> <RemoveAtDatabaseClose>true</RemoveAtDatabaseClose> <UseConfirmConstraintWhenAdding>false</UseConfirmConstraintWhenAdding> <UseLifetimeConstraintWhenAdding>true</UseLifetimeConstraintWhenAdding> <LifetimeConstraintDuration>600</LifetimeConstraintDuration> <Location> <SelectedType>attachment</SelectedType> <AttachmentName>{keyname}</AttachmentName> <SaveAttachmentToTempFile>false</SaveAttachmentToTempFile> </Location> </EntrySettings>""" attach(kp, entry, "KeeAgent.settings", entry_ssh_settings, encoding="utf-16") attach(kp, entry, keyname, priv) attach(kp, entry, f"{keyname}.pub", pub) kp.save()
def bitwarden_to_keepass(args): try: kp = PyKeePass(args.database_path, password=args.database_password, keyfile=args.database_keyfile) except FileNotFoundError: logging.info('KeePass database does not exist, creating a new one.') kp = create_database(args.database_path, password=args.database_password, keyfile=args.database_keyfile) except CredentialsError as e: logging.error(f'Wrong password for KeePass database: {e}') return folders = subprocess.check_output( f'{quote(args.bw_path)} list folders --session {quote(args.bw_session)}', shell=True, encoding='utf8') folders = json.loads(folders) groups = {} for folder in folders: groups[folder['id']] = kp.add_group(kp.root_group, folder['name']) logging.info(f'Folders done ({len(groups)}).') items = subprocess.check_output( f'{quote(args.bw_path)} list items --session {quote(args.bw_session)}', shell=True, encoding='utf8') items = json.loads(items) logging.info(f'Starting to process {len(items)} items.') for item in items: if item['type'] in [ItemTypes.CARD, ItemTypes.IDENTITY]: logging.warning( f'Skipping credit card or identity item "{item["name"]}".') continue bw_item = Item(item) is_duplicate_title = False try: while True: entry_title = bw_item.get_name( ) if not is_duplicate_title else '{name} - ({item_id}'.format( name=bw_item.get_name(), item_id=bw_item.get_id()) try: entry = kp.add_entry( destination_group=groups[bw_item.get_folder_id()], title=entry_title, username=bw_item.get_username(), password=bw_item.get_password(), notes=bw_item.get_notes()) break except Exception as e: if 'already exists' in str(e): is_duplicate_title = True continue raise totp_secret, totp_settings = bw_item.get_totp() if totp_secret and totp_settings: entry.set_custom_property('TOTP Seed', totp_secret) entry.set_custom_property('TOTP Settings', totp_settings) for uri in bw_item.get_uris(): entry.url = uri['uri'] break # todo append additional uris to notes? for field in bw_item.get_custom_fields(): entry.set_custom_property(field['name'], field['value']) for attachment in bw_item.get_attachments(): attachment_tmp_path = f'/tmp/attachment/{attachment["fileName"]}' attachment_path = subprocess.check_output( f'{quote(args.bw_path)} get attachment' f' --raw {quote(attachment["id"])} ' f'--itemid {quote(bw_item.get_id())} ' f'--output {quote(attachment_tmp_path)} --session {quote(args.bw_session)}', shell=True, encoding='utf8').rstrip() attachment_id = kp.add_binary( open(attachment_path, 'rb').read()) entry.add_attachment(attachment_id, attachment['fileName']) os.remove(attachment_path) except Exception as e: logging.warning( f'Skipping item named "{item["name"]}" because of this error: {repr(e)}' ) continue logging.info('Saving changes to KeePass database.') kp.save() logging.info('Export completed.')
def bitwarden_to_keepass(args): try: kp = PyKeePass(args.database_path, password=args.database_password, keyfile=args.database_keyfile) except FileNotFoundError: logging.info('KeePass database does not exist, creating a new one.') kp = create_database(args.database_path, password=args.database_password, keyfile=args.database_keyfile) except CredentialsIntegrityError as e: logging.error(f'Wrong password for KeePass database: {e}') return folders = subprocess.check_output( f'{quote(args.bw_path)} list folders --session {quote(args.bw_session)}', shell=True, encoding='utf8') folders = json.loads(folders) groups = {} for folder in folders: groups[folder['id']] = kp.add_group(kp.root_group, folder['name']) logging.info(f'Folders done ({len(groups)}).') items = subprocess.check_output( f'{quote(args.bw_path)} list items --session {quote(args.bw_session)}', shell=True, encoding='utf8') items = json.loads(items) logging.info(f'Starting to process {len(items)} items.') for item in items: if item['type'] == ItemTypes.CARD: logging.warning(f'Skipping credit card item "{item["name"]}".') continue bw_item = Item(item) e = kp.add_entry(groups[bw_item.get_folder_id()], title=bw_item.get_name(), username=bw_item.get_username(), password=bw_item.get_password(), notes=bw_item.get_notes()) totp_secret, totp_settings = bw_item.get_totp() if totp_secret and totp_settings: e.set_custom_property('TOTP Seed', totp_secret) e.set_custom_property('TOTP Settings', totp_settings) for uri in bw_item.get_uris(): e.url = uri['uri'] break # todo append additional uris to notes? for field in bw_item.get_custom_fields(): e.set_custom_property(str(field['name']), field['value']) for attachment in bw_item.get_attachments(): attachment_tmp_path = f'./attachment_tmp/{attachment["fileName"]}' attachment_path = subprocess.check_output( f'{quote(args.bw_path)} get attachment' f' --raw {quote(attachment["id"])} ' f'--itemid {quote(bw_item.get_id())} ' f'--output {quote(attachment_tmp_path)} --session {quote(args.bw_session)}', shell=True, encoding='utf8').rstrip() attachment_id = kp.add_binary(open(attachment_path, 'rb').read()) e.add_attachment(attachment_id, attachment['fileName']) os.remove(attachment_path) logging.info('Saving changes to KeePass database.') kp.save() logging.info('Export completed.')
# load database kp = PyKeePass(dbfile, password=os.environ['PASSWORD']) group = kp.find_groups(name=groupname, first=True) count = 1 with open(tempfile, "r") as rd: # Read lines in loop for line in rd: # All lines (besides the last) will include newline, so strip it data = line.strip() data = data.split(';;;') if len(data) != 4: print("Fehlerhafte Eingabe! Zeile: " + str(count)) print(data) sys.exit() # Variablen setzen title = str(data[0]) user = str(data[1]) password = str(data[2]) notes = str(data[3]) # Eintrag in KeePassDB schreiben (temporär, nicht gespeichert) kp.add_entry(group, title, user, password, None, notes) count += 1 kp.save() sys.exit()
def main(input_file, target_file, password): def matches(query, card): try: if query is None: return True query = query.lower() if query in card['@title'].lower(): return True # for field in card['field']: # if query in field['@name'].lower(): # return True # if '#text' in field and query in field['#text'].lower(): # return True except: pass return False if len(sys.argv) == 2: search = sys.argv[1] else: search = None db = Decrypter(input_file, password) kp = PyKeePass('New3.kdbx', password='******') kp.root_group.name = "test_title" kp.password = password entries = [] try: xml = db.decrypt() xml = parseString(xml) except: print("cannot decrypt, maybe wrong password?") sys.exit(1) for node in xml.getElementsByTagName('card'): is_template = False if node.hasAttribute('template'): is_template = (node.attributes['template'].value == 'true') if not is_template: entries.append(Entry(node)) duplicate_check = set() for entry in entries: password = "" login = "" url = "" notes = "" # extract all types the fields and their value type_value = { entry.fields[x].type: entry.fields[x].value for x in entry.fields } # map the type of the field in safeincloud to the field type in pykeepass map_types = { "password": "******", "login": "******", "website": "url", "notes": "notes" } # username and password are required fields mapped_type_value = {"username": "", "password": ""} # add all other fields with the new names mapped_type_value.update({ map_types[x]: type_value[x] for x in type_value if x in map_types }) # ensure that the title is unique title = entry.title if title in duplicate_check: i = 2 while title + str(i) in duplicate_check: i = i + 1 title = title + str(i) assert title not in duplicate_check # add entry to keepass file kp.add_entry(kp.root_group, title, **mapped_type_value) # add title to duplicate check duplicate_check.add(title) kp.save(target_file) print("file successful mapped")
from pykeepass import PyKeePass # load database kp = PyKeePass(r'C:\Users\danie\Git\ansible\ansible_home\test_db.kdbx', password='******') group = kp.groups #kp.add_entry(destination_group='test_db',title='testentry', username='******', password='******') #kp.add_group(kp.root_group, 'test_addgroup') kp.add_entry(destination_group=group[2], title='testentry', username='******', password='******') kp.save() print('ende')
class DatabaseManager: logging_manager = NotImplemented db = NotImplemented database_path = "" password_try = "" password_check = "" password = "" keyfile_hash = NotImplemented changes = False save_running = False scheduled_saves = 0 database_file_descriptor = NotImplemented def __init__(self, database_path, password=None, keyfile=None, logging_manager=None): self.logging_manager = logging_manager self.db = PyKeePass(database_path, password, keyfile) self.database_path = database_path self.database_file_descriptor = Gio.File.new_for_path(database_path) self.password = password # # Group Transformation Methods # # Return the parent group object from the child group uuid def get_group_parent_group_from_uuid(self, uuid): group = self.db.find_groups(uuid=uuid, first=True) return group.parentgroup # Return the parent group object from the child group object def get_group_parent_group_from_object(self, group): return group.parentgroup # Return the belonging group object for a group uuid def get_group_object_from_uuid(self, uuid): return self.db.find_groups(uuid=uuid, first=True) # Return the belonging group name for a group uuid def get_group_name_from_uuid(self, uuid): group = self.db.find_groups(uuid=uuid, first=True) return self.get_group_name_from_group_object(group) # Return the path for a group object def get_group_path_from_group_object(self, group): return group.path # Return the belonging group uuid for a group object def get_group_uuid_from_group_object(self, group): return group.uuid # Return the belonging name for a group object def get_group_name_from_group_object(self, group): if group.name is None: return "" else: return group.name # Return the belonging notes for a group object def get_group_notes_from_group_object(self, group): if group.notes is None: return "" else: return group.notes # Return the belonging icon for a group object def get_group_icon_from_group_object(self, group): return group.icon # Return the belonging notes for a group uuid def get_group_notes_from_group_uuid(self, uuid): group = self.db.find_groups(uuid=uuid, first=True) return self.get_group_notes_from_group_object(group) # Return path for group uuid def get_group_path_from_group_uuid(self, uuid): group = self.db.find_groups(uuid=uuid, first=True) return group.path # # Entry Transformation Methods # # Return the belonging entry object for a entry uuid def get_entry_object_from_uuid(self, uuid): return self.db.find_entries(uuid=uuid, first=True) # Return entry uuid from entry object def get_entry_uuid_from_entry_object(self, entry): return entry.uuid # Return parent group from entry uuid def get_entry_parent_group_from_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return entry.parentgroup # Return parent group from entry object def get_entry_parent_group_from_entry_object(self, entry): return entry.parentgroup # Return the belonging name for an entry object def get_entry_name_from_entry_object(self, entry): if entry.title is None: return "" else: return entry.title # Return entry name from entry uuid def get_entry_name_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return self.get_entry_name_from_entry_object(entry) # Return the belonging icon for an entry object def get_entry_icon_from_entry_object(self, entry): return entry.icon # Return entry icon from entry uuid def get_entry_icon_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return entry.icon # Return the belonging username for an entry object def get_entry_username_from_entry_object(self, entry): if entry.username is None: return "" else: return entry.username # Return the belonging username for an entry uuid def get_entry_username_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return self.get_entry_username_from_entry_object(entry) # Return the belonging password for an entry object def get_entry_password_from_entry_object(self, entry): if entry.password is None: return "" else: return entry.password # Return the belonging password for an entry uuid def get_entry_password_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return self.get_entry_password_from_entry_object(entry) # Return the belonging url for an entry uuid def get_entry_url_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return self.get_entry_url_from_entry_object(entry) # Return the belonging url for an entry object def get_entry_url_from_entry_object(self, entry): if entry.url is None: return "" else: return entry.url # Return the belonging notes for an entry uuid def get_entry_notes_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return entry.notes # Return the belonging notes for an entry object def get_entry_notes_from_entry_object(self, entry): if entry.notes is None: return "" else: return entry.notes # Return the beloging expiry date for an entry uuid def get_entry_expiry_date_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return entry.expiry_time # Return the beloging expiry date for an entry object def get_entry_expiry_date_from_entry_object(self, entry): return entry.expiry_time # Return the belonging color for an entry uuid def get_entry_color_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.get_custom_property("color_prop_LcljUMJZ9X") is None: return "NoneColorButton" else: return entry.get_custom_property("color_prop_LcljUMJZ9X") # Return the belonging value for an attribute def get_entry_attribute_value_from_entry_uuid(self, uuid, key): entry = self.db.find_entries(uuid=uuid, first=True) if entry.get_custom_property(key) is None: return "" else: return entry.get_custom_property(key) # Return all attributes for an entry uuid def get_entry_attributes_from_entry_uuid(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return entry.custom_properties # Return all attributes for an entry object def get_entry_attributes_from_entry_object(self, entry): return entry.custom_properties # # Entry Checks # def has_entry_name(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.title is None: return False else: return True def has_entry_username(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.username is None: return False else: return True def has_entry_password(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.password is None: return False else: return True def has_entry_url(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.url is None: return False else: return True def has_entry_notes(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.notes is None: return False else: return True def has_entry_icon(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.icon is None: return False else: return True def has_entry_expiry_date(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) return entry.expires def has_entry_expired(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.expired is False: return False else: return True def has_entry_color(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if entry.get_custom_property( "color_prop_LcljUMJZ9X") is None or entry.get_custom_property( "color_prop_LcljUMJZ9X") == "NoneColorButton": return False else: return True def has_entry_attributes(self, uuid): entry = self.db.find_entries(uuid=uuid, first=True) if len(entry.custom_properties) == 0: return False else: return True def has_entry_attribute(self, uuid, key): entry = self.db.find_entries(uuid=uuid, first=True) if entry.get_custom_property(key) is None: return False else: return True # # Group Checks # def has_group_name(self, uuid): group = self.db.find_groups(uuid=uuid, first=True) if group.name is None: return False else: return True def has_group_notes(self, uuid): group = self.db.find_groups(uuid=uuid, first=True) if group.notes is None: return False else: return True def has_group_icon(self, uuid): group = self.db.find_groups(uuid=uuid, first=True) if group.icon is None: return False else: return True # # Database Modifications # # Add new group to database def add_group_to_database(self, name, icon, notes, parent_group): group = self.db.add_group(parent_group, name, icon=icon, notes=notes) self.changes = True self.set_element_mtime(parent_group) return group # Delete a group def delete_group_from_database(self, group): self.db.delete_group(group) self.changes = True if group.parentgroup is not None: self.set_element_mtime(group.parentgroup) # Add new entry to database def add_entry_to_database(self, name, username, password, url, notes, icon, group_uuid): destination_group = self.get_group_object_from_uuid(group_uuid) entry = self.db.add_entry( destination_group, name, username, password, url=url, notes=notes, expiry_time=None, tags=None, icon=icon, force_creation=self.check_entry_in_group_exists( "", destination_group)) self.changes = True self.set_element_mtime(destination_group) return entry # Delete an entry def delete_entry_from_database(self, entry): self.db.delete_entry(entry) self.changes = True if entry.parentgroup is not None: self.set_element_mtime(entry.parentgroup) # Duplicate an entry def duplicate_entry(self, entry): title = entry.title if title is None: title = "" username = entry.username if username is None: username = "" password = entry.password if password is None: password = "" # NOTE: With clone is meant a duplicated object, not the process of cloning/duplication; "the" clone clone_entry = self.db.add_entry(entry.parentgroup, title + " - " + _("Clone"), username, password, url=entry.url, notes=entry.notes, expiry_time=entry.expiry_time, tags=entry.tags, icon=entry.icon, force_creation=True) # Add custom properties for key in entry.custom_properties: value = entry.custom_properties[key] if value is None: value = "" clone_entry.set_custom_property(key, value) self.changes = True if entry.parentgroup is not None: self.set_element_mtime(entry.parentgroup) # Write all changes to database def save_database(self): if self.save_running is False and self.changes is True: self.save_running = True kdbx = KDBX.build(self.db.kdbx, password=self.db.password, keyfile=self.db.keyfile) try: output_stream = self.database_file_descriptor.replace( None, False, Gio.FileCreateFlags.REPLACE_DESTINATION | Gio.FileCreateFlags.PRIVATE, None) output_stream.write_all(kdbx) output_stream.close() self.logging_manager.debug("Saved database") self.changes = False except Exception: self.logging_manager.error( "Error occured while saving database") # Workaround # Fix created and proposed: https://github.com/pschmitt/pykeepass/pull/102 self.db.kdbx = KDBX.parse_file(self.db.filename, password=self.db.password, keyfile=self.db.keyfile) self.save_running = False # Set database password def set_database_password(self, new_password): self.db.password = new_password self.changes = True # Set database keyfile def set_database_keyfile(self, new_keyfile): self.db.keyfile = new_keyfile self.changes = True # # Entry Modifications # def set_entry_name(self, uuid, name): entry = self.db.find_entries(uuid=uuid, first=True) entry.title = name self.changes = True self.set_element_mtime(entry) def set_entry_username(self, uuid, username): entry = self.db.find_entries(uuid=uuid, first=True) entry.username = username self.changes = True self.set_element_mtime(entry) def set_entry_password(self, uuid, password): entry = self.db.find_entries(uuid=uuid, first=True) entry.password = password self.changes = True self.set_element_mtime(entry) def set_entry_url(self, uuid, url): entry = self.db.find_entries(uuid=uuid, first=True) entry.url = url self.changes = True self.set_element_mtime(entry) def set_entry_notes(self, uuid, notes): entry = self.db.find_entries(uuid=uuid, first=True) entry.notes = notes self.changes = True self.set_element_mtime(entry) def set_entry_icon(self, uuid, icon): entry = self.db.find_entries(uuid=uuid, first=True) entry.icon = icon self.changes = True self.set_element_mtime(entry) def set_entry_expiry_date(self, uuid, date): entry = self.db.find_entries(uuid=uuid, first=True) entry.expiry_time = date entry.expires self.changes = True self.set_element_mtime(entry) def set_entry_color(self, uuid, color): entry = self.db.find_entries(uuid=uuid, first=True) entry.set_custom_property("color_prop_LcljUMJZ9X", color) self.changes = True self.set_element_mtime(entry) def set_entry_attribute(self, uuid, key, value): entry = self.db.find_entries(uuid=uuid, first=True) entry.set_custom_property(key, value) self.changes = True self.set_element_mtime(entry) def delete_entry_attribute(self, uuid, key): entry = self.db.find_entries(uuid=uuid, first=True) entry.delete_custom_property(key) self.changes = True self.set_element_mtime(entry) # Move an entry to another group def move_entry(self, uuid, destination_group_object): entry = self.db.find_entries(uuid=uuid, first=True) self.db.move_entry(entry, destination_group_object) if entry.parentgroup is not None: self.set_element_mtime(entry.parentgroup) self.set_element_mtime(destination_group_object) def set_element_ctime(self, element): element.ctime = datetime.utcnow() def set_element_atime(self, element): element.atime = datetime.utcnow() def set_element_mtime(self, element): element.mtime = datetime.utcnow() # # Group Modifications # def set_group_name(self, uuid, name): group = self.db.find_groups(uuid=uuid, first=True) group.name = name self.changes = True self.set_element_mtime(group) def set_group_notes(self, uuid, notes): group = self.db.find_groups(uuid=uuid, first=True) group.notes = notes self.set_element_mtime(group) def set_group_icon(self, uuid, icon): group = self.db.find_groups(uuid=uuid, first=True) group.icon = icon self.set_element_mtime(group) # Move an group def move_group(self, uuid, destination_group_object): group = self.db.find_groups(uuid=uuid, first=True) self.db.move_group(group, destination_group_object) if group.parentgroup is not None: self.set_element_mtime(group.parentgroup) self.set_element_mtime(destination_group_object) # # Read Database # def get_groups_in_root(self): return self.db.root_group.subgroups # Return list of all groups in folder def get_groups_in_folder(self, uuid): folder = self.get_group_object_from_uuid(uuid) return folder.subgroups # Return list of all entries in folder def get_entries_in_folder(self, uuid): parent_group = self.get_group_object_from_uuid(uuid) return parent_group.entries # Return the database filesystem path def get_database(self): return self.database_path # Return the root group of the database instance def get_root_group(self): return self.db.root_group # Check if root group def check_is_root_group(self, group): if group.is_root_group: return True else: return False # Check if entry with title in group exists def check_entry_in_group_exists(self, title, group): entry = self.db.find_entries(title=title, group=group, recursive=False, history=False, first=True) if entry is None: return False else: return True # Search for an entry or a group def search(self, string, fulltext, global_search=True, path=None): uuid_list = [] if fulltext is False: for group in self.db.find_groups(name="(?i)" + string.replace(" ", ".*"), recursive=global_search, path=path, regex=True): if group.is_root_group is False: uuid_list.append(group.uuid) else: for group in self.db.groups: if group.is_root_group is False and group.uuid not in uuid_list: if string.lower( ) in self.get_group_notes_from_group_object(group): uuid_list.append(group.uuid) if fulltext is False: for entry in self.db.find_entries(title="(?i)" + string.replace(" ", ".*"), recursive=global_search, path=path, regex=True): uuid_list.append(entry.uuid) else: for entry in self.db.entries: if entry.uuid not in uuid_list: if string.lower( ) in self.get_entry_username_from_entry_object(entry): uuid_list.append(entry.uuid) elif string.lower( ) in self.get_entry_notes_from_entry_object(entry): uuid_list.append(entry.uuid) return uuid_list # Check if object is group def check_is_group(self, uuid): if self.get_group_object_from_uuid(uuid) is None: return False else: return True def check_is_group_object(self, group): return hasattr(group, "name") # # Properties # def get_element_creation_date(self, element): if element.ctime is not None: local_timestamp = element.ctime.astimezone(tz.tzlocal()) timestamp = GLib.DateTime.new_local( int(datetime.strftime(local_timestamp, "%Y")), int(datetime.strftime(local_timestamp, "%m")), int(datetime.strftime(local_timestamp, "%d")), int(datetime.strftime(local_timestamp, "%H")), int(datetime.strftime(local_timestamp, "%M")), float(datetime.strftime(local_timestamp, "%S"))) return timestamp.format("%c") else: return "-" def get_element_acessed_date(self, element): if element.atime is not None: local_timestamp = element.atime.astimezone(tz.tzlocal()) timestamp = GLib.DateTime.new_local( int(datetime.strftime(local_timestamp, "%Y")), int(datetime.strftime(local_timestamp, "%m")), int(datetime.strftime(local_timestamp, "%d")), int(datetime.strftime(local_timestamp, "%H")), int(datetime.strftime(local_timestamp, "%M")), float(datetime.strftime(local_timestamp, "%S"))) return timestamp.format("%c") else: return "-" def get_element_modified_date(self, element): if element.mtime is not None: local_timestamp = element.mtime.astimezone(tz.tzlocal()) timestamp = GLib.DateTime.new_local( int(datetime.strftime(local_timestamp, "%Y")), int(datetime.strftime(local_timestamp, "%m")), int(datetime.strftime(local_timestamp, "%d")), int(datetime.strftime(local_timestamp, "%H")), int(datetime.strftime(local_timestamp, "%M")), float(datetime.strftime(local_timestamp, "%S"))) return timestamp.format("%c") else: return "-" # # Database creation methods # # Set the first password entered by the user (for comparing reasons) def set_password_try(self, password): self.password_try = password # Set the second password entered by the user (for comparing reasons) def set_password_check(self, password): self.password_check = password # Compare the first password entered by the user with the second one def compare_passwords(self): if self.password_try == self.password_check: if self.password_try == "" and self.password_check == "": return False else: return True else: return False # Create keyfile hash def create_keyfile_hash(self, keyfile_path): hasher = hashlib.sha512() with open(keyfile_path, 'rb') as file: buffer = 0 while buffer != b'': buffer = file.read(1024) hasher.update(buffer) return hasher.hexdigest() # Set keyfile hash def set_keyfile_hash(self, keyfile_path): self.keyfile_hash = self.create_keyfile_hash(keyfile_path) # Get changes def made_database_changes(self): return self.changes def parent_checker(self, current_group, moved_group): if current_group.is_root_group: return False elif current_group.uuid == moved_group.uuid: return True else: return self.parent_checker(current_group.parentgroup, moved_group) def get_database_encryption(self): return self.db.encryption_algorithm def get_database_derivation(self): return self.db.version
#!/usr/bin/env python3 from pykeepass import PyKeePass from ruamel.yaml import YAML yaml = YAML() with open('environments/secrets.yml') as fp: secrets = yaml.load(fp) kp = PyKeePass('secrets/keepass.kdbx', password='******') # FIXME(berendt): use attachments kp.add_entry(kp.root_group, 'SSH Keypair Configuration', username='', password=secrets.get('configuration_git_private_key')) kp.add_entry(kp.root_group, 'SSH Keypair Operator', username='', password=secrets.get('operator_private_key')) try: with open('secrets/vaultpass') as fp: vaultpass = fp.read() kp.add_entry(kp.root_group, 'Ansible Vault', username='', password=vaultpass) # NOTE: If Ansible Vault is not used, the file does not exist. except FileNotFoundError: pass
class KeepassLoader: kp = NotImplemented database_path = "" password_try = "" password_check = "" password = "" group_list = [] def __init__(self, database_path, password): self.kp = PyKeePass(database_path, password) self.database_path = database_path def add_group(self, name, icon, note, parent_group): group = Group(name, icon, note, parent_group) self.kp.add_group(group.get_parent_group(), group.get_name()) self.group_list.append(group) def add_entry(self, group_path, entry_name, username, password, url, notes, icon): entry = Entry(group_path, entry_name, username, password, url, notes, icon) self.kp.add_entry(self.kp.find_groups_by_path(entry.get_group_path()), entry.get_entry_name(), entry.get_username(), entry.get_password(), url=entry.get_url(), notes=entry.get_notes(), icon=entry.get_icon()) def get_entries(self, group_path): group = self.kp.find_groups_by_path(group_path, first=True) #group = self.kp.find_groups(name="Untergruppe", first=True) print(group) print(group.entries) entry_list = [] for entry in group.entries: entry_list.append( Entry(group, entry.title, entry.username, entry.password, entry.url, entry.notes, entry.icon)) return entry_list def get_groups(self): group_list = [] groups = self.kp.groups for group in groups: if group.path != "/": group_list.append( Group(group.name, group.path, icon=group.icon, notes="", parent_group=group.parent_group)) return group_list #we wanted to turn off the automatic saving after each action and connect it instead to a button def save(self): self.kp.save() #this method sets the initial password for the newly created database def set_database_password(self, new_password): self.kp.set_credentials(new_password) self.save() #this method changes the password of existing database (therefore the old password must be typed in to prevent others changing your password) def change_database_password(self, old_password, new_password): if self.password == old_password: self.kp.set_credentials(new_password) self.save() # # Return the database filesystem path # def get_database(self): print(self.database_path) return self.database_path # # Return the root group of the database instance # def get_root_group(self): return self.kp.root_group # # Set the first password entered by the user (for comparing reasons) # def set_password_try(self, password): self.password_try = password # # Set the second password entered by the user (for comparing reasons) # def set_password_check(self, password): self.password_check = password # # Compare the first password entered by the user with the second one # def compare_passwords(self): if self.password_try == self.password_check: if self.password_try == "" and self.password_check == "": return False else: return True else: return False
if arg == '--create': CREATE = True if arg == '--title': KEEPASSTITEL = sys.argv[idx+1] if arg == '--username': KEEPASSUSERNAME = sys.argv[idx+1] if arg == "--count": if (len(sys.argv) > idx+1): pwdlength = sys.argv[idx+1] else: print(f"ERROR: count value is missing using default value: {pwdlength}") if (CREATE): password = password_generator(int(pwdlength)) if (DISPLAYPWD): print(password) clipboard.copy(password) if (KEEPASSWRITE): group = kp.find_groups(name=KEEPASSDEFAULTGRP, first=True) kp.add_entry(group, str(KEEPASSTITEL), str(KEEPASSUSERNAME), str(password)) kp.save() if (config['DEFAULT']['VAULTENABLE']== 'Y'): client.write(str(config['DEFAULT']['VAULTKV'] + KEEPASSTITEL), username = KEEPASSUSERNAME, password = password, )
class P2KP2: """Convert a Pass db into a Keepass2 one.""" db: PyKeePass def __init__(self, password: str, destination: str = None, overwrite: bool = False): """Constructor for P2KP2 :param password: the password for the new Keepass db :param destination: the final db path :param overwrite: force writing over existing database """ if destination is None: destination = "pass.kdbx" if not os.path.exists(destination) or overwrite: copyfile(empty_db_path, destination) else: raise DbAlreadyExistsException() self.db = PyKeePass(destination) self.db.password = password self.db.save() self.event_stream = Subject() def populate_db(self, pass_reader: PassReader): """Populate the keepass db with data from the PassReader.""" i = 0 for pass_entry in pass_reader.entries: self.add_entry(pass_entry) i = i + 1 self.event_stream.on_next(i) self.db.save() def add_entry(self, pass_entry: PassEntry) -> Entry: """Add a keepass entry to the db containing all data from the relative pass entry. Create the group if needed. :param pass_entry: the original pass entry :return: the newly added keepass entry """ # find the correct group for the entry. If not there, create it entry_group = self.db.root_group # start from the root group if len(pass_entry.groups) > 0: for group_name in pass_entry.groups: # since pass folder names are unique, the possible first result is also the only one group = self.db.find_groups(name=group_name, recursive=False, group=entry_group, first=True) if group is None: # the group is not already there, let's create it group = self.db.add_group(destination_group=entry_group, group_name=group_name) entry_group = group # create the entry, setting group, title, user and pass entry = self.db.add_entry(entry_group, pass_entry.title, pass_entry.user, pass_entry.password) # set the url and the notes entry.url = pass_entry.url entry.notes = pass_entry.notes # add all custom fields for pass_entry, value in pass_entry.custom_properties.items(): entry.set_custom_property(pass_entry, value) return entry
print("Modo de empleo: {} <input.geco>") sys.exit() input_file = sys.argv[1] output_file = 'db.kdbx' master = getpass.getpass("Contraseña maestra: ") aes = CustomAES() kp = PyKeePass('database.kdbx', password="******") kp.password = master groups = {} with open(input_file, 'r') as f: for p in f.readlines(): name, type, description, account, password, cypher_method, updated, expiration = p.split("|") expiration = datetime.datetime.fromtimestamp(float(expiration)) updated = datetime.datetime.fromtimestamp(float(updated)) name, type, description, account =\ map(lambda x: x.replace("\\n", "\n"), (name, type, description, account)) clear = aes.decrypt(password, master) if type and not type in groups: group = kp.add_group(kp.root_group, type) groups[type] = group group = groups.get(type, kp.root_group) kp.add_entry(group, name, account, clear, notes=description) kp.save(output_file)
def bitwarden_to_keepass(args): try: kp = PyKeePass(args.database_path, password=args.database_password, keyfile=args.database_keyfile) except FileNotFoundError: logging.info('KeePass database does not exist, creating a new one.') kp = create_database(args.database_path, password=args.database_password, keyfile=args.database_keyfile) except CredentialsError as e: logging.error(f'Wrong password for KeePass database: {e}') return folders = subprocess.check_output( f'{quote(args.bw_path)} list folders --session {quote(args.bw_session)}', shell=True, encoding='utf8') folders = json.loads(folders) # sort folders so that in the case of nested folders, the parents would be guaranteed to show up before the children folders.sort(key=lambda x: x['name']) groups_by_id = {} groups_by_name = {} for folder in folders: # entries not associated with a folder should go under the root group if folder['id'] is None: groups_by_id[folder['id']] = kp.root_group continue parent_group = kp.root_group target_name = folder['name'] # check if this is a nested folder; set appropriate parent group if so folder_path_split = target_name.rsplit('/', maxsplit=1) if len(folder_path_split) > 1: parent_group = groups_by_name[folder_path_split[0]] target_name = folder_path_split[1] new_group = kp.add_group(parent_group, target_name) groups_by_id[folder['id']] = new_group groups_by_name[folder['name']] = new_group logging.info(f'Folders done ({len(groups_by_id)}).') items = subprocess.check_output( f'{quote(args.bw_path)} list items --session {quote(args.bw_session)}', shell=True, encoding='utf8') items = json.loads(items) logging.info(f'Starting to process {len(items)} items.') for item in items: bw_item = Item(item) is_duplicate_title = False try: while True: entry_title = bw_item.get_name( ) if not is_duplicate_title else '{name} - ({item_id}'.format( name=bw_item.get_name(), item_id=bw_item.get_id()) try: entry = kp.add_entry(destination_group=groups_by_id[ bw_item.get_folder_id()], title=entry_title, username=bw_item.get_username(), password=bw_item.get_password(), notes=bw_item.get_notes()) break except Exception as e: if 'already exists' in str(e): is_duplicate_title = True continue raise totp_secret, totp_settings = bw_item.get_totp() if totp_secret and totp_settings: entry.set_custom_property('TOTP Seed', totp_secret) entry.set_custom_property('TOTP Settings', totp_settings) for uri in bw_item.get_uris(): entry.url = uri['uri'] break # todo append additional uris to notes? for field in bw_item.get_custom_fields(): entry.set_custom_property(field['name'], field['value']) for card_field_name, card_field_val in bw_item.get_card().items(): if card_field_val is not None: entry.set_custom_property( f"{CUSTOM_FIELD_PREFIX}_CARD_{card_field_name}", card_field_val) for identity_field_name, identity_field_val in bw_item.get_identity( ).items(): if identity_field_val is not None: entry.set_custom_property( f"{CUSTOM_FIELD_PREFIX}_IDENTITY_{identity_field_name}", identity_field_val) for attachment in bw_item.get_attachments(): attachment_tmp_path = f'{args.tmp_path}/attachment/{attachment["fileName"]}' attachment_path = subprocess.check_output( f'{quote(args.bw_path)} get attachment' f' --raw {quote(attachment["id"])} ' f'--itemid {quote(bw_item.get_id())} ' f'--output {quote(attachment_tmp_path)} --session {quote(args.bw_session)}', shell=True, encoding='utf8').rstrip() attachment_id = kp.add_binary( open(attachment_path, 'rb').read()) entry.add_attachment(attachment_id, attachment['fileName']) os.remove(attachment_path) except Exception as e: logging.warning( f'Skipping item named "{item["name"]}" because of this error: {repr(e)}' ) continue logging.info('Saving changes to KeePass database.') kp.save() logging.info('Export completed.')
try: input_file = sys.argv[1] output_file = sys.argv[2] password = sys.argv[3] except: help() try: open(input_file) except: help() try: open(output_file) except: pky.create_database(output_file, password=password) logins = csv.reader(open(input_file), delimiter=',') kp = PyKeePass(output_file, password=password) entries = parse_data(logins) for entry in entries: kp.add_entry(kp.root_group, get_name(entry[0]), entry[1], entry[2], url=entry[0]) kp.save()
class EntryFunctionTests(unittest.TestCase): # get some things ready before testing def setUp(self): self.kp = PyKeePass(os.path.join(base_dir, 'test.kdbx'), password='******', keyfile=os.path.join(base_dir, 'test.key')) #---------- Finding entries ----------- def test_find_entries_by_title(self): results = self.kp.find_entries_by_title('root_entry') self.assertEqual(len(results), 1) results = self.kp.find_entries_by_title('Root_entry', regex=True, flags='i', first=True) self.assertEqual('root_entry', results.title) def test_find_entries_by_username(self): results = self.kp.find_entries_by_username('foobar_user') self.assertEqual(len(results), 2) results = self.kp.find_entries_by_username('Foobar_user', regex=True, flags='i', first=True) self.assertEqual('foobar_user', results.username) def test_find_entries_by_password(self): results = self.kp.find_entries_by_password('passw0rd') self.assertEqual(len(results), 2) results = self.kp.find_entries_by_password('Passw0rd', regex=True, flags='i', first=True) self.assertEqual('passw0rd', results.password) def test_find_entries_by_url(self): results = self.kp.find_entries_by_url('http://example.com') self.assertEqual(len(results), 2) results = self.kp.find_entries_by_url('http://example.com', first=True) self.assertEqual('http://example.com', results.url) def test_find_entries_by_notes(self): results = self.kp.find_entries_by_notes('entry notes') self.assertEqual(len(results), 1) results = self.kp.find_entries_by_notes('entry notes', regex=True) self.assertEqual(len(results), 2) results = self.kp.find_entries_by_notes('Entry notes', regex=True, flags='i', first=True) self.assertEqual('root entry notes', results.notes) def test_find_entries_by_path(self): results = self.kp.find_entries_by_path('foobar_group/group_entry') self.assertEqual(len(results), 1) results = self.kp.find_entries_by_path('foobar_group/Group_entry', regex=True, flags='i', first=True) self.assertIsInstance(results, Entry) self.assertEqual('group_entry', results.title) def test_find_entries_by_uuid(self): results = self.kp.find_entries_by_uuid('zF9+zSoASMqWIcIio0ewuw==')[0] self.assertIsInstance(results, Entry) self.assertEqual('zF9+zSoASMqWIcIio0ewuw==', results.uuid) self.assertEqual('foobar_user', results.username) def test_find_entries_by_string(self): results = self.kp.find_entries_by_string( {'custom_field': 'custom field value'})[0] self.assertIsInstance(results, Entry) self.assertEqual('custom field value', results.get_custom_property('custom_field')) self.assertEqual('HnN4bHSVjEybPf8nOq1bVA==', results.uuid) def test_find_entries(self): results = self.kp.find_entries(title='Root_entry', regex=True) self.assertEqual(len(results), 0) results = self.kp.find_entries(title='Root_entry', regex=True, flags='i', first=True) self.assertEqual('root_entry', results.title) results = self.kp.find_entries(url="http://example.com") self.assertEqual(len(results), 2) results = self.kp.find_entries(notes="entry notes", url="http://example.com") self.assertEqual(len(results), 1) self.assertTrue( self.kp.find_entries(title='group_entry', first=True) in results) # test `group` argument results = self.kp.find_entries(title='foobar_entry', group=None) self.assertEqual(len(results), 3) group = self.kp.find_groups(name='foobar_group', first=True) results = self.kp.find_entries(title='foobar_entry', group=group) self.assertEqual(len(results), 2) #---------- Adding/Deleting entries ----------- def test_add_delete_move_entry(self): unique_str = 'test_add_entry_' expiry_time = datetime.now() entry = self.kp.add_entry(self.kp.root_group, unique_str + 'title', unique_str + 'user', unique_str + 'pass', url=unique_str + 'url', notes=unique_str + 'notes', tags=unique_str + 'tags', expiry_time=expiry_time, icon=icons.KEY) results = self.kp.find_entries_by_title(unique_str + 'title') self.assertEqual(len(results), 1) results = self.kp.find_entries_by_title(unique_str + 'title', first=True) self.assertEqual(results.title, unique_str + 'title') self.assertEqual(results.username, unique_str + 'user') self.assertEqual(results.password, unique_str + 'pass') self.assertEqual(results.url, unique_str + 'url') self.assertEqual(results.notes, unique_str + 'notes') self.assertEqual(results.tags, [unique_str + 'tags']) # convert naive datetime to utc expiry_time_utc = expiry_time.replace(tzinfo=tz.gettz()).astimezone( tz.gettz('UTC')) self.assertEqual(results.icon, icons.KEY) sub_group = self.kp.add_group(self.kp.root_group, 'sub_group') self.kp.move_entry(entry, sub_group) results = self.kp.find_entries(path='sub_group/' + 'test_add_entry_title', first=True) self.assertEqual(results.title, entry.title) self.kp.delete_entry(entry) results = self.kp.find_entries_by_title(unique_str + 'title', first=True) self.assertIsNone(results) # test adding entry which exists in another group subgroup = self.kp.find_groups(name='subgroup2', first=True) self.kp.add_entry(subgroup, title='foobar_entry', username='******', password='******') # test adding entry to root which exists in subgroup self.kp.add_entry(subgroup, title='foobar_entry2', username='******', password='******') self.kp.add_entry(self.kp.root_group, title='foobar_entry2', username='******', password='******') #---------- Entries name collision exception ----------- def test_raise_exception_entry(self): unique_str = 'test_add_entry_' entry = self.kp.add_entry(self.kp.root_group, unique_str + 'title', unique_str + 'user', unique_str + 'pass', url=unique_str + 'url', notes=unique_str + 'notes', tags=unique_str + 'tags', icon=icons.KEY) self.assertRaises(Exception, entry) # ---------- Entries representation ----------- def test_print_entries(self): self.assertIsInstance(self.kp.entries.__repr__(), str)
def write_token_data(entry_name, kps_group, token_data, kps_pswd, history_group=None): try: kp = PyKeePass(kps_file, password=kps_pswd) entry = kp.find_entries(title=entry_name, first=True) if entry is not None: notes = entry._get_string_field('Notes') updated_notes = '' current_envs = dict() for env in notes.split('\n'): try: splitted = env.split(':') current_envs.update({splitted[0]: splitted[1]}) except: pass for updated_environment in token_data.keys(): if history_group: if updated_environment == history_group: entry.notes = updated_notes entry.expiry_time = datetime.now() + timedelta( days=365) entry.expires = True else: entry.notes = updated_notes entry.expiry_time = datetime.now() + timedelta(days=365) entry.expires = True for current_env_name, current_env_token in current_envs.items(): if current_env_name not in token_data.keys(): token_data.update({current_env_name: current_env_token}) for env_name, env_token in token_data.items(): updated_notes += env_name + ': ' + env_token + '\n' else: group = kp.find_groups(name=kps_group, first=True) entry = kp.add_entry(group, entry_name, '', '') notes = '' for env_name, env_token in token_data.items(): notes += env_name + ': ' + env_token + '\n' entry.notes = notes entry.expiry_time = datetime.now() + timedelta(days=365) entry.expires = True kp.save() return True except Exception as e: print('[ERRO] - Erro ao gravar dados no Keepass %s:\n%s' % (kps_file, str(e)), file=sys.stderr) pass