Last active
September 19, 2021 02:59
-
-
Save KenADev/216fd45b79c83004a4449276ccc43194 to your computer and use it in GitHub Desktop.
aWallet Bitwarden Exporter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
## AWALLET TO BITWARDEN EXPORTER | |
# TRANSLATES AWALLET's .CSV EXPORT TO BITWARDEN-COMPATIBLE FILES FOR IMPORT | |
# | |
# USAGE: | |
# 1.) EXPORT .CSV FILE FROM AWALLET | |
# 2.) PUT FILENAMES in files-VARIABLE | |
# 3.) ADJUST AWALLET FIELD NAMES IN mappings-VARIABLE | |
# 4.) RUN SCRIPT | |
# 5.) IMPORT OUTPUT FILES IN BITWARDEN WEB VAULT (AS "Bitwarden (csv)" AND "Bitwarden (json)") | |
# | |
# NOTICE: THE EXPORT FOR CREDIT CARDS IS A .JSON FILE, BECAUSE IMPORTING THE CREDIT CARD FIELDS PER CSV IS NOT POSSIBLE IN BITWARDEN | |
import csv, pprint, uuid, copy, re, json | |
########### BEGIN CONFIGURATION ########### | |
# INPUT FILES | |
files = { | |
"computer": "./Computer-Logins.csv", # "Konto","Name","Passwort","Notiz" | |
"web": "./Web-Accounts.csv", # "Website","URL","Name","Passwort","Notiz" | |
"email": "./Email-Accounts.csv", # "Konto","Email","Passwort","Website","Notiz" | |
"eshop": "./e-Shops.csv", # "e-Shop Web","Name","Passwort","Notiz" | |
"cards": "./Konten.csv", # "Kartenname","Kartennummer","Ablaufdatum","Sicherheitscode","Automaten-PIN","Online-Passwort","Notiz" | |
} | |
# MAP BITWARDEN FIELDS -> AWALLET FIELDS | |
mappings = { | |
"web": { | |
"name": "Website", | |
"login_uri": "URL", | |
"login_username": "Name", | |
"login_password": "Passwort", | |
"notes":"Notiz", | |
}, | |
"computer": { | |
"name": "Konto", | |
"login_username": "Name", | |
"login_password": "Passwort", | |
"notes":"Notiz", | |
}, | |
"email": { | |
"name": "Konto", | |
"login_username": "Email", | |
"login_password": "Passwort", | |
"login_uri": "Website", | |
"notes":"Notiz", | |
}, | |
"eshop": { | |
"name": "e-Shop Web", | |
"login_username": "Name", | |
"login_password": "Passwort", | |
"notes":"Notiz", | |
}, | |
"cards": { | |
"name": "Kartenname", | |
"notes": "Notiz", | |
"card": { | |
"number": "Kartennummer", | |
"code": "Sicherheitscode", | |
}, | |
"fields": { | |
"PIN-Code": "Automaten-PIN", | |
"Online-Password": "Online-Passwort", | |
"Expiration": "Ablaufdatum", # No strict "MM/YYYY" format in aWallet. I will try parsing with "MM/YYYY" and "MM/YY" to set expMonth/expYear, but I will also set this extra field just in case! | |
} | |
}, | |
} | |
########### END CONFIGURATION ########### | |
DEFAULT_FIELDS = { | |
'folder': "", | |
'favorite': "", | |
'type': "login", | |
"name": "", | |
'notes': "", | |
'fields': "", | |
'reprompt': "", | |
'login_uri': "", | |
'login_username': "", | |
'login_password': "", | |
'login_totp': "" | |
} | |
DEFAULT_CARDS_JSON = { | |
"id": "GENERATE_UUID", | |
"organizationId": None, | |
"folderId": None, | |
"type": 3, # card | |
"name": "", | |
"notes": "", | |
"favorite": False, | |
"fields": [ | |
{ | |
"name": "PIN-Code", | |
"value": "", | |
"type": 1 # hidden | |
}, | |
{ | |
"name": "Expiration", | |
"value": "", | |
"type": 1 # hidden | |
}, | |
{ | |
"name": "Online-Password", | |
"value": "", | |
"type": 1 # hidden | |
}, | |
], | |
"card": { | |
"cardholderName": "", # does not exist in aWallet | |
"brand": "Other", # does not exist in aWallet | |
"number": "", | |
"expMonth": "", | |
"expYear": "", | |
"code": "" #CVC | |
}, | |
"collectionIds": [ | |
None | |
] | |
} | |
########### READ CSV FILES ########### | |
csvinput = {} | |
for ft in files: | |
csvinput[ft] = [] | |
with open(files[ft], mode='r', encoding='utf-8-sig') as csv_file: | |
csv_reader = csv.DictReader(csv_file, delimiter=',', quotechar='"') | |
line_count = 0 | |
for row in csv_reader: | |
if line_count == 0: | |
#print(f'Column names are {", ".join(row)}') | |
#print(row) | |
pass | |
csvinput[ft].append(row) | |
line_count += 1 | |
print(f'{ft}:\t Read {line_count} lines.') | |
#print(csvinput) | |
########### TRANSLATE AND EXPORT FILES ########### | |
if True: | |
for ID in mappings.keys(): | |
if ID != "cards": | |
# CSV EXPORT FOR REGULAR FILES | |
csvout = [] | |
for r in csvinput[ID]: | |
tline = {} | |
for d in DEFAULT_FIELDS: | |
if d in mappings[ID]: | |
tline[d] = r[mappings[ID][d]] | |
else: | |
tline[d] = "" | |
csvout.append(tline) | |
#pp = pprint.PrettyPrinter(indent=4, sort_dicts=False) | |
#pp.pprint(csvout) | |
with open('./'+ID+'-export.csv', mode='w') as csv_file: | |
fieldnames = DEFAULT_FIELDS.keys() | |
writer = csv.DictWriter(csv_file, fieldnames=fieldnames, delimiter=',', quotechar='"',) | |
writer.writeheader() | |
for wr in csvout: | |
writer.writerow(wr) | |
print("EXPORTED CSV: "+ID+"-export.csv") | |
elif ID == "cards": | |
ID = "cards" | |
# JSON EXPORT FOR CARDS (Konten / CREDIT CARDS) | |
jsonout = { | |
"folders": [], | |
"items": [] | |
} | |
for r in csvinput["cards"]: | |
t = copy.deepcopy(DEFAULT_CARDS_JSON) | |
t["id"] = str(uuid.uuid4()) | |
for m in mappings[ID]: | |
if m == "card": | |
for m2 in mappings[ID]["card"]: | |
t["card"][m2] = r[mappings[ID]["card"][m2]] | |
elif m == "fields": | |
for fm in mappings[ID]["fields"]: | |
for ft in t["fields"]: | |
if ft["name"] == fm: | |
ft["value"] = r[mappings[ID]["fields"][fm]] | |
reg = r"^([0-9]{2})\/(([0-9]{2})|(20[0-9]{2}))$" | |
rmatch = re.compile(reg).match(r[mappings[ID]["fields"][fm]]) | |
if rmatch is not None: | |
rmg = rmatch.groups() | |
t["card"]["expMonth"] = str(int(rmg[0])) | |
t["card"]["expYear"] = str(rmg[1] if len(rmg[1]) == 4 else ("20"+str(rmg[1]))) | |
else: | |
t[m] = r[mappings[ID][m]] | |
jsonout["items"].append(t) | |
#pp = pprint.PrettyPrinter(indent=4, sort_dicts=False) | |
#pp.pprint(jsonout) | |
with open('./'+ID+'-export.json', mode='w') as jfile: | |
json.dump(jsonout, jfile) | |
print("EXPORTED JSON: "+ID+'-export.json') | |
print("DONE.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment