import json import pykeepass.icons from urllib.parse import quote_plus, urlparse from pykeepass import create_database class KpWriter: def __init__(self, filename, password="test"): self.kp = create_database(filename, password) self.current_entry = None def add_entry(self, dest_group_name, title): # Find group and create if not yet there group = self.kp.find_groups(name=dest_group_name, first=True) if not group: # TODO: Handle nested groups? group = self.kp.add_group(self.kp.root_group, dest_group_name) suffix_ctr = 1 new_title = title while self.kp.find_entries(title=new_title, group=group): suffix_ctr += 1 new_title = "{} - {}".format(title, suffix_ctr) self.current_entry = self.kp.add_entry(group, new_title, "", "") return self.current_entry def save(self): return self.kp.save() def set_icon(self, icon_id): if icon_id in dir(pykeepass.icons): kp_icon_id = getattr(pykeepass.icons, icon_id) else: # FIXME: Assume kp_icon is already ID, needed b/c icon 12 is missing from pykeepass.icons kp_icon_id = icon_id self.current_entry.icon = kp_icon_id def set_tags(self, tag_list): self.current_entry.tags = tag_list def add_totp(self, init_string, otp_url=None, title=""): if not otp_url: otp_url = "otpauth://totp/Sample:username?secret={}&algorithm=SHA1&digits=6&period=30&issuer=Sample".format(quote_plus(init_string)) # It's possible to define multiple OTP-secrets in 1P7, so let's not lose one suffix = "" suffix_ctr = 1 while self.current_entry.get_custom_property("otp{}".format(suffix)): suffix_ctr += 1 suffix = "_{}".format(suffix_ctr) self.set_prop("TimeOtp-Secret-Base32{}".format(suffix), init_string, True) self.set_prop("otp{}".format(suffix), otp_url, True) if len(title) > 0: self.set_prop("otp_title{}".format(suffix), title) def add_url(self, url): if not self.current_entry.url: self.current_entry.url = url else: if url == self.current_entry.url: return False # https://github.com/keepassxreboot/keepassxc/pull/3558 suffix = "" suffix_ctr = 0 while self.current_entry.get_custom_property("KP2A_URL{}".format(suffix)): suffix_ctr += 1 suffix = "_{}".format(suffix_ctr) self.set_prop("KP2A_URL{}".format(suffix), url) # KeePassHttp current_settings = self.current_entry.get_custom_property("KeePassHttp Settings") if current_settings: current_settings = json.loads(current_settings) else: current_settings = { "Allow": [], "Deny": [], "Realm": "", } parsed_url = urlparse(url) current_settings["Allow"].append(parsed_url.hostname) current_settings["Allow"] = list(set(current_settings["Allow"])) self.set_prop("KeePassHttp Settings", json.dumps(current_settings)) def set_prop(self, key, value, protected=False): self.current_entry.set_custom_property(key, value) if protected: # https://github.com/libkeepass/pykeepass/issues/89 self.current_entry._element.xpath('String[Key[text()="{}"]]/Value'.format(key))[0].attrib["Protected"] = "True"