Skip to content

Instantly share code, notes, and snippets.

@KenADev
Last active September 19, 2021 02:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KenADev/216fd45b79c83004a4449276ccc43194 to your computer and use it in GitHub Desktop.
Save KenADev/216fd45b79c83004a4449276ccc43194 to your computer and use it in GitHub Desktop.
aWallet Bitwarden Exporter
#!/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