fix: AUR installation receiving GPG key

This commit is contained in:
Vinicius Moreira
2019-09-20 18:33:53 -03:00
parent 57e17c79d8
commit 0d2770296c
8 changed files with 178 additions and 34 deletions

View File

@@ -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)])

View File

@@ -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
View 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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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 {}

View File

@@ -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 {}

View File

@@ -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 {}