From c21a45590946feb69f8b699bedb62d25ae2d4a0f Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Sun, 17 Mar 2019 22:23:16 +0100 Subject: [PATCH 01/67] Dump ynh log if an app script fails --- data/helpers.d/system | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/data/helpers.d/system b/data/helpers.d/system index fd552db7f..87529c733 100644 --- a/data/helpers.d/system +++ b/data/helpers.d/system @@ -27,6 +27,32 @@ ynh_exit_properly () { echo -e "!!\n $app's script has encountered an error. Its execution was cancelled.\n!!" >&2 + # Unset xtrace to not spoil the log + set +x + local ynh_log="/var/log/yunohost/yunohost-cli.log" + # Wait for the log to be fill with the data until the crash. + local timeout=0 + while ! tail --lines=20 "$ynh_log" | grep --quiet "ynh_exit_properly" + do + ((timeout++)) + if [ $timeout -eq 500 ]; then + break + fi + done + set -x + + echo -e "\e[34m\e[1mLog extract:\e[0m" >&2 + # Tail the last 30 lines of log of YunoHost + # But remove all lines after "ynh_exit_properly" + # Remove the timestamp at the beginning of the line + # Remove "yunohost.hook..." + # Add DEBUG and color it at the beginning of each log line. + echo -e "$(tail --lines=30 "$ynh_log" \ + | sed '1,/ynh_exit_properly/!d' \ + | sed 's/^[[:digit:]: ,-]*//g' \ + | sed 's/ *yunohost.hook.*\]//g' \ + | sed 's/^/\\e[34m\\e[1m[DEBUG]\\e[0m: /g')" >&2 + if type -t ynh_clean_setup > /dev/null; then # Check if the function exist in the app script. ynh_clean_setup # Call the function to do specific cleaning for the app. fi From f71117b401b5d33f703e31287913c6f7fff94076 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Sun, 17 Mar 2019 23:30:57 +0100 Subject: [PATCH 02/67] Support yunohost-api.log --- data/helpers.d/system | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/data/helpers.d/system b/data/helpers.d/system index 87529c733..3c065e592 100644 --- a/data/helpers.d/system +++ b/data/helpers.d/system @@ -1,5 +1,14 @@ #!/bin/bash +# Determine whether the scripts is executed from a terminal or the admin JS. +# +# [internal] +ynh_is_term () { + # Return true if $TERM return xterm-XXXcolor + # Which means the script is executed from a terminal, not from the admin JS + [[ "$TERM" =~ "xterm" ]] +} + # Manage a fail of the script # # [internal] @@ -29,7 +38,13 @@ ynh_exit_properly () { # Unset xtrace to not spoil the log set +x - local ynh_log="/var/log/yunohost/yunohost-cli.log" + + if ynh_is_term + then + local ynh_log="/var/log/yunohost/yunohost-cli.log" + else + local ynh_log="/var/log/yunohost/yunohost-api.log" + fi # Wait for the log to be fill with the data until the crash. local timeout=0 while ! tail --lines=20 "$ynh_log" | grep --quiet "ynh_exit_properly" From 73d42fd51ab25278fde4fc9e1a9804d76cd37a2a Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Tue, 19 Mar 2019 19:30:30 +0100 Subject: [PATCH 03/67] [enh] Avoid to send simultaneously too much emails --- data/templates/postfix/main.cf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index c38896a3f..aea68cd28 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -154,3 +154,7 @@ smtpd_milters = inet:localhost:11332 # Skip email without checking if milter has died milter_default_action = accept + +# Avoid to send simultaneously too much emails +smtp_destination_concurrency_limit = 1 +default_destination_rate_delay = 12s From de7a66ff0e76f21463f22ee151530dbb4f0d7bcb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 22 Mar 2019 01:14:45 +0100 Subject: [PATCH 04/67] Rework system-part of tools_update... --- src/yunohost/tools.py | 51 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index a011b1546..482d9305b 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -41,7 +41,7 @@ from moulinette import msettings, msignals, m18n from moulinette.core import init_authenticator from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger -from moulinette.utils.process import check_output +from moulinette.utils.process import check_output, call_async_output from moulinette.utils.filesystem import read_json, write_to_json from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, app_list, _install_appslist_fetch_cron from yunohost.domain import domain_add, domain_list, _get_maindomain, _set_maindomain @@ -469,23 +469,54 @@ def tools_update(ignore_apps=False, ignore_packages=False): # "packages" will list upgradable packages packages = [] if not ignore_packages: - cache = apt.Cache() # Update APT cache + # LC_ALL=C is here to make sure the results are in english + command = "LC_ALL=C apt update" + # TODO : add @is_unit_operation to tools_update so that the + # debug output can be fetched when there's an issue... + callbacks = ( + # stdout goes to debug + lambda l: logger.debug(l.rstrip()), + # stderr goes to warning + # FIXME : filter the damn "CLI interface not stable" from apt >.> + lambda l: logger.warning(l.rstrip()), + ) + logger.info(m18n.n('updating_apt_cache')) - if not cache.update(): + + returncode = call_async_output(command, callbacks, shell=True) + + if returncode != 0: + + # TODO : here, we should run something like a + # `cat /etc/apt/sources.list /etc/apt/sources.list.d/*` + # and append it to the error message to improve debugging + raise YunohostError('update_cache_failed') - cache.open(None) - cache.upgrade(True) + # List upgradable packages + # LC_ALL=C is here to make sure the results are in english + upgradable_raw = check_output("LC_ALL=C apt list --upgradable") - # Add changelogs to the result - for pkg in cache.get_changes(): + # Dirty parsing of the output + upgradable_raw = [l.strip() for l in upgradable_raw.split("\n") if l.strip()] + for line in upgradable_raw: + # Remove stupid warning and verbose messages >.> + if "apt does not have a stable CLI interface" in line or "Listing..." in line: + continue + # line should look like : + # yunohost/stable 3.5.0.2+201903211853 all [upgradable from: 3.4.2.4+201903080053] + line = line.split() + if len(line) != 6: + logger.warning("Failed to parse this line : %s" % ' '.join(line)) + continue packages.append({ - 'name': pkg.name, - 'fullname': pkg.fullname, - 'changelog': pkg.get_changelog() + "name": line[0].split("/")[0], + "new_version": line[1], + "current_version": line[5].strip("]"), }) + logger.debug(m18n.n('done')) # "apps" will list upgradable packages From 51fe6fea277e337732e47239158e68d01a1617b9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 22 Mar 2019 04:01:08 +0100 Subject: [PATCH 05/67] Factorize function to list upgradable packages --- src/yunohost/tools.py | 50 ++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 482d9305b..93e3dd177 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -495,28 +495,7 @@ def tools_update(ignore_apps=False, ignore_packages=False): raise YunohostError('update_cache_failed') - # List upgradable packages - # LC_ALL=C is here to make sure the results are in english - upgradable_raw = check_output("LC_ALL=C apt list --upgradable") - - # Dirty parsing of the output - upgradable_raw = [l.strip() for l in upgradable_raw.split("\n") if l.strip()] - for line in upgradable_raw: - # Remove stupid warning and verbose messages >.> - if "apt does not have a stable CLI interface" in line or "Listing..." in line: - continue - # line should look like : - # yunohost/stable 3.5.0.2+201903211853 all [upgradable from: 3.4.2.4+201903080053] - line = line.split() - if len(line) != 6: - logger.warning("Failed to parse this line : %s" % ' '.join(line)) - continue - packages.append({ - "name": line[0].split("/")[0], - "new_version": line[1], - "current_version": line[5].strip("]"), - }) - + packages = list(_list_upgradable_apt_packages()) logger.debug(m18n.n('done')) # "apps" will list upgradable packages @@ -545,6 +524,33 @@ def tools_update(ignore_apps=False, ignore_packages=False): return {'packages': packages, 'apps': apps} +# TODO : move this to utils/packages.py ? +def _list_upgradable_apt_packages(): + + # List upgradable packages + # LC_ALL=C is here to make sure the results are in english + upgradable_raw = check_output("LC_ALL=C apt list --upgradable") + + # Dirty parsing of the output + upgradable_raw = [l.strip() for l in upgradable_raw.split("\n") if l.strip()] + for line in upgradable_raw: + # Remove stupid warning and verbose messages >.> + if "apt does not have a stable CLI interface" in line or "Listing..." in line: + continue + # line should look like : + # yunohost/stable 3.5.0.2+201903211853 all [upgradable from: 3.4.2.4+201903080053] + line = line.split() + if len(line) != 6: + logger.warning("Failed to parse this line : %s" % ' '.join(line)) + continue + + yield { + "name": line[0].split("/")[0], + "new_version": line[1], + "current_version": line[5].strip("]"), + } + + @is_unit_operation() def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=False): """ From 096c2a7d7bfa6030206078bc9c19ec2e9305b9c7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 22 Mar 2019 04:02:38 +0100 Subject: [PATCH 06/67] Rework system-part of tools_upgrade... --- src/yunohost/tools.py | 170 +++++++++++++++++++++++++++++------------- 1 file changed, 119 insertions(+), 51 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 93e3dd177..589e49a3f 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -33,6 +33,7 @@ import socket from xmlrpclib import Fault from importlib import import_module from collections import OrderedDict +from datetime import datetime import apt import apt.progress @@ -567,61 +568,127 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal failure = False - # Retrieve interface - is_api = True if msettings.get('interface') == 'api' else False - if not ignore_packages: - apt.apt_pkg.init() - apt.apt_pkg.config.set("DPkg::Options::", "--force-confdef") - apt.apt_pkg.config.set("DPkg::Options::", "--force-confold") - - cache = apt.Cache() - cache.open(None) - cache.upgrade(True) - - # If API call - if is_api: - critical_packages = ("moulinette", "yunohost", - "yunohost-admin", "ssowat", "python") - critical_upgrades = set() - - for pkg in cache.get_changes(): - if pkg.name in critical_packages: - critical_upgrades.add(pkg.name) - # Temporarily keep package ... - pkg.mark_keep() - - # ... and set a hourly cron up to upgrade critical packages - if critical_upgrades: - logger.info(m18n.n('packages_upgrade_critical_later', - packages=', '.join(critical_upgrades))) - with open('/etc/cron.d/yunohost-upgrade', 'w+') as f: - f.write('00 * * * * root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin apt-get install %s -y && rm -f /etc/cron.d/yunohost-upgrade\n' % ' '.join(critical_upgrades)) - - if cache.get_changes(): - logger.info(m18n.n('upgrading_packages')) - - operation_logger.start() - try: - os.environ["DEBIAN_FRONTEND"] = "noninteractive" - # Apply APT changes - # TODO: Logs output for the API - cache.commit(apt.progress.text.AcquireProgress(), - apt.progress.base.InstallProgress()) - except Exception as e: - failure = True - logger.warning('unable to upgrade packages: %s' % str(e)) - logger.error(m18n.n('packages_upgrade_failed')) - operation_logger.error(m18n.n('packages_upgrade_failed')) - else: - logger.info(m18n.n('done')) - operation_logger.success() - finally: - del os.environ["DEBIAN_FRONTEND"] - else: + # Check that there's indeed some packages to upgrade + upgradables = list(_list_upgradable_apt_packages()) + if not upgradables: logger.info(m18n.n('packages_no_upgrade')) + logger.info(m18n.n('upgrading_packages')) + operation_logger.start() + + # Critical packages are packages that we can't just upgrade + # randomly from yunohost itself... upgrading them is likely to + critical_packages = ("moulinette", "yunohost", "yunohost-admin", "ssowat", "python") + + critical_packages_upgradable = [p for p in upgradables if p["name"] in critical_packages] + noncritical_packages_upgradable = [p for p in upgradables if p["name"] not in critical_packages] + + # Prepare dist-upgrade command + dist_upgrade = "DEBIAN_FRONTEND=noninteractive" + dist_upgrade += " APT_LISTCHANGES_FRONTEND=none" + dist_upgrade += " apt-get" + dist_upgrade += " --fix-broken --show-upgraded --assume-yes" + for conf_flag in ["old", "miss", "def"]: + dist_upgrade += ' -o Dpkg::Options::="--force-conf{}"'.format(conf_flag) + dist_upgrade += " dist-upgrade" + + # + # "Regular" packages upgrade + # + if not failure and noncritical_packages_upgradable: + + # TODO : i18n + logger.info("Upgrading 'regular' (non-yunohost-related) packages ...") + + # TODO : factorize this in utils/packages.py ? + # Mark all critical packages as held + for package in critical_packages: + check_output("apt-mark hold %s" % package) + # Doublecheck with apt-mark showhold that packages are indeed held ... + held_packages = check_output("apt-mark showhold").split("\n") + if any(p not in held_packages for p in critical_packages): + failure = True + logger.warning('Unable to hold critical packages ...') + logger.error(m18n.n('packages_upgrade_failed')) + # FIXME : watdo here, should this be an exception or just an + # error + operation_logger.error(m18n.n('packages_upgrade_failed')) + + if not failure: + logger.debug("Running apt command :\n{}".format(dist_upgrade)) + + callbacks = ( + lambda l: logger.info(l.rstrip()), + lambda l: logger.warning(l.rstrip()), + ) + returncode = call_async_output(dist_upgrade, callbacks, shell=True) + if returncode != 0: + failure = True + logger.warning('unable to upgrade packages: %s' % ', '.join(noncritical_packages_upgradable)) + logger.error(m18n.n('packages_upgrade_failed')) + operation_logger.error(m18n.n('packages_upgrade_failed')) + + # + # Critical packages upgrade + # + if not failure and critical_packages_upgradable: + + # TODO : i18n + logger.info("Upgrading 'special' (yunohost-related) packages ...") + + # TODO : factorize this in utils/packages.py ? + # Mark all critical packages as unheld + for package in critical_packages: + check_output("apt-mark unhold %s" % package) + # Doublecheck with apt-mark showhold that packages are indeed unheld ... + unheld_packages = check_output("apt-mark showhold").split("\n") + if any(p in unheld_packages for p in critical_packages): + failure = True + logger.warning('Unable to unhold critical packages ...') + logger.error(m18n.n('packages_upgrade_failed')) + # FIXME : watdo here, should this be an exception or just an + # error + operation_logger.error(m18n.n('packages_upgrade_failed')) + + # + # Here we use a dirty hack to run a command after the current + # "yunohost tools upgrade", because the upgrade of yunohost + # will also trigger other yunohost commands (e.g. "yunohost tools migrations migrate") + # (also the upgrade of the package, if executed from the webadmin, is + # likely to kill/restart the api which is in turn likely to kill this + # command before it ends...) + # + + logfile = "/var/log/yunohost/special_upgrade_%s.log" % datetime.utcnow().strftime("%Y%m%d_%H%M%S") + command = dist_upgrade + " 2>&1 | tee -a {}".format(logfile) + + MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" + wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK) + + # TODO : i18n + upgrade_completed = "YunoHost package upgrade completed ! Press [enter] to get the command line back" + command = "({} && {}; echo '{}') &".format(wait_until_end_of_yunohost_command, + command, + upgrade_completed) + + logger.debug("Running command :\n{}".format(command)) + os.system(command) + + # TODO / FIXME : return from this function immediately, + # otherwise the apps upgrade might happen and it's gonna be a mess + + # FIXME / open question : what about "permanently" mark yunohost + # as "hold" to avoid accidental deletion of it... + # (so, only unhold it during the upgrade) + + if not failure: + + logger.info(m18n.n('done')) + operation_logger.success() + + if not ignore_apps: try: app_upgrade(auth) @@ -634,6 +701,7 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal logger.success(m18n.n('system_upgraded')) # Return API logs if it is an API call + is_api = True if msettings.get('interface') == 'api' else False if is_api: return {"log": service_log('yunohost-api', number="100").values()[0]} From ad0f65aad713e3f582f437af4d3bc63dc02044ca Mon Sep 17 00:00:00 2001 From: ljf Date: Fri, 22 Mar 2019 15:17:39 +0100 Subject: [PATCH 07/67] [enh] Log special upgrade into operation_logger --- src/yunohost/log.py | 20 ++++++++++++++++---- src/yunohost/tools.py | 12 +++++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 857cc3658..7b0401e5b 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -318,14 +318,27 @@ class OperationLogger(object): self.flush() self._register_log() + @property + def md_path(self): + """ + Metadata path file + """ + return os.path.join(self.path, self.name + METADATA_FILE_EXT) + + @property + def log_path(self): + """ + Log path file + """ + return os.path.join(self.path, self.name + LOG_FILE_EXT) + def _register_log(self): """ Register log with a handler connected on log system """ # TODO add a way to not save password on app installation - filename = os.path.join(self.path, self.name + LOG_FILE_EXT) - self.file_handler = FileHandler(filename) + self.file_handler = FileHandler(self.log_path) self.file_handler.formatter = Formatter('%(asctime)s: %(levelname)s - %(message)s') # Listen to the root logger @@ -337,8 +350,7 @@ class OperationLogger(object): Write or rewrite the metadata file with all metadata known """ - filename = os.path.join(self.path, self.name + METADATA_FILE_EXT) - with open(filename, 'w') as outfile: + with open(self.md_path, 'w') as outfile: yaml.safe_dump(self.metadata, outfile, default_flow_style=False) @property diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 589e49a3f..6120ed3a1 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -660,18 +660,20 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal # likely to kill/restart the api which is in turn likely to kill this # command before it ends...) # - - logfile = "/var/log/yunohost/special_upgrade_%s.log" % datetime.utcnow().strftime("%Y%m%d_%H%M%S") + logfile = operation_logger.log_path command = dist_upgrade + " 2>&1 | tee -a {}".format(logfile) MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK) + update_log_metadata = "sed -i \"s/ended_at: .*$/ended_at: $(date -u +'%Y-%m-%d %H:%M:%S.%N')/\" {}" + update_log_metadata = update_log_metadata.format(operation_logger.md_path) # TODO : i18n upgrade_completed = "YunoHost package upgrade completed ! Press [enter] to get the command line back" - command = "({} && {}; echo '{}') &".format(wait_until_end_of_yunohost_command, - command, - upgrade_completed) + command = "({} && {} && {}; echo '{}') &".format(wait_until_end_of_yunohost_command, + command, + update_log_metadata, + upgrade_completed) logger.debug("Running command :\n{}".format(command)) os.system(command) From ac05ae655721469255a25885c8a451ede31ce6ca Mon Sep 17 00:00:00 2001 From: ljf Date: Fri, 22 Mar 2019 15:32:51 +0100 Subject: [PATCH 08/67] [enh] Support success info with criticla upgrades --- src/yunohost/tools.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 6120ed3a1..9b27b340a 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -665,15 +665,17 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK) + mark_success = "(echo 'success: true' | tee -a {})".format(operation_logger.md_path) update_log_metadata = "sed -i \"s/ended_at: .*$/ended_at: $(date -u +'%Y-%m-%d %H:%M:%S.%N')/\" {}" update_log_metadata = update_log_metadata.format(operation_logger.md_path) # TODO : i18n upgrade_completed = "YunoHost package upgrade completed ! Press [enter] to get the command line back" - command = "({} && {} && {}; echo '{}') &".format(wait_until_end_of_yunohost_command, - command, - update_log_metadata, - upgrade_completed) + command = "({} && {} && {}; {}; echo '{}') &".format(wait_until_end_of_yunohost_command, + command, + mark_success, + update_log_metadata, + upgrade_completed) logger.debug("Running command :\n{}".format(command)) os.system(command) @@ -685,7 +687,7 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal # as "hold" to avoid accidental deletion of it... # (so, only unhold it during the upgrade) - if not failure: + elif not failure: logger.info(m18n.n('done')) operation_logger.success() From 15ac51098d9d2ae52eaa50929f843551310a4cd9 Mon Sep 17 00:00:00 2001 From: Romuald du Song Date: Thu, 28 Mar 2019 22:03:25 +0100 Subject: [PATCH 09/67] propose a setting to remove support for TLSv1 and TLSv1.1 --- data/hooks/conf_regen/19-postfix | 11 ++- data/templates/postfix/main.cf | 116 ++++++++++++++++--------------- locales/en.json | 1 + src/yunohost/settings.py | 7 ++ 4 files changed, 76 insertions(+), 59 deletions(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index a3ad70327..b37425984 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -2,6 +2,8 @@ set -e +. /usr/share/yunohost/helpers + do_pre_regen() { pending_dir=$1 @@ -20,9 +22,12 @@ do_pre_regen() { main_domain=$(cat /etc/yunohost/current_host) domain_list=$(sudo yunohost domain list --output-as plain --quiet | tr '\n' ' ') - cat main.cf \ - | sed "s/{{ main_domain }}/${main_domain}/g" \ - > "${postfix_dir}/main.cf" + # Support different strategy for security configurations + export compatibility="$(yunohost settings get 'security.postfix.compatibility')" + + export main_domain + export domain_list + ynh_render_template "main.cf" "${postfix_dir}/main.cf" cat postsrsd \ | sed "s/{{ main_domain }}/${main_domain}/g" \ diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index c38896a3f..e5a3875d4 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -33,7 +33,11 @@ smtpd_tls_key_file = /etc/yunohost/certs/{{ main_domain }}/key.pem smtpd_tls_exclude_ciphers = aNULL, MD5, DES, ADH, RC4, 3DES smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtpd_tls_loglevel=1 +{% if compatibility == "intermediate" %} smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3 +{% else %} +smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1 +{% endif %} smtpd_tls_mandatory_ciphers=high smtpd_tls_eecdh_grade = ultra @@ -58,7 +62,7 @@ alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases mydomain = {{ main_domain }} mydestination = localhost -relayhost = +relayhost = mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_command = procmail -a "$EXTENSION" mailbox_size_limit = 0 @@ -68,71 +72,71 @@ inet_interfaces = all #### Fit to the maximum message size to 30mb, more than allowed by GMail or Yahoo #### message_size_limit = 31457280 -# Virtual Domains Control -virtual_mailbox_domains = ldap:/etc/postfix/ldap-domains.cf -virtual_mailbox_maps = ldap:/etc/postfix/ldap-accounts.cf -virtual_mailbox_base = -virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf -virtual_alias_domains = -virtual_minimum_uid = 100 -virtual_uid_maps = static:vmail +# Virtual Domains Control +virtual_mailbox_domains = ldap:/etc/postfix/ldap-domains.cf +virtual_mailbox_maps = ldap:/etc/postfix/ldap-accounts.cf +virtual_mailbox_base = +virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf +virtual_alias_domains = +virtual_minimum_uid = 100 +virtual_uid_maps = static:vmail virtual_gid_maps = static:mail smtpd_sender_login_maps= ldap:/etc/postfix/ldap-accounts.cf -# Dovecot LDA -virtual_transport = dovecot +# Dovecot LDA +virtual_transport = dovecot dovecot_destination_recipient_limit = 1 -# Enable SASL authentication for the smtpd daemon -smtpd_sasl_auth_enable = yes -smtpd_sasl_type = dovecot -smtpd_sasl_path = private/auth -# Fix some outlook's bugs -broken_sasl_auth_clients = yes -# Reject anonymous connections -smtpd_sasl_security_options = noanonymous +# Enable SASL authentication for the smtpd daemon +smtpd_sasl_auth_enable = yes +smtpd_sasl_type = dovecot +smtpd_sasl_path = private/auth +# Fix some outlook's bugs +broken_sasl_auth_clients = yes +# Reject anonymous connections +smtpd_sasl_security_options = noanonymous smtpd_sasl_local_domain = -# Wait until the RCPT TO command before evaluating restrictions -smtpd_delay_reject = yes - -# Basics Restrictions -smtpd_helo_required = yes -strict_rfc821_envelopes = yes - -# Requirements for the connecting server -smtpd_client_restrictions = - permit_mynetworks, - permit_sasl_authenticated, - reject_rbl_client bl.spamcop.net, - reject_rbl_client cbl.abuseat.org, - reject_rbl_client zen.spamhaus.org, - permit - -# Requirements for the HELO statement -smtpd_helo_restrictions = - permit_mynetworks, - permit_sasl_authenticated, - reject_non_fqdn_hostname, - reject_invalid_hostname, - permit - -# Requirements for the sender address +# Wait until the RCPT TO command before evaluating restrictions +smtpd_delay_reject = yes + +# Basics Restrictions +smtpd_helo_required = yes +strict_rfc821_envelopes = yes + +# Requirements for the connecting server +smtpd_client_restrictions = + permit_mynetworks, + permit_sasl_authenticated, + reject_rbl_client bl.spamcop.net, + reject_rbl_client cbl.abuseat.org, + reject_rbl_client zen.spamhaus.org, + permit + +# Requirements for the HELO statement +smtpd_helo_restrictions = + permit_mynetworks, + permit_sasl_authenticated, + reject_non_fqdn_hostname, + reject_invalid_hostname, + permit + +# Requirements for the sender address smtpd_sender_restrictions = - reject_sender_login_mismatch, - permit_mynetworks, - permit_sasl_authenticated, - reject_non_fqdn_sender, + reject_sender_login_mismatch, + permit_mynetworks, + permit_sasl_authenticated, + reject_non_fqdn_sender, reject_unknown_sender_domain, - permit - -# Requirement for the recipient address -smtpd_recipient_restrictions = - permit_mynetworks, - permit_sasl_authenticated, - reject_non_fqdn_recipient, - reject_unknown_recipient_domain, + permit + +# Requirement for the recipient address +smtpd_recipient_restrictions = + permit_mynetworks, + permit_sasl_authenticated, + reject_non_fqdn_recipient, + reject_unknown_recipient_domain, reject_unauth_destination, permit diff --git a/locales/en.json b/locales/en.json index 694df0707..6ab91fd2e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -215,6 +215,7 @@ "global_settings_setting_security_password_admin_strength": "Admin password strength", "global_settings_setting_security_password_user_strength": "User password strength", "global_settings_setting_security_ssh_compatibility": "Compatibility vs. security tradeoff for the SSH server. Affects the ciphers (and other security-related aspects)", + "global_settings_setting_security_postfix_compatibility": "Compatibility vs. security tradeoff for the Postfix server. Affects the ciphers (and other security-related aspects)", "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discarding it and save it in /etc/yunohost/settings-unknown.json", "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Allow the use of (deprecated) DSA hostkey for the SSH daemon configuration", "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.", diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 671ad70e9..01f27ba83 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -44,6 +44,8 @@ DEFAULTS = OrderedDict([ "choices": ["intermediate", "modern"]}), ("security.nginx.compatibility", {"type": "enum", "default": "intermediate", "choices": ["intermediate", "modern"]}), + ("security.postfix.compatibility", {"type": "enum", "default": "intermediate", + "choices": ["intermediate", "modern"]}), ]) @@ -292,3 +294,8 @@ def reconfigure_nginx(setting_name, old_value, new_value): def reconfigure_ssh(setting_name, old_value, new_value): if old_value != new_value: service_regen_conf(names=['ssh']) + +@post_change_hook("security.postfix.compatibility") +def reconfigure_ssh(setting_name, old_value, new_value): + if old_value != new_value: + service_regen_conf(names=['postfix']) From a16fb94d82a04bdfbee9cc4647bd51690ea2415c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 21 Apr 2019 02:03:59 +0200 Subject: [PATCH 10/67] Only upgrade system or apps, not both at the same time --- data/actionsmap/yunohost.yml | 10 +++++----- src/yunohost/tools.py | 19 +++++++++++++------ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index fabdcb923..08188c8c8 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1563,11 +1563,11 @@ tools: authenticate: all authenticator: ldap-anonymous arguments: - --ignore-apps: - help: Ignore apps upgrade - action: store_true - --ignore-packages: - help: Ignore APT packages upgrade + --apps: + help: List of apps to upgrade (all by default) + nargs: "*" + --system: + help: Upgrade only the system packages action: store_true ### tools_diagnosis() diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index ff02e5e39..784f5d512 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -582,22 +582,29 @@ def _dump_sources_list(): @is_unit_operation() -def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=False): +def tools_upgrade(operation_logger, auth, apps=None, system=False): """ Update apps & package cache, then display changelog Keyword arguments: - ignore_apps -- Ignore apps upgrade - ignore_packages -- Ignore APT packages upgrade - + apps -- List of apps to upgrade (or [] to update all apps) + system -- True to upgrade system """ from yunohost.utils import packages if packages.dpkg_is_broken(): raise YunohostError("dpkg_is_broken") + if system is not False and apps is not None: + # TODO : i18n + raise YunohostError("Cannot upgrade both system and apps at the same time") + + if system is False and apps is None: + # TODO : i18n + raise YunohostError("Please specify --apps OR --system") + failure = False - if not ignore_packages: + if system is True: # Check that there's indeed some packages to upgrade upgradables = list(_list_upgradable_apt_packages()) @@ -722,7 +729,7 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal operation_logger.success() - if not ignore_apps: + if apps is not None: try: app_upgrade(auth) except Exception as e: From 9e3d30283268a9c7c07d4b8d6aa33139f5b0e298 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 22 Apr 2019 22:06:35 +0200 Subject: [PATCH 11/67] Feed apps argument to app_upgrade --- src/yunohost/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 784f5d512..5f38971bf 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -731,7 +731,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): if apps is not None: try: - app_upgrade(auth) + app_upgrade(auth, app=apps) except Exception as e: failure = True logger.warning('unable to upgrade apps: %s' % str(e)) From 779a16dab1960ce7365c9782ef65cde9e3a90bbc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 22 Apr 2019 22:15:16 +0200 Subject: [PATCH 12/67] Clarify the whole error / success handling --- src/yunohost/tools.py | 51 +++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 5f38971bf..20ab7cfae 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -602,8 +602,6 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # TODO : i18n raise YunohostError("Please specify --apps OR --system") - failure = False - if system is True: # Check that there's indeed some packages to upgrade @@ -633,7 +631,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # # "Regular" packages upgrade # - if not failure and noncritical_packages_upgradable: + if noncritical_packages_upgradable: # TODO : i18n logger.info("Upgrading 'regular' (non-yunohost-related) packages ...") @@ -645,31 +643,26 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # Doublecheck with apt-mark showhold that packages are indeed held ... held_packages = check_output("apt-mark showhold").split("\n") if any(p not in held_packages for p in critical_packages): - failure = True logger.warning('Unable to hold critical packages ...') - logger.error(m18n.n('packages_upgrade_failed')) - # FIXME : watdo here, should this be an exception or just an - # error operation_logger.error(m18n.n('packages_upgrade_failed')) + raise YunohostError(m18n.n('packages_upgrade_failed')) - if not failure: - logger.debug("Running apt command :\n{}".format(dist_upgrade)) + logger.debug("Running apt command :\n{}".format(dist_upgrade)) - callbacks = ( - lambda l: logger.info(l.rstrip()), - lambda l: logger.warning(l.rstrip()), - ) - returncode = call_async_output(dist_upgrade, callbacks, shell=True) - if returncode != 0: - failure = True - logger.warning('unable to upgrade packages: %s' % ', '.join(noncritical_packages_upgradable)) - logger.error(m18n.n('packages_upgrade_failed')) - operation_logger.error(m18n.n('packages_upgrade_failed')) + callbacks = ( + lambda l: logger.info(l.rstrip()), + lambda l: logger.warning(l.rstrip()), + ) + returncode = call_async_output(dist_upgrade, callbacks, shell=True) + if returncode != 0: + logger.warning('unable to upgrade packages: %s' % ', '.join(noncritical_packages_upgradable)) + operation_logger.error(m18n.n('packages_upgrade_failed')) + raise YunohostError(m18n.n('packages_upgrade_failed')) # # Critical packages upgrade # - if not failure and critical_packages_upgradable: + if critical_packages_upgradable: # TODO : i18n logger.info("Upgrading 'special' (yunohost-related) packages ...") @@ -681,12 +674,9 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # Doublecheck with apt-mark showhold that packages are indeed unheld ... unheld_packages = check_output("apt-mark showhold").split("\n") if any(p in unheld_packages for p in critical_packages): - failure = True logger.warning('Unable to unhold critical packages ...') - logger.error(m18n.n('packages_upgrade_failed')) - # FIXME : watdo here, should this be an exception or just an - # error operation_logger.error(m18n.n('packages_upgrade_failed')) + raise YunohostError(m18n.n('packages_upgrade_failed')) # # Here we use a dirty hack to run a command after the current @@ -715,17 +705,14 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): logger.debug("Running command :\n{}".format(command)) os.system(command) - - # TODO / FIXME : return from this function immediately, - # otherwise the apps upgrade might happen and it's gonna be a mess + return # FIXME / open question : what about "permanently" mark yunohost # as "hold" to avoid accidental deletion of it... # (so, only unhold it during the upgrade) - elif not failure: - - logger.info(m18n.n('done')) + else: + logger.success(m18n.n('system_upgraded')) operation_logger.success() @@ -733,13 +720,9 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): try: app_upgrade(auth, app=apps) except Exception as e: - failure = True logger.warning('unable to upgrade apps: %s' % str(e)) logger.error(m18n.n('app_upgrade_some_app_failed')) - if not failure: - logger.success(m18n.n('system_upgraded')) - # Return API logs if it is an API call is_api = True if msettings.get('interface') == 'api' else False if is_api: From 845b7448fb23a63ebffdf88511d5d075d2425196 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 22 Apr 2019 22:25:11 +0200 Subject: [PATCH 13/67] Improve tools_upgrade description --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index d6a17e7f6..a72f2a654 100644 --- a/locales/en.json +++ b/locales/en.json @@ -270,7 +270,7 @@ "log_tools_migrations_migrate_forward": "Migrate forward", "log_tools_migrations_migrate_backward": "Migrate backward", "log_tools_postinstall": "Postinstall your YunoHost server", - "log_tools_upgrade": "Upgrade debian packages", + "log_tools_upgrade": "Upgrade system packages", "log_tools_shutdown": "Shutdown your server", "log_tools_reboot": "Reboot your server", "ldap_init_failed_to_create_admin": "LDAP initialization failed to create admin user", From ad8f7bbcbc4648b4893a06ebfaaa7f1b00cf7ec0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 22 Apr 2019 22:29:03 +0200 Subject: [PATCH 14/67] Not really conviced about this return thing for the API, we can do better than that ... --- src/yunohost/tools.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 20ab7cfae..edfaa6e32 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -723,11 +723,6 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): logger.warning('unable to upgrade apps: %s' % str(e)) logger.error(m18n.n('app_upgrade_some_app_failed')) - # Return API logs if it is an API call - is_api = True if msettings.get('interface') == 'api' else False - if is_api: - return {"log": service_log('yunohost-api', number="100").values()[0]} - def tools_diagnosis(auth, private=False): """ From 68ac7d3f1cef9e21e94eb7a41029e04281e44385 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 22 Apr 2019 22:31:21 +0200 Subject: [PATCH 15/67] Misc tweak for display --- src/yunohost/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index edfaa6e32..bfb72d2b6 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -691,12 +691,12 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK) - mark_success = "(echo 'success: true' | tee -a {})".format(operation_logger.md_path) + mark_success = "(echo 'success: true' > {})".format(operation_logger.md_path) update_log_metadata = "sed -i \"s/ended_at: .*$/ended_at: $(date -u +'%Y-%m-%d %H:%M:%S.%N')/\" {}" update_log_metadata = update_log_metadata.format(operation_logger.md_path) # TODO : i18n - upgrade_completed = "YunoHost package upgrade completed ! Press [enter] to get the command line back" + upgrade_completed = "\nYunoHost package upgrade completed !\nPress [Enter] to get the command line back" command = "({} && {} && {}; {}; echo '{}') &".format(wait_until_end_of_yunohost_command, command, mark_success, From 1b84d9a65fe315ff452c73bf0eee86f6f8f34533 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 22 Apr 2019 23:25:57 +0200 Subject: [PATCH 16/67] Reorganize for clarity --- src/yunohost/tools.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index bfb72d2b6..b9056bbb2 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -602,6 +602,23 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # TODO : i18n raise YunohostError("Please specify --apps OR --system") + # + # Apps + # This is basically just an alias to yunohost app upgrade ... + # + + if apps is not None: + try: + app_upgrade(auth, app=apps) + except Exception as e: + logger.warning('unable to upgrade apps: %s' % str(e)) + logger.error(m18n.n('app_upgrade_some_app_failed')) + + + # + # System + # + if system is True: # Check that there's indeed some packages to upgrade @@ -671,6 +688,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # Mark all critical packages as unheld for package in critical_packages: check_output("apt-mark unhold %s" % package) + # Doublecheck with apt-mark showhold that packages are indeed unheld ... unheld_packages = check_output("apt-mark showhold").split("\n") if any(p in unheld_packages for p in critical_packages): @@ -716,14 +734,6 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): operation_logger.success() - if apps is not None: - try: - app_upgrade(auth, app=apps) - except Exception as e: - logger.warning('unable to upgrade apps: %s' % str(e)) - logger.error(m18n.n('app_upgrade_some_app_failed')) - - def tools_diagnosis(auth, private=False): """ Return global info about current yunohost instance to help debugging From ec9deec9d4acc71e2484e48e50357a77c65b1f3f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 23 Apr 2019 00:08:44 +0200 Subject: [PATCH 17/67] i18n for messages --- locales/en.json | 9 +++++++++ src/yunohost/tools.py | 32 +++++++++++++------------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/locales/en.json b/locales/en.json index a72f2a654..a7033bfe0 100644 --- a/locales/en.json +++ b/locales/en.json @@ -482,6 +482,15 @@ "system_upgraded": "The system has been upgraded", "system_username_exists": "Username already exists in the system users", "this_action_broke_dpkg": "This action broke dpkg/apt (the system package managers)... You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", + "tools_upgrade_at_least_one": "Please specify --apps OR --system", + "tools_upgrade_cant_both": "Cannot upgrade both system and apps at the same time", + "tools_upgrade_cant_hold_critical_packages": "Unable to hold critical packages ...", + "tools_upgrade_cant_unhold_critical_packages": "Unable to unhold critical packages ...", + "tools_upgrade_regular_packages": "Now upgrading 'regular' (non-yunohost-related) packages ...", + "tools_upgrade_regular_packages_failed": "Unable to upgrade packages: {packages_list}", + "tools_upgrade_special_packages": "Now upgrading 'special' (yunohost-related) packages ...", + "tools_upgrade_special_packages_explanation": "This action will end but the actual special upgrade will continue in background. Please don't start any other action on your server in the next ~10 minutes (depending on your hardware speed). Once it's done, you may have to re-log on the webadmin.", + "tools_upgrade_special_packages_completed": "YunoHost package upgrade completed !\nPress [Enter] to get the command line back", "unbackup_app": "App '{app:s}' will not be saved", "unexpected_error": "An unexpected error occured: {error}", "unit_unknown": "Unknown unit '{unit:s}'", diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index b9056bbb2..f8f972d2f 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -595,12 +595,10 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): raise YunohostError("dpkg_is_broken") if system is not False and apps is not None: - # TODO : i18n - raise YunohostError("Cannot upgrade both system and apps at the same time") + raise YunohostError("tools_upgrade_cant_both") if system is False and apps is None: - # TODO : i18n - raise YunohostError("Please specify --apps OR --system") + raise YunohostError("tools_upgrade_at_least_one") # # Apps @@ -650,17 +648,17 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # if noncritical_packages_upgradable: - # TODO : i18n - logger.info("Upgrading 'regular' (non-yunohost-related) packages ...") + logger.info(m18n.n("tools_upgrade_regular_packages")) # TODO : factorize this in utils/packages.py ? # Mark all critical packages as held for package in critical_packages: check_output("apt-mark hold %s" % package) + # Doublecheck with apt-mark showhold that packages are indeed held ... held_packages = check_output("apt-mark showhold").split("\n") if any(p not in held_packages for p in critical_packages): - logger.warning('Unable to hold critical packages ...') + logger.warning(m18n.n("tools_upgrade_cant_hold_critical_packages")) operation_logger.error(m18n.n('packages_upgrade_failed')) raise YunohostError(m18n.n('packages_upgrade_failed')) @@ -672,7 +670,8 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): ) returncode = call_async_output(dist_upgrade, callbacks, shell=True) if returncode != 0: - logger.warning('unable to upgrade packages: %s' % ', '.join(noncritical_packages_upgradable)) + logger.warning('tools_upgrade_regular_packages_failed', + packages_list=', '.join(noncritical_packages_upgradable)) operation_logger.error(m18n.n('packages_upgrade_failed')) raise YunohostError(m18n.n('packages_upgrade_failed')) @@ -681,8 +680,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # if critical_packages_upgradable: - # TODO : i18n - logger.info("Upgrading 'special' (yunohost-related) packages ...") + logger.info(m18n.n("tools_upgrade_special_packages")) # TODO : factorize this in utils/packages.py ? # Mark all critical packages as unheld @@ -690,9 +688,9 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): check_output("apt-mark unhold %s" % package) # Doublecheck with apt-mark showhold that packages are indeed unheld ... - unheld_packages = check_output("apt-mark showhold").split("\n") - if any(p in unheld_packages for p in critical_packages): - logger.warning('Unable to unhold critical packages ...') + held_packages = check_output("apt-mark showhold").split("\n") + if any(p in held_packages for p in critical_packages): + logger.warning(m18n.n("tools_upgrade_cant_unhold_critical_packages")) operation_logger.error(m18n.n('packages_upgrade_failed')) raise YunohostError(m18n.n('packages_upgrade_failed')) @@ -713,22 +711,18 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): update_log_metadata = "sed -i \"s/ended_at: .*$/ended_at: $(date -u +'%Y-%m-%d %H:%M:%S.%N')/\" {}" update_log_metadata = update_log_metadata.format(operation_logger.md_path) - # TODO : i18n - upgrade_completed = "\nYunoHost package upgrade completed !\nPress [Enter] to get the command line back" + upgrade_completed = "\n"+m18n.n("tools_upgrade_special_packages_completed") command = "({} && {} && {}; {}; echo '{}') &".format(wait_until_end_of_yunohost_command, command, mark_success, update_log_metadata, upgrade_completed) + logger.warning(m18n.n("tools_upgrade_special_packages_explanation")) logger.debug("Running command :\n{}".format(command)) os.system(command) return - # FIXME / open question : what about "permanently" mark yunohost - # as "hold" to avoid accidental deletion of it... - # (so, only unhold it during the upgrade) - else: logger.success(m18n.n('system_upgraded')) operation_logger.success() From a192bdd31a7da461f3c17c0d84f26e00b2b763ce Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 23 Apr 2019 16:51:32 +0200 Subject: [PATCH 18/67] Consistent options for tools_update (compared to upgrade) + semantic improvements --- data/actionsmap/yunohost.yml | 8 ++++---- locales/en.json | 5 +++-- src/yunohost/tools.py | 38 ++++++++++++++++++++---------------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 08188c8c8..59a063a4e 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1548,11 +1548,11 @@ tools: action_help: YunoHost update api: PUT /update arguments: - --ignore-apps: - help: Ignore apps cache update and changelog + --apps: + help: Fetch the application list to check which apps can be upgraded action: store_true - --ignore-packages: - help: Ignore APT cache update and changelog + --system: + help: Fetch available system packages upgrades (equivalent to apt update) action: store_true ### tools_upgrade() diff --git a/locales/en.json b/locales/en.json index a7033bfe0..58b4dbc72 100644 --- a/locales/en.json +++ b/locales/en.json @@ -4,8 +4,9 @@ "admin_password": "Administration password", "admin_password_change_failed": "Unable to change password", "admin_password_changed": "The administration password has been changed", - "app_action_cannot_be_ran_because_required_services_down": "This app requires some services which are currently down. Before continuing, you should try to restart the following services (and possibly investigate why they are down) : {services}", "admin_password_too_long": "Please choose a password shorter than 127 characters", + "already_up_to_date": "Nothing to do! Everything is already up to date!", + "app_action_cannot_be_ran_because_required_services_down": "This app requires some services which are currently down. Before continuing, you should try to restart the following services (and possibly investigate why they are down) : {services}", "app_already_installed": "{app:s} is already installed", "app_already_installed_cant_change_url": "This app is already installed. The url cannot be changed just by this function. Look into `app changeurl` if it's available.", "app_already_up_to_date": "{app:s} is already up to date", @@ -367,7 +368,6 @@ "package_not_installed": "Package '{pkgname}' is not installed", "package_unexpected_error": "An unexpected error occurred processing the package '{pkgname}'", "package_unknown": "Unknown package '{pkgname}'", - "packages_no_upgrade": "There is no package to upgrade", "packages_upgrade_critical_later": "Critical packages ({packages:s}) will be upgraded later", "packages_upgrade_failed": "Unable to upgrade all of the packages", "password_listed": "This password is among the most used password in the world. Please choose something a bit more unique.", @@ -499,6 +499,7 @@ "update_apt_cache_failed": "Unable to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines which might help to identify problematic lines : \n{sourceslist}", "update_apt_cache_warning": "Some errors happened while updating the cache of APT (Debian's package manager). Here is a dump of the sources.list lines which might help to identify problematic lines : \n{sourceslist}", "updating_apt_cache": "Fetching available upgrades for system packages…", + "updating_app_lists": "Fetching available upgrades for applications…", "upgrade_complete": "Upgrade complete", "upgrading_packages": "Upgrading packages…", "upnp_dev_not_found": "No UPnP device found", diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index f8f972d2f..57870f544 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -470,18 +470,22 @@ def tools_regen_conf(names=[], with_diff=False, force=False, dry_run=False, return regen_conf(names, with_diff, force, dry_run, list_pending) -def tools_update(ignore_apps=False, ignore_packages=False): +def tools_update(apps=False, system=False): """ - Update apps & package cache, then display changelog + Update apps & system package cache Keyword arguments: - ignore_apps -- Ignore app list update and changelog - ignore_packages -- Ignore apt cache update and changelog - + system -- Fetch available system packages upgrades (equivalent to apt update) + apps -- Fetch the application list to check which apps can be upgraded """ - # "packages" will list upgradable packages - packages = [] - if not ignore_packages: + + # If neither --apps nor --system specified, do both + if not apps and not system: + apps = True + system = True + + upgradable_system_packages = [] + if system: # Update APT cache # LC_ALL=C is here to make sure the results are in english @@ -514,12 +518,12 @@ def tools_update(ignore_apps=False, ignore_packages=False): elif warnings: logger.error(m18n.n('update_apt_cache_warning', sourceslist='\n'.join(_dump_sources_list()))) - packages = list(_list_upgradable_apt_packages()) + upgradable_system_packages = list(_list_upgradable_apt_packages()) logger.debug(m18n.n('done')) - # "apps" will list upgradable packages - apps = [] - if not ignore_apps: + upgradable_apps = [] + if apps: + logger.info(m18n.n('updating_app_lists')) try: app_fetchlist() except YunohostError: @@ -532,15 +536,15 @@ def tools_update(ignore_apps=False, ignore_packages=False): app_dict = app_info(app_id, raw=True) if app_dict["upgradable"] == "yes": - apps.append({ + upgradable_apps.append({ 'id': app_id, 'label': app_dict['settings']['label'] }) - if len(apps) == 0 and len(packages) == 0: - logger.info(m18n.n('packages_no_upgrade')) + if len(upgradable_apps) == 0 and len(upgradable_system_packages) == 0: + logger.info(m18n.n('already_up_to_date')) - return {'packages': packages, 'apps': apps} + return {'system': upgradable_system_packages, 'apps': upgradable_apps} # TODO : move this to utils/packages.py ? @@ -622,7 +626,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # Check that there's indeed some packages to upgrade upgradables = list(_list_upgradable_apt_packages()) if not upgradables: - logger.info(m18n.n('packages_no_upgrade')) + logger.info(m18n.n('already_up_to_date')) logger.info(m18n.n('upgrading_packages')) operation_logger.start() From 4f3eaa5e2349fcffc0b26a18ec636bd172ddd934 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 24 Apr 2019 00:49:11 +0200 Subject: [PATCH 19/67] Check there's actually some apps to update when using tools upgrade --apps --- src/yunohost/tools.py | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 57870f544..67ea92bb3 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -530,16 +530,7 @@ def tools_update(apps=False, system=False): # FIXME : silent exception !? pass - app_list_installed = os.listdir(APPS_SETTING_PATH) - for app_id in app_list_installed: - - app_dict = app_info(app_id, raw=True) - - if app_dict["upgradable"] == "yes": - upgradable_apps.append({ - 'id': app_id, - 'label': app_dict['settings']['label'] - }) + upgradable_apps = list(_list_upgradable_apps()) if len(upgradable_apps) == 0 and len(upgradable_system_packages) == 0: logger.info(m18n.n('already_up_to_date')) @@ -547,6 +538,20 @@ def tools_update(apps=False, system=False): return {'system': upgradable_system_packages, 'apps': upgradable_apps} +def _list_upgradable_apps(): + + app_list_installed = os.listdir(APPS_SETTING_PATH) + for app_id in app_list_installed: + + app_dict = app_info(app_id, raw=True) + + if app_dict["upgradable"] == "yes": + yield { + 'id': app_id, + 'label': app_dict['settings']['label'] + } + + # TODO : move this to utils/packages.py ? def _list_upgradable_apt_packages(): @@ -610,12 +615,27 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): # if apps is not None: + + # Make sure there's actually something to upgrade + + upgradable_apps = [app["id"] for app in _list_upgradable_apps()] + + if not upgradable_apps: + logger.info(m18n.n("app_no_upgrade")) + return + elif len(apps) and all(app not in upgradable_apps for app in apps): + logger.info(m18n.n("apps_already_up_to_date")) + return + + # Actually start the upgrades + try: app_upgrade(auth, app=apps) except Exception as e: logger.warning('unable to upgrade apps: %s' % str(e)) logger.error(m18n.n('app_upgrade_some_app_failed')) + return # # System From 12bcd5d0f86dcab3ddff33aba6ddd1710eb17b0d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 24 Apr 2019 00:49:52 +0200 Subject: [PATCH 20/67] Avoid messy output when running apt stuff --- src/yunohost/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 67ea92bb3..d86da719a 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -689,7 +689,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): logger.debug("Running apt command :\n{}".format(dist_upgrade)) callbacks = ( - lambda l: logger.info(l.rstrip()), + lambda l: logger.info(l.rstrip() + "\r"), lambda l: logger.warning(l.rstrip()), ) returncode = call_async_output(dist_upgrade, callbacks, shell=True) From 5731e69541a4fa1a8c33546d232691ed2e84f00b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 24 Apr 2019 03:23:47 +0200 Subject: [PATCH 21/67] PEP8 --- src/yunohost/tools.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index d86da719a..fb05df940 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -34,12 +34,8 @@ from glob import glob from xmlrpclib import Fault from importlib import import_module from collections import OrderedDict -from datetime import datetime -import apt -import apt.progress - -from moulinette import msettings, msignals, m18n +from moulinette import msignals, m18n from moulinette.core import init_authenticator from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger @@ -49,7 +45,7 @@ from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, a from yunohost.domain import domain_add, domain_list, _get_maindomain, _set_maindomain from yunohost.dyndns import _dyndns_available, _dyndns_provides from yunohost.firewall import firewall_upnp -from yunohost.service import service_status, service_log, service_start, service_enable +from yunohost.service import service_status, service_start, service_enable from yunohost.regenconf import regen_conf from yunohost.monitor import monitor_disk, monitor_system from yunohost.utils.packages import ynh_packages_version @@ -496,6 +492,7 @@ def tools_update(apps=False, system=False): # Filter boring message about "apt not having a stable CLI interface" # Also keep track of wether or not we encountered a warning... warnings = [] + def is_legit_warning(m): legit_warning = m.rstrip() and "apt does not have a stable CLI interface" not in m.rstrip() if legit_warning: @@ -735,7 +732,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): update_log_metadata = "sed -i \"s/ended_at: .*$/ended_at: $(date -u +'%Y-%m-%d %H:%M:%S.%N')/\" {}" update_log_metadata = update_log_metadata.format(operation_logger.md_path) - upgrade_completed = "\n"+m18n.n("tools_upgrade_special_packages_completed") + upgrade_completed = "\n" + m18n.n("tools_upgrade_special_packages_completed") command = "({} && {} && {}; {}; echo '{}') &".format(wait_until_end_of_yunohost_command, command, mark_success, From 1ed65fa1d92731254158ca8456f09c8ffcb864a7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 24 Apr 2019 16:42:37 +0200 Subject: [PATCH 22/67] Add some explanation about where to find the log for special upgrade --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 58b4dbc72..689b2f637 100644 --- a/locales/en.json +++ b/locales/en.json @@ -489,7 +489,7 @@ "tools_upgrade_regular_packages": "Now upgrading 'regular' (non-yunohost-related) packages ...", "tools_upgrade_regular_packages_failed": "Unable to upgrade packages: {packages_list}", "tools_upgrade_special_packages": "Now upgrading 'special' (yunohost-related) packages ...", - "tools_upgrade_special_packages_explanation": "This action will end but the actual special upgrade will continue in background. Please don't start any other action on your server in the next ~10 minutes (depending on your hardware speed). Once it's done, you may have to re-log on the webadmin.", + "tools_upgrade_special_packages_explanation": "This action will end but the actual special upgrade will continue in background. Please don't start any other action on your server in the next ~10 minutes (depending on your hardware speed). Once it's done, you may have to re-log on the webadmin. The upgrade log will be available in Tools > Log (in the webadmin) or through 'yunohost log list' (in command line).", "tools_upgrade_special_packages_completed": "YunoHost package upgrade completed !\nPress [Enter] to get the command line back", "unbackup_app": "App '{app:s}' will not be saved", "unexpected_error": "An unexpected error occured: {error}", From d0faff609e19633a9aeb990be9cac503946d5453 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 24 Apr 2019 17:21:29 +0200 Subject: [PATCH 23/67] Remove weird API behavior for app upgrade, will adapt the webadmin --- src/yunohost/app.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 7813dee58..1426898f2 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -697,10 +697,6 @@ def app_upgrade(auth, app=[], url=None, file=None): logger.success(m18n.n('upgrade_complete')) - # Return API logs if it is an API call - if is_api: - return {"log": service_log('yunohost-api', number="100").values()[0]} - @is_unit_operation() def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on_failure=False, force=False): From f4b87f969522d79a672f4ba2839dfc2f86006111 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 24 Apr 2019 17:46:39 +0200 Subject: [PATCH 24/67] Move low-level apt stuff to utils/packages.py --- src/yunohost/tools.py | 45 +--------------------------------- src/yunohost/utils/packages.py | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index fb05df940..5128d0cbe 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -30,7 +30,6 @@ import json import subprocess import pwd import socket -from glob import glob from xmlrpclib import Fault from importlib import import_module from collections import OrderedDict @@ -48,7 +47,7 @@ from yunohost.firewall import firewall_upnp from yunohost.service import service_status, service_start, service_enable from yunohost.regenconf import regen_conf from yunohost.monitor import monitor_disk, monitor_system -from yunohost.utils.packages import ynh_packages_version +from yunohost.utils.packages import ynh_packages_version, _dump_sources_list, _list_upgradable_apt_packages from yunohost.utils.network import get_public_ip from yunohost.log import is_unit_operation, OperationLogger @@ -486,8 +485,6 @@ def tools_update(apps=False, system=False): # Update APT cache # LC_ALL=C is here to make sure the results are in english command = "LC_ALL=C apt update" - # TODO : add @is_unit_operation to tools_update so that the - # debug output can be fetched when there's an issue... # Filter boring message about "apt not having a stable CLI interface" # Also keep track of wether or not we encountered a warning... @@ -549,44 +546,6 @@ def _list_upgradable_apps(): } -# TODO : move this to utils/packages.py ? -def _list_upgradable_apt_packages(): - - # List upgradable packages - # LC_ALL=C is here to make sure the results are in english - upgradable_raw = check_output("LC_ALL=C apt list --upgradable") - - # Dirty parsing of the output - upgradable_raw = [l.strip() for l in upgradable_raw.split("\n") if l.strip()] - for line in upgradable_raw: - # Remove stupid warning and verbose messages >.> - if "apt does not have a stable CLI interface" in line or "Listing..." in line: - continue - # line should look like : - # yunohost/stable 3.5.0.2+201903211853 all [upgradable from: 3.4.2.4+201903080053] - line = line.split() - if len(line) != 6: - logger.warning("Failed to parse this line : %s" % ' '.join(line)) - continue - - yield { - "name": line[0].split("/")[0], - "new_version": line[1], - "current_version": line[5].strip("]"), - } - - -def _dump_sources_list(): - - filenames = glob("/etc/apt/sources.list") + glob("/etc/apt/sources.list.d/*") - for filename in filenames: - with open(filename, "r") as f: - for line in f.readlines(): - if line.startswith("#") or not line.strip(): - continue - yield filename.replace("/etc/apt/", "") + ":" + line.strip() - - @is_unit_operation() def tools_upgrade(operation_logger, auth, apps=None, system=False): """ @@ -671,7 +630,6 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): logger.info(m18n.n("tools_upgrade_regular_packages")) - # TODO : factorize this in utils/packages.py ? # Mark all critical packages as held for package in critical_packages: check_output("apt-mark hold %s" % package) @@ -703,7 +661,6 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): logger.info(m18n.n("tools_upgrade_special_packages")) - # TODO : factorize this in utils/packages.py ? # Mark all critical packages as unheld for package in critical_packages: check_output("apt-mark unhold %s" % package) diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index e10de6493..b564d2dea 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -481,3 +481,46 @@ def dpkg_is_broken(): return False return any(re.match("^[0-9]+$", f) for f in os.listdir("/var/lib/dpkg/updates/")) + + +def _list_upgradable_apt_packages(): + + from moulinette.utils.process import check_output + + # List upgradable packages + # LC_ALL=C is here to make sure the results are in english + upgradable_raw = check_output("LC_ALL=C apt list --upgradable") + + # Dirty parsing of the output + upgradable_raw = [l.strip() for l in upgradable_raw.split("\n") if l.strip()] + for line in upgradable_raw: + + # Remove stupid warning and verbose messages >.> + if "apt does not have a stable CLI interface" in line or "Listing..." in line: + continue + + # line should look like : + # yunohost/stable 3.5.0.2+201903211853 all [upgradable from: 3.4.2.4+201903080053] + line = line.split() + if len(line) != 6: + logger.warning("Failed to parse this line : %s" % ' '.join(line)) + continue + + yield { + "name": line[0].split("/")[0], + "new_version": line[1], + "current_version": line[5].strip("]"), + } + + +def _dump_sources_list(): + + from glob import glob + + filenames = glob("/etc/apt/sources.list") + glob("/etc/apt/sources.list.d/*") + for filename in filenames: + with open(filename, "r") as f: + for line in f.readlines(): + if line.startswith("#") or not line.strip(): + continue + yield filename.replace("/etc/apt/", "") + ":" + line.strip() From cf619fe13497f46f70a8c1f4ea6a23a7e99c7efb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 24 Apr 2019 19:02:35 +0200 Subject: [PATCH 25/67] Fix / improve success mark in logs --- src/yunohost/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 5128d0cbe..8797f7349 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -685,7 +685,7 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK) - mark_success = "(echo 'success: true' > {})".format(operation_logger.md_path) + mark_success = "(echo 'Done!' | tee -a {} && echo 'success: true' >> {})".format(logfile, operation_logger.md_path) update_log_metadata = "sed -i \"s/ended_at: .*$/ended_at: $(date -u +'%Y-%m-%d %H:%M:%S.%N')/\" {}" update_log_metadata = update_log_metadata.format(operation_logger.md_path) From 7ee0e986a6a85420230191dd1c68587722586c06 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Apr 2019 00:26:42 +0200 Subject: [PATCH 26/67] Explicly mark failure if command failed + more readable syntax --- src/yunohost/tools.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 8797f7349..b992fb6c6 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -686,15 +686,18 @@ def tools_upgrade(operation_logger, auth, apps=None, system=False): MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK) mark_success = "(echo 'Done!' | tee -a {} && echo 'success: true' >> {})".format(logfile, operation_logger.md_path) + mark_failure = "(echo 'Failed :(' | tee -a {} && echo 'success: false' >> {})".format(logfile, operation_logger.md_path) update_log_metadata = "sed -i \"s/ended_at: .*$/ended_at: $(date -u +'%Y-%m-%d %H:%M:%S.%N')/\" {}" update_log_metadata = update_log_metadata.format(operation_logger.md_path) upgrade_completed = "\n" + m18n.n("tools_upgrade_special_packages_completed") - command = "({} && {} && {}; {}; echo '{}') &".format(wait_until_end_of_yunohost_command, - command, - mark_success, - update_log_metadata, - upgrade_completed) + command = "(({wait} && {cmd}) && {mark_success} || {mark_failure}; {update_metadata}; echo '{done}') &".format( + wait=wait_until_end_of_yunohost_command, + cmd=command, + mark_success=mark_success, + mark_failure=mark_failure, + update_metadata=update_log_metadata, + done=upgrade_completed) logger.warning(m18n.n("tools_upgrade_special_packages_explanation")) logger.debug("Running command :\n{}".format(command)) From 9f28bfccad76ea7189edbe05e8616ac4a84b8332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Wed, 28 Nov 2018 08:20:55 +0100 Subject: [PATCH 27/67] Use root UID to authenticate to LDAP --- data/actionsmap/yunohost.yml | 36 +++++++++++++++++++++++++-------- data/templates/slapd/slapd.conf | 3 +++ src/yunohost/tools.py | 10 ++++----- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 32c1b0e42..3842b08be 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -43,12 +43,19 @@ _global: parameters: uri: ldap://localhost:389 base_dn: dc=yunohost,dc=org - user_rdn: cn=admin + user_rdn: cn=admin,dc=yunohost,dc=org ldap-anonymous: vendor: ldap parameters: uri: ldap://localhost:389 base_dn: dc=yunohost,dc=org + as-root: + vendor: ldap + parameters: + # We can get this uri by (urllib.quote_plus('/var/run/slapd/ldapi') + uri: ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi + base_dn: dc=yunohost,dc=org + user_rdn: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth argument_auth: true arguments: -v: @@ -84,6 +91,7 @@ user: api: POST /users configuration: authenticate: all + authenticator: as-root arguments: username: help: The unique username to create @@ -142,6 +150,7 @@ user: api: DELETE /users/ configuration: authenticate: all + authenticator: as-root arguments: username: help: Username to delete @@ -157,6 +166,7 @@ user: api: PUT /users/ configuration: authenticate: all + authenticator: as-root arguments: username: help: Username to update @@ -227,6 +237,7 @@ user: api: POST /users/ssh/enable configuration: authenticate: all + authenticator: as-root arguments: username: help: Username of the user @@ -239,6 +250,7 @@ user: api: POST /users/ssh/disable configuration: authenticate: all + authenticator: as-root arguments: username: help: Username of the user @@ -251,6 +263,7 @@ user: api: GET /users/ssh/keys configuration: authenticate: all + authenticator: as-root arguments: username: help: Username of the user @@ -263,6 +276,7 @@ user: api: POST /users/ssh/key configuration: authenticate: all + authenticator: as-root arguments: username: help: Username of the user @@ -280,6 +294,7 @@ user: api: DELETE /users/ssh/key configuration: authenticate: all + authenticator: as-root arguments: username: help: Username of the user @@ -310,6 +325,7 @@ domain: api: POST /domains configuration: authenticate: all + authenticator: as-root arguments: domain: help: Domain name to add @@ -328,6 +344,7 @@ domain: api: DELETE /domains/ configuration: authenticate: all + authenticator: as-root arguments: domain: help: Domain to delete @@ -544,7 +561,7 @@ app: api: POST /apps configuration: authenticate: all - authenticator: ldap-anonymous + authenticator: as-root arguments: app: help: Name, local path or git URL of the app to install @@ -569,7 +586,7 @@ app: api: DELETE /apps/ configuration: authenticate: all - authenticator: ldap-anonymous + authenticator: as-root arguments: app: help: App(s) to delete @@ -580,7 +597,7 @@ app: api: PUT /upgrade/apps configuration: authenticate: all - authenticator: ldap-anonymous + authenticator: as-root arguments: app: help: App(s) to upgrade (default all) @@ -598,7 +615,7 @@ app: api: PUT /apps//changeurl configuration: authenticate: all - authenticator: ldap-anonymous + authenticator: as-root arguments: app: help: Target app instance name @@ -744,7 +761,7 @@ app: api: PUT /access configuration: authenticate: all - authenticator: ldap-anonymous + authenticator: as-root arguments: apps: nargs: "+" @@ -758,7 +775,7 @@ app: api: DELETE /access configuration: authenticate: all - authenticator: ldap-anonymous + authenticator: as-root arguments: apps: nargs: "+" @@ -772,7 +789,7 @@ app: api: POST /access configuration: authenticate: all - authenticator: ldap-anonymous + authenticator: as-root arguments: apps: nargs: "+" @@ -1483,6 +1500,7 @@ tools: api: PUT /adminpw configuration: authenticate: all + authenticator: as-root arguments: -n: full: --new-password @@ -1500,6 +1518,7 @@ tools: - PUT /domains/main configuration: authenticate: all + authenticator: as-root arguments: -n: full: --new-domain @@ -1590,6 +1609,7 @@ tools: shell: configuration: authenticate: all + authenticator: as-root action_help: Launch a development shell arguments: -c: diff --git a/data/templates/slapd/slapd.conf b/data/templates/slapd/slapd.conf index 9a8800d9d..c8c363795 100644 --- a/data/templates/slapd/slapd.conf +++ b/data/templates/slapd/slapd.conf @@ -81,6 +81,7 @@ checkpoint 512 30 # These access lines apply to database #1 only access to attrs=userPassword,shadowLastChange by dn="cn=admin,dc=yunohost,dc=org" write + by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by anonymous auth by self write by * none @@ -90,6 +91,7 @@ access to attrs=userPassword,shadowLastChange # Others should be able to see it. access to attrs=cn,gecos,givenName,mail,maildrop,displayName,sn by dn="cn=admin,dc=yunohost,dc=org" write + by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by self write by * read @@ -108,5 +110,6 @@ access to dn.base="" by * read # can read everything. access to * by dn="cn=admin,dc=yunohost,dc=org" write + by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by group/groupOfNames/Member="cn=admin,ou=groups,dc=yunohost,dc=org" write by * read diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 4ca594cf1..7b9bf044e 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -70,11 +70,11 @@ def tools_ldapinit(): """ # Instantiate LDAP Authenticator - auth = init_authenticator(('ldap', 'default'), - {'uri': "ldap://localhost:389", - 'base_dn': "dc=yunohost,dc=org", - 'user_rdn': "cn=admin"}) - auth.authenticate('yunohost') + AUTH_IDENTIFIER = ('ldap', 'as-root') + AUTH_PARAMETERS = {'uri': 'ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi', + 'base_dn': 'dc=yunohost,dc=org', + 'user_rdn': 'gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth'} + auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) with open('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') as f: ldap_map = yaml.load(f) From 1db72311d2d9810b2f6dc0f998ecf711b3ca3b44 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Mon, 6 May 2019 12:52:47 +0200 Subject: [PATCH 28/67] Add helpers for sso config And especially restore the old documentation about those keys --- data/helpers.d/setting | 92 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index 799e26554..b5bd8c235 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -59,6 +59,98 @@ ynh_app_setting_delete() { ynh_app_setting "delete" "$app" "$key" } +# Add skipped_uris urls into the config +# +# usage: ynh_add_skipped_uris [--appid=app] --url=url1,url2 [--regex] +# | arg: -a, --appid - the application id +# | arg: -u, --url - the urls to add to the sso for this app +# | arg: -r, --regex - Use the key 'skipped_regex' instead of 'skipped_uris' +# +# An URL set with 'skipped_uris' key will be totally ignored by the SSO, +# which means that the access will be public and the logged-in user information will not be passed to the app. +# +# Requires YunoHost version ?.?.? or higher. +ynh_add_skipped_uris() { + # Declare an array to define the options of this helper. + local legacy_args=aur + declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex ) + local appid + local url + local regex + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + appid={appid:-$app} + regex={regex:-0} + + local key=skipped_uris + if [ $regex -eq 1 ]; then + key=skipped_regex + fi + + ynh_app_setting_set --app=$appid --key=$key --value="$url" +} + +# Add unprotected_uris urls into the config +# +# usage: ynh_add_unprotected_uris [--appid=app] --url=url1,url2 [--regex] +# | arg: -a, --appid - the application id +# | arg: -u, --url - the urls to add to the sso for this app +# | arg: -r, --regex - Use the key 'unprotected_regex' instead of 'unprotected_uris' +# +# An URL set with unprotected_uris key will be accessible publicly, but if an user is logged in, +# his information will be accessible (through HTTP headers) to the app. +# +# Requires YunoHost version ?.?.? or higher. +ynh_add_unprotected_uris() { + # Declare an array to define the options of this helper. + local legacy_args=aur + declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex ) + local appid + local url + local regex + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + appid={appid:-$app} + regex={regex:-0} + + local key=unprotected_uris + if [ $regex -eq 1 ]; then + key=unprotected_regex + fi + + ynh_app_setting_set --app=$appid --key=$key --value="$url" +} + +# Add protected_uris urls into the config +# +# usage: ynh_add_protected_uris [--appid=app] --url=url1,url2 [--regex] +# | arg: -a, --appid - the application id +# | arg: -u, --url - the urls to add to the sso for this app +# | arg: -r, --regex - Use the key 'protected_regex' instead of 'protected_uris' +# +# An URL set with protected_uris will be blocked by the SSO and accessible only to authenticated and authorized users. +# +# Requires YunoHost version ?.?.? or higher. +ynh_add_protected_uris() { + # Declare an array to define the options of this helper. + local legacy_args=aur + declare -Ar args_array=( [a]=appid= [u]=url= [r]=regex ) + local appid + local url + local regex + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + appid={appid:-$app} + regex={regex:-0} + + local key=protected_uris + if [ $regex -eq 1 ]; then + key=protected_regex + fi + + ynh_app_setting_set --app=$appid --key=$key --value="$url" +} + # Small "hard-coded" interface to avoid calling "yunohost app" directly each # time dealing with a setting is needed (which may be so slow on ARM boards) # From fa0e37f8daeb2b13487b1b45173edba24ecbf31c Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Wed, 8 May 2019 00:56:18 +0200 Subject: [PATCH 29/67] Print the log only in CLI --- data/helpers.d/system | 308 --------------------------- data/helpers.d/utils | 469 ++++++++++++++++++++++++++++++------------ 2 files changed, 342 insertions(+), 435 deletions(-) delete mode 100644 data/helpers.d/system diff --git a/data/helpers.d/system b/data/helpers.d/system deleted file mode 100644 index 3c065e592..000000000 --- a/data/helpers.d/system +++ /dev/null @@ -1,308 +0,0 @@ -#!/bin/bash - -# Determine whether the scripts is executed from a terminal or the admin JS. -# -# [internal] -ynh_is_term () { - # Return true if $TERM return xterm-XXXcolor - # Which means the script is executed from a terminal, not from the admin JS - [[ "$TERM" =~ "xterm" ]] -} - -# Manage a fail of the script -# -# [internal] -# -# usage: -# ynh_exit_properly is used only by the helper ynh_abort_if_errors. -# You should not use it directly. -# Instead, add to your script: -# ynh_clean_setup () { -# instructions... -# } -# -# This function provide a way to clean some residual of installation that not managed by remove script. -# -# It prints a warning to inform that the script was failed, and execute the ynh_clean_setup function if used in the app script -# -ynh_exit_properly () { - local exit_code=$? - if [ "$exit_code" -eq 0 ]; then - exit 0 # Exit without error if the script ended correctly - fi - - trap '' EXIT # Ignore new exit signals - set +eu # Do not exit anymore if a command fail or if a variable is empty - - echo -e "!!\n $app's script has encountered an error. Its execution was cancelled.\n!!" >&2 - - # Unset xtrace to not spoil the log - set +x - - if ynh_is_term - then - local ynh_log="/var/log/yunohost/yunohost-cli.log" - else - local ynh_log="/var/log/yunohost/yunohost-api.log" - fi - # Wait for the log to be fill with the data until the crash. - local timeout=0 - while ! tail --lines=20 "$ynh_log" | grep --quiet "ynh_exit_properly" - do - ((timeout++)) - if [ $timeout -eq 500 ]; then - break - fi - done - set -x - - echo -e "\e[34m\e[1mLog extract:\e[0m" >&2 - # Tail the last 30 lines of log of YunoHost - # But remove all lines after "ynh_exit_properly" - # Remove the timestamp at the beginning of the line - # Remove "yunohost.hook..." - # Add DEBUG and color it at the beginning of each log line. - echo -e "$(tail --lines=30 "$ynh_log" \ - | sed '1,/ynh_exit_properly/!d' \ - | sed 's/^[[:digit:]: ,-]*//g' \ - | sed 's/ *yunohost.hook.*\]//g' \ - | sed 's/^/\\e[34m\\e[1m[DEBUG]\\e[0m: /g')" >&2 - - if type -t ynh_clean_setup > /dev/null; then # Check if the function exist in the app script. - ynh_clean_setup # Call the function to do specific cleaning for the app. - fi - - ynh_die # Exit with error status -} - -# Exits if an error occurs during the execution of the script. -# -# usage: ynh_abort_if_errors -# -# This configure the rest of the script execution such that, if an error occurs -# or if an empty variable is used, the execution of the script stops -# immediately and a call to `ynh_clean_setup` is triggered if it has been -# defined by your script. -# -ynh_abort_if_errors () { - set -eu # Exit if a command fail, and if a variable is used unset. - trap ynh_exit_properly EXIT # Capturing exit signals on shell script -} - -# Fetch the Debian release codename -# -# usage: ynh_get_debian_release -# | ret: The Debian release codename (i.e. jessie, stretch, ...) -ynh_get_debian_release () { - echo $(lsb_release --codename --short) -} - -# Start (or other actions) a service, print a log in case of failure and optionnaly wait until the service is completely started -# -# usage: ynh_systemd_action [-n service_name] [-a action] [ [-l "line to match"] [-p log_path] [-t timeout] [-e length] ] -# | arg: -n, --service_name= - Name of the service to start. Default : $app -# | arg: -a, --action= - Action to perform with systemctl. Default: start -# | arg: -l, --line_match= - Line to match - The line to find in the log to attest the service have finished to boot. -# If not defined it don't wait until the service is completely started. -# WARNING: When using --line_match, you should always add `ynh_clean_check_starting` into your -# `ynh_clean_setup` at the beginning of the script. Otherwise, tail will not stop in case of failure -# of the script. The script will then hang forever. -# | arg: -p, --log_path= - Log file - Path to the log file. Default : /var/log/$app/$app.log -# | arg: -t, --timeout= - Timeout - The maximum time to wait before ending the watching. Default : 300 seconds. -# | arg: -e, --length= - Length of the error log : Default : 20 -ynh_systemd_action() { - # Declare an array to define the options of this helper. - declare -Ar args_array=( [n]=service_name= [a]=action= [l]=line_match= [p]=log_path= [t]=timeout= [e]=length= ) - local service_name - local action - local line_match - local length - local log_path - local timeout - - # Manage arguments with getopts - ynh_handle_getopts_args "$@" - - local service_name="${service_name:-$app}" - local action=${action:-start} - local log_path="${log_path:-/var/log/$service_name/$service_name.log}" - local length=${length:-20} - local timeout=${timeout:-300} - - # Start to read the log - if [[ -n "${line_match:-}" ]] - then - local templog="$(mktemp)" - # Following the starting of the app in its log - if [ "$log_path" == "systemd" ] ; then - # Read the systemd journal - journalctl --unit=$service_name --follow --since=-0 --quiet > "$templog" & - # Get the PID of the journalctl command - local pid_tail=$! - else - # Read the specified log file - tail -F -n0 "$log_path" > "$templog" 2>&1 & - # Get the PID of the tail command - local pid_tail=$! - fi - fi - - ynh_print_info --message="${action^} the service $service_name" - - # Use reload-or-restart instead of reload. So it wouldn't fail if the service isn't running. - if [ "$action" == "reload" ]; then - action="reload-or-restart" - fi - - systemctl $action $service_name \ - || ( journalctl --no-pager --lines=$length -u $service_name >&2 \ - ; test -e "$log_path" && echo "--" >&2 && tail --lines=$length "$log_path" >&2 \ - ; false ) - - # Start the timeout and try to find line_match - if [[ -n "${line_match:-}" ]] - then - local i=0 - for i in $(seq 1 $timeout) - do - # Read the log until the sentence is found, that means the app finished to start. Or run until the timeout - if grep --quiet "$line_match" "$templog" - then - ynh_print_info --message="The service $service_name has correctly started." - break - fi - if [ $i -eq 3 ]; then - echo -n "Please wait, the service $service_name is ${action}ing" >&2 - fi - if [ $i -ge 3 ]; then - echo -n "." >&2 - fi - sleep 1 - done - if [ $i -ge 3 ]; then - echo "" >&2 - fi - if [ $i -eq $timeout ] - then - ynh_print_warn --message="The service $service_name didn't fully started before the timeout." - ynh_print_warn --message="Please find here an extract of the end of the log of the service $service_name:" - journalctl --no-pager --lines=$length -u $service_name >&2 - test -e "$log_path" && echo "--" >&2 && tail --lines=$length "$log_path" >&2 - fi - ynh_clean_check_starting - fi -} - -# Clean temporary process and file used by ynh_check_starting -# (usually used in ynh_clean_setup scripts) -# -# usage: ynh_clean_check_starting -ynh_clean_check_starting () { - # Stop the execution of tail. - kill -s 15 $pid_tail 2>&1 - ynh_secure_remove "$templog" 2>&1 -} - -# Read the value of a key in a ynh manifest file -# -# usage: ynh_read_manifest manifest key -# | arg: -m, --manifest= - Path of the manifest to read -# | arg: -k, --key= - Name of the key to find -ynh_read_manifest () { - # Declare an array to define the options of this helper. - declare -Ar args_array=( [m]=manifest= [k]=manifest_key= ) - local manifest - local manifest_key - # Manage arguments with getopts - ynh_handle_getopts_args "$@" - - if [ ! -e "$manifest" ]; then - # If the manifest isn't found, try the common place for backup and restore script. - manifest="../settings/manifest.json" - fi - - jq ".$manifest_key" "$manifest" --raw-output -} - -# Read the upstream version from the manifest -# The version number in the manifest is defined by ~ynh -# For example : 4.3-2~ynh3 -# This include the number before ~ynh -# In the last example it return 4.3-2 -# -# usage: ynh_app_upstream_version [-m manifest] -# | arg: -m, --manifest= - Path of the manifest to read -ynh_app_upstream_version () { - declare -Ar args_array=( [m]=manifest= ) - local manifest - # Manage arguments with getopts - ynh_handle_getopts_args "$@" - - manifest="${manifest:-../manifest.json}" - version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") - echo "${version_key/~ynh*/}" -} - -# Read package version from the manifest -# The version number in the manifest is defined by ~ynh -# For example : 4.3-2~ynh3 -# This include the number after ~ynh -# In the last example it return 3 -# -# usage: ynh_app_package_version [-m manifest] -# | arg: -m, --manifest= - Path of the manifest to read -ynh_app_package_version () { - declare -Ar args_array=( [m]=manifest= ) - local manifest - # Manage arguments with getopts - ynh_handle_getopts_args "$@" - - manifest="${manifest:-../manifest.json}" - version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") - echo "${version_key/*~ynh/}" -} - -# Checks the app version to upgrade with the existing app version and returns: -# - UPGRADE_APP if the upstream app version has changed -# - UPGRADE_PACKAGE if only the YunoHost package has changed -# -## It stops the current script without error if the package is up-to-date -# -# This helper should be used to avoid an upgrade of an app, or the upstream part -# of it, when it's not needed -# -# To force an upgrade, even if the package is up to date, -# you have to set the variable YNH_FORCE_UPGRADE before. -# example: sudo YNH_FORCE_UPGRADE=1 yunohost app upgrade MyApp -# -# usage: ynh_check_app_version_changed -ynh_check_app_version_changed () { - local force_upgrade=${YNH_FORCE_UPGRADE:-0} - local package_check=${PACKAGE_CHECK_EXEC:-0} - - # By default, upstream app version has changed - local return_value="UPGRADE_APP" - - local current_version=$(ynh_read_manifest --manifest="/etc/yunohost/apps/$YNH_APP_INSTANCE_NAME/manifest.json" --manifest_key="version" || echo 1.0) - local current_upstream_version="$(ynh_app_upstream_version --manifest="/etc/yunohost/apps/$YNH_APP_INSTANCE_NAME/manifest.json")" - local update_version=$(ynh_read_manifest --manifest="../manifest.json" --manifest_key="version" || echo 1.0) - local update_upstream_version="$(ynh_app_upstream_version)" - - if [ "$current_version" == "$update_version" ] ; then - # Complete versions are the same - if [ "$force_upgrade" != "0" ] - then - echo "Upgrade forced by YNH_FORCE_UPGRADE." >&2 - unset YNH_FORCE_UPGRADE - elif [ "$package_check" != "0" ] - then - echo "Upgrade forced for package check." >&2 - else - ynh_die "Up-to-date, nothing to do" 0 - fi - elif [ "$current_upstream_version" == "$update_upstream_version" ] ; then - # Upstream versions are the same, only YunoHost package versions differ - return_value="UPGRADE_PACKAGE" - fi - echo $return_value -} diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 5ba2946a2..c6603ab38 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -1,109 +1,96 @@ #!/bin/bash -# Extract a key from a plain command output +# Determine whether the scripts is executed from a terminal or the admin JS. # -# example: yunohost user info tata --output-as plain | ynh_get_plain_key mail -# -# usage: ynh_get_plain_key key [subkey [subsubkey ...]] -# | ret: string - the key's value -ynh_get_plain_key() { - local prefix="#" - local founded=0 - local key=$1 - shift - while read line; do - if [[ "$founded" == "1" ]] ; then - [[ "$line" =~ ^${prefix}[^#] ]] && return - echo $line - elif [[ "$line" =~ ^${prefix}${key}$ ]]; then - if [[ -n "${1:-}" ]]; then - prefix+="#" - key=$1 - shift - else - founded=1 - fi - fi - done +# [internal] +ynh_is_term () { + # Return true if $TERM return xterm + # Which means the script is executed from a terminal, not from the admin JS + [[ "$TERM" =~ "xterm" ]] } -# Restore a previous backup if the upgrade process failed +# Manage a fail of the script +# +# [internal] # # usage: -# ynh_backup_before_upgrade +# ynh_exit_properly is used only by the helper ynh_abort_if_errors. +# You should not use it directly. +# Instead, add to your script: # ynh_clean_setup () { -# ynh_restore_upgradebackup +# instructions... # } -# ynh_abort_if_errors # -ynh_restore_upgradebackup () { - echo "Upgrade failed." >&2 - local app_bck=${app//_/-} # Replace all '_' by '-' - - NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0} - - if [ "$NO_BACKUP_UPGRADE" -eq 0 ] - then - # Check if an existing backup can be found before removing and restoring the application. - if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$backup_number - then - # Remove the application then restore it - sudo yunohost app remove $app - # Restore the backup - sudo yunohost backup restore $app_bck-pre-upgrade$backup_number --apps $app --force --debug - ynh_die --message="The app was restored to the way it was before the failed upgrade." - fi - else - echo "\$NO_BACKUP_UPGRADE is set, that means there's no backup to restore. You have to fix this upgrade by yourself !" >&2 - fi -} - -# Make a backup in case of failed upgrade +# This function provide a way to clean some residual of installation that not managed by remove script. # -# usage: -# ynh_backup_before_upgrade -# ynh_clean_setup () { -# ynh_restore_upgradebackup -# } -# ynh_abort_if_errors +# It prints a warning to inform that the script was failed, and execute the ynh_clean_setup function if used in the app script # -ynh_backup_before_upgrade () { - if [ ! -e "/etc/yunohost/apps/$app/scripts/backup" ] - then - echo "This app doesn't have any backup script." >&2 - return +ynh_exit_properly () { + local exit_code=$? + if [ "$exit_code" -eq 0 ]; then + exit 0 # Exit without error if the script ended correctly fi - backup_number=1 - local old_backup_number=2 - local app_bck=${app//_/-} # Replace all '_' by '-' - NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0} - if [ "$NO_BACKUP_UPGRADE" -eq 0 ] - then - # Check if a backup already exists with the prefix 1 - if sudo yunohost backup list | grep -q $app_bck-pre-upgrade1 - then - # Prefix becomes 2 to preserve the previous backup - backup_number=2 - old_backup_number=1 - fi + trap '' EXIT # Ignore new exit signals + set +eu # Do not exit anymore if a command fail or if a variable is empty - # Create backup - sudo BACKUP_CORE_ONLY=1 yunohost backup create --apps $app --name $app_bck-pre-upgrade$backup_number --debug - if [ "$?" -eq 0 ] - then - # If the backup succeeded, remove the previous backup - if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$old_backup_number - then - # Remove the previous backup only if it exists - sudo yunohost backup delete $app_bck-pre-upgrade$old_backup_number > /dev/null + echo -e "!!\n $app's script has encountered an error. Its execution was cancelled.\n!!" >&2 + + # If the script is executed from a terminal, dump the end of the log that precedes the crash. + if ynh_is_term + then + # Unset xtrace to not spoil the log + set +x + + local ynh_log="/var/log/yunohost/yunohost-cli.log" + + # Wait for the log to be fill with the data until the crash. + local timeout=0 + while ! tail --lines=20 "$ynh_log" | grep --quiet "ynh_exit_properly" + do + ((timeout++)) + if [ $timeout -eq 500 ]; then + break fi - else - ynh_die --message="Backup failed, the upgrade process was aborted." - fi - else - echo "\$NO_BACKUP_UPGRADE is set, backup will be avoided. Be careful, this upgrade is going to be operated without a security backup" - fi + done + + echo -e "\e[34m\e[1mPlease find here an extract of the log before the crash:\e[0m" >&2 + # Tail the last 30 lines of log of YunoHost + # But remove all lines after "ynh_exit_properly" + # Remove the timestamp at the beginning of the line + # Remove "yunohost.hook..." + # Add DEBUG and color it at the beginning of each log line. + echo -e "$(tail --lines=30 "$ynh_log" \ + | sed '1,/ynh_exit_properly/!d' \ + | sed 's/^[[:digit:]: ,-]*//g' \ + | sed 's/ *yunohost.hook.*\]/ -/g' \ + | sed 's/^WARNING /&/g' \ + | sed 's/^DEBUG /& /g' \ + | sed 's/^INFO /& /g' \ + | sed 's/^/\\e[34m\\e[1m[DEBUG]\\e[0m: /g')" >&2 + set -x + fi + + if type -t ynh_clean_setup > /dev/null; then # Check if the function exist in the app script. + ynh_clean_setup # Call the function to do specific cleaning for the app. + fi + + ynh_die # Exit with error status +} + +# Exits if an error occurs during the execution of the script. +# +# usage: ynh_abort_if_errors +# +# This configure the rest of the script execution such that, if an error occurs +# or if an empty variable is used, the execution of the script stops +# immediately and a call to `ynh_clean_setup` is triggered if it has been +# defined by your script. +# +# Requires YunoHost version 2.6.4 or higher. +ynh_abort_if_errors () { + set -eu # Exit if a command fail, and if a variable is used unset. + trap ynh_exit_properly EXIT # Capturing exit signals on shell script } # Download, check integrity, uncompress and patch the source from app.src @@ -125,7 +112,7 @@ ynh_backup_before_upgrade () { # SOURCE_IN_SUBDIR=false # # (Optionnal) Name of the local archive (offline setup support) # # default: ${src_id}.${src_format} -# SOURCE_FILENAME=example.tar.gz +# SOURCE_FILENAME=example.tar.gz # # (Optional) If it set as false don't extract the source. # # (Useful to get a debian package or a python wheel.) # # default: true @@ -150,6 +137,8 @@ ynh_backup_before_upgrade () { # usage: ynh_setup_source --dest_dir=dest_dir [--source_id=source_id] # | arg: -d, --dest_dir - Directory where to setup sources # | arg: -s, --source_id - Name of the app, if the package contains more than one app +# +# Requires YunoHost version 2.6.4 or higher. ynh_setup_source () { # Declare an array to define the options of this helper. local legacy_args=ds @@ -160,15 +149,22 @@ ynh_setup_source () { ynh_handle_getopts_args "$@" source_id="${source_id:-app}" # If the argument is not given, source_id equals "app" + local src_file_path="$YNH_CWD/../conf/${source_id}.src" + # In case of restore script the src file is in an other path. + # So try to use the restore path if the general path point to no file. + if [ ! -e "$src_file_path" ]; then + src_file_path="$YNH_CWD/../settings/conf/${source_id}.src" + fi + # Load value from configuration file (see above for a small doc about this file # format) - local src_url=$(grep 'SOURCE_URL=' "$YNH_CWD/../conf/${source_id}.src" | cut -d= -f2-) - local src_sum=$(grep 'SOURCE_SUM=' "$YNH_CWD/../conf/${source_id}.src" | cut -d= -f2-) - local src_sumprg=$(grep 'SOURCE_SUM_PRG=' "$YNH_CWD/../conf/${source_id}.src" | cut -d= -f2-) - local src_format=$(grep 'SOURCE_FORMAT=' "$YNH_CWD/../conf/${source_id}.src" | cut -d= -f2-) - local src_extract=$(grep 'SOURCE_EXTRACT=' "$YNH_CWD/../conf/${source_id}.src" | cut -d= -f2-) - local src_in_subdir=$(grep 'SOURCE_IN_SUBDIR=' "$YNH_CWD/../conf/${source_id}.src" | cut -d= -f2-) - local src_filename=$(grep 'SOURCE_FILENAME=' "$YNH_CWD/../conf/${source_id}.src" | cut -d= -f2-) + local src_url=$(grep 'SOURCE_URL=' "$src_file_path" | cut -d= -f2-) + local src_sum=$(grep 'SOURCE_SUM=' "$src_file_path" | cut -d= -f2-) + local src_sumprg=$(grep 'SOURCE_SUM_PRG=' "$src_file_path" | cut -d= -f2-) + local src_format=$(grep 'SOURCE_FORMAT=' "$src_file_path" | cut -d= -f2-) + local src_extract=$(grep 'SOURCE_EXTRACT=' "$src_file_path" | cut -d= -f2-) + local src_in_subdir=$(grep 'SOURCE_IN_SUBDIR=' "$src_file_path" | cut -d= -f2-) + local src_filename=$(grep 'SOURCE_FILENAME=' "$src_file_path" | cut -d= -f2-) # Default value src_sumprg=${src_sumprg:-sha256sum} @@ -199,7 +195,7 @@ ynh_setup_source () { then mv $src_filename $dest_dir elif [ "$src_format" = "zip" ] - then + then # Zip format # Using of a temp directory, because unzip doesn't manage --strip-components if $src_in_subdir ; then @@ -249,45 +245,47 @@ ynh_setup_source () { # $domain and $path_url should be defined externally (and correspond to the domain.tld and the /path (of the app?)) # # example: ynh_local_curl "/install.php?installButton" "foo=$var1" "bar=$var2" -# +# # usage: ynh_local_curl "page_uri" "key1=value1" "key2=value2" ... # | arg: page_uri - Path (relative to $path_url) of the page where POST data will be sent # | arg: key1=value1 - (Optionnal) POST key and corresponding value # | arg: key2=value2 - (Optionnal) Another POST key and corresponding value # | arg: ... - (Optionnal) More POST keys and values +# +# Requires YunoHost version 2.6.4 or higher. ynh_local_curl () { - # Define url of page to curl - local local_page=$(ynh_normalize_url_path $1) - local full_path=$path_url$local_page - - if [ "${path_url}" == "/" ]; then - full_path=$local_page - fi - - local full_page_url=https://localhost$full_path + # Define url of page to curl + local local_page=$(ynh_normalize_url_path $1) + local full_path=$path_url$local_page - # Concatenate all other arguments with '&' to prepare POST data - local POST_data="" - local arg="" - for arg in "${@:2}" - do - POST_data="${POST_data}${arg}&" - done - if [ -n "$POST_data" ] - then - # Add --data arg and remove the last character, which is an unecessary '&' - POST_data="--data ${POST_data::-1}" - fi - - # Wait untils nginx has fully reloaded (avoid curl fail with http2) - sleep 2 + if [ "${path_url}" == "/" ]; then + full_path=$local_page + fi - # Curl the URL - curl --silent --show-error -kL -H "Host: $domain" --resolve $domain:443:127.0.0.1 $POST_data "$full_page_url" + local full_page_url=https://localhost$full_path + + # Concatenate all other arguments with '&' to prepare POST data + local POST_data="" + local arg="" + for arg in "${@:2}" + do + POST_data="${POST_data}${arg}&" + done + if [ -n "$POST_data" ] + then + # Add --data arg and remove the last character, which is an unecessary '&' + POST_data="--data ${POST_data::-1}" + fi + + # Wait untils nginx has fully reloaded (avoid curl fail with http2) + sleep 2 + + # Curl the URL + curl --silent --show-error -kL -H "Host: $domain" --resolve $domain:443:127.0.0.1 $POST_data "$full_page_url" } # Render templates with Jinja2 -# +# # Attention : Variables should be exported before calling this helper to be # accessible inside templates. # @@ -303,3 +301,220 @@ ynh_render_template() { jinja2.Template(sys.stdin.read() ).render(os.environ));' < $template_path > $output_path } + +# Fetch the Debian release codename +# +# usage: ynh_get_debian_release +# | ret: The Debian release codename (i.e. jessie, stretch, ...) +# +# Requires YunoHost version 2.7.12 or higher. +ynh_get_debian_release () { + echo $(lsb_release --codename --short) +} + +# Create a directory under /tmp +# +# [internal] +# +# Deprecated helper +# +# usage: ynh_mkdir_tmp +# | ret: the created directory path +ynh_mkdir_tmp() { + ynh_print_warn --message="The helper ynh_mkdir_tmp is deprecated." + ynh_print_warn --message="You should use 'mktemp -d' instead and manage permissions \ +properly with chmod/chown." + local TMP_DIR=$(mktemp -d) + + # Give rights to other users could be a security risk. + # But for retrocompatibility we need it. (This helpers is deprecated) + chmod 755 $TMP_DIR + echo $TMP_DIR +} + +# Remove a file or a directory securely +# +# usage: ynh_secure_remove --file=path_to_remove +# | arg: -f, --file - File or directory to remove +# +# Requires YunoHost version 2.6.4 or higher. +ynh_secure_remove () { + # Declare an array to define the options of this helper. + local legacy_args=f + declare -Ar args_array=( [f]=file= ) + local file + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + local forbidden_path=" \ + /var/www \ + /home/yunohost.app" + + if [ $# -ge 2 ] + then + ynh_print_warn --message="/!\ Packager ! You provided more than one argument to ynh_secure_remove but it will be ignored... Use this helper with one argument at time." + fi + + if [[ "$forbidden_path" =~ "$file" \ + # Match all paths or subpaths in $forbidden_path + || "$file" =~ ^/[[:alnum:]]+$ \ + # Match all first level paths from / (Like /var, /root, etc...) + || "${file:${#file}-1}" = "/" ]] + # Match if the path finishes by /. Because it seems there is an empty variable + then + ynh_print_warn --message="Avoid deleting $file." + else + if [ -e "$file" ] + then + sudo rm -R "$file" + else + ynh_print_info --message="$file wasn't deleted because it doesn't exist." + fi + fi +} + +# Extract a key from a plain command output +# +# example: yunohost user info tata --output-as plain | ynh_get_plain_key mail +# +# usage: ynh_get_plain_key key [subkey [subsubkey ...]] +# | ret: string - the key's value +# +# Requires YunoHost version 2.2.4 or higher. +ynh_get_plain_key() { + local prefix="#" + local founded=0 + local key=$1 + shift + while read line; do + if [[ "$founded" == "1" ]] ; then + [[ "$line" =~ ^${prefix}[^#] ]] && return + echo $line + elif [[ "$line" =~ ^${prefix}${key}$ ]]; then + if [[ -n "${1:-}" ]]; then + prefix+="#" + key=$1 + shift + else + founded=1 + fi + fi + done +} + +# Read the value of a key in a ynh manifest file +# +# usage: ynh_read_manifest manifest key +# | arg: -m, --manifest= - Path of the manifest to read +# | arg: -k, --key= - Name of the key to find +# +# Requires YunoHost version 3.?.? or higher. +ynh_read_manifest () { + # Declare an array to define the options of this helper. + local legacy_args=mk + declare -Ar args_array=( [m]=manifest= [k]=manifest_key= ) + local manifest + local manifest_key + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if [ ! -e "$manifest" ]; then + # If the manifest isn't found, try the common place for backup and restore script. + manifest="../settings/manifest.json" + fi + + jq ".$manifest_key" "$manifest" --raw-output +} + +# Read the upstream version from the manifest +# The version number in the manifest is defined by ~ynh +# For example : 4.3-2~ynh3 +# This include the number before ~ynh +# In the last example it return 4.3-2 +# +# usage: ynh_app_upstream_version [-m manifest] +# | arg: -m, --manifest= - Path of the manifest to read +# +# Requires YunoHost version 3.?.? or higher. +ynh_app_upstream_version () { + # Declare an array to define the options of this helper. + local legacy_args=m + declare -Ar args_array=( [m]=manifest= ) + local manifest + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + manifest="${manifest:-../manifest.json}" + version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") + echo "${version_key/~ynh*/}" +} + +# Read package version from the manifest +# The version number in the manifest is defined by ~ynh +# For example : 4.3-2~ynh3 +# This include the number after ~ynh +# In the last example it return 3 +# +# usage: ynh_app_package_version [-m manifest] +# | arg: -m, --manifest= - Path of the manifest to read +# +# Requires YunoHost version 3.?.? or higher. +ynh_app_package_version () { + # Declare an array to define the options of this helper. + local legacy_args=m + declare -Ar args_array=( [m]=manifest= ) + local manifest + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + manifest="${manifest:-../manifest.json}" + version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") + echo "${version_key/*~ynh/}" +} + +# Checks the app version to upgrade with the existing app version and returns: +# - UPGRADE_APP if the upstream app version has changed +# - UPGRADE_PACKAGE if only the YunoHost package has changed +# +# It stops the current script without error if the package is up-to-date +# +# This helper should be used to avoid an upgrade of an app, or the upstream part +# of it, when it's not needed +# +# To force an upgrade, even if the package is up to date, +# you have to set the variable YNH_FORCE_UPGRADE before. +# example: sudo YNH_FORCE_UPGRADE=1 yunohost app upgrade MyApp +# +# usage: ynh_check_app_version_changed +# +# Requires YunoHost version 3.?.? or higher. +ynh_check_app_version_changed () { + local force_upgrade=${YNH_FORCE_UPGRADE:-0} + local package_check=${PACKAGE_CHECK_EXEC:-0} + + # By default, upstream app version has changed + local return_value="UPGRADE_APP" + + local current_version=$(ynh_read_manifest --manifest="/etc/yunohost/apps/$YNH_APP_INSTANCE_NAME/manifest.json" --manifest_key="version" || echo 1.0) + local current_upstream_version="$(ynh_app_upstream_version --manifest="/etc/yunohost/apps/$YNH_APP_INSTANCE_NAME/manifest.json")" + local update_version=$(ynh_read_manifest --manifest="../manifest.json" --manifest_key="version" || echo 1.0) + local update_upstream_version="$(ynh_app_upstream_version)" + + if [ "$current_version" == "$update_version" ] ; then + # Complete versions are the same + if [ "$force_upgrade" != "0" ] + then + ynh_print_info --message="Upgrade forced by YNH_FORCE_UPGRADE." + unset YNH_FORCE_UPGRADE + elif [ "$package_check" != "0" ] + then + ynh_print_info --message="Upgrade forced for package check." + else + ynh_die "Up-to-date, nothing to do" 0 + fi + elif [ "$current_upstream_version" == "$update_upstream_version" ] ; then + # Upstream versions are the same, only YunoHost package versions differ + return_value="UPGRADE_PACKAGE" + fi + echo $return_value +} From a4f3248545337ad2e45f3ff590e67a54e80605da Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Wed, 8 May 2019 01:04:57 +0200 Subject: [PATCH 30/67] Restore usage of ynh_print_err --- data/helpers.d/utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index c6603ab38..d24474ebc 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -34,7 +34,7 @@ ynh_exit_properly () { trap '' EXIT # Ignore new exit signals set +eu # Do not exit anymore if a command fail or if a variable is empty - echo -e "!!\n $app's script has encountered an error. Its execution was cancelled.\n!!" >&2 + ynh_print_err --message="!!\n $app's script has encountered an error. Its execution was cancelled.\n!!" # If the script is executed from a terminal, dump the end of the log that precedes the crash. if ynh_is_term From c15cc788ed8265db01a224b763f82ca23f51fdc2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 10 May 2019 01:33:52 +0200 Subject: [PATCH 31/67] Add utils to fetch LDAP interface --- src/yunohost/tools.py | 19 +++++++----------- src/yunohost/utils/ldap.py | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 src/yunohost/utils/ldap.py diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 7b9bf044e..a3b6bc8cd 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -39,8 +39,6 @@ import apt import apt.progress from moulinette import msettings, msignals, m18n -from moulinette.core import init_authenticator -from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output, call_async_output from moulinette.utils.filesystem import read_json, write_to_json @@ -53,6 +51,7 @@ from yunohost.regenconf import regen_conf from yunohost.monitor import monitor_disk, monitor_system from yunohost.utils.packages import ynh_packages_version from yunohost.utils.network import get_public_ip +from yunohost.utils.error import YunohostError from yunohost.log import is_unit_operation, OperationLogger # FIXME this is a duplicate from apps.py @@ -69,25 +68,21 @@ def tools_ldapinit(): """ - # Instantiate LDAP Authenticator - AUTH_IDENTIFIER = ('ldap', 'as-root') - AUTH_PARAMETERS = {'uri': 'ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi', - 'base_dn': 'dc=yunohost,dc=org', - 'user_rdn': 'gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth'} - auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) - with open('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') as f: ldap_map = yaml.load(f) + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + for rdn, attr_dict in ldap_map['parents'].items(): try: - auth.add(rdn, attr_dict) + ldap.add(rdn, attr_dict) except Exception as e: logger.warn("Error when trying to inject '%s' -> '%s' into ldap: %s" % (rdn, attr_dict, e)) for rdn, attr_dict in ldap_map['children'].items(): try: - auth.add(rdn, attr_dict) + ldap.add(rdn, attr_dict) except Exception as e: logger.warn("Error when trying to inject '%s' -> '%s' into ldap: %s" % (rdn, attr_dict, e)) @@ -103,7 +98,7 @@ def tools_ldapinit(): 'userPassword': 'yunohost' } - auth.update('cn=admin', admin_dict) + ldap.update('cn=admin', admin_dict) # Force nscd to refresh cache to take admin creation into account subprocess.call(['nscd', '-i', 'passwd']) diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py new file mode 100644 index 000000000..f4a5f355c --- /dev/null +++ b/src/yunohost/utils/ldap.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +""" License + + Copyright (C) 2019 YunoHost + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, see http://www.gnu.org/licenses + +""" + +from moulinette.core import init_authenticator + +# We use a global variable to do some caching +# to avoid re-authenticating in case we call _get_ldap_authenticator multiple times +_ldap_interface = None + +def _get_ldap_interface(): + + global _ldap_interface + + if _ldap_interface is None: + # Instantiate LDAP Authenticator + AUTH_IDENTIFIER = ('ldap', 'as-root') + AUTH_PARAMETERS = {'uri': 'ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi', + 'base_dn': 'dc=yunohost,dc=org', + 'user_rdn': 'gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth'} + _ldap_interface = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) + + return _ldap_interface From f010f4952893c6dd31a4b3fa773d83022d44c9f4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 10 May 2019 02:11:40 +0200 Subject: [PATCH 32/67] Get rid of all those crazy 'auth' arguments. Instead : directly fetch the ldap interface when and where it's actually needed --- src/yunohost/app.py | 88 ++++++++++++++++++------------------- src/yunohost/backup.py | 2 +- src/yunohost/certificate.py | 28 ++++++------ src/yunohost/domain.py | 53 ++++++++++++---------- src/yunohost/log.py | 7 ++- src/yunohost/ssh.py | 38 +++++++++------- src/yunohost/tools.py | 37 +++++++++------- src/yunohost/user.py | 87 +++++++++++++++++++++--------------- 8 files changed, 186 insertions(+), 154 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 7813dee58..f27a10abd 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -38,12 +38,12 @@ from collections import OrderedDict from datetime import datetime from moulinette import msignals, m18n, msettings -from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_json from yunohost.service import service_log, service_status, _run_service_command from yunohost.utils import packages +from yunohost.utils.error import YunohostError from yunohost.log import is_unit_operation, OperationLogger logger = getActionLogger('yunohost.app') @@ -437,7 +437,7 @@ def app_map(app=None, raw=False, user=None): @is_unit_operation() -def app_change_url(operation_logger, auth, app, domain, path): +def app_change_url(operation_logger, app, domain, path): """ Modify the URL at which an application is installed. @@ -468,7 +468,7 @@ def app_change_url(operation_logger, auth, app, domain, path): raise YunohostError("app_change_url_identical_domains", domain=domain, path=path) # Check the url is available - conflicts = _get_conflicting_apps(auth, domain, path, ignore_app=app) + conflicts = _get_conflicting_apps(domain, path, ignore_app=app) if conflicts: apps = [] for path, app_id, app_label in conflicts: @@ -484,7 +484,7 @@ def app_change_url(operation_logger, auth, app, domain, path): # Retrieve arguments list for change_url script # TODO: Allow to specify arguments - args_odict = _parse_args_from_manifest(manifest, 'change_url', auth=auth) + args_odict = _parse_args_from_manifest(manifest, 'change_url') args_list = args_odict.values() args_list.append(app) @@ -538,7 +538,7 @@ def app_change_url(operation_logger, auth, app, domain, path): app_setting(app, 'domain', value=domain) app_setting(app, 'path', value=path) - app_ssowatconf(auth) + app_ssowatconf() # avoid common mistakes if _run_service_command("reload", "nginx") == False: @@ -557,7 +557,7 @@ def app_change_url(operation_logger, auth, app, domain, path): hook_callback('post_app_change_url', args=args_list, env=env_dict) -def app_upgrade(auth, app=[], url=None, file=None): +def app_upgrade(app=[], url=None, file=None): """ Upgrade app @@ -633,7 +633,7 @@ def app_upgrade(auth, app=[], url=None, file=None): # Retrieve arguments list for upgrade script # TODO: Allow to specify arguments - args_odict = _parse_args_from_manifest(manifest, 'upgrade', auth=auth) + args_odict = _parse_args_from_manifest(manifest, 'upgrade') args_list = args_odict.values() args_list.append(app_instance_name) @@ -693,7 +693,7 @@ def app_upgrade(auth, app=[], url=None, file=None): if not_upgraded_apps: raise YunohostError('app_not_upgraded', apps=', '.join(not_upgraded_apps)) - app_ssowatconf(auth) + app_ssowatconf() logger.success(m18n.n('upgrade_complete')) @@ -703,7 +703,7 @@ def app_upgrade(auth, app=[], url=None, file=None): @is_unit_operation() -def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on_failure=False, force=False): +def app_install(operation_logger, app, label=None, args=None, no_remove_on_failure=False, force=False): """ Install apps @@ -795,7 +795,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on # Retrieve arguments list for install script args_dict = {} if not args else \ dict(urlparse.parse_qsl(args, keep_blank_values=True)) - args_odict = _parse_args_from_manifest(manifest, 'install', args=args_dict, auth=auth) + args_odict = _parse_args_from_manifest(manifest, 'install', args=args_dict) args_list = args_odict.values() args_list.append(app_instance_name) @@ -887,7 +887,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on shutil.rmtree(app_setting_path) shutil.rmtree(extracted_app_folder) - app_ssowatconf(auth) + app_ssowatconf() if packages.dpkg_is_broken(): logger.error(m18n.n("this_action_broke_dpkg")) @@ -914,7 +914,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on os.system('chown -R root: %s' % app_setting_path) os.system('chown -R admin: %s/scripts' % app_setting_path) - app_ssowatconf(auth) + app_ssowatconf() logger.success(m18n.n('installation_complete')) @@ -922,7 +922,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on @is_unit_operation() -def app_remove(operation_logger, auth, app): +def app_remove(operation_logger, app): """ Remove app @@ -974,13 +974,13 @@ def app_remove(operation_logger, auth, app): shutil.rmtree(app_setting_path) shutil.rmtree('/tmp/yunohost_remove') hook_remove(app) - app_ssowatconf(auth) + app_ssowatconf() if packages.dpkg_is_broken(): raise YunohostError("this_action_broke_dpkg") -def app_addaccess(auth, apps, users=[]): +def app_addaccess(apps, users=[]): """ Grant access right to users (everyone by default) @@ -995,7 +995,7 @@ def app_addaccess(auth, apps, users=[]): result = {} if not users: - users = user_list(auth)['users'].keys() + users = user_list()['users'].keys() elif not isinstance(users, list): users = [users, ] if not isinstance(apps, list): @@ -1025,7 +1025,7 @@ def app_addaccess(auth, apps, users=[]): for allowed_user in users: if allowed_user not in allowed_users: try: - user_info(auth, allowed_user) + user_info(allowed_user) except YunohostError: logger.warning(m18n.n('user_unknown', user=allowed_user)) continue @@ -1041,12 +1041,12 @@ def app_addaccess(auth, apps, users=[]): result[app] = allowed_users - app_ssowatconf(auth) + app_ssowatconf() return {'allowed_users': result} -def app_removeaccess(auth, apps, users=[]): +def app_removeaccess(apps, users=[]): """ Revoke access right to users (everyone by default) @@ -1088,7 +1088,7 @@ def app_removeaccess(auth, apps, users=[]): if allowed_user not in users: allowed_users.add(allowed_user) else: - for allowed_user in user_list(auth)['users'].keys(): + for allowed_user in user_list()['users'].keys(): if allowed_user not in users: allowed_users.add(allowed_user) @@ -1102,12 +1102,12 @@ def app_removeaccess(auth, apps, users=[]): operation_logger.success() - app_ssowatconf(auth) + app_ssowatconf() return {'allowed_users': result} -def app_clearaccess(auth, apps): +def app_clearaccess(apps): """ Reset access rights for the app @@ -1140,7 +1140,7 @@ def app_clearaccess(auth, apps): operation_logger.success() - app_ssowatconf(auth) + app_ssowatconf() def app_debug(app): @@ -1167,7 +1167,7 @@ def app_debug(app): @is_unit_operation() -def app_makedefault(operation_logger, auth, app, domain=None): +def app_makedefault(operation_logger, app, domain=None): """ Redirect domain root to an app @@ -1185,7 +1185,7 @@ def app_makedefault(operation_logger, auth, app, domain=None): if domain is None: domain = app_domain operation_logger.related_to.append(('domain', domain)) - elif domain not in domain_list(auth)['domains']: + elif domain not in domain_list()['domains']: raise YunohostError('domain_unknown') operation_logger.start() @@ -1264,7 +1264,7 @@ def app_checkport(port): raise YunohostError('port_unavailable', port=int(port)) -def app_register_url(auth, app, domain, path): +def app_register_url(app, domain, path): """ Book/register a web path for a given app @@ -1290,7 +1290,7 @@ def app_register_url(auth, app, domain, path): raise YunohostError('app_already_installed_cant_change_url') # Check the url is available - conflicts = _get_conflicting_apps(auth, domain, path) + conflicts = _get_conflicting_apps(domain, path) if conflicts: apps = [] for path, app_id, app_label in conflicts: @@ -1307,7 +1307,7 @@ def app_register_url(auth, app, domain, path): app_setting(app, 'path', value=path) -def app_checkurl(auth, url, app=None): +def app_checkurl(url, app=None): """ Check availability of a web path @@ -1337,7 +1337,7 @@ def app_checkurl(auth, url, app=None): apps_map = app_map(raw=True) - if domain not in domain_list(auth)['domains']: + if domain not in domain_list()['domains']: raise YunohostError('domain_unknown') if domain in apps_map: @@ -1394,7 +1394,7 @@ def app_initdb(user, password=None, db=None, sql=None): logger.success(m18n.n('mysql_db_initialized')) -def app_ssowatconf(auth): +def app_ssowatconf(): """ Regenerate SSOwat configuration file @@ -1404,7 +1404,7 @@ def app_ssowatconf(auth): from yunohost.user import user_list main_domain = _get_maindomain() - domains = domain_list(auth)['domains'] + domains = domain_list()['domains'] skipped_urls = [] skipped_regex = [] @@ -1481,7 +1481,7 @@ def app_ssowatconf(auth): 'redirected_urls': redirected_urls, 'redirected_regex': redirected_regex, 'users': {username: app_map(user=username) - for username in user_list(auth)['users'].keys()}, + for username in user_list()['users'].keys()}, } with open('/etc/ssowat/conf.json', 'w+') as f: @@ -1490,14 +1490,14 @@ def app_ssowatconf(auth): logger.success(m18n.n('ssowat_conf_generated')) -def app_change_label(auth, app, new_label): +def app_change_label(app, new_label): installed = _is_installed(app) if not installed: raise YunohostError('app_not_installed', app=app) app_setting(app, "label", value=new_label) - app_ssowatconf(auth) + app_ssowatconf() # actions todo list: @@ -2143,7 +2143,7 @@ def _check_manifest_requirements(manifest, app_instance_name): spec=spec, app=app_instance_name) -def _parse_args_from_manifest(manifest, action, args={}, auth=None): +def _parse_args_from_manifest(manifest, action, args={}): """Parse arguments needed for an action from the manifest Retrieve specified arguments for the action from the manifest, and parse @@ -2162,10 +2162,10 @@ def _parse_args_from_manifest(manifest, action, args={}, auth=None): return OrderedDict() action_args = manifest['arguments'][action] - return _parse_action_args_in_yunohost_format(args, action_args, auth) + return _parse_action_args_in_yunohost_format(args, action_args) -def _parse_args_for_action(action, args={}, auth=None): +def _parse_args_for_action(action, args={}): """Parse arguments needed for an action from the actions list Retrieve specified arguments for the action from the manifest, and parse @@ -2186,10 +2186,10 @@ def _parse_args_for_action(action, args={}, auth=None): action_args = action['arguments'] - return _parse_action_args_in_yunohost_format(args, action_args, auth) + return _parse_action_args_in_yunohost_format(args, action_args) -def _parse_action_args_in_yunohost_format(args, action_args, auth=None): +def _parse_action_args_in_yunohost_format(args, action_args): """Parse arguments store in either manifest.json or actions.json """ from yunohost.domain import (domain_list, _get_maindomain, @@ -2242,12 +2242,12 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): arg_default = _get_maindomain() ask_string += ' (default: {0})'.format(arg_default) msignals.display(m18n.n('domains_available')) - for domain in domain_list(auth)['domains']: + for domain in domain_list()['domains']: msignals.display("- {}".format(domain)) elif arg_type == 'user': msignals.display(m18n.n('users_available')) - for user in user_list(auth)['users'].keys(): + for user in user_list()['users'].keys(): msignals.display("- {}".format(user)) elif arg_type == 'password': @@ -2283,11 +2283,11 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): # Validate argument type if arg_type == 'domain': - if arg_value not in domain_list(auth)['domains']: + if arg_value not in domain_list()['domains']: raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown')) elif arg_type == 'user': try: - user_info(auth, arg_value) + user_info(arg_value) except YunohostError as e: raise YunohostError('app_argument_invalid', name=arg_name, error=e) elif arg_type == 'app': @@ -2328,7 +2328,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): domain, path = _normalize_domain_path(domain, path) # Check the url is available - conflicts = _get_conflicting_apps(auth, domain, path) + conflicts = _get_conflicting_apps(domain, path) if conflicts: apps = [] for path, app_id, app_label in conflicts: diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 82a778491..9a87015c0 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -2090,7 +2090,7 @@ def backup_create(name=None, description=None, methods=[], } -def backup_restore(auth, name, system=[], apps=[], force=False): +def backup_restore(name, system=[], apps=[], force=False): """ Restore from a local backup archive diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index d7e8c0157..4235d522d 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -86,7 +86,7 @@ DNS_RESOLVERS = [ # -def certificate_status(auth, domain_list, full=False): +def certificate_status(domain_list, full=False): """ Print the status of certificate for given domains (all by default) @@ -99,10 +99,10 @@ def certificate_status(auth, domain_list, full=False): # If no domains given, consider all yunohost domains if domain_list == []: - domain_list = yunohost.domain.domain_list(auth)['domains'] + domain_list = yunohost.domain.domain_list()['domains'] # Else, validate that yunohost knows the domains given else: - yunohost_domains_list = yunohost.domain.domain_list(auth)['domains'] + yunohost_domains_list = yunohost.domain.domain_list()['domains'] for domain in domain_list: # Is it in Yunohost domain list? if domain not in yunohost_domains_list: @@ -126,7 +126,7 @@ def certificate_status(auth, domain_list, full=False): return {"certificates": certificates} -def certificate_install(auth, domain_list, force=False, no_checks=False, self_signed=False, staging=False): +def certificate_install(domain_list, force=False, no_checks=False, self_signed=False, staging=False): """ Install a Let's Encrypt certificate for given domains (all by default) @@ -142,7 +142,7 @@ def certificate_install(auth, domain_list, force=False, no_checks=False, self_si _certificate_install_selfsigned(domain_list, force) else: _certificate_install_letsencrypt( - auth, domain_list, force, no_checks, staging) + domain_list, force, no_checks, staging) def _certificate_install_selfsigned(domain_list, force=False): @@ -237,7 +237,7 @@ def _certificate_install_selfsigned(domain_list, force=False): operation_logger.error(msg) -def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=False, staging=False): +def _certificate_install_letsencrypt(domain_list, force=False, no_checks=False, staging=False): import yunohost.domain if not os.path.exists(ACCOUNT_KEY_FILE): @@ -246,7 +246,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F # If no domains given, consider all yunohost domains with self-signed # certificates if domain_list == []: - for domain in yunohost.domain.domain_list(auth)['domains']: + for domain in yunohost.domain.domain_list()['domains']: status = _get_status(domain) if status["CA_type"]["code"] != "self-signed": @@ -257,7 +257,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F # Else, validate that yunohost knows the domains given else: for domain in domain_list: - yunohost_domains_list = yunohost.domain.domain_list(auth)['domains'] + yunohost_domains_list = yunohost.domain.domain_list()['domains'] if domain not in yunohost_domains_list: raise YunohostError('certmanager_domain_unknown', domain=domain) @@ -285,7 +285,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F operation_logger.start() - _configure_for_acme_challenge(auth, domain) + _configure_for_acme_challenge(domain) _fetch_and_enable_new_certificate(domain, staging, no_checks=no_checks) _install_cron(no_checks=no_checks) @@ -300,7 +300,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F operation_logger.error(msg) -def certificate_renew(auth, domain_list, force=False, no_checks=False, email=False, staging=False): +def certificate_renew(domain_list, force=False, no_checks=False, email=False, staging=False): """ Renew Let's Encrypt certificate for given domains (all by default) @@ -317,7 +317,7 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal # If no domains given, consider all yunohost domains with Let's Encrypt # certificates if domain_list == []: - for domain in yunohost.domain.domain_list(auth)['domains']: + for domain in yunohost.domain.domain_list()['domains']: # Does it have a Let's Encrypt cert? status = _get_status(domain) @@ -344,7 +344,7 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal for domain in domain_list: # Is it in Yunohost dmomain list? - if domain not in yunohost.domain.domain_list(auth)['domains']: + if domain not in yunohost.domain.domain_list()['domains']: raise YunohostError('certmanager_domain_unknown', domain=domain) status = _get_status(domain) @@ -468,7 +468,7 @@ Subject: %s smtp.quit() -def _configure_for_acme_challenge(auth, domain): +def _configure_for_acme_challenge(domain): nginx_conf_folder = "/etc/nginx/conf.d/%s.d" % domain nginx_conf_file = "%s/000-acmechallenge.conf" % nginx_conf_folder @@ -511,7 +511,7 @@ location ^~ '/.well-known/acme-challenge/' # any clean function already implemented in yunohost to do this though) _run_service_command("reload", "nginx") - app_ssowatconf(auth) + app_ssowatconf() def _check_acme_challenge_configuration(domain): diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index a39f45a0e..42a4881ba 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -42,7 +42,7 @@ from yunohost.hook import hook_callback logger = getActionLogger('yunohost.domain') -def domain_list(auth): +def domain_list(): """ List domains @@ -52,10 +52,12 @@ def domain_list(auth): limit -- Maximum number of domain fetched """ + from yunohost.utils.ldap import _get_ldap_interface + + ldap = _get_ldap_interface() + result = ldap.search('ou=domains,dc=yunohost,dc=org', 'virtualdomain=*', ['virtualdomain']) + result_list = [] - - result = auth.search('ou=domains,dc=yunohost,dc=org', 'virtualdomain=*', ['virtualdomain']) - for domain in result: result_list.append(domain['virtualdomain'][0]) @@ -63,7 +65,7 @@ def domain_list(auth): @is_unit_operation() -def domain_add(operation_logger, auth, domain, dyndns=False): +def domain_add(operation_logger, domain, dyndns=False): """ Create a custom domain @@ -74,9 +76,12 @@ def domain_add(operation_logger, auth, domain, dyndns=False): """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf + from yunohost.utils.ldap import _get_ldap_interface + + ldap = _get_ldap_interface() try: - auth.validate_uniqueness({'virtualdomain': domain}) + ldap.validate_uniqueness({'virtualdomain': domain}) except MoulinetteError: raise YunohostError('domain_exists') @@ -107,18 +112,18 @@ def domain_add(operation_logger, auth, domain, dyndns=False): 'virtualdomain': domain, } - if not auth.add('virtualdomain=%s,ou=domains' % domain, attr_dict): + if not ldap.add('virtualdomain=%s,ou=domains' % domain, attr_dict): raise YunohostError('domain_creation_failed') # Don't regen these conf if we're still in postinstall if os.path.exists('/etc/yunohost/installed'): regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix', 'rspamd']) - app_ssowatconf(auth) + app_ssowatconf() except Exception: # Force domain removal silently try: - domain_remove(auth, domain, True) + domain_remove(domain, True) except: pass raise @@ -129,7 +134,7 @@ def domain_add(operation_logger, auth, domain, dyndns=False): @is_unit_operation() -def domain_remove(operation_logger, auth, domain, force=False): +def domain_remove(operation_logger, domain, force=False): """ Delete domains @@ -140,8 +145,9 @@ def domain_remove(operation_logger, auth, domain, force=False): """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf + from yunohost.utils.ldap import _get_ldap_interface - if not force and domain not in domain_list(auth)['domains']: + if not force and domain not in domain_list()['domains']: raise YunohostError('domain_unknown') # Check domain is not the main domain @@ -160,13 +166,14 @@ def domain_remove(operation_logger, auth, domain, force=False): raise YunohostError('domain_uninstall_app_first') operation_logger.start() - if auth.remove('virtualdomain=' + domain + ',ou=domains') or force: + ldap = _get_ldap_interface() + if ldap.remove('virtualdomain=' + domain + ',ou=domains') or force: os.system('rm -rf /etc/yunohost/certs/%s' % domain) else: raise YunohostError('domain_deletion_failed') regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix']) - app_ssowatconf(auth) + app_ssowatconf() hook_callback('post_domain_remove', args=[domain]) @@ -222,19 +229,19 @@ def domain_dns_conf(domain, ttl=None): return result -def domain_cert_status(auth, domain_list, full=False): - return yunohost.certificate.certificate_status(auth, domain_list, full) +def domain_cert_status(domain_list, full=False): + return yunohost.certificate.certificate_status(domain_list, full) -def domain_cert_install(auth, domain_list, force=False, no_checks=False, self_signed=False, staging=False): - return yunohost.certificate.certificate_install(auth, domain_list, force, no_checks, self_signed, staging) +def domain_cert_install(domain_list, force=False, no_checks=False, self_signed=False, staging=False): + return yunohost.certificate.certificate_install(domain_list, force, no_checks, self_signed, staging) -def domain_cert_renew(auth, domain_list, force=False, no_checks=False, email=False, staging=False): - return yunohost.certificate.certificate_renew(auth, domain_list, force, no_checks, email, staging) +def domain_cert_renew(domain_list, force=False, no_checks=False, email=False, staging=False): + return yunohost.certificate.certificate_renew(domain_list, force, no_checks, email, staging) -def _get_conflicting_apps(auth, domain, path, ignore_app=None): +def _get_conflicting_apps(domain, path, ignore_app=None): """ Return a list of all conflicting apps with a domain/path (it can be empty) @@ -247,7 +254,7 @@ def _get_conflicting_apps(auth, domain, path, ignore_app=None): domain, path = _normalize_domain_path(domain, path) # Abort if domain is unknown - if domain not in domain_list(auth)['domains']: + if domain not in domain_list()['domains']: raise YunohostError('domain_unknown') # This import cannot be put on top of file because it would create a @@ -274,7 +281,7 @@ def _get_conflicting_apps(auth, domain, path, ignore_app=None): return conflicts -def domain_url_available(auth, domain, path): +def domain_url_available(domain, path): """ Check availability of a web path @@ -283,7 +290,7 @@ def domain_url_available(auth, domain, path): path -- The path to check (e.g. /coffee) """ - return len(_get_conflicting_apps(auth, domain, path)) == 0 + return len(_get_conflicting_apps(domain, path)) == 0 def _get_maindomain(): diff --git a/src/yunohost/log.py b/src/yunohost/log.py index e576ae536..3620ac990 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -208,7 +208,7 @@ def log_display(path, number=50, share=False): def is_unit_operation(entities=['app', 'domain', 'service', 'user'], - exclude=['auth', 'password'], operation_key=None): + exclude=['password'], operation_key=None): """ Configure quickly a unit operation @@ -222,9 +222,8 @@ def is_unit_operation(entities=['app', 'domain', 'service', 'user'], (argname, entity_type) instead of just put the entity type. exclude Remove some arguments from the context. By default, arguments - called 'password' and 'auth' are removed. If an argument is an object, you - need to exclude it or create manually the unit operation without this - decorator. + called 'password' are removed. If an argument is an object, you need to + exclude it or create manually the unit operation without this decorator. operation_key A key to describe the unit operation log used to create the filename and search a translation. Please ensure that this key prefixed by diff --git a/src/yunohost/ssh.py b/src/yunohost/ssh.py index b4ac31dbb..f0110b34e 100644 --- a/src/yunohost/ssh.py +++ b/src/yunohost/ssh.py @@ -11,7 +11,7 @@ from moulinette.utils.filesystem import read_file, write_to_file, chown, chmod, SSHD_CONFIG_PATH = "/etc/ssh/sshd_config" -def user_ssh_allow(auth, username): +def user_ssh_allow(username): """ Allow YunoHost user connect as ssh. @@ -20,17 +20,19 @@ def user_ssh_allow(auth, username): """ # TODO it would be good to support different kind of shells - if not _get_user_for_ssh(auth, username): + if not _get_user_for_ssh(username): raise YunohostError('user_unknown', user=username) - auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/bash'}) + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + ldap.update('uid=%s,ou=users' % username, {'loginShell': '/bin/bash'}) # Somehow this is needed otherwise the PAM thing doesn't forget about the # old loginShell value ? subprocess.call(['nscd', '-i', 'passwd']) -def user_ssh_disallow(auth, username): +def user_ssh_disallow(username): """ Disallow YunoHost user connect as ssh. @@ -39,18 +41,20 @@ def user_ssh_disallow(auth, username): """ # TODO it would be good to support different kind of shells - if not _get_user_for_ssh(auth, username): + if not _get_user_for_ssh(username): raise YunohostError('user_unknown', user=username) - auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/false'}) + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + ldap.update('uid=%s,ou=users' % username, {'loginShell': '/bin/false'}) # Somehow this is needed otherwise the PAM thing doesn't forget about the # old loginShell value ? subprocess.call(['nscd', '-i', 'passwd']) -def user_ssh_list_keys(auth, username): - user = _get_user_for_ssh(auth, username, ["homeDirectory"]) +def user_ssh_list_keys(username): + user = _get_user_for_ssh(username, ["homeDirectory"]) if not user: raise Exception("User with username '%s' doesn't exists" % username) @@ -82,8 +86,8 @@ def user_ssh_list_keys(auth, username): return {"keys": keys} -def user_ssh_add_key(auth, username, key, comment): - user = _get_user_for_ssh(auth, username, ["homeDirectory", "uid"]) +def user_ssh_add_key(username, key, comment): + user = _get_user_for_ssh(username, ["homeDirectory", "uid"]) if not user: raise Exception("User with username '%s' doesn't exists" % username) @@ -116,8 +120,8 @@ def user_ssh_add_key(auth, username, key, comment): write_to_file(authorized_keys_file, authorized_keys_content) -def user_ssh_remove_key(auth, username, key): - user = _get_user_for_ssh(auth, username, ["homeDirectory", "uid"]) +def user_ssh_remove_key(username, key): + user = _get_user_for_ssh(username, ["homeDirectory", "uid"]) if not user: raise Exception("User with username '%s' doesn't exists" % username) @@ -148,8 +152,8 @@ def user_ssh_remove_key(auth, username, key): # -def _get_user_for_ssh(auth, username, attrs=None): - def ssh_root_login_status(auth): +def _get_user_for_ssh(username, attrs=None): + def ssh_root_login_status(): # XXX temporary placed here for when the ssh_root commands are integrated # extracted from https://github.com/YunoHost/yunohost/pull/345 # XXX should we support all the options? @@ -172,7 +176,7 @@ def _get_user_for_ssh(auth, username, attrs=None): 'username': 'root', 'fullname': '', 'mail': '', - 'ssh_allowed': ssh_root_login_status(auth)["PermitRootLogin"], + 'ssh_allowed': ssh_root_login_status()["PermitRootLogin"], 'shell': root_unix.pw_shell, 'home_path': root_unix.pw_dir, } @@ -189,7 +193,9 @@ def _get_user_for_ssh(auth, username, attrs=None): } # TODO escape input using https://www.python-ldap.org/doc/html/ldap-filter.html - user = auth.search('ou=users,dc=yunohost,dc=org', + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + user = ldap.search('ou=users,dc=yunohost,dc=org', '(&(objectclass=person)(uid=%s))' % username, attrs) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index a3b6bc8cd..572328392 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -111,10 +111,9 @@ def tools_ldapinit(): raise YunohostError('installation_failed') logger.success(m18n.n('ldap_initialized')) - return auth -def tools_adminpw(auth, new_password, check_strength=True): +def tools_adminpw(new_password, check_strength=True): """ Change admin password @@ -136,8 +135,11 @@ def tools_adminpw(auth, new_password, check_strength=True): new_hash = _hash_user_password(new_password) + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + try: - auth.update("cn=admin", {"userPassword": new_hash, }) + ldap.update("cn=admin", {"userPassword": new_hash, }) except: logger.exception('unable to change admin password') raise YunohostError('admin_password_change_failed') @@ -161,7 +163,7 @@ def tools_adminpw(auth, new_password, check_strength=True): @is_unit_operation() -def tools_maindomain(operation_logger, auth, new_domain=None): +def tools_maindomain(operation_logger, new_domain=None): """ Check the current main domain, or change it @@ -175,7 +177,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): return {'current_main_domain': _get_maindomain()} # Check domain exists - if new_domain not in domain_list(auth)['domains']: + if new_domain not in domain_list()['domains']: raise YunohostError('domain_unknown') operation_logger.related_to.append(('domain', new_domain)) @@ -204,7 +206,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): _set_hostname(new_domain) # Generate SSOwat configuration file - app_ssowatconf(auth) + app_ssowatconf() # Regen configurations try: @@ -331,7 +333,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, # Initialize LDAP for YunoHost # TODO: Improve this part by integrate ldapinit into conf_regen hook - auth = tools_ldapinit() + tools_ldapinit() # Create required folders folders_to_create = [ @@ -405,11 +407,11 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, # New domain config regen_conf(['nsswitch'], force=True) - domain_add(auth, domain, dyndns) - tools_maindomain(auth, domain) + domain_add(domain, dyndns) + tools_maindomain(domain) # Change LDAP admin password - tools_adminpw(auth, password, check_strength=not force_password) + tools_adminpw(password, check_strength=not force_password) # Enable UPnP silently and reload firewall firewall_upnp('enable', no_refresh=True) @@ -573,7 +575,7 @@ def _dump_sources_list(): @is_unit_operation() -def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=False): +def tools_upgrade(operation_logger, ignore_apps=False, ignore_packages=False): """ Update apps & package cache, then display changelog @@ -645,7 +647,7 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal if not ignore_apps: try: - app_upgrade(auth) + app_upgrade() except Exception as e: failure = True logger.warning('unable to upgrade apps: %s' % str(e)) @@ -659,7 +661,7 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal return {"log": service_log('yunohost-api', number="100").values()[0]} -def tools_diagnosis(auth, private=False): +def tools_diagnosis(private=False): """ Return global info about current yunohost instance to help debugging @@ -754,7 +756,7 @@ def tools_diagnosis(auth, private=False): diagnosis['private']['public_ip']['IPv6'] = get_public_ip(6) # Domains - diagnosis['private']['domains'] = domain_list(auth)['domains'] + diagnosis['private']['domains'] = domain_list()['domains'] diagnosis['private']['regen_conf'] = regen_conf(with_diff=True, dry_run=True) @@ -1078,18 +1080,21 @@ def tools_migrations_state(): return read_json(MIGRATIONS_STATE_PATH) -def tools_shell(auth, command=None): +def tools_shell(command=None): """ Launch an (i)python shell in the YunoHost context. This is entirely aim for development. """ + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + if command: exec(command) return - logger.warn("The \033[1;34mauth\033[0m is available in this context") + logger.warn("The \033[1;34mldap\033[0m interface is available in this context") try: from IPython import embed embed() diff --git a/src/yunohost/user.py b/src/yunohost/user.py index a38f0b4c5..db0d80ce3 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -41,7 +41,7 @@ from yunohost.log import is_unit_operation logger = getActionLogger('yunohost.user') -def user_list(auth, fields=None): +def user_list(fields=None): """ List users @@ -52,6 +52,8 @@ def user_list(auth, fields=None): fields -- fields to fetch """ + from yunohost.utils.ldap import _get_ldap_interface + user_attrs = { 'uid': 'username', 'cn': 'fullname', @@ -75,7 +77,8 @@ def user_list(auth, fields=None): else: attrs = ['uid', 'cn', 'mail', 'mailuserquota', 'loginShell'] - result = auth.search('ou=users,dc=yunohost,dc=org', + ldap = _get_ldap_interface() + result = ldap.search('ou=users,dc=yunohost,dc=org', '(&(objectclass=person)(!(uid=root))(!(uid=nobody)))', attrs) @@ -98,7 +101,7 @@ def user_list(auth, fields=None): @is_unit_operation([('username', 'user')]) -def user_create(operation_logger, auth, username, firstname, lastname, mail, password, +def user_create(operation_logger, username, firstname, lastname, mail, password, mailbox_quota="0"): """ Create user @@ -116,12 +119,15 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf from yunohost.utils.password import assert_password_is_strong_enough + from yunohost.utils.ldap import _get_ldap_interface # Ensure sufficiently complex password assert_password_is_strong_enough("user", password) + ldap = _get_ldap_interface() + # Validate uniqueness of username and mail in LDAP - auth.validate_uniqueness({ + ldap.validate_uniqueness({ 'uid': username, 'mail': mail }) @@ -143,7 +149,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas raise YunohostError('mail_unavailable') # Check that the mail domain exists - if mail.split("@")[1] not in domain_list(auth)['domains']: + if mail.split("@")[1] not in domain_list()['domains']: raise YunohostError('mail_domain_unknown', domain=mail.split("@")[1]) operation_logger.start() @@ -177,7 +183,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas } # If it is the first user, add some aliases - if not auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=*'): + if not ldap.search(base='ou=users,dc=yunohost,dc=org', filter='uid=*'): attr_dict['mail'] = [attr_dict['mail']] + aliases # If exists, remove the redirection from the SSO @@ -197,14 +203,14 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas except IOError as e: raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror) - if auth.add('uid=%s,ou=users' % username, attr_dict): + if ldap.add('uid=%s,ou=users' % username, attr_dict): # Invalidate passwd to take user creation into account subprocess.call(['nscd', '-i', 'passwd']) # Update SFTP user group - memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] + memberlist = ldap.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] memberlist.append(username) - if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): + if ldap.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): try: # Attempt to create user home folder subprocess.check_call( @@ -213,7 +219,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas if not os.path.isdir('/home/{0}'.format(username)): logger.warning(m18n.n('user_home_creation_failed'), exc_info=1) - app_ssowatconf(auth) + app_ssowatconf() # TODO: Send a welcome mail to user logger.success(m18n.n('user_created')) hook_callback('post_user_create', @@ -225,7 +231,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas @is_unit_operation([('username', 'user')]) -def user_delete(operation_logger, auth, username, purge=False): +def user_delete(operation_logger, username, purge=False): """ Delete user @@ -236,34 +242,37 @@ def user_delete(operation_logger, auth, username, purge=False): """ from yunohost.app import app_ssowatconf from yunohost.hook import hook_callback + from yunohost.utils.ldap import _get_ldap_interface operation_logger.start() - if auth.remove('uid=%s,ou=users' % username): + + ldap = _get_ldap_interface() + if ldap.remove('uid=%s,ou=users' % username): # Invalidate passwd to take user deletion into account subprocess.call(['nscd', '-i', 'passwd']) # Update SFTP user group - memberlist = auth.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] + memberlist = ldap.search(filter='cn=sftpusers', attrs=['memberUid'])[0]['memberUid'] try: memberlist.remove(username) except: pass - if auth.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): + if ldap.update('cn=sftpusers,ou=groups', {'memberUid': memberlist}): if purge: subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)]) else: raise YunohostError('user_deletion_failed') - app_ssowatconf(auth) + app_ssowatconf() hook_callback('post_user_delete', args=[username, purge]) logger.success(m18n.n('user_deleted')) -@is_unit_operation([('username', 'user')], exclude=['auth', 'change_password']) -def user_update(operation_logger, auth, username, firstname=None, lastname=None, mail=None, +@is_unit_operation([('username', 'user')], exclude=['change_password']) +def user_update(operation_logger, username, firstname=None, lastname=None, mail=None, change_password=None, add_mailforward=None, remove_mailforward=None, add_mailalias=None, remove_mailalias=None, mailbox_quota=None): """ @@ -284,13 +293,15 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, from yunohost.domain import domain_list, _get_maindomain from yunohost.app import app_ssowatconf from yunohost.utils.password import assert_password_is_strong_enough + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() attrs_to_fetch = ['givenName', 'sn', 'mail', 'maildrop'] new_attr_dict = {} - domains = domain_list(auth)['domains'] + domains = domain_list()['domains'] # Populate user informations - result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch) + result = ldap.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch) if not result: raise YunohostError('user_unknown', user=username) user = result[0] @@ -321,7 +332,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, 'webmaster@' + main_domain, 'postmaster@' + main_domain, ] - auth.validate_uniqueness({'mail': mail}) + ldap.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) if mail in aliases: @@ -334,7 +345,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, if not isinstance(add_mailalias, list): add_mailalias = [add_mailalias] for mail in add_mailalias: - auth.validate_uniqueness({'mail': mail}) + ldap.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) user['mail'].append(mail) @@ -374,15 +385,15 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, operation_logger.start() - if auth.update('uid=%s,ou=users' % username, new_attr_dict): + if ldap.update('uid=%s,ou=users' % username, new_attr_dict): logger.success(m18n.n('user_updated')) - app_ssowatconf(auth) - return user_info(auth, username) + app_ssowatconf() + return user_info(username) else: raise YunohostError('user_update_failed') -def user_info(auth, username): +def user_info(username): """ Get user informations @@ -390,6 +401,10 @@ def user_info(auth, username): username -- Username or mail to get informations """ + from yunohost.utils.ldap import _get_ldap_interface + + ldap = _get_ldap_interface() + user_attrs = [ 'cn', 'mail', 'uid', 'maildrop', 'givenName', 'sn', 'mailuserquota' ] @@ -399,7 +414,7 @@ def user_info(auth, username): else: filter = 'uid=' + username - result = auth.search('ou=users,dc=yunohost,dc=org', filter, user_attrs) + result = ldap.search('ou=users,dc=yunohost,dc=org', filter, user_attrs) if result: user = result[0] @@ -469,24 +484,24 @@ def user_info(auth, username): import yunohost.ssh -def user_ssh_allow(auth, username): - return yunohost.ssh.user_ssh_allow(auth, username) +def user_ssh_allow(username): + return yunohost.ssh.user_ssh_allow(username) -def user_ssh_disallow(auth, username): - return yunohost.ssh.user_ssh_disallow(auth, username) +def user_ssh_disallow(username): + return yunohost.ssh.user_ssh_disallow(username) -def user_ssh_list_keys(auth, username): - return yunohost.ssh.user_ssh_list_keys(auth, username) +def user_ssh_list_keys(username): + return yunohost.ssh.user_ssh_list_keys(username) -def user_ssh_add_key(auth, username, key, comment): - return yunohost.ssh.user_ssh_add_key(auth, username, key, comment) +def user_ssh_add_key(username, key, comment): + return yunohost.ssh.user_ssh_add_key(username, key, comment) -def user_ssh_remove_key(auth, username, key): - return yunohost.ssh.user_ssh_remove_key(auth, username, key) +def user_ssh_remove_key(username, key): + return yunohost.ssh.user_ssh_remove_key(username, key) # # End SSH subcategory From afdfb13d68f788db7c767ca4371948c987f1f1f2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 10 May 2019 02:16:31 +0200 Subject: [PATCH 33/67] We probably don't need this stuff in the actionsmap anymore ... --- data/actionsmap/yunohost.yml | 108 ----------------------------------- 1 file changed, 108 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 3842b08be..f38a7fcc9 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -77,9 +77,6 @@ user: list: action_help: List users api: GET /users - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: --fields: help: fields to fetch @@ -89,9 +86,6 @@ user: create: action_help: Create user api: POST /users - configuration: - authenticate: all - authenticator: as-root arguments: username: help: The unique username to create @@ -148,9 +142,6 @@ user: delete: action_help: Delete user api: DELETE /users/ - configuration: - authenticate: all - authenticator: as-root arguments: username: help: Username to delete @@ -164,9 +155,6 @@ user: update: action_help: Update user informations api: PUT /users/ - configuration: - authenticate: all - authenticator: as-root arguments: username: help: Username to update @@ -219,9 +207,6 @@ user: info: action_help: Get user information api: GET /users/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: username: help: Username or email to get information @@ -235,9 +220,6 @@ user: allow: action_help: Allow the user to uses ssh api: POST /users/ssh/enable - configuration: - authenticate: all - authenticator: as-root arguments: username: help: Username of the user @@ -248,9 +230,6 @@ user: disallow: action_help: Disallow the user to uses ssh api: POST /users/ssh/disable - configuration: - authenticate: all - authenticator: as-root arguments: username: help: Username of the user @@ -261,9 +240,6 @@ user: list-keys: action_help: Show user's authorized ssh keys api: GET /users/ssh/keys - configuration: - authenticate: all - authenticator: as-root arguments: username: help: Username of the user @@ -274,9 +250,6 @@ user: add-key: action_help: Add a new authorized ssh key for this user api: POST /users/ssh/key - configuration: - authenticate: all - authenticator: as-root arguments: username: help: Username of the user @@ -292,9 +265,6 @@ user: remove-key: action_help: Remove an authorized ssh key for this user api: DELETE /users/ssh/key - configuration: - authenticate: all - authenticator: as-root arguments: username: help: Username of the user @@ -315,17 +285,11 @@ domain: list: action_help: List domains api: GET /domains - configuration: - authenticate: all - authenticator: ldap-anonymous ### domain_add() add: action_help: Create a custom domain api: POST /domains - configuration: - authenticate: all - authenticator: as-root arguments: domain: help: Domain name to add @@ -342,9 +306,6 @@ domain: remove: action_help: Delete domains api: DELETE /domains/ - configuration: - authenticate: all - authenticator: as-root arguments: domain: help: Domain to delete @@ -355,9 +316,6 @@ domain: dns-conf: action_help: Generate DNS configuration for a domain api: GET /domains//dns - configuration: - authenticate: - - api arguments: domain: help: Target domain @@ -373,9 +331,6 @@ domain: cert-status: action_help: List status of current certificates (all by default). api: GET /domains/cert-status/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: domain_list: help: Domains to check @@ -388,9 +343,6 @@ domain: cert-install: action_help: Install Let's Encrypt certificates for given domains (all by default). api: POST /domains/cert-install/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: domain_list: help: Domains for which to install the certificates @@ -412,9 +364,6 @@ domain: cert-renew: action_help: Renew the Let's Encrypt certificates for given domains (all by default). api: POST /domains/cert-renew/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: domain_list: help: Domains for which to renew the certificates @@ -436,9 +385,6 @@ domain: url-available: action_help: Check availability of a web path api: GET /domain/urlavailable - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: domain: help: The domain for the web path (e.g. your.domain.tld) @@ -559,9 +505,6 @@ app: install: action_help: Install apps api: POST /apps - configuration: - authenticate: all - authenticator: as-root arguments: app: help: Name, local path or git URL of the app to install @@ -584,9 +527,6 @@ app: remove: action_help: Remove app api: DELETE /apps/ - configuration: - authenticate: all - authenticator: as-root arguments: app: help: App(s) to delete @@ -595,9 +535,6 @@ app: upgrade: action_help: Upgrade app api: PUT /upgrade/apps - configuration: - authenticate: all - authenticator: as-root arguments: app: help: App(s) to upgrade (default all) @@ -613,9 +550,6 @@ app: change-url: action_help: Change app's URL api: PUT /apps//changeurl - configuration: - authenticate: all - authenticator: as-root arguments: app: help: Target app instance name @@ -668,9 +602,6 @@ app: action_help: Check availability of a web path api: GET /tools/checkurl deprecated: True - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: url: help: Url to check @@ -682,9 +613,6 @@ app: register-url: action_help: Book/register a web path for a given app api: PUT /tools/registerurl - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: App which will use the web path @@ -724,9 +652,6 @@ app: makedefault: action_help: Redirect domain root to an app api: PUT /apps//default - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: App name to put on domain root @@ -738,17 +663,11 @@ app: ssowatconf: action_help: Regenerate SSOwat configuration file api: PUT /ssowatconf - configuration: - authenticate: all - authenticator: ldap-anonymous ### app_change_label() change-label: action_help: Change app label api: PUT /apps//label - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: app: help: App ID @@ -759,9 +678,6 @@ app: addaccess: action_help: Grant access right to users (everyone by default) api: PUT /access - configuration: - authenticate: all - authenticator: as-root arguments: apps: nargs: "+" @@ -773,9 +689,6 @@ app: removeaccess: action_help: Revoke access right to users (everyone by default) api: DELETE /access - configuration: - authenticate: all - authenticator: as-root arguments: apps: nargs: "+" @@ -787,9 +700,6 @@ app: clearaccess: action_help: Reset access rights for the app api: POST /access - configuration: - authenticate: all - authenticator: as-root arguments: apps: nargs: "+" @@ -887,9 +797,6 @@ backup: restore: action_help: Restore from a local backup archive. If neither --apps or --system are given, this will restore all apps and all system parts in the archive. If only --apps if given, this will only restore apps and no system parts. Similarly, if only --system is given, this will only restore system parts and no apps. api: POST /backup/restore/ - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: name: help: Name of the local backup archive @@ -1498,9 +1405,6 @@ tools: adminpw: action_help: Change password of admin and root users api: PUT /adminpw - configuration: - authenticate: all - authenticator: as-root arguments: -n: full: --new-password @@ -1516,9 +1420,6 @@ tools: api: - GET /domains/main - PUT /domains/main - configuration: - authenticate: all - authenticator: as-root arguments: -n: full: --new-domain @@ -1571,9 +1472,6 @@ tools: upgrade: action_help: YunoHost upgrade api: PUT /upgrade - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: --ignore-apps: help: Ignore apps upgrade @@ -1586,9 +1484,6 @@ tools: diagnosis: action_help: YunoHost diagnosis api: GET /diagnosis - configuration: - authenticate: all - authenticator: ldap-anonymous arguments: -p: full: --private @@ -1607,9 +1502,6 @@ tools: ### tools_shell() shell: - configuration: - authenticate: all - authenticator: as-root action_help: Launch a development shell arguments: -c: From ce0ffe93249b7740c5f08396275c76e09ba00b27 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 10 May 2019 02:17:49 +0200 Subject: [PATCH 34/67] And in fact ... neither do we need this stuff ? --- data/actionsmap/yunohost.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index f38a7fcc9..7242f9f9d 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -44,19 +44,7 @@ _global: uri: ldap://localhost:389 base_dn: dc=yunohost,dc=org user_rdn: cn=admin,dc=yunohost,dc=org - ldap-anonymous: - vendor: ldap - parameters: - uri: ldap://localhost:389 - base_dn: dc=yunohost,dc=org - as-root: - vendor: ldap - parameters: - # We can get this uri by (urllib.quote_plus('/var/run/slapd/ldapi') - uri: ldapi://%2Fvar%2Frun%2Fslapd%2Fldapi - base_dn: dc=yunohost,dc=org - user_rdn: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth - argument_auth: true + argument_auth: false arguments: -v: full: --version From ece153e8b15455c6b42799d536d1b5d8f7546e23 Mon Sep 17 00:00:00 2001 From: Josue-T Date: Fri, 10 May 2019 15:44:59 +0200 Subject: [PATCH 35/67] Disable VRFY command in Postfix command --- data/templates/postfix/main.cf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index c38896a3f..feeeb4c9f 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -154,3 +154,9 @@ smtpd_milters = inet:localhost:11332 # Skip email without checking if milter has died milter_default_action = accept + +# Avoid email adress scanning +# By default it's possible to detect if the email adress exist +# So it's easly possible to scan a server to know which email adress is valid +# and after to send spam +disable_vrfy_command = yes From c077b8def322965408746d4c49e1a2423b292e28 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 14 May 2019 19:31:47 +0200 Subject: [PATCH 36/67] 5s rate delay instead of 12s --- data/templates/postfix/main.cf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index aea68cd28..7699b73cf 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -157,4 +157,4 @@ milter_default_action = accept # Avoid to send simultaneously too much emails smtp_destination_concurrency_limit = 1 -default_destination_rate_delay = 12s +default_destination_rate_delay = 5s From 1294f4c22f7039f81bbf9eafd5a472d6018e5507 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 15 May 2019 20:37:30 +0200 Subject: [PATCH 37/67] Optimize ynh_script_progression --- data/helpers.d/logging | 56 ++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/data/helpers.d/logging b/data/helpers.d/logging index 087cfc80c..e98285889 100644 --- a/data/helpers.d/logging +++ b/data/helpers.d/logging @@ -198,6 +198,13 @@ ynh_print_ON () { # Requires YunoHost version 3.?.? or higher. increment_progression=0 previous_weight=0 +max_progression=-1 +# Set the scale of the progression bar +# progress_string(0,1,2) should have the size of the scale. +progress_scale=20 +progress_string2="####################" +progress_string1="++++++++++++++++++++" +progress_string0="...................." # Define base_time when the file is sourced base_time=$(date +%s) ynh_script_progression () { @@ -218,54 +225,51 @@ ynh_script_progression () { local exec_time=$(( $(date +%s) - $base_time )) base_time=$(date +%s) - # Get the number of occurrences of 'ynh_script_progression' in the script. Except those are commented. - local helper_calls="$(grep --count "^[^#]*ynh_script_progression" $0)" - # Get the number of call with a weight value - local weight_calls=$(grep --perl-regexp --count "^[^#]*ynh_script_progression.*(--weight|-w )" $0) + # Compute $max_progression (if we didn't already) + if [ "$max_progression" = -1 ] + then + # Get the number of occurrences of 'ynh_script_progression' in the script. Except those are commented. + local helper_calls="$(grep --count "^[^#]*ynh_script_progression" $0)" + # Get the number of call with a weight value + local weight_calls=$(grep --perl-regexp --count "^[^#]*ynh_script_progression.*(--weight|-w )" $0) - # Get the weight of each occurrences of 'ynh_script_progression' in the script using --weight - local weight_valuesA="$(grep --perl-regexp "^[^#]*ynh_script_progression.*--weight" $0 | sed 's/.*--weight[= ]\([[:digit:]]*\).*/\1/g')" - # Get the weight of each occurrences of 'ynh_script_progression' in the script using -w - local weight_valuesB="$(grep --perl-regexp "^[^#]*ynh_script_progression.*-w " $0 | sed 's/.*-w[= ]\([[:digit:]]*\).*/\1/g')" - # Each value will be on a different line. - # Remove each 'end of line' and replace it by a '+' to sum the values. - local weight_values=$(( $(echo "$weight_valuesA" | tr '\n' '+') + $(echo "$weight_valuesB" | tr '\n' '+') 0 )) + # Get the weight of each occurrences of 'ynh_script_progression' in the script using --weight + local weight_valuesA="$(grep --perl-regexp "^[^#]*ynh_script_progression.*--weight" $0 | sed 's/.*--weight[= ]\([[:digit:]]*\).*/\1/g')" + # Get the weight of each occurrences of 'ynh_script_progression' in the script using -w + local weight_valuesB="$(grep --perl-regexp "^[^#]*ynh_script_progression.*-w " $0 | sed 's/.*-w[= ]\([[:digit:]]*\).*/\1/g')" + # Each value will be on a different line. + # Remove each 'end of line' and replace it by a '+' to sum the values. + local weight_values=$(( $(echo "$weight_valuesA" | tr '\n' '+') + $(echo "$weight_valuesB" | tr '\n' '+') 0 )) - # max_progression is a total number of calls to this helper. - # Less the number of calls with a weight value. - # Plus the total of weight values - local max_progression=$(( $helper_calls - $weight_calls + $weight_values )) + # max_progression is a total number of calls to this helper. + # Less the number of calls with a weight value. + # Plus the total of weight values + max_progression=$(( $helper_calls - $weight_calls + $weight_values )) + fi # Increment each execution of ynh_script_progression in this script by the weight of the previous call. increment_progression=$(( $increment_progression + $previous_weight )) # Store the weight of the current call in $previous_weight for next call previous_weight=$weight - # Set the scale of the progression bar - local scale=20 - # progress_string(0,1,2) should have the size of the scale. - local progress_string2="####################" - local progress_string1="++++++++++++++++++++" - local progress_string0="...................." - # Reduce $increment_progression to the size of the scale if [ $last -eq 0 ] then - local effective_progression=$(( $increment_progression * $scale / $max_progression )) + local effective_progression=$(( $increment_progression * $progress_scale / $max_progression )) # If last is specified, fill immediately the progression_bar else - local effective_progression=$scale + local effective_progression=$progress_scale fi # Build $progression_bar from progress_string(0,1,2) according to $effective_progression and the weight of the current task # expected_progression is the progression expected after the current task - local expected_progression="$(( ( $increment_progression + $weight ) * $scale / $max_progression - $effective_progression ))" + local expected_progression="$(( ( $increment_progression + $weight ) * $progress_scale / $max_progression - $effective_progression ))" if [ $last -eq 1 ] then expected_progression=0 fi # left_progression is the progression not yet done - local left_progression="$(( $scale - $effective_progression - $expected_progression ))" + local left_progression="$(( $progress_scale - $effective_progression - $expected_progression ))" # Build the progression bar with $effective_progression, work done, $expected_progression, current work and $left_progression, work to be done. local progression_bar="${progress_string2:0:$effective_progression}${progress_string1:0:$expected_progression}${progress_string0:0:$left_progression}" From 08dc22b75095222163eca59df0dd261a24f8eee4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 May 2019 15:25:46 +0200 Subject: [PATCH 38/67] Fix funky errors by explicitly destroying the global variable at exit time --- src/yunohost/utils/ldap.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py index f4a5f355c..186cdbdec 100644 --- a/src/yunohost/utils/ldap.py +++ b/src/yunohost/utils/ldap.py @@ -19,6 +19,7 @@ """ +import atexit from moulinette.core import init_authenticator # We use a global variable to do some caching @@ -38,3 +39,14 @@ def _get_ldap_interface(): _ldap_interface = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) return _ldap_interface + +# Add this to properly close / delete the ldap interface / authenticator +# when Python exits ... +# Otherwise there's a risk that some funky error appears at the very end +# of the command due to Python stuff being unallocated in wrong order. +def _destroy_ldap_interface(): + global _ldap_interface + if _ldap_interface is not None: + del _ldap_interface + +atexit.register(_destroy_ldap_interface) From 95fdfb327598cd62cc346d12d03b9a41afba3802 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 May 2019 15:48:51 +0200 Subject: [PATCH 39/67] Unit test was broken, gotta foce this to be a str otherwise string formatting fails later --- src/yunohost/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 01f27ba83..a24ecb637 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -116,7 +116,7 @@ def settings_set(key, value): elif key_type == "enum": if value not in settings[key]["choices"]: raise YunohostError('global_settings_bad_choice_for_enum', setting=key, - choice=value, + choice=str(value), available_choices=", ".join(settings[key]["choices"])) else: raise YunohostError('global_settings_unknown_type', setting=key, From b464bc576c8bf40ff7500408794d7d9e65a2f082 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 May 2019 15:49:16 +0200 Subject: [PATCH 40/67] Propagate changes on tests as well --- src/yunohost/tests/test_appurl.py | 22 ++++------- src/yunohost/tests/test_backuprestore.py | 50 ++++++++---------------- src/yunohost/tests/test_changeurl.py | 15 ++----- 3 files changed, 29 insertions(+), 58 deletions(-) diff --git a/src/yunohost/tests/test_appurl.py b/src/yunohost/tests/test_appurl.py index 3a3a1db35..25dc68f9f 100644 --- a/src/yunohost/tests/test_appurl.py +++ b/src/yunohost/tests/test_appurl.py @@ -1,15 +1,9 @@ import pytest -from moulinette.core import init_authenticator from yunohost.utils.error import YunohostError from yunohost.app import app_install, app_remove from yunohost.domain import _get_maindomain, domain_url_available, _normalize_domain_path -# Instantiate LDAP Authenticator -auth_identifier = ('ldap', 'ldap-anonymous') -auth_parameters = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'} -auth = init_authenticator(auth_identifier, auth_parameters) - # Get main domain maindomain = _get_maindomain() @@ -18,7 +12,7 @@ maindomain = _get_maindomain() def setup_function(function): try: - app_remove(auth, "register_url_app") + app_remove("register_url_app") except: pass @@ -26,7 +20,7 @@ def setup_function(function): def teardown_function(function): try: - app_remove(auth, "register_url_app") + app_remove("register_url_app") except: pass @@ -41,28 +35,28 @@ def test_normalize_domain_path(): def test_urlavailable(): # Except the maindomain/macnuggets to be available - assert domain_url_available(auth, maindomain, "/macnuggets") + assert domain_url_available(maindomain, "/macnuggets") # We don't know the domain yolo.swag with pytest.raises(YunohostError): - assert domain_url_available(auth, "yolo.swag", "/macnuggets") + assert domain_url_available("yolo.swag", "/macnuggets") def test_registerurl(): - app_install(auth, "./tests/apps/register_url_app_ynh", + app_install("./tests/apps/register_url_app_ynh", args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"), force=True) - assert not domain_url_available(auth, maindomain, "/urlregisterapp") + assert not domain_url_available(maindomain, "/urlregisterapp") # Try installing at same location with pytest.raises(YunohostError): - app_install(auth, "./tests/apps/register_url_app_ynh", + app_install("./tests/apps/register_url_app_ynh", args="domain=%s&path=%s" % (maindomain, "/urlregisterapp"), force=True) def test_registerurl_baddomain(): with pytest.raises(YunohostError): - app_install(auth, "./tests/apps/register_url_app_ynh", + app_install("./tests/apps/register_url_app_ynh", args="domain=%s&path=%s" % ("yolo.swag", "/urlregisterapp"), force=True) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 353b88f27..4ace0c8b8 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -1,13 +1,10 @@ import pytest -import time -import requests import os import shutil import subprocess from mock import ANY from moulinette import m18n -from moulinette.core import init_authenticator from yunohost.app import app_install, app_remove, app_ssowatconf from yunohost.app import _is_installed from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete, _recursive_umount @@ -17,12 +14,6 @@ from yunohost.utils.error import YunohostError # Get main domain maindomain = "" -# Instantiate LDAP Authenticator -AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous') -AUTH_PARAMETERS = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'} -auth = None - - def setup_function(function): global maindomain @@ -30,9 +21,6 @@ def setup_function(function): print "" - global auth - auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) - assert backup_test_dependencies_are_met() clean_tmp_backup_directory() @@ -72,10 +60,6 @@ def setup_function(function): def teardown_function(function): - print "" - global auth - auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) - assert tmp_backup_directory_is_empty() reset_ssowat_conf() @@ -146,7 +130,7 @@ def reset_ssowat_conf(): # Make sure we have a ssowat os.system("mkdir -p /etc/ssowat/") - app_ssowatconf(auth) + app_ssowatconf() def delete_all_backups(): @@ -158,18 +142,18 @@ def delete_all_backups(): def uninstall_test_apps_if_needed(): if _is_installed("backup_legacy_app"): - app_remove(auth, "backup_legacy_app") + app_remove("backup_legacy_app") if _is_installed("backup_recommended_app"): - app_remove(auth, "backup_recommended_app") + app_remove("backup_recommended_app") if _is_installed("wordpress"): - app_remove(auth, "wordpress") + app_remove("wordpress") def install_app(app, path, additionnal_args=""): - app_install(auth, "./tests/apps/%s" % app, + app_install("./tests/apps/%s" % app, args="domain=%s&path=%s%s" % (maindomain, path, additionnal_args), force=True) @@ -249,7 +233,7 @@ def test_backup_and_restore_all_sys(): assert not os.path.exists("/etc/ssowat/conf.json") # Restore the backup - backup_restore(auth, name=archives[0], force=True, + backup_restore(name=archives[0], force=True, system=[], apps=None) # Check ssowat conf is back @@ -270,13 +254,13 @@ def test_restore_system_from_Ynh2p4(monkeypatch, mocker): # Restore system archive from 2.4 try: - backup_restore(auth, name=backup_list()["archives"][1], + backup_restore(name=backup_list()["archives"][1], system=[], apps=None, force=True) finally: # Restore system as it was - backup_restore(auth, name=backup_list()["archives"][0], + backup_restore(name=backup_list()["archives"][0], system=[], apps=None, force=True) @@ -412,7 +396,7 @@ def test_backup_with_no_compress(): @pytest.mark.with_wordpress_archive_from_2p4 def test_restore_app_wordpress_from_Ynh2p4(): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) @@ -430,7 +414,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker): assert not _is_installed("wordpress") with pytest.raises(YunohostError): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_app_failed', app='wordpress') @@ -451,7 +435,7 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker): assert not _is_installed("wordpress") with pytest.raises(YunohostError): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_not_enough_disk_space', @@ -470,7 +454,7 @@ def test_restore_app_not_in_backup(mocker): mocker.spy(m18n, "n") with pytest.raises(YunohostError): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["yoloswag"]) m18n.n.assert_any_call('backup_archive_app_not_found', app="yoloswag") @@ -483,14 +467,14 @@ def test_restore_app_already_installed(mocker): assert not _is_installed("wordpress") - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) assert _is_installed("wordpress") mocker.spy(m18n, "n") with pytest.raises(YunohostError): - backup_restore(auth, system=None, name=backup_list()["archives"][0], + backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_already_installed_app', app="wordpress") @@ -531,11 +515,11 @@ def _test_backup_and_restore_app(app): assert app in archives_info["apps"].keys() # Uninstall the app - app_remove(auth, app) + app_remove(app) assert not app_is_installed(app) # Restore the app - backup_restore(auth, system=None, name=archives[0], + backup_restore(system=None, name=archives[0], apps=[app]) assert app_is_installed(app) @@ -555,7 +539,7 @@ def test_restore_archive_with_no_json(mocker): mocker.spy(m18n, "n") with pytest.raises(YunohostError): - backup_restore(auth, name="badbackup", force=True) + backup_restore(name="badbackup", force=True) m18n.n.assert_any_call('backup_invalid_archive') diff --git a/src/yunohost/tests/test_changeurl.py b/src/yunohost/tests/test_changeurl.py index e11acdb59..cb9b5d290 100644 --- a/src/yunohost/tests/test_changeurl.py +++ b/src/yunohost/tests/test_changeurl.py @@ -2,18 +2,11 @@ import pytest import time import requests -from moulinette.core import init_authenticator from yunohost.app import app_install, app_change_url, app_remove, app_map from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError -# Instantiate LDAP Authenticator -AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous') -AUTH_PARAMETERS = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'} - -auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) - # Get main domain maindomain = _get_maindomain() @@ -23,11 +16,11 @@ def setup_function(function): def teardown_function(function): - app_remove(auth, "change_url_app") + app_remove("change_url_app") def install_changeurl_app(path): - app_install(auth, "./tests/apps/change_url_app_ynh", + app_install("./tests/apps/change_url_app_ynh", args="domain=%s&path=%s" % (maindomain, path), force=True) @@ -46,7 +39,7 @@ def test_appchangeurl(): install_changeurl_app("/changeurl") check_changeurl_app("/changeurl") - app_change_url(auth, "change_url_app", maindomain, "/newchangeurl") + app_change_url("change_url_app", maindomain, "/newchangeurl") # For some reason the nginx reload can take some time to propagate ...? time.sleep(2) @@ -59,4 +52,4 @@ def test_appchangeurl_sameurl(): check_changeurl_app("/changeurl") with pytest.raises(YunohostError): - app_change_url(auth, "change_url_app", maindomain, "changeurl") + app_change_url("change_url_app", maindomain, "changeurl") From ee33e60ab399131c8ee7077f726f965e22046f51 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Sat, 18 May 2019 11:47:50 +0200 Subject: [PATCH 41/67] Set min version to 3.5.0 for helpers --- data/helpers.d/fail2ban | 4 ++-- data/helpers.d/logging | 6 +++--- data/helpers.d/postgresql | 16 ++++++++-------- data/helpers.d/utils | 8 ++++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/data/helpers.d/fail2ban b/data/helpers.d/fail2ban index 85f568520..58af9ec0b 100644 --- a/data/helpers.d/fail2ban +++ b/data/helpers.d/fail2ban @@ -61,7 +61,7 @@ # To validate your regex you can test with this command: # fail2ban-regex /var/log/YOUR_LOG_FILE_PATH /etc/fail2ban/filter.d/YOUR_APP.conf # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_add_fail2ban_config () { # Declare an array to define the options of this helper. local legacy_args=lrmptv @@ -143,7 +143,7 @@ EOF # # usage: ynh_remove_fail2ban_config # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_remove_fail2ban_config () { ynh_secure_remove "/etc/fail2ban/jail.d/$app.conf" ynh_secure_remove "/etc/fail2ban/filter.d/$app.conf" diff --git a/data/helpers.d/logging b/data/helpers.d/logging index 087cfc80c..3fd08ebfc 100644 --- a/data/helpers.d/logging +++ b/data/helpers.d/logging @@ -195,7 +195,7 @@ ynh_print_ON () { # The execution time is given for the duration since the previous call. So the weight should be applied to this previous call. # | arg: -l, --last= - Use for the last call of the helper, to fill te progression bar. # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. increment_progression=0 previous_weight=0 # Define base_time when the file is sourced @@ -284,7 +284,7 @@ ynh_script_progression () { # | arg: -m, --message= - The text to print # | arg: -t, --trace= - Turn on or off the trace of the script. Usefull to trace nonly a small part of a script. # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_debug () { # Disable set xtrace for the helper itself, to not pollute the debug log set +x @@ -339,7 +339,7 @@ ynh_debug () { # # | arg: command - command to execute # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_debug_exec () { ynh_debug --message="$(eval $@)" } diff --git a/data/helpers.d/postgresql b/data/helpers.d/postgresql index 8e3297458..8c96c78ac 100644 --- a/data/helpers.d/postgresql +++ b/data/helpers.d/postgresql @@ -12,7 +12,7 @@ PSQL_ROOT_PWD_FILE=/etc/yunohost/psql # | arg: -p, --password - the user password # | arg: -d, --database - the database to connect to # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_psql_connect_as() { # Declare an array to define the options of this helper. local legacy_args=upd @@ -33,7 +33,7 @@ ynh_psql_connect_as() { # | arg: -s, --sql - the SQL command to execute # | arg: -d, --database - the database to connect to # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_psql_execute_as_root() { # Declare an array to define the options of this helper. local legacy_args=sd @@ -54,7 +54,7 @@ ynh_psql_execute_as_root() { # | arg: -f, --file - the file containing SQL commands # | arg: -d, --database - the database to connect to # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_psql_execute_file_as_root() { # Declare an array to define the options of this helper. local legacy_args=fd @@ -77,7 +77,7 @@ ynh_psql_execute_file_as_root() { # | arg: db - the database name to create # | arg: user - the user to grant privilegies # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_psql_create_db() { local db=$1 local user=${2:-} @@ -102,7 +102,7 @@ ynh_psql_create_db() { # usage: ynh_psql_drop_db db # | arg: db - the database name to drop # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_psql_drop_db() { local db=$1 # First, force disconnection of all clients connected to the database @@ -120,7 +120,7 @@ ynh_psql_drop_db() { # | arg: -d, --database - the database name to dump # | ret: the psqldump output # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_psql_dump_db() { # Declare an array to define the options of this helper. local legacy_args=d @@ -140,7 +140,7 @@ ynh_psql_dump_db() { # | arg: user - the user name to create # | arg: pwd - the password to identify user by # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_psql_create_user() { local user=$1 local pwd=$2 @@ -192,7 +192,7 @@ ynh_psql_database_exists() { # usage: ynh_psql_drop_user user # | arg: user - the user name to drop # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_psql_drop_user() { ynh_psql_execute_as_root --sql="DROP USER ${1};" } diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 1dd83c0e2..630ee539e 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -365,7 +365,7 @@ ynh_get_plain_key() { # | arg: -m, --manifest= - Path of the manifest to read # | arg: -k, --key= - Name of the key to find # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_read_manifest () { # Declare an array to define the options of this helper. local legacy_args=mk @@ -392,7 +392,7 @@ ynh_read_manifest () { # usage: ynh_app_upstream_version [-m manifest] # | arg: -m, --manifest= - Path of the manifest to read # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_app_upstream_version () { # Declare an array to define the options of this helper. local legacy_args=m @@ -415,7 +415,7 @@ ynh_app_upstream_version () { # usage: ynh_app_package_version [-m manifest] # | arg: -m, --manifest= - Path of the manifest to read # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_app_package_version () { # Declare an array to define the options of this helper. local legacy_args=m @@ -444,7 +444,7 @@ ynh_app_package_version () { # # usage: ynh_check_app_version_changed # -# Requires YunoHost version 3.?.? or higher. +# Requires YunoHost version 3.5.0 or higher. ynh_check_app_version_changed () { local force_upgrade=${YNH_FORCE_UPGRADE:-0} local package_check=${PACKAGE_CHECK_EXEC:-0} From 95d2c8f79940feb8bf757bec48422bcc484b9253 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 May 2019 18:31:02 +0200 Subject: [PATCH 42/67] Inject a new environment variable YNH_INTERFACE to test inside helpers if we're using Yunohost from the CLI or API --- data/helpers.d/utils | 13 ++----------- src/yunohost/hook.py | 4 +++- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index d24474ebc..a561b2f73 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -1,14 +1,5 @@ #!/bin/bash -# Determine whether the scripts is executed from a terminal or the admin JS. -# -# [internal] -ynh_is_term () { - # Return true if $TERM return xterm - # Which means the script is executed from a terminal, not from the admin JS - [[ "$TERM" =~ "xterm" ]] -} - # Manage a fail of the script # # [internal] @@ -36,8 +27,8 @@ ynh_exit_properly () { ynh_print_err --message="!!\n $app's script has encountered an error. Its execution was cancelled.\n!!" - # If the script is executed from a terminal, dump the end of the log that precedes the crash. - if ynh_is_term + # If the script is executed from the CLI, dump the end of the log that precedes the crash. + if [ "$YNH_INTERFACE" == "cli" ] then # Unset xtrace to not spoil the log set +x diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 2841dd425..42807fdf7 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -28,7 +28,7 @@ import re import tempfile from glob import iglob -from moulinette import m18n +from moulinette import m18n, msettings from yunohost.utils.error import YunohostError from moulinette.utils import log from moulinette.utils.filesystem import read_json @@ -337,6 +337,8 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, env = {} env['YNH_CWD'] = chdir + env['YNH_INTERFACE'] = msettings.get('interface') + stdinfo = os.path.join(tempfile.mkdtemp(), "stdinfo") env['YNH_STDINFO'] = stdinfo From 899d3d4f105e1627a0c10bd939ecca74035f9365 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 May 2019 18:46:46 +0200 Subject: [PATCH 43/67] Improve ynh_exit_properly detection for case where failure happens right at the beginning --- data/helpers.d/utils | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index a561b2f73..73638dc90 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -37,7 +37,7 @@ ynh_exit_properly () { # Wait for the log to be fill with the data until the crash. local timeout=0 - while ! tail --lines=20 "$ynh_log" | grep --quiet "ynh_exit_properly" + while ! tail --lines=20 "$ynh_log" | grep --quiet "+ ynh_exit_properly" do ((timeout++)) if [ $timeout -eq 500 ]; then @@ -52,7 +52,7 @@ ynh_exit_properly () { # Remove "yunohost.hook..." # Add DEBUG and color it at the beginning of each log line. echo -e "$(tail --lines=30 "$ynh_log" \ - | sed '1,/ynh_exit_properly/!d' \ + | sed '1,/+ ynh_exit_properly/!d' \ | sed 's/^[[:digit:]: ,-]*//g' \ | sed 's/ *yunohost.hook.*\]/ -/g' \ | sed 's/^WARNING /&/g' \ From 6376b05b604415de5f0236d0b05e144a09cf21e7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 May 2019 19:29:18 +0200 Subject: [PATCH 44/67] Add a small tempo to avoid the next message being mixed up with other DEBUG messages --- data/helpers.d/utils | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 73638dc90..8b6c11a80 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -25,6 +25,9 @@ ynh_exit_properly () { trap '' EXIT # Ignore new exit signals set +eu # Do not exit anymore if a command fail or if a variable is empty + # Small tempo to avoid the next message being mixed up with other DEBUG messages + sleep 0.5 + ynh_print_err --message="!!\n $app's script has encountered an error. Its execution was cancelled.\n!!" # If the script is executed from the CLI, dump the end of the log that precedes the crash. From a61f956a0392a7312e6fb7453970cc890d474f48 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 21 May 2019 15:07:13 +0200 Subject: [PATCH 45/67] Set minimum version for these new helpers --- data/helpers.d/setting | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index b5bd8c235..5ee737842 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -69,7 +69,7 @@ ynh_app_setting_delete() { # An URL set with 'skipped_uris' key will be totally ignored by the SSO, # which means that the access will be public and the logged-in user information will not be passed to the app. # -# Requires YunoHost version ?.?.? or higher. +# Requires YunoHost version 3.6.0 or higher. ynh_add_skipped_uris() { # Declare an array to define the options of this helper. local legacy_args=aur @@ -100,7 +100,7 @@ ynh_add_skipped_uris() { # An URL set with unprotected_uris key will be accessible publicly, but if an user is logged in, # his information will be accessible (through HTTP headers) to the app. # -# Requires YunoHost version ?.?.? or higher. +# Requires YunoHost version 3.6.0 or higher. ynh_add_unprotected_uris() { # Declare an array to define the options of this helper. local legacy_args=aur @@ -130,7 +130,7 @@ ynh_add_unprotected_uris() { # # An URL set with protected_uris will be blocked by the SSO and accessible only to authenticated and authorized users. # -# Requires YunoHost version ?.?.? or higher. +# Requires YunoHost version 3.6.0 or higher. ynh_add_protected_uris() { # Declare an array to define the options of this helper. local legacy_args=aur From d31c81b18deb632f1394650001cdf7b7ff333fdd Mon Sep 17 00:00:00 2001 From: Maximilien Thiel Date: Tue, 21 May 2019 16:35:52 +0100 Subject: [PATCH 46/67] [fix] Update censurfridns ipv6 Previous Unicast IPV6 for censurfridns,dk was out of date --- data/templates/dnsmasq/plain/resolv.dnsmasq.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/dnsmasq/plain/resolv.dnsmasq.conf b/data/templates/dnsmasq/plain/resolv.dnsmasq.conf index 197ee2d64..6b3bb95d3 100644 --- a/data/templates/dnsmasq/plain/resolv.dnsmasq.conf +++ b/data/templates/dnsmasq/plain/resolv.dnsmasq.conf @@ -42,4 +42,4 @@ nameserver 2001:1608:10:25::9249:d69b nameserver 91.239.100.100 nameserver 2001:67c:28a4:: nameserver 89.233.43.71 -nameserver 2002:d596:2a92:1:71:53:: +nameserver 2a01:3a0:53:53:: From 2bc0debcde5691735c0bd87a32ac084ad9e7a437 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 21 May 2019 18:02:02 +0200 Subject: [PATCH 47/67] Add python-yaml as Build dependency (to compile bash-autocompletion stuff) --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index bb697d074..f19ddd465 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: yunohost Section: utils Priority: extra Maintainer: YunoHost Contributors -Build-Depends: debhelper (>=9), dh-systemd, dh-python, python-all (>= 2.7) +Build-Depends: debhelper (>=9), dh-systemd, dh-python, python-all (>= 2.7), python-yaml Standards-Version: 3.9.6 X-Python-Version: >= 2.7 Homepage: https://yunohost.org/ From 616054adec714781aee179091bfc60cbd7ce170c Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Wed, 10 Apr 2019 18:57:25 +0000 Subject: [PATCH 48/67] Translated using Weblate (Catalan) Currently translated at 51.1% (260 of 509 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 51 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/locales/ca.json b/locales/ca.json index c53e3400b..d41f7227c 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -209,5 +209,54 @@ "format_datetime_short": "%d/%m/%Y %H:%M", "global_settings_bad_choice_for_enum": "Opció pel paràmetre {setting:s} incorrecta, s'ha rebut «{choice:s}» però les opcions disponibles són: {available_choices:s}", "global_settings_bad_type_for_setting": "El tipus del paràmetre {setting:s} és incorrecte. S'ha rebut {received_type:s}, però s'esperava {expected_type:s}", - "global_settings_cant_open_settings": "No s'ha pogut obrir el fitxer de configuració, raó: {reason:s}" + "global_settings_cant_open_settings": "No s'ha pogut obrir el fitxer de configuració, raó: {reason:s}", + "global_settings_cant_serialize_settings": "No s'ha pogut serialitzar les dades de configuració, raó: {reason:s}", + "global_settings_cant_write_settings": "No s'ha pogut escriure el fitxer de configuració, raó: {reason:s}", + "global_settings_key_doesnt_exists": "La clau « {settings_key:s} » no existeix en la configuració global, podeu veure totes les claus disponibles executant « yunohost settings list »", + "global_settings_reset_success": "Èxit. S'ha fet una còpia de seguretat de la configuració anterior a {path:s}", + "global_settings_setting_example_bool": "Exemple d'opció booleana", + "global_settings_setting_example_enum": "Exemple d'opció de tipus enumeració", + "global_settings_setting_example_int": "Exemple d'opció de tipus enter", + "global_settings_setting_example_string": "Exemple d'opció de tipus cadena", + "global_settings_setting_security_nginx_compatibility": "Solució de compromís entre compatibilitat i seguretat pel servidor web nginx. Afecta els criptògrafs (i altres aspectes relacionats amb la seguretat)", + "global_settings_setting_security_password_admin_strength": "Robustesa de la contrasenya d'administrador", + "global_settings_setting_security_password_user_strength": "Robustesa de la contrasenya de l'usuari", + "global_settings_setting_security_ssh_compatibility": "Solució de compromís entre compatibilitat i seguretat pel servidor SSH. Afecta els criptògrafs (i altres aspectes relacionats amb la seguretat)", + "global_settings_unknown_setting_from_settings_file": "Clau de configuració desconeguda: «{setting_key:s}», refusant-la i guardant-la a /etc/yunohost/settings-unknown.json", + "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Permetre la clau d'hoste DSA (obsolet) per la configuració del servei SSH", + "global_settings_unknown_type": "Situació inesperada, la configuració {setting:s} sembla tenir el tipus {unknown_type:s} però no és un tipus reconegut pel sistema.", + "good_practices_about_admin_password": "Esteu a punt de definir una nova contrasenya d'administrador. La contrasenya ha de tenir un mínim de 8 caràcters ; tot i que és de bona pràctica utilitzar una contrasenya més llarga (és a dir una frase de contrasenya) i/o utilitzar diferents tipus de caràcters (majúscules, minúscules, dígits i caràcters especials).", + "hook_exec_failed": "No s'ha pogut executar l'script: {path:s}", + "hook_exec_not_terminated": "L'execució de l'script « {path:s} » no s'ha acabat correctament", + "hook_json_return_error": "No s'ha pogut llegir el retorn de l'script {path:s}. Error: {msg:s}. Contingut en brut: {raw_content}", + "hook_list_by_invalid": "Propietat per llistar les accions invàlida", + "hook_name_unknown": "Nom de script « {name:s} » desconegut", + "installation_complete": "Instal·lació completada", + "installation_failed": "Ha fallat la instal·lació", + "invalid_url_format": "Format d'URL invàlid", + "ip6tables_unavailable": "No podeu modificar les ip6tables aquí. O bé sou en un contenidor o bé el vostre nucli no és compatible amb aquesta opció", + "iptables_unavailable": "No podeu modificar les iptables aquí. O bé sou en un contenidor o bé el vostre nucli no és compatible amb aquesta opció", + "log_corrupted_md_file": "El fitxer de metadades yaml associat amb els registres està malmès: « {md_file} »", + "log_category_404": "La categoria de registres « {category} » no existeix", + "log_link_to_log": "El registre complet d'aquesta operació: «{desc}»", + "log_help_to_get_log": "Per veure el registre de l'operació « {desc} », utilitzeu l'ordre « yunohost log display {name} »", + "log_link_to_failed_log": "L'operació « {dec} » ha fallat! Per obtenir ajuda, proveïu el registre complete de l'operació clicant aquí", + "log_help_to_get_failed_log": "L'operació « {dec} » ha fallat! Per obtenir ajuda, compartiu el registre complete de l'operació utilitzant l'ordre « yunohost log display {name} --share »", + "log_does_exists": "No hi ha cap registre per l'operació amb el nom« {log} », utilitzeu « yunohost log list » per veure tots els registre d'operació disponibles", + "log_operation_unit_unclosed_properly": "L'operació no s'ha tancat de forma correcta", + "log_app_addaccess": "Afegir accés a « {} »", + "log_app_removeaccess": "Suprimeix accés a « {} »", + "log_app_clearaccess": "Suprimeix tots els accessos a « {} »", + "log_app_fetchlist": "Afegeix una llista d'aplicacions", + "log_app_removelist": "Elimina una llista d'aplicacions", + "log_app_change_url": "Canvia l'URL de l'aplicació « {} »", + "log_app_install": "Instal·la l'aplicació « {} »", + "log_app_remove": "Elimina l'aplicació « {} »", + "log_app_upgrade": "Actualitza l'aplicació « {} »", + "log_app_makedefault": "Fes « {} » l'aplicació per defecte", + "log_available_on_yunopaste": "Aquest registre està disponible via {url}", + "log_backup_restore_system": "Restaura el sistema a partir d'una còpia de seguretat", + "log_backup_restore_app": "Restaura « {} » a partir d'una còpia de seguretat", + "log_remove_on_failed_restore": "Elimina « {} » després de que la restauració a partir de la còpia de seguretat hagi fallat", + "log_remove_on_failed_install": "Elimina « {} » després de que la instal·lació hagi fallat" } From 1b037b3b41507fc9d6e5e8f26099f4bfd31e646b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quent=C3=AD?= Date: Sun, 14 Apr 2019 15:38:10 +0000 Subject: [PATCH 49/67] Translated using Weblate (Occitan) Currently translated at 95.1% (484 of 509 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/oc/ --- locales/oc.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/locales/oc.json b/locales/oc.json index 433da4478..90641fabd 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -247,7 +247,7 @@ "firewall_reload_failed": "Impossible de recargar lo parafuòc", "firewall_reloaded": "Lo parafuòc es estat recargat", "firewall_rules_cmd_failed": "Unas règlas del parafuòc an fracassat. Per mai informacions, consultatz lo jornal.", - "global_settings_bad_choice_for_enum": "La valor del paramètre {setting:s} es incorrècta. Recebut : {received_type:s}, esperat {expected_type:s}", + "global_settings_bad_choice_for_enum": "La valor del paramètre {setting:s} es incorrècta. Recebut : {received_type:s}, mas las opcions esperadas son : {expected_type:s}", "global_settings_bad_type_for_setting": "Lo tipe del paramètre {setting:s} es incorrècte. Recebut : {received_type:s}, esperat {expected_type:s}", "global_settings_cant_write_settings": "Fracàs de l’escritura del fichièr de configuracion, rason : {reason:s}", "global_settings_setting_example_enum": "Exemple d’opcion de tipe enumeracion", @@ -486,5 +486,10 @@ "migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Daissar YunoHost gerir la configuracion SSH (etapa 2, manuala)", "migration_0007_cancelled": "YunoHost a pas reüssit a melhorar lo biais de gerir la configuracion SSH.", "root_password_replaced_by_admin_password": "Lo senhal root es estat remplaçat pel senhal administrator.", - "service_restarted": "Lo servici '{service:s}' es estat reaviat" + "service_restarted": "Lo servici '{service:s}' es estat reaviat", + "admin_password_too_long": "Causissètz un senhal d’almens 127 caractèrs", + "migration_0007_cannot_restart": "SSH pòt pas èsser reavit aprèp aver ensajat d’anullar la migracion numèro 6.", + "migrations_success": "Migracion {number} {name} reüssida !", + "service_conf_now_managed_by_yunohost": "Lo fichièr de configuracion « {conf} » es ara gerit per YunoHost.", + "service_reloaded": "Lo servici « {servici:s} » es estat tornat cargar" } From d189a25a6d07201f00059cb5b40ee61d8938fe5c Mon Sep 17 00:00:00 2001 From: Julien Maulny Date: Fri, 19 Apr 2019 16:19:17 +0000 Subject: [PATCH 50/67] Translated using Weblate (French) Currently translated at 97.5% (499 of 512 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 1a8399454..db5f06c97 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -522,5 +522,18 @@ "service_restarted": "Le service '{service:s}' a été redémarré", "service_reload_or_restart_failed": "Impossible de recharger ou de redémarrer le service '{service:s}'\n\nJournaux historisés récents de ce service : {logs:s}", "service_reloaded_or_restarted": "Le service '{service:s}' a été rechargé ou redémarré", - "this_action_broke_dpkg": "Cette action a laissé des paquets non configurés par dpkg/apt (les gestionnaires de paquets système). Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a`." + "this_action_broke_dpkg": "Cette action a laissé des paquets non configurés par dpkg/apt (les gestionnaires de paquets système). Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a`.", + "app_action_cannot_be_ran_because_required_services_down": "Cette application requiert certains services qui sont actuellement en pannes. Avant de continuer, vous devriez essayer de redémarrer les services suivant (et éventuellement rechercher pourquoi ils sont en panne) : {services}", + "admin_password_too_long": "Choisisez un mot de passe de maximum 127 caractères.", + "log_regen_conf": "Régénérer les configurations du système '{}'", + "migration_0009_not_needed": "Cette migration semble avoir déjà été jouée ? On l'ignore.", + "regenconf_file_backed_up": "Le fichier de configuration '{conf}' a été sauvegardé sous '{backup}'", + "regenconf_file_copy_failed": "Impossible de copier le nouveau fichier de configuration '{new}' vers '{conf}'", + "regenconf_file_manually_modified": "Le fichier de configuration '{conf}' a été modifié manuellement et ne sera pas mis à jour", + "regenconf_file_manually_removed": "Le fichier de configuration '{conf}' a été supprimé manuellement et ne sera pas créé", + "regenconf_file_remove_failed": "Impossible de supprimer le fichier de configuration '{conf}'", + "regenconf_file_removed": "Le fichier de configuration '{conf}' a été supprimé", + "regenconf_file_updated": "Le fichier de configuration '{conf}' a été mis à jour", + "regenconf_now_managed_by_yunohost": "Le fichier de configuration '{conf}' est maintenant géré par YunoHost (catégorie {category}).", + "regenconf_up_to_date": "La configuration est déjà à jour pour la catégorie '{category}'" } From b38a3267d60f344c3273218f1d500c25df10a942 Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Wed, 24 Apr 2019 15:34:45 +0000 Subject: [PATCH 51/67] Translated using Weblate (Catalan) Currently translated at 53.4% (274 of 513 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/locales/ca.json b/locales/ca.json index d41f7227c..83a867b1f 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -258,5 +258,19 @@ "log_backup_restore_system": "Restaura el sistema a partir d'una còpia de seguretat", "log_backup_restore_app": "Restaura « {} » a partir d'una còpia de seguretat", "log_remove_on_failed_restore": "Elimina « {} » després de que la restauració a partir de la còpia de seguretat hagi fallat", - "log_remove_on_failed_install": "Elimina « {} » després de que la instal·lació hagi fallat" + "log_remove_on_failed_install": "Elimina « {} » després de que la instal·lació hagi fallat", + "log_domain_add": "Afegir el domini « {} » a la configuració del sistema", + "log_domain_remove": "Elimina el domini « {} » de la configuració del sistema", + "log_dyndns_subscribe": "Subscriure's a un subdomini YunoHost « {} »", + "log_dyndns_update": "Actualitza la IP associada al subdomini YunoHost « {} »", + "log_letsencrypt_cert_install": "Instal·la el certificat Let's Encrypt al domini « {} »", + "log_selfsigned_cert_install": "Instal·la el certificat autosignat al domini « {} »", + "log_letsencrypt_cert_renew": "Renova el certificat Let's Encrypt de « {} »", + "log_service_enable": "Activa el servei « {} »", + "log_regen_conf": "Regenera la configuració del sistema « {} »", + "log_user_create": "Afegeix l'usuari « {} »", + "log_user_delete": "Elimina l'usuari « {} »", + "log_user_update": "Actualitza la informació de l'usuari « {} »", + "log_tools_maindomain": "Fes de « {} » el domini principal", + "log_tools_migrations_migrate_forward": "Migrar" } From 69e4a3251368eeeb5857c9dd732daf1f687d48d6 Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Wed, 24 Apr 2019 15:53:23 +0000 Subject: [PATCH 52/67] Translated using Weblate (Catalan) Currently translated at 54.4% (279 of 513 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/locales/ca.json b/locales/ca.json index 83a867b1f..19f18ba12 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -272,5 +272,10 @@ "log_user_delete": "Elimina l'usuari « {} »", "log_user_update": "Actualitza la informació de l'usuari « {} »", "log_tools_maindomain": "Fes de « {} » el domini principal", - "log_tools_migrations_migrate_forward": "Migrar" + "log_tools_migrations_migrate_forward": "Migrar", + "log_tools_migrations_migrate_backward": "Migrar endarrera", + "log_tools_postinstall": "Fer la post instal·lació del servidor YunoHost", + "log_tools_upgrade": "Actualitza els paquets Debian", + "log_tools_shutdown": "Apaga el servidor", + "log_tools_reboot": "Reinicia el servidor" } From 21ca5551c9f4cf926cbb1ebe60c2e537e1b13b54 Mon Sep 17 00:00:00 2001 From: Leandro Noferini Date: Sun, 28 Apr 2019 10:44:50 +0000 Subject: [PATCH 53/67] Translated using Weblate (Italian) Currently translated at 59.6% (306 of 513 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/it/ --- locales/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/it.json b/locales/it.json index 171d67106..34848c181 100644 --- a/locales/it.json +++ b/locales/it.json @@ -225,7 +225,7 @@ "unit_unknown": "Unità '{unit:s}' sconosciuta", "unlimit": "Nessuna quota", "update_cache_failed": "Impossibile aggiornare la cache APT", - "updating_apt_cache": "Recupero degli aggiornamenti disponibili per i pacchietti di sistema…", + "updating_apt_cache": "Recupero degli aggiornamenti disponibili per i pacchetti di sistema…", "upgrade_complete": "Aggiornamento completo", "upnp_dev_not_found": "Nessuno supporto UPnP trovato", "upnp_disabled": "UPnP è stato disattivato", From 4b0cd9915868c9830246f2b48495b990f4c3e39f Mon Sep 17 00:00:00 2001 From: advocatux Date: Sun, 28 Apr 2019 11:11:48 +0000 Subject: [PATCH 54/67] Translated using Weblate (Spanish) Currently translated at 59.3% (304 of 513 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/locales/es.json b/locales/es.json index ddc879364..8861c15b8 100644 --- a/locales/es.json +++ b/locales/es.json @@ -317,5 +317,11 @@ "backup_with_no_restore_script_for_app": "La aplicación {app:s} no tiene script de restauración, no podrá restaurar automáticamente la copia de seguridad de esta aplicación.", "dyndns_could_not_check_provide": "No se pudo verificar si {provider:s} puede ofrecer {domain:s}.", "dyndns_domain_not_provided": "El proveedor Dyndns {provider:s} no puede proporcionar el dominio {domain:s}.", - "experimental_feature": "Cuidado : esta funcionalidad es experimental y no es considerada estable, no debería usarla excepto si sabe lo que hace." + "experimental_feature": "Cuidado : esta funcionalidad es experimental y no es considerada estable, no debería usarla excepto si sabe lo que hace.", + "good_practices_about_user_password": "Está a punto de establecer una nueva contraseña de usuario. La contraseña debería de ser de al menos 8 caracteres, aunque es una buena práctica usar una contraseña más larga (es decir, una frase de paso) y/o usar varias clases de caracteres (mayúsculas, minúsculas, dígitos y caracteres especiales).", + "password_listed": "Esta contraseña es una de las más usadas en el mundo. Elija algo un poco más único.", + "password_too_simple_1": "La contraseña debe tener al menos 8 caracteres de longitud", + "password_too_simple_2": "La contraseña debe tener al menos 8 caracteres de longitud y contiene dígitos, mayúsculas y minúsculas", + "password_too_simple_3": "La contraseña debe tener al menos 8 caracteres de longitud y contiene dígitos, mayúsculas, minúsculas y caracteres especiales", + "password_too_simple_4": "La contraseña debe tener al menos 12 caracteres de longitud y contiene dígitos, mayúsculas, minúsculas y caracteres especiales" } From af40274f1d27cc6fc34c2c4904a88636e475c41b Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Mon, 6 May 2019 11:53:10 +0000 Subject: [PATCH 55/67] Translated using Weblate (Arabic) Currently translated at 75.4% (387 of 513 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ar/ --- locales/ar.json | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index cc5b016ea..285a0f819 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -42,7 +42,7 @@ "appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is corrupted.", "appslist_could_not_migrate": "Could not migrate app list {appslist:s} ! Unable to parse the url... The old cron job has been kept in {bkp_file:s}.", "appslist_fetched": "تم جلب قائمة تطبيقات {appslist:s}", - "appslist_migrating": "Migrating application list {appslist:s} ...", + "appslist_migrating": "Migrating application list {appslist:s} …", "appslist_name_already_tracked": "There is already a registered application list with name {name:s}.", "appslist_removed": "تم حذف قائمة البرمجيات {appslist:s}", "appslist_retrieve_bad_format": "Retrieved file for application list {appslist:s} is not valid", @@ -61,10 +61,10 @@ "backup_abstract_method": "This backup method hasn't yet been implemented", "backup_action_required": "You must specify something to save", "backup_app_failed": "Unable to back up the app '{app:s}'", - "backup_applying_method_borg": "Sending all files to backup into borg-backup repository...", + "backup_applying_method_borg": "Sending all files to backup into borg-backup repository…", "backup_applying_method_copy": "جارٍ نسخ كافة الملفات إلى النسخة الإحتياطية …", - "backup_applying_method_custom": "Calling the custom backup method '{method:s}'...", - "backup_applying_method_tar": "Creating the backup tar archive...", + "backup_applying_method_custom": "Calling the custom backup method '{method:s}'…", + "backup_applying_method_tar": "جارٍ إنشاء ملف tar للنسخة الاحتياطية…", "backup_archive_app_not_found": "App '{app:s}' not found in the backup archive", "backup_archive_broken_link": "Unable to access backup archive (broken link to {path:s})", "backup_archive_mount_failed": "Mounting the backup archive failed", @@ -80,7 +80,7 @@ "backup_copying_to_organize_the_archive": "Copying {size:s}MB to organize the archive", "backup_couldnt_bind": "Couldn't bind {src:s} to {dest:s}.", "backup_created": "تم إنشاء النسخة الإحتياطية", - "backup_creating_archive": "Creating the backup archive...", + "backup_creating_archive": "جارٍ إنشاء ملف النسخة الاحتياطية…", "backup_creation_failed": "Backup creation failed", "backup_csv_addition_failed": "Unable to add files to backup into the CSV file", "backup_csv_creation_failed": "Unable to create the CSV file needed for future restore operations", @@ -89,7 +89,7 @@ "backup_custom_need_mount_error": "Custom backup method failure on 'need_mount' step", "backup_delete_error": "Unable to delete '{path:s}'", "backup_deleted": "The backup has been deleted", - "backup_extracting_archive": "Extracting the backup archive...", + "backup_extracting_archive": "Extracting the backup archive…", "backup_hook_unknown": "Backup hook '{hook:s}' unknown", "backup_invalid_archive": "نسخة إحتياطية غير صالحة", "backup_method_borg_finished": "Backup into borg finished", @@ -103,7 +103,7 @@ "backup_output_directory_required": "يتوجب عليك تحديد مجلد لتلقي النسخ الإحتياطية", "backup_output_symlink_dir_broken": "You have a broken symlink instead of your archives directory '{path:s}'. You may have a specific setup to backup your data on an other filesystem, in this case you probably forgot to remount or plug your hard dirve or usb key.", "backup_running_app_script": "Running backup script of app '{app:s}'...", - "backup_running_hooks": "Running backup hooks...", + "backup_running_hooks": "Running backup hooks…", "backup_system_part_failed": "Unable to backup the '{part:s}' system part", "backup_unable_to_organize_files": "Unable to organize files in the archive with the quick method", "backup_with_no_backup_script_for_app": "App {app:s} has no backup script. Ignoring.", @@ -117,7 +117,7 @@ "certmanager_cert_install_success_selfsigned": "Successfully installed a self-signed certificate for domain {domain:s}!", "certmanager_cert_renew_success": "نجحت عملية تجديد شهادة Let's Encrypt الخاصة باسم النطاق {domain:s} !", "certmanager_cert_signing_failed": "فشل إجراء توقيع الشهادة الجديدة", - "certmanager_certificate_fetching_or_enabling_failed": "Sounds like enabling the new certificate for {domain:s} failed somehow...", + "certmanager_certificate_fetching_or_enabling_failed": "Sounds like enabling the new certificate for {domain:s} failed somehow…", "certmanager_conflicting_nginx_file": "Unable to prepare domain for ACME challenge: the nginx configuration file {filepath:s} is conflicting and should be removed first", "certmanager_couldnt_fetch_intermediate_cert": "Timed out when trying to fetch intermediate certificate from Let's Encrypt. Certificate installation/renewal aborted - please try again later.", "certmanager_domain_cert_not_selfsigned": "The certificate for domain {domain:s} is not self-signed. Are you sure you want to replace it? (Use --force)", @@ -174,8 +174,8 @@ "dyndns_registration_failed": "Unable to register DynDNS domain: {error:s}", "dyndns_domain_not_provided": "Dyndns provider {provider:s} cannot provide domain {domain:s}.", "dyndns_unavailable": "Domain {domain:s} is not available.", - "executing_command": "Executing command '{command:s}'...", - "executing_script": "Executing script '{script:s}'...", + "executing_command": "Executing command '{command:s}'…", + "executing_script": "Executing script '{script:s}'…", "extracting": "عملية فك الضغط جارية …", "field_invalid": "Invalid field '{:s}'", "firewall_reload_failed": "Unable to reload the firewall", @@ -216,7 +216,7 @@ "migrate_tsig_end": "Migration to hmac-sha512 finished", "migrate_tsig_failed": "Migrating the dyndns domain {domain} to hmac-sha512 failed, rolling back. Error: {error_code} - {error}", "migrate_tsig_start": "Not secure enough key algorithm detected for TSIG signature of domain '{domain}', initiating migration to the more secure one hmac-sha512", - "migrate_tsig_wait": "Let's wait 3min for the dyndns server to take the new key into account...", + "migrate_tsig_wait": "لننتظر الآن 3 دقائق ريثما يأخذ خادم أسماء النطاقات الديناميكية بعين الاعتبار المفتاح الجديد…", "migrate_tsig_wait_2": "دقيقتين …", "migrate_tsig_wait_3": "دقيقة واحدة …", "migrate_tsig_wait_4": "30 ثانية …", @@ -227,12 +227,12 @@ "migrations_current_target": "Migration target is {}", "migrations_error_failed_to_load_migration": "ERROR: failed to load migration {number} {name}", "migrations_forward": "Migrating forward", - "migrations_loading_migration": "Loading migration {number} {name}...", + "migrations_loading_migration": "Loading migration {number} {name}…", "migrations_migration_has_failed": "Migration {number} {name} has failed with exception {exception}, aborting", "migrations_no_migrations_to_run": "No migrations to run", - "migrations_show_currently_running_migration": "Running migration {number} {name}...", + "migrations_show_currently_running_migration": "Running migration {number} {name}…", "migrations_show_last_migration": "Last ran migration is {}", - "migrations_skip_migration": "Skipping migration {number} {name}...", + "migrations_skip_migration": "جارٍ تجاهل التهجير {number} {name}…", "monitor_disabled": "The server monitoring has been disabled", "monitor_enabled": "The server monitoring has been enabled", "monitor_glances_con_failed": "Unable to connect to Glances server", @@ -283,7 +283,7 @@ "restore_cleaning_failed": "Unable to clean-up the temporary restoration directory", "restore_complete": "Restore complete", "restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]", - "restore_extracting": "فك الضغط عن الملفات التي نحتاجها من النسخة الإحتياطية ...", + "restore_extracting": "جارٍ فك الضغط عن الملفات التي نحتاجها من النسخة الاحتياطية…", "restore_failed": "Unable to restore the system", "restore_hook_unavailable": "Restoration script for '{part:s}' not available on your system and not in the archive either", "restore_may_be_not_enough_disk_space": "Your system seems not to have enough disk space (freespace: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)", @@ -291,8 +291,8 @@ "restore_not_enough_disk_space": "Not enough disk space (freespace: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)", "restore_nothings_done": "Nothing has been restored", "restore_removing_tmp_dir_failed": "Unable to remove an old temporary directory", - "restore_running_app_script": "Running restore script of app '{app:s}'...", - "restore_running_hooks": "Running restoration hooks...", + "restore_running_app_script": "Running restore script of app '{app:s}'…", + "restore_running_hooks": "Running restoration hooks…", "restore_system_part_failed": "Unable to restore the '{part:s}' system part", "server_shutdown": "سوف ينطفئ الخادوم", "server_shutdown_confirm": "سوف ينطفئ الخادوم حالا. متأكد ؟ [{answers:s}]", @@ -366,9 +366,9 @@ "yunohost_installing": "عملية تنصيب يونوهوست جارية …", "yunohost_not_installed": "إنَّ واي يونوهوست ليس مُنَصَّب أو هو مثبت حاليا بشكل خاطئ. قم بتنفيذ الأمر 'yunohost tools postinstall'", "migration_description_0003_migrate_to_stretch": "تحديث النظام إلى ديبيان ستريتش و واي يونوهوست 3.0", - "migration_0003_patching_sources_list": "عملية تعديل ملف المصادر sources.lists جارية ...", - "migration_0003_main_upgrade": "بداية عملية التحديث الأساسية ...", - "migration_0003_fail2ban_upgrade": "بداية عملية تحديث fail2ban ...", + "migration_0003_patching_sources_list": "عملية تصحيح ملف المصادر sources.lists جارية…", + "migration_0003_main_upgrade": "بداية عملية التحديث الأساسية…", + "migration_0003_fail2ban_upgrade": "بداية عملية تحديث fail2ban…", "migration_0003_not_jessie": "إن توزيعة ديبيان الحالية تختلف عن جيسي !", "migration_description_0002_migrate_to_tsig_sha256": "يقوم بتحسين أمان TSIG لنظام أسماء النطاقات الديناميكة باستخدام SHA512 بدلًا مِن MD5", "migration_0003_backward_impossible": "لا يُمكن إلغاء عملية الإنتقال إلى ستريتش.", From 4463b0ee7c3087368f79daa765e33c5f1b31042d Mon Sep 17 00:00:00 2001 From: Sylke Vicious Date: Wed, 8 May 2019 14:03:13 +0000 Subject: [PATCH 56/67] Translated using Weblate (Italian) Currently translated at 66.1% (339 of 513 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/it/ --- locales/it.json | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/locales/it.json b/locales/it.json index 34848c181..cbd1baf8d 100644 --- a/locales/it.json +++ b/locales/it.json @@ -321,5 +321,38 @@ "password_too_simple_3": "La password deve essere lunga almeno 8 caratteri e contenere numeri, maiuscole e minuscole e simboli", "password_too_simple_4": "La password deve essere lunga almeno 12 caratteri e contenere numeri, maiuscole e minuscole", "users_available": "Utenti disponibili:", - "yunohost_ca_creation_success": "L'autorità di certificazione locale è stata creata." + "yunohost_ca_creation_success": "L'autorità di certificazione locale è stata creata.", + "app_action_cannot_be_ran_because_required_services_down": "Questa app richiede alcuni servizi che attualmente non sono attivi. Prima di continuare, dovresti provare a riavviare i seguenti servizi (e possibilmente capire perchè questi non siano attivi) : {services}", + "backup_output_symlink_dir_broken": "Hai un collegamento errato alla tua cartella di archiviazione '{path:s}'. Potresti avere delle impostazioni particolari per salvare i tuoi dati su un altro spazio, in questo caso probabilmente ti sei scordato di rimontare o collegare il tuo hard disk o la chiavetta usb.", + "certmanager_conflicting_nginx_file": "Impossibile preparare il dominio per il controllo ACME: il file di configurazione nginx {filepath:s} è in conflitto e dovrebbe essere prima rimosso", + "certmanager_couldnt_fetch_intermediate_cert": "Tempo scaduto durante il tentativo di recupero di un certificato intermedio da Let's Encrypt. Installazione/rinnovo non riuscito - per favore riprova più tardi.", + "certmanager_domain_dns_ip_differs_from_public_ip": "Il valore DNS 'A' per il dominio {domain:s} è diverso dall'IP di questo server. Se hai modificato recentemente il tuo valore A, attendi che si propaghi (esistono online alcuni siti per il controllo della propagazione DNS). (Se sai cosa stai facendo, usa --no-checks per disabilitare quei controlli.)", + "certmanager_domain_not_resolved_locally": "Il dominio {domain:s} non può essere risolto in locale dal server Yunohost. Questo può accadere se hai modificato recentemente il tuo valore DNS. Se così fosse, per favore aspetta qualche ora per far si che si propaghi. Se il problema persiste, prova ad aggiungere {domain:s} in /etc/hosts. (Se sai cosa stai facendo, usa --no-checks per disabilitare quei controlli.)", + "certmanager_error_no_A_record": "Nessun valore DNS 'A' trovato per {domain:s}. Devi far puntare il tuo nome di dominio verso la tua macchina per essere in grado di installare un certificato Let's Encrypt! (Se sai cosa stai facendo, usa --no-checks per disabilitare quei controlli.)", + "certmanager_hit_rate_limit": "Troppi certificati già rilasciati per l'esatta serie di dominii {domain:s} recentemente. Per favore riprova più tardi. Guarda https://letsencrypt.org/docs/rate-limits/ per maggiori dettagli", + "certmanager_http_check_timeout": "Tempo scaduto durante il tentativo di contatto del tuo server a se stesso attraverso HTTP utilizzando l'indirizzo IP pubblico (dominio {domain:s} con ip {ip:s}). Potresti avere un problema di hairpinning o il firewall/router davanti al tuo server non è correttamente configurato.", + "certmanager_no_cert_file": "Impossibile leggere il file di certificato per il dominio {domain:s} (file: {file:s})", + "certmanager_self_ca_conf_file_not_found": "File di configurazione non trovato per l'autorità di autofirma (file: {file:s})", + "certmanager_unable_to_parse_self_CA_name": "Impossibile analizzare il nome dell'autorità di autofirma (file: {file:s})", + "confirm_app_install_warning": "Attenzione: questa applicazione potrebbe funzionare ma non è ben integrata in YunoHost. Alcune funzionalità come l'accesso unico e il backup/ripristino potrebbero non essere disponibili. Installare comunque? [{answers:s}] ", + "confirm_app_install_danger": "ATTENZIONE! Questa applicazione è ancora sperimentale (se non esplicitamente non funzionante) e probabilmente potrebbe danneggiare il tuo sistema! Probabilmente NON dovresti installarla a meno che tu non sappia cosa stai facendo. Sicuro di volerti prendere questo rischio? [{answers:s}] ", + "confirm_app_install_thirdparty": "ATTENZIONE! Installando applicazioni di terze parti potresti compromettere l'integrita e la sicurezza del tuo sistema. Probabilmente NON dovresti installarle a meno che tu non sappia cosa stai facendo. Sicuro di volerti prendere questo rischio? [{answers:s}] ", + "dpkg_is_broken": "Non puoi eseguire questo ora perchè dpkg/apt (i gestori di pacchetti del sistema) sembrano essere in stato danneggiato... Puoi provare a risolvere il problema connettendoti via SSH ed eseguire `sudo dpkg --configure -a`.", + "domain_cannot_remove_main": "Non è possibile rimuovere il dominio principale ora. Prima imposta un nuovo dominio principale", + "domain_dns_conf_is_just_a_recommendation": "Questo comando ti mostra qual è la configurazione *raccomandata*. Non ti imposta la configurazione DNS al tuo posto. È tua responsabilità configurare la tua zona DNS nel tuo registrar in accordo con queste raccomandazioni.", + "domain_dyndns_dynette_is_unreachable": "Impossibile raggiungere la dynette YunoHost, o il tuo YunHost non è correttamente connesso a internet o il server dynette non è attivo. Errore: {error}", + "dyndns_could_not_check_provide": "Impossibile controllare se {provider:s} possano fornire {domain:s}.", + "dyndns_could_not_check_available": "Impossibile controllare se {domain:s} è disponibile su {provider:s}.", + "dyndns_domain_not_provided": "Il fornitore Dyndns {provider:s} non può fornire il dominio {domain:s}.", + "experimental_feature": "Attenzione: questa funzionalità è sperimentale e non è considerata stabile, non dovresti utilizzarla a meno che tu non sappia cosa stai facendo.", + "file_does_not_exist": "Il file {path:s} non esiste.", + "global_settings_bad_choice_for_enum": "Scelta sbagliata per l'impostazione {setting:s}, ricevuta '{choice:s}' ma le scelte disponibili sono : {available_choices:s}", + "global_settings_bad_type_for_setting": "Tipo errato per l'impostazione {setting:s}, ricevuto {received_type:s}, atteso {expected_type:s}", + "global_settings_cant_open_settings": "Apertura del file delle impostazioni non riuscita, motivo: {reason:s}", + "global_settings_cant_serialize_settings": "Serializzazione dei dati delle impostazioni non riuscita, motivo: {reason:s}", + "global_settings_cant_write_settings": "Scrittura del file delle impostazioni non riuscita, motivo: {reason:s}", + "global_settings_key_doesnt_exists": "La chiave '{settings_key:s}' non esiste nelle impostazioni globali, puoi vedere tutte le chiavi disponibili eseguendo 'yunohost settings list'", + "global_settings_reset_success": "Successo. Le tue impostazioni precedenti sono state salvate in {path:s}", + "global_settings_setting_example_bool": "Esempio di opzione booleana", + "global_settings_setting_example_enum": "Esempio di opzione enum" } From 2d2fcf279c66ac695cbfd5a3fb63a1927cbcdd20 Mon Sep 17 00:00:00 2001 From: Sylke Vicious Date: Mon, 13 May 2019 14:38:23 +0000 Subject: [PATCH 57/67] Translated using Weblate (Italian) Currently translated at 67.5% (353 of 523 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/it/ --- locales/it.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/locales/it.json b/locales/it.json index cbd1baf8d..2a1fb737f 100644 --- a/locales/it.json +++ b/locales/it.json @@ -354,5 +354,21 @@ "global_settings_key_doesnt_exists": "La chiave '{settings_key:s}' non esiste nelle impostazioni globali, puoi vedere tutte le chiavi disponibili eseguendo 'yunohost settings list'", "global_settings_reset_success": "Successo. Le tue impostazioni precedenti sono state salvate in {path:s}", "global_settings_setting_example_bool": "Esempio di opzione booleana", - "global_settings_setting_example_enum": "Esempio di opzione enum" + "global_settings_setting_example_enum": "Esempio di opzione enum", + "already_up_to_date": "Niente da fare! Tutto è già aggiornato!", + "global_settings_setting_example_int": "Esempio di opzione int", + "global_settings_setting_example_string": "Esempio di opzione string", + "global_settings_setting_security_nginx_compatibility": "Bilanciamento tra compatibilità e sicurezza per il server web nginx. Riguarda gli algoritmi di cifratura (e altri aspetti legati alla sicurezza)", + "global_settings_setting_security_password_admin_strength": "Complessità della password di amministratore", + "global_settings_setting_security_password_user_strength": "Complessità della password utente", + "global_settings_setting_security_ssh_compatibility": "Bilanciamento tra compatibilità e sicurezza per il server SSH. Riguarda gli algoritmi di cifratura (e altri aspetti legati alla sicurezza)", + "global_settings_unknown_setting_from_settings_file": "Chiave sconosciuta nelle impostazioni: '{setting_key:s}', scartata e salvata in /etc/yunohost/settings-unknown.json", + "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Consenti l'uso del (deprecato) hostkey DSA per la configurazione del demone SSH", + "global_settings_unknown_type": "Situazione inaspettata, l'impostazione {setting:s} sembra essere di tipo {unknown_type:s} ma non è un tipo supportato dal sistema.", + "good_practices_about_admin_password": "Stai per definire una nuova password di amministratore. La password deve essere almeno di 8 caratteri - anche se è buona pratica utilizzare password più lunghe (es. una frase, una serie di parole) e/o utilizzare vari tipi di caratteri (maiuscole, minuscole, numeri e simboli).", + "invalid_url_format": "Formato URL non valido", + "log_corrupted_md_file": "Il file dei metadati yaml associato con i registri è corrotto: '{md_file}'", + "log_category_404": "La categoria di registrazione '{category}' non esiste", + "log_link_to_log": "Registro completo di questa operazione: '{desc}'", + "log_help_to_get_log": "Per vedere il registro dell'operazione '{desc}', usa il comando 'yunohost log display {name}'" } From 155e6a2f9402539a32555421b3256a64425fb217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt?= Date: Tue, 14 May 2019 22:13:41 +0000 Subject: [PATCH 58/67] Translated using Weblate (French) Currently translated at 94.8% (497 of 524 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index db5f06c97..ef9954a6e 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -458,7 +458,7 @@ "log_tools_migrations_migrate_forward": "Migrer vers", "log_tools_migrations_migrate_backward": "Revenir en arrière", "log_tools_postinstall": "Faire la post-installation de votre serveur YunoHost", - "log_tools_upgrade": "Mise à jour des paquets Debian", + "log_tools_upgrade": "Mettre à jour les paquets système", "log_tools_shutdown": "Éteindre votre serveur", "log_tools_reboot": "Redémarrer votre serveur", "mail_unavailable": "Cette adresse de courriel est réservée et doit être automatiquement attribuée au tout premier utilisateur", @@ -524,7 +524,7 @@ "service_reloaded_or_restarted": "Le service '{service:s}' a été rechargé ou redémarré", "this_action_broke_dpkg": "Cette action a laissé des paquets non configurés par dpkg/apt (les gestionnaires de paquets système). Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a`.", "app_action_cannot_be_ran_because_required_services_down": "Cette application requiert certains services qui sont actuellement en pannes. Avant de continuer, vous devriez essayer de redémarrer les services suivant (et éventuellement rechercher pourquoi ils sont en panne) : {services}", - "admin_password_too_long": "Choisisez un mot de passe de maximum 127 caractères.", + "admin_password_too_long": "Choisissez un mot de passe de 127 caractères maximum.", "log_regen_conf": "Régénérer les configurations du système '{}'", "migration_0009_not_needed": "Cette migration semble avoir déjà été jouée ? On l'ignore.", "regenconf_file_backed_up": "Le fichier de configuration '{conf}' a été sauvegardé sous '{backup}'", @@ -535,5 +535,21 @@ "regenconf_file_removed": "Le fichier de configuration '{conf}' a été supprimé", "regenconf_file_updated": "Le fichier de configuration '{conf}' a été mis à jour", "regenconf_now_managed_by_yunohost": "Le fichier de configuration '{conf}' est maintenant géré par YunoHost (catégorie {category}).", - "regenconf_up_to_date": "La configuration est déjà à jour pour la catégorie '{category}'" + "regenconf_up_to_date": "La configuration est déjà à jour pour la catégorie « {catégorie} ».", + "already_up_to_date": "Il n'y a rien à faire ! Tout est déjà à jour !", + "global_settings_setting_security_nginx_compatibility": "Compatibilité vs. compromis sécuritaire pour le serveur web nginx. Affecte les cryptogrammes (et d'autres aspects liés à la sécurité)", + "global_settings_setting_security_ssh_compatibility": "Compatibilité vs. compromis sécuritaire pour le serveur SSH. Affecte les cryptogrammes (et d'autres aspects liés à la sécurité)", + "global_settings_setting_security_postfix_compatibility": "Compatibilité vs. compromis sécuritaire pour le serveur Postfix. Affecte les cryptogrammes (et d'autres aspects liés à la sécurité)", + "migration_description_0009_decouple_regenconf_from_services": "Dissocier le mécanisme « regen-conf » des services", + "migration_description_0010_migrate_to_apps_json": "Supprimer les listes d'applications obsolètes et utiliser la nouvelle liste unifiée « apps.json » à la place", + "regenconf_file_kept_back": "Le fichier de configuration « {conf} » s'attendait à être supprimé par « regen-conf » (catégorie {catégorie}) mais a été conservé.", + "regenconf_updated": "La configuration a été mise à jour pour la catégorie « {category} ».", + "regenconf_would_be_updated": "La configuration aurait dû être mise à jour pour la catégorie « {category} ».", + "regenconf_dry_pending_applying": "Vérification de la configuration en attente qui aurait été appliquée pour la catégorie « {category} ».", + "regenconf_failed": "Impossible de régénérer la configuration pour la ou les catégorie(s) : « {categories} ».", + "regenconf_pending_applying": "Appliquer la configuration en attente pour la catégorie « {catégorie} »…", + "service_regen_conf_is_deprecated": "« yunohost service regen-conf » est obsolète ! Veuillez plutôt utiliser « yunohost tools regen-conf ».", + "tools_upgrade_at_least_one": "Veuillez spécifier --apps OU --system", + "tools_upgrade_cant_both": "Impossible de mettre à niveau le système et les applications en même temps", + "tools_upgrade_cant_hold_critical_packages": "Impossibilité de maintenir les paquets critiques..." } From 84fe7f50047ee52d771aae2366b227b39b01e347 Mon Sep 17 00:00:00 2001 From: Sylke Vicious Date: Fri, 17 May 2019 08:45:02 +0000 Subject: [PATCH 59/67] Translated using Weblate (Italian) Currently translated at 70.6% (370 of 524 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/it/ --- locales/it.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/locales/it.json b/locales/it.json index 2a1fb737f..31f00c409 100644 --- a/locales/it.json +++ b/locales/it.json @@ -370,5 +370,22 @@ "log_corrupted_md_file": "Il file dei metadati yaml associato con i registri è corrotto: '{md_file}'", "log_category_404": "La categoria di registrazione '{category}' non esiste", "log_link_to_log": "Registro completo di questa operazione: '{desc}'", - "log_help_to_get_log": "Per vedere il registro dell'operazione '{desc}', usa il comando 'yunohost log display {name}'" + "log_help_to_get_log": "Per vedere il registro dell'operazione '{desc}', usa il comando 'yunohost log display {name}'", + "global_settings_setting_security_postfix_compatibility": "Bilanciamento tra compatibilità e sicurezza per il server Postfix. Riguarda gli algoritmi di cifratura (e altri aspetti legati alla sicurezza)", + "log_link_to_failed_log": "L'operazione '{desc}' è fallita! Per ottenere aiuto, per favore fornisci il registro completo dell'operazione cliccando qui", + "log_help_to_get_failed_log": "L'operazione '{desc}' è fallita! Per ottenere aiuto, per favore condividi il registro completo dell'operazione utilizzando il comando 'yunohost log display {name} --share'", + "log_does_exists": "Non esiste nessun registro delle operazioni chiamato '{log}', usa 'yunohost log list' per vedere tutti i registri delle operazioni disponibili", + "log_app_addaccess": "Aggiungi accesso a '{}'", + "log_app_removeaccess": "Rimuovi accesso a '{}'", + "log_app_clearaccess": "Rimuovi tutti gli accessi a '{}'", + "log_app_fetchlist": "Aggiungi un elenco di applicazioni", + "log_app_removelist": "Rimuovi un elenco di applicazioni", + "log_app_change_url": "Cambia l'url dell'applicazione '{}'", + "log_app_install": "Installa l'applicazione '{}'", + "log_app_remove": "Rimuovi l'applicazione '{}'", + "log_app_upgrade": "Aggiorna l'applicazione '{}'", + "log_app_makedefault": "Rendi predefinita l'applicazione '{}'", + "log_available_on_yunopaste": "Questo registro è ora disponibile via {url}", + "log_backup_restore_system": "Ripristina sistema da un archivio di backup", + "log_backup_restore_app": "Ripristina '{}' da un archivio di backup" } From 8b12c0c81d993bf60ea734ea703c092f98c06b70 Mon Sep 17 00:00:00 2001 From: Sylke Vicious Date: Fri, 17 May 2019 14:13:36 +0000 Subject: [PATCH 60/67] Translated using Weblate (Italian) Currently translated at 79.0% (414 of 524 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/it/ --- locales/it.json | 50 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/locales/it.json b/locales/it.json index 31f00c409..2c194d5a6 100644 --- a/locales/it.json +++ b/locales/it.json @@ -387,5 +387,53 @@ "log_app_makedefault": "Rendi predefinita l'applicazione '{}'", "log_available_on_yunopaste": "Questo registro è ora disponibile via {url}", "log_backup_restore_system": "Ripristina sistema da un archivio di backup", - "log_backup_restore_app": "Ripristina '{}' da un archivio di backup" + "log_backup_restore_app": "Ripristina '{}' da un archivio di backup", + "log_remove_on_failed_restore": "Rimuovi '{}' dopo un ripristino fallito da un archivio di backup", + "log_remove_on_failed_install": "Rimuovi '{}' dopo un'installazione fallita", + "log_domain_add": "Aggiungi il dominio '{}' nella configurazione di sistema", + "log_domain_remove": "Rimuovi il dominio '{}' dalla configurazione di sistema", + "log_dyndns_subscribe": "Sottoscrivi un sottodominio YunoHost '{}'", + "log_dyndns_update": "Aggiorna l'ip associato con il tuo sottodominio YunoHost '{}'", + "log_letsencrypt_cert_install": "Installa un certificato Let's encrypt sul dominio '{}'", + "log_selfsigned_cert_install": "Installa un certificato autofirmato sul dominio '{}'", + "log_letsencrypt_cert_renew": "Rinnova il certificato Let's encrypt sul dominio '{}'", + "log_service_enable": "Abilita il servizio '{}'", + "log_regen_conf": "Rigenera configurazioni di sistema '{}'", + "log_user_create": "Aggiungi l'utente '{}'", + "log_user_delete": "Elimina l'utente '{}'", + "log_user_update": "Aggiornate le informazioni dell'utente '{}'", + "log_tools_maindomain": "Rendi '{}' dominio principale", + "log_tools_migrations_migrate_forward": "Migra avanti", + "log_tools_migrations_migrate_backward": "Migra indietro", + "log_tools_postinstall": "Postinstallazione del tuo server YunoHost", + "log_tools_upgrade": "Aggiornamento dei pacchetti di sistema", + "log_tools_shutdown": "Spegni il tuo server", + "log_tools_reboot": "Riavvia il tuo server", + "mail_unavailable": "Questo indirizzo email è riservato e dovrebbe essere automaticamente assegnato al primo utente", + "migrate_tsig_end": "Migrazione a hmac-sha512 terminata", + "migrate_tsig_failed": "Migrazione del dominio dyndns {domain} verso hmac-sha512 fallita, torno indetro. Errore: {error_code} - {error}", + "migrate_tsig_start": "Trovato un algoritmo di chiave non abbastanza sicuro per la firma TSIG del dominio '{domain}', inizio della migrazione verso la più sicura hmac-sha512", + "migrate_tsig_wait": "Aspetta 3 minuti che il server dyndns prenda la nuova chiave in gestione…", + "migrate_tsig_wait_2": "2 minuti…", + "migrate_tsig_wait_3": "1 minuto…", + "migrate_tsig_wait_4": "30 secondi…", + "migrate_tsig_not_needed": "Non sembra tu stia utilizzando un dominio dyndns, quindi non è necessaria nessuna migrazione!", + "migration_description_0001_change_cert_group_to_sslcert": "Cambia permessi del gruppo di certificati da 'metronome' a 'ssl-cert'", + "migration_description_0002_migrate_to_tsig_sha256": "Migliora la sicurezza del TSIG dyndns utilizzando SHA512 invece di MD5", + "migration_description_0003_migrate_to_stretch": "Aggiorna il sistema a Debian Stretch e YunoHost 3.0", + "migration_description_0004_php5_to_php7_pools": "Riconfigura le PHP pools ad utilizzare PHP 7 invece di 5", + "migration_description_0005_postgresql_9p4_to_9p6": "Migra i database da postgresql 9.4 a 9.6", + "migration_description_0006_sync_admin_and_root_passwords": "Sincronizza password di amministratore e root", + "migration_description_0010_migrate_to_apps_json": "Rimuovi gli elenchi di app deprecati ed usa invece il nuovo elenco unificato 'apps.json'", + "migration_0003_backward_impossible": "La migrazione a Stretch non può essere annullata.", + "migration_0003_start": "Migrazione a Stretch iniziata. I registri saranno disponibili in {logfile}.", + "migration_0003_patching_sources_list": "Sistemando il file sources.lists…", + "migration_0003_main_upgrade": "Iniziando l'aggiornamento principale…", + "migration_0003_fail2ban_upgrade": "Iniziando l'aggiornamento di fail2ban…", + "migration_0003_restoring_origin_nginx_conf": "Il tuo file /etc/nginx/nginx.conf è stato modificato in qualche modo. La migrazione lo riporterà al suo stato originale… Il file precedente sarà disponibile come {backup_dest}.", + "migration_0003_yunohost_upgrade": "Iniziando l'aggiornamento dei pacchetti yunohost… La migrazione terminerà, ma l'aggiornamento attuale avverrà subito dopo. Dopo che l'operazione sarà completata, probabilmente dovrai riaccedere all'interfaccia di amministrazione.", + "migration_0003_not_jessie": "La distribuzione attuale non è Jessie!", + "migration_0003_system_not_fully_up_to_date": "Il tuo sistema non è completamente aggiornato. Per favore prima esegui un aggiornamento normale prima di migrare a stretch.", + "this_action_broke_dpkg": "Questa azione ha danneggiato dpkg/apt (i gestori di pacchetti del sistema)… Puoi provare a risolvere questo problema connettendoti via SSH ed eseguendo `sudo dpkg --configure -a`.", + "updating_app_lists": "Recupero degli aggiornamenti disponibili per le applicazioni…" } From 9341da10ce38341da13af552f2d22b51bdc6c7de Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 22 May 2019 19:36:31 +0200 Subject: [PATCH 61/67] Add changelog for 3.6.0 --- debian/changelog | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index a22959899..0a92b9e51 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,48 @@ +yunohost (3.6.0) testing; urgency=low + + ## Major changes + + - [enh] Simplify the whole LDAP interface thing (#721) + - [enh] Rework how system upgrade is handled (#692) + - [enh] Properly reimplement bash completion for yunohost cli (#678) + - [enh] Migrate to apps.json / use it as default list (#666, #665) + - [enh] Decouple the regen-conf mechanism from services (#653) + - [i18n] Update translations for Catalan, Occitan, French, Italian, Spanish, Arabic + + ## App helpers + + - [mod] Set min version to 3.5.0 for helpers (#725) + - [enh] Add helpers for sso config (#720) + - [enh] Reorganize helpers (#717) + - [enh] Add the ongoing part to the progression bar when using ynh_script_progression (#715) + - [fix] postgresql helpers : force disconnection of all clients connected to the database (#713) + - [enh] Use printers in helpers (#712) + - [enh] Use ynh_systemd_action in helpers (#711) + - [fix] Fix extraction of weight value for ynh_script_progression (#710) + - [enh] Add support for ynh_setup_source in restore script (#703) + + # Other changes + + - [fix] Update censurfridns ipv6 (#727) + - [enh] Optimize ynh_script_progression (#723) + - [enh] Disable VRFY command in Postfix command (#722) + - [enh] Add a --with-details option for log list (#716) + - [enh] Specify -a parameter on dovecot lda for Sieve (#709) + - [fix] Fix an issue with config panels following changes in hook_exec (#707) + - [enh] Don't expose LDAP server to the outside world (#706) + - [fix] Remove backup hook warning about cron file (#704) + - [enh] Update nginx conf to handle WebSocket proxying (#701) + - [enh] Add size of apps in backup_info result (#699) + - [enh] Add a setting to remove support for TLSv1 and TLSv1.1 in Postfix (#696) + - [enh] Mark YunoHost as essential to avoid removing it inadvertenly (#694) + - [enh] Avoid to send simultaneously too many emails (#691) + - [enh] Dump log when an app script fails in CLI to help with debugging (#687) + - [fix] Many small technical fixes (ec48edf,251a338,d11d31d,3668bf7,c7eb5bb,9b08afc,cecaee4,95fdfb3,2bc0deb) + + Thanks to all contributors : Aleks, Benoît, Bram, ButterflyOfFire, C. Vuillot, Josue, J. Maulny, Kayou, L. Noferini, Maniack, M. Thiel, Quentí, R. du Song, Sylkevicious, ljf, xaloc33, yalh76 ! <3 + + -- Alexandre Aubin Wed, 22 May 2019 19:10:00 +0000 + yunohost (3.5.2.2) stable; urgency=low - Hotfix for ynh_psql_remove_db (from ljf) @@ -17,7 +62,7 @@ yunohost (3.5.2) stable; urgency=low - [i18n] Update translations for Catalan, Arabic, Italian Thanks to all contributors: Aleks, xaloc, BoF, silkevicious ! <3 - + -- Alexandre Aubin Wed, 10 Apr 2019 01:53:00 +0000 yunohost (3.5.1.1) testing; urgency=low From b091ab402906b90b5656a8c8c57aaefd89d8ac2e Mon Sep 17 00:00:00 2001 From: Luke Murphy Date: Thu, 23 May 2019 09:42:16 +0200 Subject: [PATCH 62/67] Allow to display logs when postinstall fails Closes https://github.com/YunoHost/issues/issues/1345. --- bin/yunohost | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/yunohost b/bin/yunohost index fd9c2dbfd..10a21a9da 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -195,7 +195,8 @@ if __name__ == '__main__': # Check that YunoHost is installed if not os.path.isfile('/etc/yunohost/installed') and \ (len(args) < 2 or (args[0] +' '+ args[1] != 'tools postinstall' and \ - args[0] +' '+ args[1] != 'backup restore')): + args[0] +' '+ args[1] != 'backup restore' and \ + args[0] +' '+ args[1] != 'log display')): from moulinette import m18n # Init i18n From a1ab50dcf1ecb3b2505b7b7920a1efe815b283d7 Mon Sep 17 00:00:00 2001 From: MCMic Date: Thu, 23 May 2019 14:49:27 +0000 Subject: [PATCH 63/67] Add indexes for fields listed by slapd in the logs I get lots of suggestion of indexes by slapd in my logs: <= mdb_equality_candidates: (cn) not indexed <= mdb_equality_candidates: (gidNumber) not indexed <= mdb_equality_candidates: (mail) not indexed <= mdb_equality_candidates: (member) not indexed <= mdb_equality_candidates: (memberUid) not indexed <= mdb_equality_candidates: (sudoUser) not indexed <= mdb_equality_candidates: (uidNumber) not indexed <= mdb_equality_candidates: (uniqueMember) not indexed <= mdb_equality_candidates: (virtualdomain) not indexed <= mdb_substring_candidates: (sudoUser) not indexed Since Yunohost makes it hard to edit LDAP server configuration (see https://github.com/YunoHost/issues/issues/1350), the default configuration should contain indexes for the fields used by Yunohost a lot. --- data/templates/slapd/slapd.conf | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/data/templates/slapd/slapd.conf b/data/templates/slapd/slapd.conf index c8c363795..cc7ac9230 100644 --- a/data/templates/slapd/slapd.conf +++ b/data/templates/slapd/slapd.conf @@ -63,9 +63,13 @@ suffix "dc=yunohost,dc=org" directory "/var/lib/ldap" # Indexing options for database #1 -index objectClass eq -index uid eq,sub -index entryCSN,entryUUID eq +index objectClass eq +index uid,sudoUser eq,sub +index entryCSN,entryUUID eq +index cn,mail eq +index gidNumber,uidNumber eq +index member,memberUid,uniqueMember eq +index virtualdomain eq # Save the time that the entry gets modified, for database #1 lastmod on From f6f73c23b17ad37ea138b818939c91f07d169a96 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Tue, 28 May 2019 02:09:27 +0200 Subject: [PATCH 64/67] [mod] make error more explicit --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 511f69028..ca766d642 100644 --- a/locales/en.json +++ b/locales/en.json @@ -37,7 +37,7 @@ "app_requirements_checking": "Checking required packages for {app}…", "app_requirements_failed": "Unable to meet requirements for {app}: {error}", "app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}", - "app_sources_fetch_failed": "Unable to fetch sources files", + "app_sources_fetch_failed": "Unable to fetch sources files, is the url correct?", "app_start_install": "Installing application {app}…", "app_start_remove": "Removing application {app}…", "app_start_backup": "Collecting files to be backuped for {app}…", From a9c9cbac36568676be194024f6f660e4fc3f03b6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 28 May 2019 20:24:03 +0200 Subject: [PATCH 65/67] Add old list to applist migration --- src/yunohost/data_migrations/0010_migrate_to_apps_json.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py index 442d52492..86664ecf1 100644 --- a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py +++ b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py @@ -22,6 +22,7 @@ class MyMigration(Migration): # Remove all the deprecated lists lists_to_remove = [ + "http://app.yunohost.org/list.json", # Old list on old installs, alias to official.json "https://app.yunohost.org/official.json", "https://app.yunohost.org/community.json", "https://labriqueinter.net/apps/labriqueinternet.json" From 330f06c3581ae88a5a3097d9f98617637d943962 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 28 May 2019 20:28:13 +0200 Subject: [PATCH 66/67] Check list url independently of scheme --- .../data_migrations/0010_migrate_to_apps_json.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py index 86664ecf1..78f6d733b 100644 --- a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py +++ b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py @@ -22,15 +22,15 @@ class MyMigration(Migration): # Remove all the deprecated lists lists_to_remove = [ - "http://app.yunohost.org/list.json", # Old list on old installs, alias to official.json - "https://app.yunohost.org/official.json", - "https://app.yunohost.org/community.json", - "https://labriqueinter.net/apps/labriqueinternet.json" + "app.yunohost.org/list.json", # Old list on old installs, alias to official.json + "app.yunohost.org/official.json", + "app.yunohost.org/community.json", + "labriqueinter.net/apps/labriqueinternet.json" ] appslists = _read_appslist_list() for appslist, infos in appslists.items(): - if infos["url"] in lists_to_remove: + if infos["url"].split("//")[-1] in lists_to_remove: app_removelist(name=appslist) # Replace by apps.json list From d0c982a422cde33cdc5c9432304a35d1cb82bfd6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 31 May 2019 16:09:22 +0200 Subject: [PATCH 67/67] Check for obvious conflict with already running apt/dpkg commands when running yunohost upgrade --- locales/en.json | 1 + src/yunohost/tools.py | 4 ++++ src/yunohost/utils/packages.py | 2 ++ 3 files changed, 7 insertions(+) diff --git a/locales/en.json b/locales/en.json index ca766d642..901d36574 100644 --- a/locales/en.json +++ b/locales/en.json @@ -157,6 +157,7 @@ "diagnosis_monitor_system_error": "Can't monitor system: {error}", "diagnosis_no_apps": "No installed application", "dpkg_is_broken": "You cannot do this right now because dpkg/apt (the system package managers) seems to be in a broken state... You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", + "dpkg_lock_not_available": "This command can't be ran right now because another program seems to be using the lock of dpkg (the system package manager)", "dnsmasq_isnt_installed": "dnsmasq does not seem to be installed, please run 'apt-get remove bind9 && apt-get install dnsmasq'", "domain_cannot_remove_main": "Cannot remove main domain. Set a new main domain first", "domain_cert_gen_failed": "Unable to generate certificate", diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 3bb69c961..b0ed1c449 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -553,6 +553,10 @@ def tools_upgrade(operation_logger, apps=None, system=False): if packages.dpkg_is_broken(): raise YunohostError("dpkg_is_broken") + # Check for obvious conflict with other dpkg/apt commands already running in parallel + if not packages.dpkg_lock_available(): + raise YunohostError("dpkg_lock_not_available") + if system is not False and apps is not None: raise YunohostError("tools_upgrade_cant_both") diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index b564d2dea..84901bbff 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -482,6 +482,8 @@ def dpkg_is_broken(): return any(re.match("^[0-9]+$", f) for f in os.listdir("/var/lib/dpkg/updates/")) +def dpkg_lock_available(): + return os.system("lsof /var/lib/dpkg/lock >/dev/null") != 0 def _list_upgradable_apt_packages():