From 2d9e73bc3e50ba2b1644831e21eef4f62ce0c356 Mon Sep 17 00:00:00 2001 From: Manu Date: Tue, 20 Nov 2018 09:57:18 +0800 Subject: [PATCH] Add fallback keyring for systems without keyring backend. Fixes #10 --- src/vorta/assets/UI/mainwindow.ui | 2 +- src/vorta/borg/create.py | 2 +- src/vorta/models.py | 13 +++++++++- src/vorta/utils.py | 40 +++++++++++++++++++++++++++---- src/vorta/views/repo_tab.py | 2 +- src/vorta/views/schedule_tab.py | 10 ++++---- 6 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/vorta/assets/UI/mainwindow.ui b/src/vorta/assets/UI/mainwindow.ui index 2e2950d7..a1916522 100644 --- a/src/vorta/assets/UI/mainwindow.ui +++ b/src/vorta/assets/UI/mainwindow.ui @@ -244,7 +244,7 @@ - true + false diff --git a/src/vorta/borg/create.py b/src/vorta/borg/create.py index 0fee66a3..66263fd1 100644 --- a/src/vorta/borg/create.py +++ b/src/vorta/borg/create.py @@ -66,7 +66,7 @@ def prepare(cls, profile): & (WifiSettingModel.allowed == False) & (WifiSettingModel.profile == profile.id) ) - if wifi_is_disallowed.count() > 0: + if wifi_is_disallowed.count() > 0 and profile.repo.is_remote_repo(): ret['message'] = 'Current Wifi is not allowed.' return ret diff --git a/src/vorta/models.py b/src/vorta/models.py index 27cefc07..f91b98e6 100644 --- a/src/vorta/models.py +++ b/src/vorta/models.py @@ -41,10 +41,21 @@ class RepoModel(pw.Model): total_size = pw.IntegerField(null=True) total_unique_chunks = pw.IntegerField(null=True) + def is_remote_repo(self): + return not self.url.startswith('/') + class Meta: database = db +class RepoPassword(pw.Model): + """Fallback to save repo passwords. Only used if no Keyring available.""" + url = pw.CharField(unique=True) + password = pw.CharField() + + class Meta: + database = db + class BackupProfileModel(pw.Model): """Allows the user to switch between different configurations.""" name = pw.CharField() @@ -158,7 +169,7 @@ def _apply_schema_update(current_schema, version_after, *operations): def init_db(con): db.initialize(con) db.connect() - db.create_tables([RepoModel, BackupProfileModel, SourceDirModel, + db.create_tables([RepoModel, RepoPassword, BackupProfileModel, SourceDirModel, SnapshotModel, WifiSettingModel, EventLogModel, SchemaVersion]) if BackupProfileModel.select().count() == 0: diff --git a/src/vorta/utils.py b/src/vorta/utils.py index 7a191d02..0464065f 100644 --- a/src/vorta/utils.py +++ b/src/vorta/utils.py @@ -9,18 +9,50 @@ from PyQt5.QtWidgets import QFileDialog from PyQt5 import uic, QtCore import subprocess - -"""Workaround for pyinstaller+keyring issue.""" import keyring +from keyring import backend + + +class VortaKeyring(backend.KeyringBackend): + """Fallback keyring service.""" + @classmethod + def priority(cls): + return 5 + + def set_password(self, service, repo_url, password): + from .models import RepoPassword + keyring_entry, created = RepoPassword.get_or_create(url=repo_url, defaults={'password': password}) + keyring_entry.password = password + keyring_entry.save() + + def get_password(self, service, repo_url): + from .models import RepoPassword + try: + keyring_entry = RepoPassword.get(url=repo_url) + return keyring_entry.password + except Exception: + return None + + def delete_password(self, service, repo_url): + pass + + +"""Select keyring/Workaround for pyinstaller+keyring issue.""" if sys.platform == 'darwin': from keyring.backends import OS_X keyring.set_keyring(OS_X.Keyring()) elif sys.platform == 'win32': from keyring.backends import Windows keyring.set_keyring(Windows.WinVaultKeyring()) -else: +elif sys.platform == 'linux': from keyring.backends import SecretService - keyring.set_keyring(SecretService.Keyring()) + try: + SecretService.Keyring.priority() # Test if keyring works. + keyring.set_keyring(SecretService.Keyring()) + except Exception: + keyring.set_keyring(VortaKeyring()) +else: # Fall back to saving password to database. + keyring.set_keyring(VortaKeyring()) from .models import WifiSettingModel diff --git a/src/vorta/views/repo_tab.py b/src/vorta/views/repo_tab.py index aa537c12..7fc477a0 100644 --- a/src/vorta/views/repo_tab.py +++ b/src/vorta/views/repo_tab.py @@ -158,9 +158,9 @@ def repo_unlink_action(self): if selected_repo_index > 2: repo = RepoModel.get(id=selected_repo_id) SnapshotModel.delete().where(SnapshotModel.repo_id == repo.id).execute() - repo.delete_instance() profile.repo = None profile.save() + repo.delete_instance(recursive=True) # This also deletes snapshots. self.repoSelector.setCurrentIndex(0) self.repoSelector.removeItem(selected_repo_index) msg.setText('Repository was Unlinked') diff --git a/src/vorta/views/schedule_tab.py b/src/vorta/views/schedule_tab.py index a47ca94c..a1765c29 100644 --- a/src/vorta/views/schedule_tab.py +++ b/src/vorta/views/schedule_tab.py @@ -44,7 +44,7 @@ def populate_from_profile(self): self.validationCheckBox.setTristate(False) self.pruneCheckBox.setTristate(False) - self._draw_next_scheduled_backup(profile.id) + self._draw_next_scheduled_backup() self.init_wifi() def init_wifi(self): @@ -85,10 +85,10 @@ def init_logs(self): self.logTableWidget.setItem(row, 3, QTableWidgetItem(log_line.repo_url)) self.logTableWidget.setItem(row, 4, QTableWidgetItem(str(log_line.returncode))) self.logTableWidget.setRowCount(len(event_logs)) - self.nextBackupDateTimeLabel.setText(self.app.scheduler.next_job) + self._draw_next_scheduled_backup() - def _draw_next_scheduled_backup(self, profile_id): - self.nextBackupDateTimeLabel.setText(self.app.scheduler.next_job_for_profile(profile_id)) + def _draw_next_scheduled_backup(self): + self.nextBackupDateTimeLabel.setText(self.app.scheduler.next_job_for_profile(self.profile().id)) self.nextBackupDateTimeLabel.repaint() def on_scheduler_apply(self): @@ -109,5 +109,5 @@ def on_scheduler_apply(self): profile.schedule_fixed_hour, profile.schedule_fixed_minute = qtime.hour(), qtime.minute() profile.save() self.app.scheduler.reload() - self._draw_next_scheduled_backup(profile.id) + self._draw_next_scheduled_backup()