Skip to content

Commit

Permalink
Merge pull request #2 from deltreey/master
Browse files Browse the repository at this point in the history
latest enpass, use json export, include folders
  • Loading branch information
jsphpl authored Apr 20, 2020
2 parents bf46d05 + 33d5bd2 commit 93a55a5
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 35 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# enpass-to-keepass
Convert an Enpass csv export so it can be imported to a KeePass database using KeePassXC

## Updates
This now imports from a JSON export from enpass, and includes folders from enpass as groups. Since enpass now uses folders as "labels" and can have multiple, this chooses the first item in the list as the group

## Background
Read this blog article for some background on this tool: [https://jsph.pl/migrating-from-enpass-to-keepass/](https://jsph.pl/migrating-from-enpass-to-keepass/)

Expand All @@ -17,7 +20,7 @@ License: Public Domain
Author: Joseph Paul <[email protected]>
positional arguments:
input_file Path to Enpass csv export file
input_file Path to Enpass json export file
output_file Path to output file (csv)
optional arguments:
Expand Down
71 changes: 37 additions & 34 deletions enpass-to-keepass.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,22 @@

import argparse
import csv
import json

ALLOWED_FIELDS = ['url', 'username', 'password']
HEADERS = ['title', 'url', 'username', 'password', 'notes']
MAP_FIELDNAMES = {
'login': 'username',
'benutzername': 'username',
'login kennwort': 'password',
'kennwort': 'password',
'email': 'username',
'e-mail': 'username',
}
ALLOWED_FIELDS = ['website', 'username', 'password']
HEADERS = ['title', 'website', 'username', 'password', 'group', 'notes']

extra_keys = set([])

def main(args):
results = []
reader = csv.reader(args.input_file)
with open('export.json') as json_file:
data = json.load(json_file)
folders = data['folders']
items = data['items']

reader.__next__() # skip titles row

for row in reader:
results.append(processRow(row))
for item in items:
results.append(processItem(item, folders))

if (len(results) == 0):
print('No rows to write (empty input file?)')
Expand All @@ -47,33 +41,42 @@ def main(args):
writer.writerows(results)


def processRow(row):
notes = row.pop()
def processItem(item, folders):
print('Reading item: ' + item['title'])
result = {
'title': row.pop(0),
'notes': notes + '\n\n' if len(notes) else ''
'title': item['title'],
'notes': item['subtitle'] + '\n\n\n'
}

for (key, value) in makePairs(row):
if key in result or key not in ALLOWED_FIELDS:
result['notes'] += "%s: %s\n" % (key, value)
extra_keys.add(key)
else:
result[key] = value

return result
if item.get('folders'):
for folder in folders:
if folder['uuid'] == item['folders'][0]:
result['group'] = folder['title']
break

email = ''
username = ''

def makePairs(row):
return [(sanitizeKey(row[i]), row[i+1]) for i in range(0, len(row), 2)]
for field in item.get('fields', []):
if field['label'] in result or field['label'].lower() not in ALLOWED_FIELDS:
if field['label'].lower() == 'e-mail':
email = field['value']
continue

def sanitizeKey(key):
key = key.strip().lower()
result['notes'] += "%s\n%s\n\n" % (field['label'], field['value'])
extra_keys.add(field['label'])
else:
result[field['label'].lower()] = field['value']
if field['label'].lower() == 'username':
username = field['value']

if key in MAP_FIELDNAMES:
return MAP_FIELDNAMES[key]
result['notes'] += "NOTES\n\n" + item['note']
if not username and email:
result['username'] = email
else:
result['notes'] += "E-mail\n" + email

return key
return result

if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
Expand Down

0 comments on commit 93a55a5

Please sign in to comment.