vorta/src/vorta/keyring/secretstorage.py

82 lines
2.7 KiB
Python

import asyncio
import logging
import os
import secretstorage
from vorta.keyring.abc import VortaKeyring
logger = logging.getLogger(__name__)
LABEL_TEMPLATE = "Vorta Backup Repo {repo_url}"
class VortaSecretStorageKeyring(VortaKeyring):
"""A wrapper for the secretstorage package to support the custom keyring backend"""
def __init__(self):
"""
Test whether DBus and a SecretStorage provider are available.
"""
try:
self.connection = secretstorage.dbus_init()
except secretstorage.exceptions.SecretServiceNotAvailableException as e:
logger.debug("SecretStorage provider or DBus daemon is not available.")
raise e
asyncio.set_event_loop(asyncio.new_event_loop())
self.collection = secretstorage.get_default_collection(self.connection)
def set_password(self, service, repo_url, password):
"""
Writes a password to the underlying store.
"""
if self.is_unlocked:
asyncio.set_event_loop(asyncio.new_event_loop())
attributes = {
'application': 'Vorta',
'service': service,
'repo_url': repo_url,
'xdg:schema': 'org.freedesktop.Secret.Generic',
}
self.collection.create_item(
LABEL_TEMPLATE.format(repo_url=repo_url),
attributes,
password,
replace=True,
)
logger.debug(f"Saved password for repo {repo_url}")
def get_password(self, service, repo_url):
"""
Retrieve a password from the underlying store. Return None if not found.
"""
if self.is_unlocked:
asyncio.set_event_loop(asyncio.new_event_loop())
attributes = {
'application': 'Vorta',
'service': service,
'repo_url': repo_url,
}
items = list(self.collection.search_items(attributes))
logger.debug('Found %i passwords matching repo URL.', len(items))
if len(items) > 0:
item = items[0]
if item.is_locked() and item.unlock():
return None
logger.debug(f"Retrieved password for repo {repo_url}")
return item.get_secret().decode("utf-8")
return None
@property
def is_unlocked(self):
# unlock() will return True if the unlock prompt is dismissed
return not (self.collection.is_locked() and self.collection.unlock())
@classmethod
def get_priority(cls):
return 6 if "GNOME" in os.getenv("XDG_CURRENT_DESKTOP", "") else 5
@property
def is_system(self):
return True