mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 00:04:15 +02:00
fix: AUR installation receiving GPG key
This commit is contained in:
@@ -1,11 +1,16 @@
|
||||
import re
|
||||
from typing import Set, List
|
||||
|
||||
import requests
|
||||
|
||||
from bauh.api.http import HttpClient
|
||||
|
||||
URL_INFO = 'https://aur.archlinux.org/rpc/?v=5&type=info&'
|
||||
URL_SRC_INFO = 'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h='
|
||||
URL_SEARCH = 'https://aur.archlinux.org/rpc/?v=5&type=search&arg='
|
||||
|
||||
RE_SRCINFO_KEYS = re.compile(r'(\w+)\s+=\s+(.+)\n')
|
||||
|
||||
|
||||
def map_pkgbuild(pkgbuild: str) -> dict:
|
||||
return {attr: val.replace('"', '').replace("'", '').replace('(', '').replace(')', '') for attr, val in re.findall(r'\n(\w+)=(.+)', pkgbuild)}
|
||||
@@ -24,5 +29,24 @@ class AURClient:
|
||||
res = self.http_client.get_json(URL_INFO + self._map_names_as_queries(names))
|
||||
return res['results'] if res and res.get('results') else []
|
||||
|
||||
def get_src_info(self, name: str) -> dict:
|
||||
res = self.http_client.get(URL_SRC_INFO + name)
|
||||
|
||||
if res and res.text:
|
||||
info = {}
|
||||
for field in RE_SRCINFO_KEYS.findall(res.text):
|
||||
if field[0] not in info:
|
||||
info[field[0]] = field[1]
|
||||
else:
|
||||
if not isinstance(info[field[0]], list):
|
||||
info[field[0]] = [info[field[0]]]
|
||||
|
||||
info[field[0]].append(field[1])
|
||||
|
||||
if info.get('validpgpkeys') and isinstance(info['validpgpkeys'], str):
|
||||
info['validpgpkeys'] = [info['validpgpkeys']]
|
||||
|
||||
return info
|
||||
|
||||
def _map_names_as_queries(self, names) -> str:
|
||||
return '&'.join(['arg[{}]={}'.format(i, n) for i, n in enumerate(names)])
|
||||
|
||||
@@ -14,7 +14,7 @@ from bauh.api.abstract.model import PackageUpdate, PackageHistory, SoftwarePacka
|
||||
from bauh.api.abstract.view import MessageType
|
||||
from bauh.commons.html import bold
|
||||
from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, run_cmd, new_root_subprocess
|
||||
from bauh.gems.arch import BUILD_DIR, aur, pacman, makepkg, pkgbuild, message, confirmation, disk, git, suggestions
|
||||
from bauh.gems.arch import BUILD_DIR, aur, pacman, makepkg, pkgbuild, message, confirmation, disk, git, suggestions, gpg
|
||||
from bauh.gems.arch.aur import AURClient
|
||||
from bauh.gems.arch.mapper import ArchDataMapper
|
||||
from bauh.gems.arch.model import ArchPackage
|
||||
@@ -254,11 +254,14 @@ class ArchManager(SoftwareManager):
|
||||
'10_url': pkg.url_download,
|
||||
}
|
||||
|
||||
res_srcinfo = self.context.http_client.get(URL_SRC_INFO + pkg.name)
|
||||
srcinfo = self.aur_client.get_src_info(pkg.name)
|
||||
|
||||
if res_srcinfo and res_srcinfo.text:
|
||||
info['11_dependson'] = '\n'.join(pkgbuild.read_depends_on(res_srcinfo.text))
|
||||
info['12_optdepends'] = '\n'.join(pkgbuild.read_optdeps(res_srcinfo.text))
|
||||
if srcinfo:
|
||||
if srcinfo.get('depends'):
|
||||
info['11_dependson'] = srcinfo['depends']
|
||||
|
||||
if srcinfo.get('optdepends'):
|
||||
info['12_optdepends'] = srcinfo['optdepends']
|
||||
|
||||
if pkg.pkgbuild:
|
||||
info['13_pkg_build'] = pkg.pkgbuild
|
||||
@@ -344,7 +347,7 @@ class ArchManager(SoftwareManager):
|
||||
def _make_pkg(self, pkgname: str, root_password: str, handler: ProcessHandler, build_dir: str, project_dir: str, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
|
||||
|
||||
self._update_progress(handler.watcher, 50, change_progress)
|
||||
if not self._install_missings_deps(pkgname, root_password, handler, project_dir):
|
||||
if not self._install_missings_deps_and_keys(pkgname, root_password, handler, project_dir):
|
||||
return False
|
||||
|
||||
# building main package
|
||||
@@ -373,30 +376,44 @@ class ArchManager(SoftwareManager):
|
||||
|
||||
return False
|
||||
|
||||
def _install_missings_deps(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str) -> bool:
|
||||
def _install_missings_deps_and_keys(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str) -> bool:
|
||||
handler.watcher.change_substatus(self.i18n['arch.checking.deps'].format(bold(pkgname)))
|
||||
missing_deps = makepkg.check_missing_deps(pkgdir, handler.watcher)
|
||||
check_res = makepkg.check_missing_deps(pkgdir, handler.watcher)
|
||||
|
||||
if missing_deps:
|
||||
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in missing_deps}
|
||||
dep_mirrors = self._map_mirrors(depnames)
|
||||
if check_res:
|
||||
|
||||
for dep in depnames: # cheking if a dependency could not be found in any mirror
|
||||
if dep not in dep_mirrors:
|
||||
message.show_dep_not_found(dep, self.i18n, handler.watcher)
|
||||
if check_res.get('gpg_key'):
|
||||
if handler.watcher.request_confirmation(title=self.i18n['arch.aur.install.unknown_key.title'],
|
||||
body=self.i18n['arch.install.aur.unknown_key.body'].format(bold(pkgname), bold(check_res['gpg_key']))):
|
||||
handler.watcher.change_substatus(self.i18n['arch.aur.install.unknown_key.status'].format(bold(check_res['gpg_key'])))
|
||||
if not handler.handle(gpg.receive_key(check_res['gpg_key'])):
|
||||
handler.watcher.show_message(title=self.i18n['error'],
|
||||
body=self.i18n['arch.aur.install.unknown_key.receive_error'].format(bold(check_res['gpg_key'])))
|
||||
return False
|
||||
else:
|
||||
handler.watcher.print(self.i18n['action.cancelled'])
|
||||
return False
|
||||
|
||||
handler.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(pkgname)))
|
||||
if check_res.get('missing_deps'):
|
||||
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in check_res['missing_deps']}
|
||||
dep_mirrors = self._map_mirrors(depnames)
|
||||
|
||||
if not confirmation.request_install_missing_deps(pkgname, dep_mirrors, handler.watcher, self.i18n):
|
||||
handler.watcher.print(self.i18n['action.cancelled'])
|
||||
return False
|
||||
for dep in depnames: # cheking if a dependency could not be found in any mirror
|
||||
if dep not in dep_mirrors:
|
||||
message.show_dep_not_found(dep, self.i18n, handler.watcher)
|
||||
return False
|
||||
|
||||
dep_not_installed = self._install_deps(depnames, dep_mirrors, root_password, handler, change_progress=False)
|
||||
handler.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(pkgname)))
|
||||
|
||||
if dep_not_installed:
|
||||
message.show_dep_not_installed(handler.watcher, pkgname, dep_not_installed, self.i18n)
|
||||
return False
|
||||
if not confirmation.request_install_missing_deps(pkgname, dep_mirrors, handler.watcher, self.i18n):
|
||||
handler.watcher.print(self.i18n['action.cancelled'])
|
||||
return False
|
||||
|
||||
dep_not_installed = self._install_deps(depnames, dep_mirrors, root_password, handler, change_progress=False)
|
||||
|
||||
if dep_not_installed:
|
||||
message.show_dep_not_installed(handler.watcher, pkgname, dep_not_installed, self.i18n)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -478,6 +495,41 @@ class ArchManager(SoftwareManager):
|
||||
if change_progress:
|
||||
watcher.change_progress(val)
|
||||
|
||||
def _import_pgp_keys(self, pkgname: str, root_password: str, handler: ProcessHandler):
|
||||
srcinfo = self.aur_client.get_src_info(pkgname)
|
||||
|
||||
if srcinfo.get('validpgpkeys'):
|
||||
handler.watcher.print(self.i18n['arch.aur.install.verifying_pgp'])
|
||||
keys_to_download = [key for key in srcinfo['validpgpkeys'] if not pacman.verify_pgp_key(key)]
|
||||
|
||||
if keys_to_download:
|
||||
keys_str = ''.join(
|
||||
['<br/><span style="font-weight:bold"> - {}</span>'.format(k) for k in keys_to_download])
|
||||
msg_body = '{}:<br/>{}<br/><br/>{}'.format(self.i18n['arch.aur.install.pgp.body'].format(bold(pkgname)),
|
||||
keys_str, self.i18n['ask.continue'])
|
||||
|
||||
if handler.watcher.request_confirmation(title=self.i18n['arch.aur.install.pgp.title'], body=msg_body):
|
||||
for key in keys_to_download:
|
||||
handler.watcher.change_substatus(self.i18n['arch.aur.install.pgp.substatus'].format(bold(key)))
|
||||
if not handler.handle(pacman.receive_key(key, root_password)):
|
||||
handler.watcher.show_message(title=self.i18n['error'],
|
||||
body=self.i18n['arch.aur.install.pgp.receive_fail'].format(
|
||||
bold(key)),
|
||||
type_=MessageType.ERROR)
|
||||
return False
|
||||
|
||||
if not handler.handle(pacman.sign_key(key, root_password)):
|
||||
handler.watcher.show_message(title=self.i18n['error'],
|
||||
body=self.i18n['arch.aur.install.pgp.sign_fail'].format(
|
||||
bold(key)),
|
||||
type_=MessageType.ERROR)
|
||||
return False
|
||||
|
||||
handler.watcher.change_substatus(self.i18n['arch.aur.install.pgp.success'])
|
||||
else:
|
||||
handler.watcher.print(self.i18n['action.cancelled'])
|
||||
return False
|
||||
|
||||
def _install_from_aur(self, pkgname: str, root_password: str, handler: ProcessHandler, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
|
||||
app_build_dir = '{}/build_{}'.format(BUILD_DIR, int(time.time()))
|
||||
|
||||
|
||||
5
bauh/gems/arch/gpg.py
Normal file
5
bauh/gems/arch/gpg.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from bauh.commons.system import SystemProcess, new_subprocess
|
||||
|
||||
|
||||
def receive_key(key: str) -> SystemProcess:
|
||||
return SystemProcess(new_subprocess(['gpg', '--recv-key', key]), check_error_output=False)
|
||||
@@ -1,15 +1,15 @@
|
||||
import re
|
||||
from typing import List
|
||||
|
||||
from bauh.api.abstract.handler import ProcessWatcher
|
||||
from bauh.commons.system import new_subprocess
|
||||
|
||||
RE_DEPS_PATTERN = re.compile(r'\n?\s+->\s(.+)\n')
|
||||
RE_UNKNOWN_GPG_KEY = re.compile(r'\(unknown public key (\w+)\)')
|
||||
|
||||
|
||||
def check_missing_deps(pkgdir: str, watcher: ProcessWatcher) -> List[str]:
|
||||
def check_missing_deps(pkgdir: str, watcher: ProcessWatcher) -> dict:
|
||||
depcheck = new_subprocess(['makepkg', '-L', '--check'], cwd=pkgdir)
|
||||
|
||||
res = {}
|
||||
for o in depcheck.stdout:
|
||||
if o:
|
||||
line = o.decode().strip()
|
||||
@@ -33,4 +33,10 @@ def check_missing_deps(pkgdir: str, watcher: ProcessWatcher) -> List[str]:
|
||||
error_str = ''.join(error_lines)
|
||||
|
||||
if 'Missing dependencies' in error_str:
|
||||
return RE_DEPS_PATTERN.findall(error_str)
|
||||
res['missing_deps'] = RE_DEPS_PATTERN.findall(error_str)
|
||||
|
||||
gpg_keys = RE_UNKNOWN_GPG_KEY.findall(error_str)
|
||||
if gpg_keys:
|
||||
res['gpg_key'] = gpg_keys[0]
|
||||
|
||||
return res
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import re
|
||||
import subprocess
|
||||
from typing import List, Set
|
||||
|
||||
from bauh.commons.system import run_cmd, new_subprocess, new_root_subprocess, SystemProcess
|
||||
from bauh.api.abstract.handler import ProcessWatcher
|
||||
from bauh.commons.system import run_cmd, new_subprocess, new_root_subprocess, SystemProcess, ProcessHandler
|
||||
|
||||
RE_DEPS = re.compile(r'[\w\-_]+:[\s\w_\-\.]+\s+\[\w+\]')
|
||||
|
||||
@@ -51,7 +53,8 @@ def get_info_dict(pkg_name: str) -> dict:
|
||||
info_dict = {}
|
||||
for info_data in info_list:
|
||||
attr = info_data[0].lower().strip()
|
||||
info_dict[attr] = info_data[1] if '\n' not in info_data[1] else ' '.join([l.strip() for l in info_data[1].split('\n')])
|
||||
info_dict[attr] = info_data[1] if '\n' not in info_data[1] else ' '.join(
|
||||
[l.strip() for l in info_data[1].split('\n')])
|
||||
|
||||
if info_dict[attr] == 'None':
|
||||
info_dict[attr] = None
|
||||
@@ -76,7 +79,8 @@ def list_and_map_installed() -> dict: # returns a dict with with package names
|
||||
allinfo = new_subprocess(['pacman', '-Qi'], stdin=installed).stdout # retrieving all installed packages info
|
||||
|
||||
pkgs, current_pkg = {'mirrors': {}, 'not_signed': {}}, {}
|
||||
for out in new_subprocess(['grep', '-E', '(Name|Description|Version|Validated By)'], stdin=allinfo).stdout: # filtering only the Name and Validated By fields:
|
||||
for out in new_subprocess(['grep', '-E', '(Name|Description|Version|Validated By)'],
|
||||
stdin=allinfo).stdout: # filtering only the Name and Validated By fields:
|
||||
if out:
|
||||
line = out.decode()
|
||||
|
||||
@@ -90,7 +94,8 @@ def list_and_map_installed() -> dict: # returns a dict with with package names
|
||||
elif line.startswith('Validated'):
|
||||
|
||||
if line.split(':')[1].strip().lower() == 'none':
|
||||
pkgs['not_signed'][current_pkg['name']] = {'version': current_pkg['version'], 'description': current_pkg['description']}
|
||||
pkgs['not_signed'][current_pkg['name']] = {'version': current_pkg['version'],
|
||||
'description': current_pkg['description']}
|
||||
|
||||
current_pkg = {}
|
||||
|
||||
@@ -98,7 +103,6 @@ def list_and_map_installed() -> dict: # returns a dict with with package names
|
||||
|
||||
|
||||
def install_as_process(pkgpath: str, root_password: str, aur: bool, pkgdir: str = '.') -> SystemProcess:
|
||||
|
||||
if aur:
|
||||
cmd = ['pacman', '-U', pkgpath, '--noconfirm'] # pkgpath = install file path
|
||||
else:
|
||||
@@ -143,3 +147,23 @@ def list_bin_paths(pkgnames: Set[str]) -> List[str]:
|
||||
bin_paths.append(line)
|
||||
|
||||
return bin_paths
|
||||
|
||||
|
||||
def verify_pgp_key(key: str) -> bool:
|
||||
list_key = new_subprocess(['pacman-key', '-l']).stdout
|
||||
|
||||
for out in new_subprocess(['grep', " " + key], stdin=list_key).stdout:
|
||||
if out:
|
||||
line = out.decode().strip()
|
||||
if line and key in line:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def receive_key(key: str, root_password: str) -> SystemProcess:
|
||||
return SystemProcess(new_root_subprocess(['pacman-key', '-r', key], root_password=root_password), check_error_output=False)
|
||||
|
||||
|
||||
def sign_key(key: str, root_password: str) -> SystemProcess:
|
||||
return SystemProcess(new_root_subprocess(['pacman-key', '--lsign-key', key], root_password=root_password), check_error_output=False)
|
||||
|
||||
@@ -47,4 +47,15 @@ arch.install.optdeps.request.body={} was succesfully installed ! There are some
|
||||
arch.install.optdep.error=Could not install the optional package {}
|
||||
arch.optdeps.checking=Checking {} optional dependencies
|
||||
arch.warning.disabled={} seems not to be installed. It will not be possible to manage Arch / AUR packages.
|
||||
arch.warning.git={} seems not to be installed. It will not be possible to downgrade AUR packages.
|
||||
arch.warning.git={} seems not to be installed. It will not be possible to downgrade AUR packages.
|
||||
arch.aur.install.verifying_pgp=Verifying PGP keys
|
||||
arch.aur.install.pgp.title=PGP keys required
|
||||
arch.aur.install.pgp.body=To install {} is necessary to receive the following PGP keys
|
||||
arch.aur.install.pgp.substatus=Receiving PGP key {}
|
||||
arch.aur.install.pgp.success=PGP keys received and signed
|
||||
arch.aur.install.pgp.sign_fail=Could not sign PGP key {}
|
||||
arch.aur.install.pgp.receive_fail=Could not receive PGP key {}
|
||||
arch.aur.install.unknown_key.title=Public key required
|
||||
arch.install.aur.unknown_key.body=To continue {} installation is necessary to trust the following public key {}
|
||||
arch.aur.install.unknown_key.status=Receiving public key {}
|
||||
arch.aur.install.unknown_key.receive_error=Could not receive public key {}
|
||||
@@ -79,4 +79,15 @@ arch.install.optdeps.request.body=¡{} se instaló correctamente! También hay a
|
||||
arch.install.optdep.error=No se pudo instalar el paquete opcional {}
|
||||
arch.optdeps.checking=Verificando las dependencias opcionales de {}
|
||||
arch.warning.disabled={} parece no estar instalado. No será posible administrar paquetes Arch / AUR.
|
||||
arch.warning.git={} parece no estar instalado. No será posible revertir las versiones de paquetes Arch / AUR.
|
||||
arch.warning.git={} parece no estar instalado. No será posible revertir las versiones de paquetes Arch / AUR.
|
||||
arch.aur.install.verifying_pgp=Verificando claves PGP
|
||||
arch.aur.install.pgp.title=Claves PGP necesarias
|
||||
arch.aur.install.pgp.body=Para instalar {} es necesario recibir las siguientes claves PGP
|
||||
arch.aur.install.pgp.substatus=Recibiendo clave PGP {}
|
||||
arch.aur.install.pgp.success=Claves PGP recibidas y firmadas
|
||||
arch.aur.install.pgp.sign_fail=No fue posible firmar la clave PGP {}
|
||||
arch.aur.install.pgp.sign_fail=No fue posible recibir la clave PGP {}
|
||||
arch.aur.install.unknown_key.title=Clave pública necesaria
|
||||
arch.aur.install.unknown_key.status=Recibiendo la clave pública {}
|
||||
arch.aur.install.unknown_key.receive_error=No fue posible recibir la clave pública {}
|
||||
arch.install.aur.unknown_key.body=Para continuar la instalación de {} es necesario confiar en la siguiente clave pública {}
|
||||
@@ -79,4 +79,15 @@ arch.install.optdeps.request.body={} foi instalado com sucesso ! Existem alguns
|
||||
arch.install.optdep.error=Não foi possível instalar o pacote opcional {}
|
||||
arch.optdeps.checking=Verificando as dependências opcionais de {}
|
||||
arch.warning.disabled={} parece não estar instalado. Não será possível gerenciar pacotes Arch / AUR.
|
||||
arch.warning.git={} parece não estar instalado. Não será possível reverter versões de pacotes Arch / AUR.
|
||||
arch.warning.git={} parece não estar instalado. Não será possível reverter versões de pacotes Arch / AUR.
|
||||
arch.aur.install.verifying_pgp=Verificando chaves PGP
|
||||
arch.aur.install.pgp.title=Chaves PGP necessárias
|
||||
arch.aur.install.pgp.body=Para instalar {} é necessário receber as seguintes chaves PGP
|
||||
arch.aur.install.pgp.substatus=Recebendo chave PGP {}
|
||||
arch.aur.install.pgp.success=Chaves PGP recebidas e assinadas
|
||||
arch.aur.install.pgp.sign_fail=Não foi possível assinar a chave PGP {}
|
||||
arch.aur.install.pgp.receive_fail=Não foi possível receber a chave PGP {}
|
||||
arch.aur.install.unknown_key.title=Chave pública necessária
|
||||
arch.install.aur.unknown_key.body=Para continuar a instalação de {} é ncessário confiar na seguinte chave pública {}
|
||||
arch.aur.install.unknown_key.status=Recebendo a chave pública {}
|
||||
arch.aur.install.unknown_key.receive_error=Não fui possível receber a chave pública {}
|
||||
Reference in New Issue
Block a user