diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 290952aa3..b7afe2703 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -295,6 +295,9 @@ user: action_help: List permissions and corresponding accesses api: GET /users/permissions arguments: + apps: + help: Apps to list permission for (all by default) + nargs: "*" -s: full: --short help: Only list permission names diff --git a/data/helpers.d/nodejs b/data/helpers.d/nodejs index c7903ddcc..6f38a3e62 100644 --- a/data/helpers.d/nodejs +++ b/data/helpers.d/nodejs @@ -1,6 +1,6 @@ #!/bin/bash -n_version=7.0.2 +n_version=7.1.0 n_install_dir="/opt/node_n" node_version_path="$n_install_dir/n/versions/node" # N_PREFIX is the directory of n, it needs to be loaded as a environment variable. @@ -17,7 +17,7 @@ ynh_install_n () { ynh_print_info --message="Installation of N - Node.js version management" # Build an app.src for n echo "SOURCE_URL=https://github.com/tj/n/archive/v${n_version}.tar.gz -SOURCE_SUM=fa80a8685f0fb1b4187fc0a1228b44f0ea2f244e063fe8f443b8913ea595af89" > "$YNH_APP_BASEDIR/conf/n.src" +SOURCE_SUM=20100f3bc56648cc414717fb7367fcf0e8229dc59a10b0530ccac90042ee0a74" > "$YNH_APP_BASEDIR/conf/n.src" # Download and extract n ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n # Install n diff --git a/data/helpers.d/permission b/data/helpers.d/permission index 995bff0eb..a5c09cded 100644 --- a/data/helpers.d/permission +++ b/data/helpers.d/permission @@ -182,7 +182,7 @@ ynh_permission_exists() { local permission ynh_handle_getopts_args "$@" - yunohost user permission list --output-as json --quiet \ + yunohost user permission list "$app" --output-as json --quiet \ | jq -e --arg perm "$app.$permission" '.permissions[$perm]' >/dev/null } diff --git a/data/hooks/backup/05-conf_ldap b/data/hooks/backup/05-conf_ldap old mode 100755 new mode 100644 diff --git a/data/hooks/backup/08-conf_ssh b/data/hooks/backup/08-conf_ssh deleted file mode 100755 index ee976080c..000000000 --- a/data/hooks/backup/08-conf_ssh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# Exit hook on subcommand error or unset variable -set -eu - -# Source YNH helpers -source /usr/share/yunohost/helpers - -# Backup destination -backup_dir="${1}/conf/ssh" - -# Backup the configuration -if [ -d /etc/ssh/ ]; then - ynh_backup "/etc/ssh" "$backup_dir" -else - echo "SSH is not installed" -fi diff --git a/data/hooks/backup/14-conf_ssowat b/data/hooks/backup/14-conf_ssowat deleted file mode 100755 index d4db72493..000000000 --- a/data/hooks/backup/14-conf_ssowat +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# Exit hook on subcommand error or unset variable -set -eu - -# Source YNH helpers -source /usr/share/yunohost/helpers - -# Backup destination -backup_dir="${1}/conf/ssowat" - -# Backup the configuration -ynh_backup "/etc/ssowat" "$backup_dir" diff --git a/data/hooks/backup/17-data_home b/data/hooks/backup/17-data_home old mode 100755 new mode 100644 diff --git a/data/hooks/backup/20-conf_ynh_firewall b/data/hooks/backup/20-conf_ynh_firewall deleted file mode 100755 index 98be3eb09..000000000 --- a/data/hooks/backup/20-conf_ynh_firewall +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# Exit hook on subcommand error or unset variable -set -eu - -# Source YNH helpers -source /usr/share/yunohost/helpers - -# Backup destination -backup_dir="${1}/conf/ynh/firewall" - -# Backup the configuration -ynh_backup "/etc/yunohost/firewall.yml" "${backup_dir}/firewall.yml" diff --git a/data/hooks/backup/20-conf_ynh_settings b/data/hooks/backup/20-conf_ynh_settings new file mode 100644 index 000000000..77148c4d9 --- /dev/null +++ b/data/hooks/backup/20-conf_ynh_settings @@ -0,0 +1,17 @@ +#!/bin/bash + +# Exit hook on subcommand error or unset variable +set -eu + +# Source YNH helpers +source /usr/share/yunohost/helpers + +# Backup destination +backup_dir="${1}/conf/ynh" + +# Backup the configuration +ynh_backup "/etc/yunohost/firewall.yml" "${backup_dir}/firewall.yml" +ynh_backup "/etc/yunohost/current_host" "${backup_dir}/current_host" +[ ! -e "/etc/yunohost/settings.json" ] || ynh_backup "/etc/yunohost/settings.json" "${backup_dir}/settings.json" +[ ! -d "/etc/yunohost/dyndns" ] || ynh_backup "/etc/yunohost/dyndns" "${backup_dir}/dyndns" +[ ! -d "/etc/dkim" ] || ynh_backup "/etc/dkim" "${backup_dir}/dkim" diff --git a/data/hooks/backup/21-conf_ynh_certs b/data/hooks/backup/21-conf_ynh_certs old mode 100755 new mode 100644 diff --git a/data/hooks/backup/22-conf_mail b/data/hooks/backup/22-conf_mail deleted file mode 100644 index b604d8aa8..000000000 --- a/data/hooks/backup/22-conf_mail +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -source /usr/share/yunohost/helpers -ynh_abort_if_errors -YNH_CWD="${YNH_BACKUP_DIR%/}/conf/dkim" -mkdir -p "$YNH_CWD" -cd "$YNH_CWD" - -ynh_backup --src_path="/etc/dkim" diff --git a/data/hooks/backup/23-data_mail b/data/hooks/backup/23-data_mail old mode 100755 new mode 100644 diff --git a/data/hooks/backup/26-conf_xmpp b/data/hooks/backup/26-conf_xmpp deleted file mode 100755 index b55ad2bfc..000000000 --- a/data/hooks/backup/26-conf_xmpp +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Exit hook on subcommand error or unset variable -set -eu - -# Source YNH helpers -source /usr/share/yunohost/helpers - -# Backup destination -backup_dir="${1}/conf/xmpp" - -# Backup the configuration -ynh_backup /etc/metronome "${backup_dir}/etc" -ynh_backup /var/lib/metronome "${backup_dir}/var" diff --git a/data/hooks/backup/27-data_xmpp b/data/hooks/backup/27-data_xmpp new file mode 100644 index 000000000..2cd93e02b --- /dev/null +++ b/data/hooks/backup/27-data_xmpp @@ -0,0 +1,13 @@ +#!/bin/bash + +# Exit hook on subcommand error or unset variable +set -eu + +# Source YNH helpers +source /usr/share/yunohost/helpers + +# Backup destination +backup_dir="${1}/data/xmpp" + +ynh_backup /var/lib/metronome "${backup_dir}/var_lib_metronome" +ynh_backup /var/xmpp-upload/ "${backup_dir}/var_xmpp-upload" diff --git a/data/hooks/backup/29-conf_nginx b/data/hooks/backup/29-conf_nginx deleted file mode 100755 index 81e145e24..000000000 --- a/data/hooks/backup/29-conf_nginx +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# Exit hook on subcommand error or unset variable -set -eu - -# Source YNH helpers -source /usr/share/yunohost/helpers - -# Backup destination -backup_dir="${1}/conf/nginx" - -# Backup the configuration -ynh_backup "/etc/nginx/conf.d" "$backup_dir" diff --git a/data/hooks/backup/40-conf_ynh_currenthost b/data/hooks/backup/40-conf_ynh_currenthost deleted file mode 100755 index 6a98fd0d2..000000000 --- a/data/hooks/backup/40-conf_ynh_currenthost +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# Exit hook on subcommand error or unset variable -set -eu - -# Source YNH helpers -source /usr/share/yunohost/helpers - -# Backup destination -backup_dir="${1}/conf/ynh" - -# Backup the configuration -ynh_backup "/etc/yunohost/current_host" "${backup_dir}/current_host" diff --git a/data/hooks/backup/42-conf_ynh_dyndns b/data/hooks/backup/42-conf_ynh_dyndns deleted file mode 100644 index 6343f9086..000000000 --- a/data/hooks/backup/42-conf_ynh_dyndns +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -source /usr/share/yunohost/helpers -ynh_abort_if_errors -YNH_CWD="${YNH_BACKUP_DIR%/}/conf/ynh/dyndns" -mkdir -p $YNH_CWD -cd "$YNH_CWD" - -# Backup the configuration -ynh_exec_warn_less ynh_backup --src_path="/etc/yunohost/dyndns" --not_mandatory diff --git a/data/hooks/backup/50-conf_manually_modified_files b/data/hooks/backup/50-conf_manually_modified_files new file mode 100644 index 000000000..685fb56a8 --- /dev/null +++ b/data/hooks/backup/50-conf_manually_modified_files @@ -0,0 +1,18 @@ +#!/bin/bash + +source /usr/share/yunohost/helpers +ynh_abort_if_errors +YNH_CWD="${YNH_BACKUP_DIR%/}/conf/manually_modified_files" +mkdir -p "$YNH_CWD" +cd "$YNH_CWD" + +yunohost tools shell -c "from yunohost.regenconf import manually_modified_files; print('\n'.join(manually_modified_files()))" > ./manually_modified_files_list + +ynh_backup --src_path="./manually_modified_files_list" + +for file in $(cat ./manually_modified_files_list) +do + ynh_backup --src_path="$file" +done + +ynh_backup --src_path="/etc/ssowat/conf.json.persistent" diff --git a/data/hooks/restore/05-conf_ldap b/data/hooks/restore/05-conf_ldap index bdc1ebcdf..8dc511695 100644 --- a/data/hooks/restore/05-conf_ldap +++ b/data/hooks/restore/05-conf_ldap @@ -1,61 +1,54 @@ +#!/bin/bash + backup_dir="${1}/conf/ldap" -if [[ $EUID -ne 0 ]]; then +systemctl stop slapd - # We need to execute this script as root, since the ldap - # service will be shut down during the operation (and sudo - # won't be available) - /bin/bash $(readlink -f $0) $1 +# Create a directory for backup +TMPDIR="/tmp/$(date +%s)" +mkdir -p "$TMPDIR" -else +die() { + state=$1 + error=$2 - service slapd stop || true + # Restore saved configuration and database + [[ $state -ge 1 ]] \ + && (rm -rf /etc/ldap/slapd.d && + mv "${TMPDIR}/slapd.d" /etc/ldap/slapd.d) + [[ $state -ge 2 ]] \ + && (rm -rf /var/lib/ldap && + mv "${TMPDIR}/ldap" /var/lib/ldap) + chown -R openldap: /etc/ldap/slapd.d /var/lib/ldap - # Create a directory for backup - TMPDIR="/tmp/$(date +%s)" - mkdir -p "$TMPDIR" - - die() { - state=$1 - error=$2 - - # Restore saved configuration and database - [[ $state -ge 1 ]] \ - && (rm -rf /etc/ldap/slapd.d && - mv "${TMPDIR}/slapd.d" /etc/ldap/slapd.d) - [[ $state -ge 2 ]] \ - && (rm -rf /var/lib/ldap && - mv "${TMPDIR}/ldap" /var/lib/ldap) - chown -R openldap: /etc/ldap/slapd.d /var/lib/ldap - - service slapd start - rm -rf "$TMPDIR" - - # Print an error message and exit - printf "%s" "$error" 1>&2 - exit 1 - } - - # Restore the configuration - mv /etc/ldap/slapd.d "$TMPDIR" - mkdir -p /etc/ldap/slapd.d - cp -a "${backup_dir}/ldap.conf" /etc/ldap/ldap.conf - cp -a "${backup_dir}/slapd.ldif" /etc/ldap/slapd.ldif - # Legacy thing but we need it to force the regen-conf in case of it exist - cp -a "${backup_dir}/slapd.conf" /etc/ldap/slapd.conf - slapadd -F /etc/ldap/slapd.d -b cn=config \ - -l "${backup_dir}/cn=config.master.ldif" \ - || die 1 "Unable to restore LDAP configuration" - chown -R openldap: /etc/ldap/slapd.d - - # Restore the database - mv /var/lib/ldap "$TMPDIR" - mkdir -p /var/lib/ldap - slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \ - -l "${backup_dir}/dc=yunohost-dc=org.ldif" \ - || die 2 "Unable to restore LDAP database" - chown -R openldap: /var/lib/ldap - - service slapd start + systemctl start slapd rm -rf "$TMPDIR" -fi + + # Print an error message and exit + printf "%s" "$error" 1>&2 + exit 1 +} + +# Restore the configuration +mv /etc/ldap/slapd.d "$TMPDIR" +mkdir -p /etc/ldap/slapd.d +cp -a "${backup_dir}/ldap.conf" /etc/ldap/ldap.conf +cp -a "${backup_dir}/slapd.ldif" /etc/ldap/slapd.ldif +# Legacy thing but we need it to force the regen-conf in case of it exist +[ ! -e "${backup_dir}/slapd.conf" ] \ + || cp -a "${backup_dir}/slapd.conf" /etc/ldap/slapd.conf +slapadd -F /etc/ldap/slapd.d -b cn=config \ + -l "${backup_dir}/cn=config.master.ldif" \ + || die 1 "Unable to restore LDAP configuration" +chown -R openldap: /etc/ldap/slapd.d + +# Restore the database +mv /var/lib/ldap "$TMPDIR" +mkdir -p /var/lib/ldap +slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \ + -l "${backup_dir}/dc=yunohost-dc=org.ldif" \ + || die 2 "Unable to restore LDAP database" +chown -R openldap: /var/lib/ldap + +systemctl start slapd +rm -rf "$TMPDIR" diff --git a/data/hooks/restore/08-conf_ssh b/data/hooks/restore/08-conf_ssh deleted file mode 100644 index 4b69d1696..000000000 --- a/data/hooks/restore/08-conf_ssh +++ /dev/null @@ -1,9 +0,0 @@ -backup_dir="$1/conf/ssh" - -if [ -d /etc/ssh/ ]; then - cp -a $backup_dir/. /etc/ssh - service ssh restart -else - echo "SSH is not installed" -fi - diff --git a/data/hooks/restore/14-conf_ssowat b/data/hooks/restore/14-conf_ssowat deleted file mode 100644 index 71a011488..000000000 --- a/data/hooks/restore/14-conf_ssowat +++ /dev/null @@ -1,3 +0,0 @@ -backup_dir="$1/conf/ssowat" - -cp -a $backup_dir/. /etc/ssowat diff --git a/data/hooks/restore/20-conf_ynh_firewall b/data/hooks/restore/20-conf_ynh_firewall deleted file mode 100644 index 1789aed1e..000000000 --- a/data/hooks/restore/20-conf_ynh_firewall +++ /dev/null @@ -1,4 +0,0 @@ -backup_dir="$1/conf/ynh/firewall" - -cp -a $backup_dir/. /etc/yunohost -yunohost firewall reload diff --git a/data/hooks/restore/20-conf_ynh_settings b/data/hooks/restore/20-conf_ynh_settings new file mode 100644 index 000000000..4de29a4aa --- /dev/null +++ b/data/hooks/restore/20-conf_ynh_settings @@ -0,0 +1,7 @@ +backup_dir="$1/conf/ynh" + +cp -a "${backup_dir}/current_host" /etc/yunohost/current_host +cp -a "${backup_dir}/firewall.yml" /etc/yunohost/firewall.yml +[ ! -e "${backup_dir}/settings.json" ] || cp -a "${backup_dir}/settings.json" "/etc/yunohost/settings.json" +[ ! -d "${backup_dir}/dyndns" ] || cp -raT "${backup_dir}/dyndns" "/etc/yunohost/dyndns" +[ ! -d "${backup_dir}/dkim" ] || cp -raT "${backup_dir}/dkim" "/etc/dkim" diff --git a/data/hooks/restore/21-conf_ynh_certs b/data/hooks/restore/21-conf_ynh_certs index 983bfb5a1..a6b45efeb 100644 --- a/data/hooks/restore/21-conf_ynh_certs +++ b/data/hooks/restore/21-conf_ynh_certs @@ -3,5 +3,3 @@ backup_dir="$1/conf/ynh/certs" mkdir -p /etc/yunohost/certs/ cp -a $backup_dir/. /etc/yunohost/certs/ -service nginx reload -service metronome reload diff --git a/data/hooks/restore/22-conf_mail b/data/hooks/restore/22-conf_mail deleted file mode 100644 index 77e0a4d42..000000000 --- a/data/hooks/restore/22-conf_mail +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -backup_dir="$1/conf/dkim" - -cp -a $backup_dir/etc/dkim/. /etc/dkim - -chown -R root:root /etc/dkim -chown _rspamd:root /etc/dkim -chown _rspamd:root /etc/dkim/*.mail.key diff --git a/data/hooks/restore/23-data_mail b/data/hooks/restore/23-data_mail index f9fd6e699..b3946f341 100644 --- a/data/hooks/restore/23-data_mail +++ b/data/hooks/restore/23-data_mail @@ -2,7 +2,3 @@ backup_dir="$1/data/mail" cp -a $backup_dir/. /var/mail/ || echo 'No mail found' chown -R vmail:mail /var/mail/ - -# Restart services to use migrated certs -service postfix restart -service dovecot restart diff --git a/data/hooks/restore/26-conf_xmpp b/data/hooks/restore/26-conf_xmpp deleted file mode 100644 index a300a7268..000000000 --- a/data/hooks/restore/26-conf_xmpp +++ /dev/null @@ -1,7 +0,0 @@ -backup_dir="$1/conf/xmpp" - -cp -a $backup_dir/etc/. /etc/metronome -cp -a $backup_dir/var/. /var/lib/metronome - -# Restart to apply new conf and certs -service metronome restart diff --git a/data/hooks/restore/27-data_xmpp b/data/hooks/restore/27-data_xmpp new file mode 100644 index 000000000..02a4c6703 --- /dev/null +++ b/data/hooks/restore/27-data_xmpp @@ -0,0 +1,4 @@ +backup_dir="$1/data/xmpp" + +cp -a $backup_dir/var_lib_metronome/. /var/lib/metronome +cp -a $backup_dir/var_xmpp-upload/. /var/xmpp-upload diff --git a/data/hooks/restore/29-conf_nginx b/data/hooks/restore/29-conf_nginx deleted file mode 100644 index 7288f52f3..000000000 --- a/data/hooks/restore/29-conf_nginx +++ /dev/null @@ -1,7 +0,0 @@ -backup_dir="$1/conf/nginx" - -# Copy all conf except apps specific conf located in DOMAIN.d -find $backup_dir/ -mindepth 1 -maxdepth 1 -name '*.d' -or -exec cp -a {} /etc/nginx/conf.d/ \; - -# Restart to use new conf and certs -service nginx restart diff --git a/data/hooks/restore/40-conf_ynh_currenthost b/data/hooks/restore/40-conf_ynh_currenthost deleted file mode 100644 index 700e806b4..000000000 --- a/data/hooks/restore/40-conf_ynh_currenthost +++ /dev/null @@ -1,3 +0,0 @@ -backup_dir="$1/conf/ynh" - -cp -a "${backup_dir}/current_host" /etc/yunohost/current_host diff --git a/data/hooks/restore/42-conf_ynh_dyndns b/data/hooks/restore/42-conf_ynh_dyndns deleted file mode 100644 index 8ed4941ef..000000000 --- a/data/hooks/restore/42-conf_ynh_dyndns +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -source /usr/share/yunohost/helpers -ynh_abort_if_errors -YNH_CWD="${YNH_BACKUP_DIR%/}/conf/ynh/dyndns" -cd "$YNH_CWD" - -# Restore file if exists -ynh_restore_file --origin_path="/etc/yunohost/dyndns" --not_mandatory diff --git a/data/hooks/restore/50-conf_manually_modified_files b/data/hooks/restore/50-conf_manually_modified_files new file mode 100644 index 000000000..2d0943043 --- /dev/null +++ b/data/hooks/restore/50-conf_manually_modified_files @@ -0,0 +1,13 @@ +#!/bin/bash + +source /usr/share/yunohost/helpers +ynh_abort_if_errors +YNH_CWD="${YNH_BACKUP_DIR%/}/conf/manually_modified_files" +cd "$YNH_CWD" + +for file in $(cat ./manually_modified_files_list) +do + ynh_restore_file --origin_path="$file" --not_mandatory +done + +ynh_restore_file --origin_path="/etc/ssowat/conf.json.persistent" --not_mandatory diff --git a/data/templates/nginx/plain/yunohost_admin.conf.inc b/data/templates/nginx/plain/yunohost_admin.conf.inc index 26f348dea..326e003ee 100644 --- a/data/templates/nginx/plain/yunohost_admin.conf.inc +++ b/data/templates/nginx/plain/yunohost_admin.conf.inc @@ -6,6 +6,6 @@ location /yunohost/admin/ { default_type text/html; index index.html; - more_set_headers "Content-Security-Policy: upgrade-insecure-requests; default-src 'self'; connect-src 'self' https://raw.githubusercontent.com https://paste.yunohost.org wss://$host; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; object-src 'none';"; + more_set_headers "Content-Security-Policy: upgrade-insecure-requests; default-src 'self'; connect-src 'self' https://raw.githubusercontent.com https://paste.yunohost.org wss://$host; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; object-src 'none'; img-src 'self' data:;"; more_set_headers "Content-Security-Policy-Report-Only:"; } diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index 13781881f..cdf6aaf96 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -54,7 +54,12 @@ smtpd_tls_loglevel=1 # -- TLS for outgoing connections # Use TLS if this is supported by the remote SMTP server, otherwise use plaintext. +{% if relay_port == "465" %} +smtp_tls_wrappermode = yes +smtp_tls_security_level = encrypt +{% else %} smtp_tls_security_level = may +{% endif %} smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache smtp_tls_exclude_ciphers = aNULL, MD5, DES, ADH, RC4, 3DES smtp_tls_mandatory_ciphers= high diff --git a/locales/ca.json b/locales/ca.json index b6888d391..ceaa6791e 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -327,7 +327,7 @@ "regenconf_failed": "No s'ha pogut regenerar la configuració per la/les categoria/es : {categories}", "regenconf_pending_applying": "Aplicació de la configuració pendent per la categoria «{category}»...", "restore_already_installed_app": "Una aplicació amb la ID «{app:s}» ja està instal·lada", - "restore_app_failed": "No s'ha pogut restaurar {app:s}", + "app_restore_failed": "No s'ha pogut restaurar {app:s}: {error:s}", "restore_cleaning_failed": "No s'ha pogut netejar el directori temporal de restauració", "restore_complete": "Restauració completada", "restore_confirm_yunohost_installed": "Esteu segur de voler restaurar un sistema ja instal·lat? [{answers:s}]", diff --git a/locales/de.json b/locales/de.json index 968b8bae3..7e061974f 100644 --- a/locales/de.json +++ b/locales/de.json @@ -96,7 +96,7 @@ "port_already_closed": "Der Port {port:d} wurde bereits für {ip_version:s} Verbindungen geschlossen", "port_already_opened": "Der Port {port:d} wird bereits von {ip_version:s} benutzt", "restore_already_installed_app": "Es ist bereits eine App mit der ID '{app:s}' installiet", - "restore_app_failed": "'{app:s}' konnte nicht wiederhergestellt werden", + "restore_app_failed": "'{app:s}' konnte nicht wiederhergestellt werden: {error:s}", "restore_cleaning_failed": "Das temporäre Dateiverzeichnis für Systemrestaurierung konnte nicht gelöscht werden", "restore_complete": "Vollständig wiederhergestellt", "restore_confirm_yunohost_installed": "Möchtest du die Wiederherstellung wirklich starten? [{answers:s}]", diff --git a/locales/en.json b/locales/en.json index 0058880c3..aef38c693 100644 --- a/locales/en.json +++ b/locales/en.json @@ -44,6 +44,8 @@ "app_requirements_checking": "Checking required packages for {app}...", "app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}", "app_remove_after_failed_install": "Removing the app following the installation failure...", + "app_restore_failed": "Could not restore {app:s}: {error:s}", + "app_restore_script_failed": "An error occured inside the app restore script", "app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?", "app_start_install": "Installing {app}...", "app_start_remove": "Removing {app}...", @@ -524,7 +526,6 @@ "regex_with_only_domain": "You can't use a regex for domain, only for path", "restore_already_installed_app": "An app with the ID '{app:s}' is already installed", "restore_already_installed_apps": "The following apps can't be restored because they are already installed: {apps}", - "restore_app_failed": "Could not restore {app:s}", "restore_backup_too_old": "This backup archive can not be restored because it comes from a too-old YunoHost version.", "restore_cleaning_failed": "Could not clean up the temporary restoration directory", "restore_complete": "Restoration completed", diff --git a/locales/eo.json b/locales/eo.json index 1a27831f2..95030d8fa 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -479,7 +479,7 @@ "service_restarted": "Servo '{service:s}' rekomencis", "pattern_username": "Devas esti minuskulaj literoj kaj minuskloj nur", "extracting": "Eltirante…", - "restore_app_failed": "Ne povis restarigi la programon '{app:s}'", + "app_restore_failed": "Ne povis restarigi la programon '{app:s}': {error:s}", "yunohost_configured": "YunoHost nun estas agordita", "certmanager_self_ca_conf_file_not_found": "Ne povis trovi agorddosieron por mem-subskriba aŭtoritato (dosiero: {file:s})", "log_app_remove": "Forigu la aplikon '{}'", diff --git a/locales/es.json b/locales/es.json index cfcca071f..a93c3f244 100644 --- a/locales/es.json +++ b/locales/es.json @@ -107,7 +107,7 @@ "port_already_closed": "El puerto {port:d} ya está cerrado para las conexiones {ip_version:s}", "port_already_opened": "El puerto {port:d} ya está abierto para las conexiones {ip_version:s}", "restore_already_installed_app": "Una aplicación con el ID «{app:s}» ya está instalada", - "restore_app_failed": "No se pudo restaurar la aplicación «{app:s}»", + "app_restore_failed": "No se pudo restaurar la aplicación «{app:s}»: {error:s}", "restore_cleaning_failed": "No se pudo limpiar el directorio temporal de restauración", "restore_complete": "Restaurada", "restore_confirm_yunohost_installed": "¿Realmente desea restaurar un sistema ya instalado? [{answers:s}]", diff --git a/locales/eu.json b/locales/eu.json index 1891e00a3..539fb9157 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -1,3 +1,3 @@ { "password_too_simple_1": "Pasahitzak gutxienez 8 karaktere izan behar ditu" -} \ No newline at end of file +} diff --git a/locales/fr.json b/locales/fr.json index 56a811bcc..d0405c4fc 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -107,7 +107,7 @@ "port_already_closed": "Le port {port:d} est déjà fermé pour les connexions {ip_version:s}", "port_already_opened": "Le port {port:d} est déjà ouvert pour les connexions {ip_version:s}", "restore_already_installed_app": "Une application est déjà installée avec l’identifiant '{app:s}'", - "restore_app_failed": "Impossible de restaurer '{app:s}'", + "app_restore_failed": "Impossible de restaurer '{app:s}': {error:s}", "restore_cleaning_failed": "Impossible de nettoyer le dossier temporaire de restauration", "restore_complete": "Restauration terminée", "restore_confirm_yunohost_installed": "Voulez-vous vraiment restaurer un système déjà installé ? [{answers:s}]", diff --git a/locales/it.json b/locales/it.json index 45b85d548..22248367a 100644 --- a/locales/it.json +++ b/locales/it.json @@ -120,7 +120,7 @@ "pattern_username": "Caratteri minuscoli alfanumerici o trattini bassi soli", "port_already_closed": "La porta {port:d} è già chiusa per le connessioni {ip_version:s}", "restore_already_installed_app": "Un'applicazione con l'ID '{app:s}' è già installata", - "restore_app_failed": "Impossibile ripristinare l'applicazione '{app:s}'", + "app_restore_failed": "Impossibile ripristinare l'applicazione '{app:s}': {error:s}", "restore_cleaning_failed": "Impossibile pulire la directory temporanea di ripristino", "restore_complete": "Ripristino completo", "restore_confirm_yunohost_installed": "Sei sicuro di volere ripristinare un sistema già installato? {answers:s}", diff --git a/locales/nl.json b/locales/nl.json index 59de95f58..23ad8d0cb 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -54,7 +54,7 @@ "pattern_password": "Wachtwoord moet tenminste 3 karakters lang zijn", "port_already_closed": "Poort {port:d} is al gesloten voor {ip_version:s} verbindingen", "port_already_opened": "Poort {port:d} is al open voor {ip_version:s} verbindingen", - "restore_app_failed": "De app '{app:s}' kon niet worden terug gezet", + "app_restore_failed": "De app '{app:s}' kon niet worden terug gezet: {error:s}", "restore_hook_unavailable": "De herstel-hook '{part:s}' is niet beschikbaar op dit systeem", "service_add_failed": "Kan service '{service:s}' niet toevoegen", "service_already_started": "Service '{service:s}' draait al", diff --git a/locales/oc.json b/locales/oc.json index 7fd423617..3bed9cd5a 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -170,7 +170,7 @@ "port_already_closed": "Lo pòrt {port:d} es ja tampat per las connexions {ip_version:s}", "port_already_opened": "Lo pòrt {port:d} es ja dubèrt per las connexions {ip_version:s}", "restore_already_installed_app": "Una aplicacion es ja installada amb l’id « {app:s} »", - "restore_app_failed": "Impossible de restaurar l’aplicacion « {app:s} »", + "app_restore_failed": "Impossible de restaurar l’aplicacion « {app:s} »: {error:s}", "backup_ask_for_copying_if_needed": "Volètz far una salvagarda en utilizant {size:s} Mo temporàriament ? (Aqueste biais de far es emplegat perque unes fichièrs an pas pogut èsser preparats amb un metòde mai eficaç.)", "yunohost_not_installed": "YunoHost es pas installat o corrèctament installat. Mercés d’executar « yunohost tools postinstall »", "backup_output_directory_forbidden": "Causissètz un repertòri de destinacion deferent. Las salvagardas pòdon pas se realizar dins los repertòris bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index b4f162e17..394def0ba 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -194,7 +194,7 @@ def app_info(app, full=False): ) local_manifest = _get_manifest_of_app(os.path.join(APPS_SETTING_PATH, app)) - permissions = user_permission_list(full=True, absolute_urls=True)["permissions"] + permissions = user_permission_list(full=True, absolute_urls=True, apps=[app])["permissions"] settings = _get_app_settings(app) @@ -229,9 +229,7 @@ def app_info(app, full=False): local_manifest.get("multi_instance", False) ) - ret["permissions"] = { - p: i for p, i in permissions.items() if p.startswith(app + ".") - } + ret["permissions"] = permissions ret["label"] = permissions.get(app + ".main", {}).get("label") if not ret["label"]: @@ -1435,7 +1433,7 @@ def app_setting(app, key, value=None, delete=False): permission_url, ) - permissions = user_permission_list(full=True)["permissions"] + permissions = user_permission_list(full=True, apps=[app])["permissions"] permission_name = "%s.legacy_%s_uris" % (app, key.split("_")[0]) permission = permissions.get(permission_name) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 50bd617b7..5c83f6651 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1392,7 +1392,6 @@ class RestoreManager: self.targets.set_result("apps", app_instance_name, "Warning") return - logger.debug(m18n.n("restore_running_app_script", app=app_instance_name)) try: # Restore app settings app_settings_new_path = os.path.join( @@ -1401,7 +1400,7 @@ class RestoreManager: app_scripts_new_path = os.path.join(app_settings_new_path, "scripts") shutil.copytree(app_settings_in_archive, app_settings_new_path) filesystem.chmod(app_settings_new_path, 0o400, 0o400, True) - filesystem.chown(app_scripts_new_path, "admin", None, True) + filesystem.chown(app_scripts_new_path, "root", None, True) # Copy the app scripts to a writable temporary folder # FIXME : use 'install -Dm555' or something similar to what's done @@ -1409,7 +1408,7 @@ class RestoreManager: tmp_folder_for_app_restore = tempfile.mkdtemp(prefix="restore") copytree(app_scripts_in_archive, tmp_folder_for_app_restore) filesystem.chmod(tmp_folder_for_app_restore, 0o550, 0o550, True) - filesystem.chown(tmp_folder_for_app_restore, "admin", None, True) + filesystem.chown(tmp_folder_for_app_restore, "root", None, True) restore_script = os.path.join(tmp_folder_for_app_restore, "restore") # Restore permissions @@ -1454,81 +1453,111 @@ class RestoreManager: os.remove("%s/permissions.yml" % app_settings_new_path) _tools_migrations_run_before_app_restore(backup_version=self.info["from_yunohost_version"], app_id=app_instance_name) - - # Prepare env. var. to pass to script - env_dict = _make_environment_for_app_script(app_instance_name) - env_dict.update( - { - "YNH_BACKUP_DIR": self.work_dir, - "YNH_BACKUP_CSV": os.path.join(self.work_dir, "backup.csv"), - "YNH_APP_BACKUP_DIR": os.path.join( - self.work_dir, "apps", app_instance_name, "backup" - ), - } - ) - - operation_logger.extra["env"] = env_dict - operation_logger.flush() - - # Execute app restore script - hook_exec( - restore_script, - chdir=app_backup_in_archive, - raise_on_error=True, - env=env_dict, - )[0] except Exception: - msg = m18n.n("restore_app_failed", app=app_instance_name) + import traceback + error = m18n.n("unexpected_error", error="\n" + traceback.format_exc()) + msg = m18n.n("app_restore_failed", app=app_instance_name, error=error) logger.error(msg) operation_logger.error(msg) - if msettings.get("interface") != "api": - dump_app_log_extract_for_debugging(operation_logger) - self.targets.set_result("apps", app_instance_name, "Error") - remove_script = os.path.join(app_scripts_in_archive, "remove") - - # Setup environment for remove script - env_dict_remove = _make_environment_for_app_script(app_instance_name) - - operation_logger = OperationLogger( - "remove_on_failed_restore", - [("app", app_instance_name)], - env=env_dict_remove, - ) - operation_logger.start() - - # Execute remove script - if hook_exec(remove_script, env=env_dict_remove)[0] != 0: - msg = m18n.n("app_not_properly_removed", app=app_instance_name) - logger.warning(msg) - operation_logger.error(msg) - else: - operation_logger.success() - - # Cleaning app directory + # Cleanup shutil.rmtree(app_settings_new_path, ignore_errors=True) + shutil.rmtree(tmp_folder_for_app_restore, ignore_errors=True) - # Remove all permission in LDAP for this app - for permission_name in user_permission_list()["permissions"].keys(): - if permission_name.startswith(app_instance_name + "."): - permission_delete(permission_name, force=True) + return - # TODO Cleaning app hooks - else: - self.targets.set_result("apps", app_instance_name, "Success") - operation_logger.success() + logger.debug(m18n.n("restore_running_app_script", app=app_instance_name)) + + # Prepare env. var. to pass to script + env_dict = _make_environment_for_app_script(app_instance_name) + env_dict.update( + { + "YNH_BACKUP_DIR": self.work_dir, + "YNH_BACKUP_CSV": os.path.join(self.work_dir, "backup.csv"), + "YNH_APP_BACKUP_DIR": os.path.join( + self.work_dir, "apps", app_instance_name, "backup" + ), + } + ) + + operation_logger.extra["env"] = env_dict + operation_logger.flush() + + # Execute the app install script + restore_failed = True + try: + restore_retcode = hook_exec( + restore_script, + chdir=app_backup_in_archive, + env=env_dict, + )[0] + # "Common" app restore failure : the script failed and returned exit code != 0 + restore_failed = True if restore_retcode != 0 else False + if restore_failed: + error = m18n.n("app_restore_script_failed") + logger.error(m18n.n("app_restore_failed", app=app_instance_name, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) + if msettings.get("interface") != "api": + dump_app_log_extract_for_debugging(operation_logger) + # Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception + except (KeyboardInterrupt, EOFError): + error = m18n.n("operation_interrupted") + logger.error(m18n.n("app_restore_failed", app=app_instance_name, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) + # Something wrong happened in Yunohost's code (most probably hook_exec) + except Exception: + import traceback + error = m18n.n("unexpected_error", error="\n" + traceback.format_exc()) + logger.error(m18n.n("app_restore_failed", app=app_instance_name, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) finally: # Cleaning temporary scripts directory shutil.rmtree(tmp_folder_for_app_restore, ignore_errors=True) + if not restore_failed: + self.targets.set_result("apps", app_instance_name, "Success") + operation_logger.success() + else: + + self.targets.set_result("apps", app_instance_name, "Error") + + remove_script = os.path.join(app_scripts_in_archive, "remove") + + # Setup environment for remove script + env_dict_remove = _make_environment_for_app_script(app_instance_name) + + remove_operation_logger = OperationLogger( + "remove_on_failed_restore", + [("app", app_instance_name)], + env=env_dict_remove, + ) + remove_operation_logger.start() + + # Execute remove script + if hook_exec(remove_script, env=env_dict_remove)[0] != 0: + msg = m18n.n("app_not_properly_removed", app=app_instance_name) + logger.warning(msg) + remove_operation_logger.error(msg) + else: + remove_operation_logger.success() + + # Cleaning app directory + shutil.rmtree(app_settings_new_path, ignore_errors=True) + + # Remove all permission in LDAP for this app + for permission_name in user_permission_list()["permissions"].keys(): + if permission_name.startswith(app_instance_name + "."): + permission_delete(permission_name, force=True) + + # TODO Cleaning app hooks + + logger.error(failure_message_with_debug_instructions) # # Backup methods # # - - class BackupMethod(object): """ diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index b2ac3de6d..c7a501b9c 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -260,7 +260,7 @@ def dyndns_update( ok, result = dig(dyn_host, "A") dyn_host_ip = result[0] if ok == "ok" and len(result) else None if not dyn_host_ip: - raise YunohostError("Failed to resolve %s" % dyn_host) + raise YunohostError("Failed to resolve %s" % dyn_host, raw_msg=True) ok, result = dig(domain, rdtype, resolvers=[dyn_host_ip]) if ok == "ok": diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index bc21f1948..af1cea2e3 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -188,18 +188,19 @@ def firewall_list(raw=False, by_ip_version=False, list_forwarded=False): for i in ["ipv4", "ipv6"]: f = firewall[i] # Combine TCP and UDP ports - ports[i] = sorted(set(f["TCP"]) | set(f["UDP"])) + ports[i] = sorted(set(f["TCP"]) | set(f["UDP"]), key=lambda p: int(p.split(':')[0]) if isinstance(p, str) else p) if not by_ip_version: # Combine IPv4 and IPv6 ports - ports = sorted(set(ports["ipv4"]) | set(ports["ipv6"])) + ports = sorted(set(ports["ipv4"]) | set(ports["ipv6"]), key=lambda p: int(p.split(':')[0]) if isinstance(p, str) else p) # Format returned dict ret = {"opened_ports": ports} if list_forwarded: # Combine TCP and UDP forwarded ports ret["forwarded_ports"] = sorted( - set(firewall["uPnP"]["TCP"]) | set(firewall["uPnP"]["UDP"]) + set(firewall["uPnP"]["TCP"]) | set(firewall["uPnP"]["UDP"]), + key=lambda p: int(p.split(':')[0]) if isinstance(p, str) else p ) return ret diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 592e76bb4..9a3eb9fa6 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -414,7 +414,7 @@ class RedactingFormatter(Formatter): # This matches stuff like db_pwd=the_secret or admin_password=other_secret # (the secret part being at least 3 chars to avoid catching some lines like just "db_pwd=") # Some names like "key" or "manifest_key" are ignored, used in helpers like ynh_app_setting_set or ynh_read_manifest - match = re.search(r'(pwd|pass|password|secret\w*|\w+key|token)=(\S{3,})$', record.strip()) + match = re.search(r'(pwd|pass|password|passphrase|secret\w*|\w+key|token)=(\S{3,})$', record.strip()) if match and match.group(2) not in self.data_to_redact and match.group(1) not in ["key", "manifest_key"]: self.data_to_redact.append(match.group(2)) except Exception as e: diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index e0a3c6be8..3ed9590d4 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -46,7 +46,7 @@ SYSTEM_PERMS = ["mail", "xmpp", "sftp", "ssh"] def user_permission_list( - short=False, full=False, ignore_system_perms=False, absolute_urls=False + short=False, full=False, ignore_system_perms=False, absolute_urls=False, apps=[] ): """ List permissions and corresponding accesses @@ -74,7 +74,9 @@ def user_permission_list( ) # Parse / organize information to be outputed - apps = sorted(_installed_apps()) + if apps: + ignore_system_perms = True + apps = apps if apps else sorted(_installed_apps()) apps_base_path = { app: app_setting(app, "domain") + app_setting(app, "path") for app in apps @@ -85,11 +87,14 @@ def user_permission_list( for infos in permissions_infos: name = infos["cn"][0] - if ignore_system_perms and name.split(".")[0] in SYSTEM_PERMS: - continue - app = name.split(".")[0] + if app in SYSTEM_PERMS: + if ignore_system_perms: + continue + elif app not in apps: + continue + perm = {} perm["allowed"] = [ _ldap_path_extract(p, "cn") for p in infos.get("groupPermission", []) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 8af9f7149..30204fa86 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -413,7 +413,7 @@ def test_backup_with_different_output_directory(mocker): # Create the backup with message(mocker, "backup_created"): backup_create( - system=["conf_ssh"], + system=["conf_ynh_settings"], apps=None, output_directory="/opt/test_backup_output_directory", name="backup", @@ -427,7 +427,7 @@ def test_backup_with_different_output_directory(mocker): archives_info = backup_info(archives[0], with_details=True) assert archives_info["apps"] == {} assert len(archives_info["system"].keys()) == 1 - assert "conf_ssh" in archives_info["system"].keys() + assert "conf_ynh_settings" in archives_info["system"].keys() @pytest.mark.clean_opt_dir @@ -436,7 +436,7 @@ def test_backup_using_copy_method(mocker): # Create the backup with message(mocker, "backup_created"): backup_create( - system=["conf_nginx"], + system=["conf_ynh_settings"], apps=None, output_directory="/opt/test_backup_output_directory", methods=["copy"], @@ -467,13 +467,13 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker): def custom_hook_exec(name, *args, **kwargs): if os.path.basename(name).startswith("restore"): monkeypatch.undo() - raise Exception + return (1, None) monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec) assert not _is_installed("wordpress") - with message(mocker, "restore_app_failed", app="wordpress"): + with message(mocker, "app_restore_script_failed"): with raiseYunohostError(mocker, "restore_nothings_done"): backup_restore( system=None, name=backup_list()["archives"][0], apps=["wordpress"] @@ -675,9 +675,9 @@ def test_backup_binds_are_readonly(mocker, monkeypatch): def custom_mount_and_backup(self): self._organize_files() - confssh = os.path.join(self.work_dir, "conf/ssh") + conf = os.path.join(self.work_dir, "conf/ynh/dkim") output = subprocess.check_output( - "touch %s/test 2>&1 || true" % confssh, + "touch %s/test 2>&1 || true" % conf, shell=True, env={"LANG": "en_US.UTF-8"}, ) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 089f2ba0e..aee5bf473 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -862,10 +862,10 @@ def user_group_info(groupname): # -def user_permission_list(short=False, full=False): +def user_permission_list(short=False, full=False, apps=[]): import yunohost.permission - return yunohost.permission.user_permission_list(short, full, absolute_urls=True) + return yunohost.permission.user_permission_list(short, full, absolute_urls=True, apps=apps) def user_permission_update(