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/
|
build/
|
||||||
dist/
|
dist/
|
||||||
venv/
|
venv/
|
||||||
|
*.csv
|
||||||
|
|
1
LICENSE
1
LICENSE
|
@ -1,6 +1,7 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) The pass2csv authors
|
Copyright (c) The pass2csv authors
|
||||||
|
Copyright (c) 2024, Daniel Ponte
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
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
|
import gnupg
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
__version__ = '1.1.1'
|
__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()
|
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):
|
def write(file, entries, get_fields, get_lines):
|
||||||
get_field_names = set(x[0] for x in get_fields)
|
get_field_names = set(x[0] for x in get_fields)
|
||||||
get_line_names = set(x[0] for x in get_lines)
|
get_line_names = set(x[0] for x in get_lines)
|
||||||
field_names = get_field_names | get_line_names
|
field_names = get_field_names | get_line_names
|
||||||
header = ["Group(/)", "Title", "Password", *field_names, "Notes"]
|
|
||||||
csvw = csv.writer(file, dialect='unix')
|
csvw = csv.writer(file, dialect='unix')
|
||||||
stderr(f"\nWriting data to {file.name}\n")
|
stderr(f"\nWriting data to {file.name}\n")
|
||||||
csvw.writerow(header)
|
csvw.writerow(header)
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
fields = [entry['fields'].get(name) for name in field_names]
|
if entry['title'] == 'ChromePasswords':
|
||||||
columns = [
|
continue
|
||||||
entry['group'], entry['title'], entry['password'],
|
columns = []
|
||||||
*fields,
|
fieldIndex = -1
|
||||||
entry['notes']
|
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)
|
csvw.writerow(columns)
|
||||||
|
|
||||||
|
|
||||||
|
@ -202,7 +242,7 @@ def parse_args(args=None):
|
||||||
metavar='pattern',
|
metavar='pattern',
|
||||||
action='append',
|
action='append',
|
||||||
type=str,
|
type=str,
|
||||||
default=[],
|
default=['^autotype:'],
|
||||||
help=(
|
help=(
|
||||||
"regexp for lines which should not be exported, can be specified"
|
"regexp for lines which should not be exported, can be specified"
|
||||||
" multiple times"
|
" multiple times"
|
||||||
|
@ -216,7 +256,7 @@ def parse_args(args=None):
|
||||||
action='append',
|
action='append',
|
||||||
nargs=2,
|
nargs=2,
|
||||||
type=str,
|
type=str,
|
||||||
default=[],
|
default=[['pin', '^(phone)?pin: '], ['username', '^(user(name)?|login): ']],
|
||||||
help=(
|
help=(
|
||||||
"a name and a regexp, the part of the line matching the regexp"
|
"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"
|
" will be removed and the remaining line will be added to a field"
|
||||||
|
@ -232,7 +272,7 @@ def parse_args(args=None):
|
||||||
action='append',
|
action='append',
|
||||||
nargs=2,
|
nargs=2,
|
||||||
type=str,
|
type=str,
|
||||||
default=[],
|
default=[['otp', 'otpauth://'], ['url', 'https?://']],
|
||||||
help=(
|
help=(
|
||||||
"a name and a regexp for which all lines that match are included"
|
"a name and a regexp for which all lines that match are included"
|
||||||
" in a field with the chosen name"
|
" in a field with the chosen name"
|
||||||
|
|
Loading…
Reference in a new issue