diff --git a/bauh/api/abstract/handler.py b/bauh/api/abstract/handler.py index ffdc52c6..3f8ec64e 100644 --- a/bauh/api/abstract/handler.py +++ b/bauh/api/abstract/handler.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Tuple from bauh.api.abstract.view import MessageType, ViewComponent @@ -65,3 +65,9 @@ class ProcessWatcher: """ :return: if the use requested to stop the process. """ + + def request_root_password(self) -> Tuple[str, bool]: + """ + asks the root password for the user + :return: a tuple with the typed password and if it is valid + """ \ No newline at end of file diff --git a/bauh/app.py b/bauh/app.py index 01602d06..460b3688 100755 --- a/bauh/app.py +++ b/bauh/app.py @@ -4,7 +4,6 @@ from threading import Thread import urllib3 from PyQt5.QtCore import Qt -from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QApplication from bauh import __version__, __app_name__, app_args, ROOT_DIR @@ -15,7 +14,7 @@ from bauh.view.core.controller import GenericSoftwareManager from bauh.view.core.downloader import AdaptableFileDownloader from bauh.view.qt.systray import TrayIcon from bauh.view.qt.window import ManageWindow -from bauh.view.util import util, logs, resource, translation +from bauh.view.util import util, logs, translation from bauh.view.util.cache import DefaultMemoryCacheFactory, CacheCleaner from bauh.view.util.disk import DefaultDiskCacheLoaderFactory from bauh.view.util.translation import I18n diff --git a/bauh/commons/user.py b/bauh/commons/user.py new file mode 100644 index 00000000..6b5a0f63 --- /dev/null +++ b/bauh/commons/user.py @@ -0,0 +1,5 @@ +import os + + +def is_root(): + return os.getuid() == 0 diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index 455944a0..26a97ba3 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -9,6 +9,7 @@ from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion, \ SuggestionPriority from bauh.api.abstract.view import MessageType +from bauh.commons import user from bauh.commons.html import strip_html, bold from bauh.commons.system import SystemProcess, ProcessHandler, SimpleProcess from bauh.gems.flatpak import flatpak, SUGGESTIONS_FILE, CONFIG_FILE @@ -67,7 +68,7 @@ class FlatpakManager(SoftwareManager): remote_level = 'user' else: remote_level = 'user' - flatpak.set_default_remotes(remote_level) + ProcessHandler().handle_simple(flatpak.set_default_remotes(remote_level)) return remote_level @@ -264,8 +265,28 @@ class FlatpakManager(SoftwareManager): deny_label=self.i18n['yes'].capitalize()) pkg.installation = 'user' if user_level else 'system' + remotes = flatpak.list_remotes() + handler = ProcessHandler(watcher) + if pkg.installation == 'user' and not remotes['user']: + handler.handle_simple(flatpak.set_default_remotes('user')) + elif pkg.installation == 'system' and not remotes['system']: + if user.is_root(): + handler.handle_simple(flatpak.set_default_remotes('system')) + else: + user_password, valid = watcher.request_root_password() + if not valid: + watcher.print('Operation aborted') + return False + else: + if not handler.handle_simple(flatpak.set_default_remotes('system', user_password)): + watcher.show_message(title=self.i18n['error'].capitalize(), + body=self.i18n['flatpak.remotes.system_flathub.error'], + type_=MessageType.ERROR) + watcher.print("Operation cancelled") + return False + if pkg.installation == 'user': if not handler.handle_simple(flatpak.register_flathub('user')): return False @@ -318,7 +339,7 @@ class FlatpakManager(SoftwareManager): loader.join() for app in to_update: - updates.append(PackageUpdate(pkg_id='{}:{}'.format(app.id, app.branch), + updates.append(PackageUpdate(pkg_id='{}:{}:{}'.format(app.id, app.branch, app.installation), pkg_type='flatpak', version=app.version)) diff --git a/bauh/gems/flatpak/flatpak.py b/bauh/gems/flatpak/flatpak.py index 354bd8c2..cbd0251f 100755 --- a/bauh/gems/flatpak/flatpak.py +++ b/bauh/gems/flatpak/flatpak.py @@ -6,7 +6,7 @@ from io import StringIO from typing import List, Dict, Set from bauh.api.exception import NoInternetException -from bauh.commons.system import new_subprocess, run_cmd, new_root_subprocess, SimpleProcess +from bauh.commons.system import new_subprocess, run_cmd, new_root_subprocess, SimpleProcess, SystemProcess BASE_CMD = 'flatpak' RE_SEVERAL_SPACES = re.compile(r'\s+') @@ -343,8 +343,9 @@ def install(app_id: str, origin: str, installation: str): return new_subprocess([BASE_CMD, 'install', origin, app_id, '-y', '--{}'.format(installation)]) -def set_default_remotes(installation: str): - run_cmd('{} remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo --{}'.format(BASE_CMD, installation)) +def set_default_remotes(installation: str, root_password: str = None) -> SimpleProcess: + cmd = [BASE_CMD, 'remote-add', '--if-not-exists', 'flathub', 'https://flathub.org/repo/flathub.flatpakrepo', '--{}'.format(installation)] + return SimpleProcess(cmd, root_password=root_password) def has_remotes_set() -> bool: diff --git a/bauh/gems/flatpak/resources/locale/ca b/bauh/gems/flatpak/resources/locale/ca index c82658a6..10f50234 100644 --- a/bauh/gems/flatpak/resources/locale/ca +++ b/bauh/gems/flatpak/resources/locale/ca @@ -44,4 +44,5 @@ flatpak.info.translateurl=Traducció flatpak.info.developername=desenvolupador flatpak.install.install_level.title=Tipus d'instal·lació flatpak.install.install_level.body=S'ha d'instal·lar {} per a tots els usuaris del dispositiu ( sistema ) ? -flatpak.install.bad_install_level.body=Valor invàlid per a {field} al fitxer de configuració {file} \ No newline at end of file +flatpak.install.bad_install_level.body=Valor invàlid per a {field} al fitxer de configuració {file} +flatpak.remotes.system_flathub.error=No s'ha pogut afegir Flathub com a dipòsit del sistema ( remote ) \ No newline at end of file diff --git a/bauh/gems/flatpak/resources/locale/de b/bauh/gems/flatpak/resources/locale/de index b25fa3b1..dc60e06b 100644 --- a/bauh/gems/flatpak/resources/locale/de +++ b/bauh/gems/flatpak/resources/locale/de @@ -43,4 +43,5 @@ flatpak.history.date=Datum flatpak.history.commit=Commit flatpak.install.install_level.title=Installationstyp flatpak.install.install_level.body=Sollte {} für alle Gerätebenutzer installiert werden ( system ) ? -flatpak.install.bad_install_level.body=Ungültiger Wert für {field} in der Konfigurationsdatei {file} \ No newline at end of file +flatpak.install.bad_install_level.body=Ungültiger Wert für {field} in der Konfigurationsdatei {file} +flatpak.remotes.system_flathub.error=Flathub konnte nicht als System-Repository ( remote ) hinzugefügt werden \ No newline at end of file diff --git a/bauh/gems/flatpak/resources/locale/en b/bauh/gems/flatpak/resources/locale/en index 3cb1c488..4176e3cf 100644 --- a/bauh/gems/flatpak/resources/locale/en +++ b/bauh/gems/flatpak/resources/locale/en @@ -43,4 +43,5 @@ flatpak.history.date=date flatpak.history.commit=commit flatpak.install.install_level.title=Installation type flatpak.install.install_level.body=Should {} be installed for all the device users ( system ) ? -flatpak.install.bad_install_level.body=Invalid value for {field} in the configuration file {file} \ No newline at end of file +flatpak.install.bad_install_level.body=Invalid value for {field} in the configuration file {file} +flatpak.remotes.system_flathub.error=It was not possible to add Flathub as a system repository ( remote ) \ No newline at end of file diff --git a/bauh/gems/flatpak/resources/locale/es b/bauh/gems/flatpak/resources/locale/es index 3b77bb87..2aaa0e92 100644 --- a/bauh/gems/flatpak/resources/locale/es +++ b/bauh/gems/flatpak/resources/locale/es @@ -44,4 +44,5 @@ flatpak.info.translateurl=Traducción flatpak.info.developername=desarrollador flatpak.install.install_level.title=Tipo de instalación flatpak.install.install_level.body=¿Debería {} estar instalado para todos los usuarios del dispositivo ( sistema )? -flatpak.install.bad_install_level.body=Valor inválido para {field} en el archivo de configuración {file} \ No newline at end of file +flatpak.install.bad_install_level.body=Valor inválido para {field} en el archivo de configuración {file} +flatpak.remotes.system_flathub.error=No fue posible agregar Flathub como repositorio del sistema ( remote ) \ No newline at end of file diff --git a/bauh/gems/flatpak/resources/locale/it b/bauh/gems/flatpak/resources/locale/it index a7823990..42521925 100644 --- a/bauh/gems/flatpak/resources/locale/it +++ b/bauh/gems/flatpak/resources/locale/it @@ -23,4 +23,5 @@ flatpak.info.installation.user=utente flatpak.info.installation.system=sistema flatpak.install.install_level.title=Tipo di installazione flatpak.install.install_level.body={} deve essere installato per tutti gli utenti del dispositivo ( sistema ) ? -flatpak.install.bad_install_level.body=Valore non valido per {field} nel file di configurazione {file} \ No newline at end of file +flatpak.install.bad_install_level.body=Valore non valido per {field} nel file di configurazione {file} +flatpak.remotes.system_flathub.error=Non è stato possibile aggiungere Flathub come repository di sistema ( remote ) \ No newline at end of file diff --git a/bauh/gems/flatpak/resources/locale/pt b/bauh/gems/flatpak/resources/locale/pt index a18fc962..bd1a5301 100644 --- a/bauh/gems/flatpak/resources/locale/pt +++ b/bauh/gems/flatpak/resources/locale/pt @@ -44,4 +44,5 @@ flatpak.info.translateurl=tradução flatpak.info.developername=desenvolvedor flatpak.install.install_level.title=Tipo de instalação flatpak.install.install_level.body={} deve ser instalado para todos os usuários desse dispositivo ( sistema ) ? -flatpak.install.bad_install_level.body=Valor inválido para {field} no arquivo de configuração {file} \ No newline at end of file +flatpak.install.bad_install_level.body=Valor inválido para {field} no arquivo de configuração {file} +flatpak.remotes.system_flathub.error=Não foi possível adicionar o Flathub como um repositório do sistema ( remote ) \ No newline at end of file diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index a3edd855..24d81103 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -1,7 +1,7 @@ import re import time from datetime import datetime, timedelta -from typing import List, Type, Set +from typing import List, Type, Set, Tuple import requests from PyQt5.QtCore import QThread, pyqtSignal @@ -28,11 +28,13 @@ class AsyncAction(QThread, ProcessWatcher): signal_status = pyqtSignal(str) # changes the GUI status message signal_substatus = pyqtSignal(str) # changes the GUI substatus message signal_progress = pyqtSignal(int) + signal_root_password = pyqtSignal() def __init__(self): super(AsyncAction, self).__init__() self.wait_confirmation = False self.confirmation_res = None + self.root_password = None self.stop = False def request_confirmation(self, title: str, body: str, components: List[InputViewComponent] = None, confirmation_label: str = None, deny_label: str = None) -> bool: @@ -41,10 +43,22 @@ class AsyncAction(QThread, ProcessWatcher): self.wait_user() return self.confirmation_res + def request_root_password(self) -> Tuple[str, bool]: + self.wait_confirmation = True + self.signal_root_password.emit() + self.wait_user() + res = self.root_password + self.root_password = None + return res + def confirm(self, res: bool): self.confirmation_res = res self.wait_confirmation = False + def set_root_password(self, password: str, valid: bool): + self.root_password = (password, valid) + self.wait_confirmation = False + def wait_user(self): while self.wait_confirmation: time.sleep(0.01) diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index 1a5f8db9..bc64b648 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -1,7 +1,5 @@ import logging -import operator import time -from functools import reduce from pathlib import Path from typing import List, Type, Set @@ -17,9 +15,10 @@ from bauh.api.abstract.controller import SoftwareManager from bauh.api.abstract.model import SoftwarePackage, PackageAction from bauh.api.abstract.view import MessageType from bauh.api.http import HttpClient +from bauh.commons import user from bauh.commons.html import bold from bauh.view.core.controller import GenericSoftwareManager -from bauh.view.qt import dialog, commons, qt_utils +from bauh.view.qt import dialog, commons, qt_utils, root from bauh.view.qt.about import AboutDialog from bauh.view.qt.apps_table import AppsTable, UpdateToggleButton from bauh.view.qt.components import new_spacer, InputFilter, IconButton @@ -27,7 +26,7 @@ from bauh.view.qt.confirmation import ConfirmationDialog from bauh.view.qt.gem_selector import GemSelectorPanel from bauh.view.qt.history import HistoryDialog from bauh.view.qt.info import InfoDialog -from bauh.view.qt.root import is_root, ask_root_password +from bauh.view.qt.root import ask_root_password from bauh.view.qt.screenshots import ScreenshotsDialog from bauh.view.qt.styles import StylesComboBox from bauh.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \ @@ -49,6 +48,7 @@ class ManageWindow(QWidget): __BASE_HEIGHT__ = 400 signal_user_res = pyqtSignal(bool) + signal_root_password = pyqtSignal(str, bool) signal_table_update = pyqtSignal() def __init__(self, i18n: I18n, icon_cache: MemoryCache, manager: SoftwareManager, screen_size, config: dict, @@ -396,8 +396,10 @@ class ManageWindow(QWidget): action.signal_status.connect(self._change_label_status) action.signal_substatus.connect(self._change_label_substatus) action.signal_progress.connect(self._update_process_progress) + action.signal_root_password.connect(self._ask_root_password) self.signal_user_res.connect(action.confirm) + self.signal_root_password.connect(action.set_root_password) return action @@ -414,6 +416,12 @@ class ManageWindow(QWidget): self.thread_animate_progress.animate() self.signal_user_res.emit(res) + def _ask_root_password(self): + self.thread_animate_progress.pause() + password, valid = root.ask_root_password(self.i18n) + self.thread_animate_progress.animate() + self.signal_root_password.emit(password, valid) + def _show_message(self, msg: dict): self.thread_animate_progress.pause() dialog.show_message(title=msg['title'], body=msg['body'], type_=msg['type']) @@ -549,7 +557,7 @@ class ManageWindow(QWidget): pwd = None requires_root = self.manager.requires_root('uninstall', app.model) - if not is_root() and requires_root: + if not user.is_root() and requires_root: pwd, ok = ask_root_password(self.i18n) if not ok: @@ -865,7 +873,7 @@ class ManageWindow(QWidget): widgets=[UpdateToggleButton(None, self, self.i18n, clickable=False)]): pwd = None - if not is_root() and requires_root: + if not user.is_root() and requires_root: pwd, ok = ask_root_password(self.i18n) if not ok: @@ -983,7 +991,7 @@ class ManageWindow(QWidget): pwd = None requires_root = self.manager.requires_root('downgrade', pkgv.model) - if not is_root() and requires_root: + if not user.is_root() and requires_root: pwd, ok = ask_root_password(self.i18n) if not ok: @@ -1076,7 +1084,7 @@ class ManageWindow(QWidget): pwd = None requires_root = self.manager.requires_root('install', pkg.model) - if not is_root() and requires_root: + if not user.is_root() and requires_root: pwd, ok = ask_root_password(self.i18n) if not ok: @@ -1131,7 +1139,7 @@ class ManageWindow(QWidget): def execute_custom_action(self, pkg: PackageView, action: PackageAction): pwd = None - if not is_root() and action.requires_root: + if not user.is_root() and action.requires_root: pwd, ok = ask_root_password(self.i18n) if not ok: