Update for pass->bitwarden CSV
This commit is contained in:
parent
9cb3f40167
commit
a602b8064c
3 changed files with 52 additions and 10 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@ __pycache__/
|
|||
build/
|
||||
dist/
|
||||
venv/
|
||||
*.csv
|
||||
|
|
1
LICENSE
1
LICENSE
|
@ -1,6 +1,7 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) The pass2csv authors
|
||||
Copyright (c) 2024, Daniel Ponte
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
60
pass2csv.py
60
pass2csv.py
|
@ -7,6 +7,8 @@ import sys
|
|||
|
||||
import gnupg
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
__version__ = '1.1.1'
|
||||
|
||||
|
||||
|
@ -74,21 +76,59 @@ def set_data(entry, data, exclude, get_fields, get_lines):
|
|||
entry['notes'] = '\n'.join(final_tail).strip()
|
||||
|
||||
|
||||
bwFieldSet = OrderedDict({
|
||||
'folder': 'group',
|
||||
'favorite': None,
|
||||
'type': 'type',
|
||||
'name': 'title',
|
||||
'notes': 'notes',
|
||||
'fields': '__fields',
|
||||
'reprompt': 'reprompt',
|
||||
'login_uri': 'url',
|
||||
'login_username': 'username',
|
||||
'login_password': 'password',
|
||||
'login_totp': 'otp'
|
||||
})
|
||||
|
||||
header = [key for key, value in bwFieldSet.items()]
|
||||
|
||||
def write(file, entries, get_fields, get_lines):
|
||||
get_field_names = set(x[0] for x in get_fields)
|
||||
get_line_names = set(x[0] for x in get_lines)
|
||||
field_names = get_field_names | get_line_names
|
||||
header = ["Group(/)", "Title", "Password", *field_names, "Notes"]
|
||||
csvw = csv.writer(file, dialect='unix')
|
||||
stderr(f"\nWriting data to {file.name}\n")
|
||||
csvw.writerow(header)
|
||||
for entry in entries:
|
||||
fields = [entry['fields'].get(name) for name in field_names]
|
||||
columns = [
|
||||
entry['group'], entry['title'], entry['password'],
|
||||
*fields,
|
||||
entry['notes']
|
||||
]
|
||||
if entry['title'] == 'ChromePasswords':
|
||||
continue
|
||||
columns = []
|
||||
fieldIndex = -1
|
||||
for bwf, pf in bwFieldSet.items():
|
||||
if pf is None:
|
||||
columns.append('')
|
||||
elif pf == 'type':
|
||||
columns.append('login')
|
||||
elif pf == '__fields':
|
||||
columns.append('')
|
||||
fieldIndex = len(columns) - 1
|
||||
pass
|
||||
elif pf in ['group', 'title', 'notes', 'password']:
|
||||
try:
|
||||
columns.append(entry[pf])
|
||||
except KeyError:
|
||||
columns.append('')
|
||||
else:
|
||||
try:
|
||||
columns.append(entry['fields'][pf])
|
||||
del entry['fields'][pf]
|
||||
except KeyError:
|
||||
columns.append('')
|
||||
# get remaining fields
|
||||
fields = ""
|
||||
for fKey, value in entry['fields'].items():
|
||||
fields += "{}: {}\n".format(fKey, value)
|
||||
columns[fieldIndex] = fields.rstrip('\n')
|
||||
csvw.writerow(columns)
|
||||
|
||||
|
||||
|
@ -202,7 +242,7 @@ def parse_args(args=None):
|
|||
metavar='pattern',
|
||||
action='append',
|
||||
type=str,
|
||||
default=[],
|
||||
default=['^autotype:'],
|
||||
help=(
|
||||
"regexp for lines which should not be exported, can be specified"
|
||||
" multiple times"
|
||||
|
@ -216,7 +256,7 @@ def parse_args(args=None):
|
|||
action='append',
|
||||
nargs=2,
|
||||
type=str,
|
||||
default=[],
|
||||
default=[['pin', '^(phone)?pin: '], ['username', '^(user(name)?|login): ']],
|
||||
help=(
|
||||
"a name and a regexp, the part of the line matching the regexp"
|
||||
" will be removed and the remaining line will be added to a field"
|
||||
|
@ -232,7 +272,7 @@ def parse_args(args=None):
|
|||
action='append',
|
||||
nargs=2,
|
||||
type=str,
|
||||
default=[],
|
||||
default=[['otp', 'otpauth://'], ['url', 'https?://']],
|
||||
help=(
|
||||
"a name and a regexp for which all lines that match are included"
|
||||
" in a field with the chosen name"
|
||||
|
|
Loading…
Reference in a new issue