From ebb492fd1e61b8ed4a3fa39ab51b905af30ff399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Mon, 30 Dec 2019 14:43:10 +0100 Subject: [PATCH 001/262] Add force option in upgrade script --- data/actionsmap/yunohost.yml | 4 ++++ src/yunohost/app.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 3a4c9db97..af9b6b2e3 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -640,6 +640,10 @@ app: -f: full: --file help: Folder or tarball for upgrade + -F: + full: --force + help: Force the update, even though the app is up to date + action: store_true ### app_change_url() change-url: diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 30d3ab31b..76de6d86b 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -410,7 +410,7 @@ def app_change_url(operation_logger, app, domain, path): hook_callback('post_app_change_url', args=args_list, env=env_dict) -def app_upgrade(app=[], url=None, file=None): +def app_upgrade(app=[], url=None, file=None, force=False): """ Upgrade app From ead80c72f8d136d8f178137b19c9dbfbb57de39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Mon, 30 Dec 2019 14:43:48 +0100 Subject: [PATCH 002/262] Implement upgrade type management and avoid unusefull upgrade --- src/yunohost/app.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 76de6d86b..97a3857a8 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -459,12 +459,39 @@ def app_upgrade(app=[], url=None, file=None, force=False): elif app_dict["upgradable"] == "url_required": logger.warning(m18n.n('custom_app_url_required', app=app_instance_name)) continue - elif app_dict["upgradable"] == "yes": + elif app_dict["upgradable"] == "yes" or force: manifest, extracted_app_folder = _fetch_app_from_git(app_instance_name) else: logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) continue + # Manage upgrade type and avoid any upgrade if there are nothing to do + upgrade_type = "UNKNOWN" + if manifest.get("upgrade_only_if_version_changes", None) is True: + # Get actual_version and new version + app_actual_version = manifest["version"] + app_new_version = app_dict["version"] + + # do only the upgrade if there are a change + if app_actual_version == app_new_version and not force: + logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) + # Save update time + now = int(time.time()) + app_setting(app_instance_name, 'update_time', now) + app_setting(app_instance_name, 'current_revision', manifest.get('remote', {}).get('revision', "?")) + continue + elif app_actual_version == app_new_version: + upgrade_type = "UPGRADE_FORCED" + elif "~ynh" in app_actual_version and "~ynh" in app_new_version: + app_actual_version_upstream, app_actual_version_pkg = app_actual_version.split("~ynh") + app_new_version_upstream, app_new_version_pkg = app_new_version.split("~ynh") + if app_actual_version_upstream == app_new_version_upstream: + upgrade_type = "UPGRADE_PACKAGE" + elif app_actual_version_pkg == app_new_version_pkg: + upgrade_type = "UPGRADE_APP" + else: + upgrade_type = "UPGRADE_FULL" + # Check requirements _check_manifest_requirements(manifest, app_instance_name=app_instance_name) _assert_system_is_sane_for_app(manifest, "pre") @@ -483,6 +510,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) + env_dict["YNH_APP_UPGRADE_TYPE"] = upgrade_type # Start register change on system related_to = [('app', app_instance_name)] From 58cce48195e8711a3dd9ae7a22772dd5793b8307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Sat, 11 Apr 2020 22:52:42 +0200 Subject: [PATCH 003/262] Export old and new version in environnement --- data/helpers.d/utils | 6 ++---- src/yunohost/app.py | 13 ++++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index d449f0c39..9ea9294bc 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -405,8 +405,7 @@ ynh_app_upstream_version () { # Manage arguments with getopts ynh_handle_getopts_args "$@" - manifest="${manifest:-../manifest.json}" - version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") + version_key=$YNH_APP_MANIFEST_VERSION echo "${version_key/~ynh*/}" } @@ -429,8 +428,7 @@ ynh_app_package_version () { # Manage arguments with getopts ynh_handle_getopts_args "$@" - manifest="${manifest:-../manifest.json}" - version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") + version_key=$YNH_APP_MANIFEST_VERSION echo "${version_key/*~ynh/}" } diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 97a3857a8..d380235bf 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -347,6 +347,7 @@ def app_change_url(operation_logger, app, domain, path): env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_INSTANCE_NAME"] = app env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) + env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") env_dict["YNH_APP_OLD_DOMAIN"] = old_domain env_dict["YNH_APP_OLD_PATH"] = old_path @@ -467,10 +468,11 @@ def app_upgrade(app=[], url=None, file=None, force=False): # Manage upgrade type and avoid any upgrade if there are nothing to do upgrade_type = "UNKNOWN" + # Get actual_version and new version + app_new_version = manifest.get("version", "?") + app_actual_version = app_dict.get("version", "?") + if manifest.get("upgrade_only_if_version_changes", None) is True: - # Get actual_version and new version - app_actual_version = manifest["version"] - app_new_version = app_dict["version"] # do only the upgrade if there are a change if app_actual_version == app_new_version and not force: @@ -511,6 +513,8 @@ def app_upgrade(app=[], url=None, file=None, force=False): env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_UPGRADE_TYPE"] = upgrade_type + env_dict["YNH_APP_MANIFEST_VERSION"] = app_new_version + env_dict["YNH_APP_OLD_VERSION"] = app_actual_version # Start register change on system related_to = [('app', app_instance_name)] @@ -723,6 +727,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) + env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") # Start register change on system operation_logger.extra.update({'env': env_dict}) @@ -831,6 +836,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu env_dict_remove["YNH_APP_ID"] = app_id env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) + env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") # Execute remove script operation_logger_remove = OperationLogger('remove_on_failed_install', @@ -1008,6 +1014,7 @@ def app_remove(operation_logger, app): env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_INSTANCE_NAME"] = app env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) + env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") operation_logger.extra.update({'env': env_dict}) operation_logger.flush() From 640a46728078898251808210aec7e2b8c1c17d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Mon, 13 Apr 2020 16:59:32 +0200 Subject: [PATCH 004/262] Add helper ynh_compare_package_version --- data/helpers.d/utils | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 9ea9294bc..2cf9d2a7f 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -479,3 +479,52 @@ ynh_check_app_version_changed () { fi echo $return_value } + +# Compare the old package version and a other version passer as argument. +# This is really useful we we need to do some action only for some old package version. +# +# example: ynh_compare_package_version --comparaison gt --version 2.3.2~ynh1 +# In word this example will check if the installed version is grater than (gt) the version 2.3.2~ynh1 +# +# usage: ynh_compare_package_version --comparaision lt|gt|le|ge +# | arg: --comparaison - comparaison type. Could be : le (lower than), gt (grater than), le (lower or equals), ge (grater or equals) +# | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like 2.3.1~ynh4) +# +# Requires YunoHost version 3.8.0 or higher. +ynh_compare_package_version() { + local legacy_args=cv + declare -Ar args_array=( [c]=comparaison= [v]=version= ) + + local version + local comparaison + local old_version=$YNH_APP_OLD_VERSION + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + if [[ ! $version =~ '~ynh' ]] || [[ ! $old_version =~ '~ynh' ]]; then + ynh_print_warn "Invalid agument for version." + return 1 + fi + + if [ $version == $old_version ]; then + if [ $comparaison == ge ] || [ $comparaison == le ]; then + return 0 + else + return 1 + fi + fi + + if [ $comparaison == ge ] || [ $comparaison == gt ]; then + if [ $(printf "$version\n$old_version" | sort -V | tail -n 1) == $old_version ]; then + return 0 + else + return 1 + fi + else + if [ $(printf "$version\n$old_version" | sort -V | tail -n 1) == $version ]; then + return 0 + else + return 1 + fi + fi +} From 3484f73506e75cb4d44beb2bdb40f6b5509b8cb3 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Tue, 14 Apr 2020 12:35:35 +0200 Subject: [PATCH 005/262] Clean and syntax --- data/helpers.d/utils | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 576fa5a56..c491b1aa0 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -486,48 +486,58 @@ ynh_check_app_version_changed () { echo $return_value } -# Compare the old package version and a other version passer as argument. -# This is really useful we we need to do some action only for some old package version. +# Compare the current package version against another version given as an argument. +# This is really useful when we need to do some actions only for some old package versions. # -# example: ynh_compare_package_version --comparaison gt --version 2.3.2~ynh1 -# In word this example will check if the installed version is grater than (gt) the version 2.3.2~ynh1 +# example: ynh_compare_package_version --comparison gt --version 2.3.2~ynh1 +# This example will check if the installed version is greater than (gt) the version 2.3.2~ynh1 # -# usage: ynh_compare_package_version --comparaision lt|gt|le|ge -# | arg: --comparaison - comparaison type. Could be : le (lower than), gt (grater than), le (lower or equals), ge (grater or equals) +# usage: ynh_compare_package_version --comparison lt|gt|le|ge +# | arg: --comparison - Comparison type. Could be : le (lower than), gt (greater than), le (lower or equal), ge (greater or equal) # | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like 2.3.1~ynh4) # +# Return 0 if the evaluation is true. 1 if false. +# # Requires YunoHost version 3.8.0 or higher. ynh_compare_package_version() { local legacy_args=cv - declare -Ar args_array=( [c]=comparaison= [v]=version= ) - + declare -Ar args_array=( [c]=comparison= [v]=version= ) local version - local comparaison - local old_version=$YNH_APP_OLD_VERSION + local comparison # Manage arguments with getopts ynh_handle_getopts_args "$@" - if [[ ! $version =~ '~ynh' ]] || [[ ! $old_version =~ '~ynh' ]]; then + local current_version=$YNH_APP_OLD_VERSION + + # Check the syntax of the versions + if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]] + then ynh_print_warn "Invalid agument for version." return 1 fi - if [ $version == $old_version ]; then - if [ $comparaison == ge ] || [ $comparaison == le ]; then + # If the version are identical, and the evaluation allows equal versions. + if [ $version == $current_version ] + then + if [ $comparison == ge ] || [ $comparison == le ]; then return 0 else return 1 fi fi - if [ $comparaison == ge ] || [ $comparaison == gt ]; then - if [ $(printf "$version\n$old_version" | sort -V | tail -n 1) == $old_version ]; then + # Check if the current version is greater than the one given as argument + if [ $comparison == ge ] || [ $comparison == gt ] + then + if [ $(printf "$version\n$current_version" | sort --version-sort | tail -n 1) == $current_version ]; then return 0 else return 1 fi + + # Else if the current version is lower than the one given as argument else - if [ $(printf "$version\n$old_version" | sort -V | tail -n 1) == $version ]; then + if [ $(printf "$version\n$current_version" | sort --version-sort | tail -n 1) == $version ]; then return 0 else return 1 From 14ef523585834aff3cfd280020ce5f06f4aeda20 Mon Sep 17 00:00:00 2001 From: Josue-T Date: Tue, 14 Apr 2020 13:45:05 +0200 Subject: [PATCH 006/262] Improve env_dict variable in upgrade Co-Authored-By: Maniack Crudelis --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index ff112ec14..5e4e9abf4 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -514,7 +514,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_UPGRADE_TYPE"] = upgrade_type env_dict["YNH_APP_MANIFEST_VERSION"] = app_new_version - env_dict["YNH_APP_OLD_VERSION"] = app_actual_version + env_dict["YNH_APP_CURRENT_VERSION"] = app_actual_version # Start register change on system related_to = [('app', app_instance_name)] From 3d51e235e89815ce6da247ccaa92cb5ff956e54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Tue, 14 Apr 2020 13:56:19 +0200 Subject: [PATCH 007/262] Add comments --- data/helpers.d/utils | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index c491b1aa0..759d9f1b1 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -492,6 +492,12 @@ ynh_check_app_version_changed () { # example: ynh_compare_package_version --comparison gt --version 2.3.2~ynh1 # This example will check if the installed version is greater than (gt) the version 2.3.2~ynh1 # +# Generally you might probably use it as follow in the upgrade script +# +# if ynh_compare_package_version --comparaison gt --version 2.3.2~ynh1; then +# # Do something that is needed for the package version older than 2.3.2~ynh1 +# fi +# # usage: ynh_compare_package_version --comparison lt|gt|le|ge # | arg: --comparison - Comparison type. Could be : le (lower than), gt (greater than), le (lower or equal), ge (greater or equal) # | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like 2.3.1~ynh4) @@ -519,6 +525,7 @@ ynh_compare_package_version() { # If the version are identical, and the evaluation allows equal versions. if [ $version == $current_version ] then + # manage equals case. if [ $comparison == ge ] || [ $comparison == le ]; then return 0 else From fec5d3d084849980e43d2b5bc49da6d939797879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Tue, 14 Apr 2020 14:01:01 +0200 Subject: [PATCH 008/262] Rename variable --- 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 759d9f1b1..65b6d0d1c 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -513,7 +513,7 @@ ynh_compare_package_version() { # Manage arguments with getopts ynh_handle_getopts_args "$@" - local current_version=$YNH_APP_OLD_VERSION + local current_version=$YNH_APP_CURRENT_VERSION # Check the syntax of the versions if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]] From 17e8bdedf6ade8cfed69cf4ec7895c688c925e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Tue, 14 Apr 2020 14:03:04 +0200 Subject: [PATCH 009/262] Rename heper --- data/helpers.d/utils | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 65b6d0d1c..e1fdc1333 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -489,23 +489,24 @@ ynh_check_app_version_changed () { # Compare the current package version against another version given as an argument. # This is really useful when we need to do some actions only for some old package versions. # -# example: ynh_compare_package_version --comparison gt --version 2.3.2~ynh1 +# example: ynh_compare_current_package_version --comparison gt --version 2.3.2~ynh1 # This example will check if the installed version is greater than (gt) the version 2.3.2~ynh1 # # Generally you might probably use it as follow in the upgrade script # -# if ynh_compare_package_version --comparaison gt --version 2.3.2~ynh1; then +# if ynh_compare_current_package_version --comparaison gt --version 2.3.2~ynh1 +# then # # Do something that is needed for the package version older than 2.3.2~ynh1 # fi # -# usage: ynh_compare_package_version --comparison lt|gt|le|ge +# usage: ynh_compare_current_package_version --comparison lt|gt|le|ge # | arg: --comparison - Comparison type. Could be : le (lower than), gt (greater than), le (lower or equal), ge (greater or equal) # | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like 2.3.1~ynh4) # # Return 0 if the evaluation is true. 1 if false. # # Requires YunoHost version 3.8.0 or higher. -ynh_compare_package_version() { +ynh_compare_current_package_version() { local legacy_args=cv declare -Ar args_array=( [c]=comparison= [v]=version= ) local version From 5315807ea76a8c2a0c23e49e2b7681202fb7f23f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Tue, 14 Apr 2020 14:04:26 +0200 Subject: [PATCH 010/262] Cleanup comment --- data/helpers.d/utils | 1 - 1 file changed, 1 deletion(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index e1fdc1333..f9db320d5 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -526,7 +526,6 @@ ynh_compare_current_package_version() { # If the version are identical, and the evaluation allows equal versions. if [ $version == $current_version ] then - # manage equals case. if [ $comparison == ge ] || [ $comparison == le ]; then return 0 else From 35785888608c370f8dc00c0b664f290df0e9bf6d Mon Sep 17 00:00:00 2001 From: Josue-T Date: Wed, 15 Apr 2020 11:53:39 +0200 Subject: [PATCH 011/262] Replace actual by current Co-Authored-By: Alexandre Aubin --- src/yunohost/app.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5e4e9abf4..baccb3359 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -468,28 +468,28 @@ def app_upgrade(app=[], url=None, file=None, force=False): # Manage upgrade type and avoid any upgrade if there are nothing to do upgrade_type = "UNKNOWN" - # Get actual_version and new version + # Get current_version and new version app_new_version = manifest.get("version", "?") - app_actual_version = app_dict.get("version", "?") + app_current_version = app_dict.get("version", "?") if manifest.get("upgrade_only_if_version_changes", None) is True: # do only the upgrade if there are a change - if app_actual_version == app_new_version and not force: + if app_current_version == app_new_version and not force: logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) # Save update time now = int(time.time()) app_setting(app_instance_name, 'update_time', now) app_setting(app_instance_name, 'current_revision', manifest.get('remote', {}).get('revision', "?")) continue - elif app_actual_version == app_new_version: + elif app_current_version == app_new_version: upgrade_type = "UPGRADE_FORCED" - elif "~ynh" in app_actual_version and "~ynh" in app_new_version: - app_actual_version_upstream, app_actual_version_pkg = app_actual_version.split("~ynh") + elif "~ynh" in app_current_version and "~ynh" in app_new_version: + app_current_version_upstream, app_current_version_pkg = app_current_version.split("~ynh") app_new_version_upstream, app_new_version_pkg = app_new_version.split("~ynh") - if app_actual_version_upstream == app_new_version_upstream: + if app_current_version_upstream == app_new_version_upstream: upgrade_type = "UPGRADE_PACKAGE" - elif app_actual_version_pkg == app_new_version_pkg: + elif app_current_version_pkg == app_new_version_pkg: upgrade_type = "UPGRADE_APP" else: upgrade_type = "UPGRADE_FULL" From f416b94fb8915aa38b8dabfaf19d553783be4e8f Mon Sep 17 00:00:00 2001 From: Josue-T Date: Wed, 15 Apr 2020 11:54:55 +0200 Subject: [PATCH 012/262] Put upgrade_only_if_version_changes in integration section Co-Authored-By: Alexandre Aubin --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index baccb3359..938984167 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -472,7 +472,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): app_new_version = manifest.get("version", "?") app_current_version = app_dict.get("version", "?") - if manifest.get("upgrade_only_if_version_changes", None) is True: + if manifest.get('integration', {}).get("upgrade_only_if_version_changes", None) is True: # do only the upgrade if there are a change if app_current_version == app_new_version and not force: From d947724b70df83f7ec2a6448490ef660286e6748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Wed, 15 Apr 2020 11:57:21 +0200 Subject: [PATCH 013/262] Fix typo --- 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 f9db320d5..68ce6c7f2 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -494,7 +494,7 @@ ynh_check_app_version_changed () { # # Generally you might probably use it as follow in the upgrade script # -# if ynh_compare_current_package_version --comparaison gt --version 2.3.2~ynh1 +# if ynh_compare_current_package_version --comparaison lt --version 2.3.2~ynh1 # then # # Do something that is needed for the package version older than 2.3.2~ynh1 # fi From ceeb34f68edc67d277ac8187ee14d5493af72f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Wed, 15 Apr 2020 12:07:45 +0200 Subject: [PATCH 014/262] Use 'dpkg --compare-versions' --- data/helpers.d/utils | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 68ce6c7f2..29eba2f07 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -499,8 +499,9 @@ ynh_check_app_version_changed () { # # Do something that is needed for the package version older than 2.3.2~ynh1 # fi # -# usage: ynh_compare_current_package_version --comparison lt|gt|le|ge -# | arg: --comparison - Comparison type. Could be : le (lower than), gt (greater than), le (lower or equal), ge (greater or equal) +# usage: ynh_compare_current_package_version --comparison lt|le|eq|ne|ge|gt +# | arg: --comparison - Comparison type. Could be : le (lower than), le (lower or equal), +# | eq (equal), ne (not equal), ge (greater or equal), gt (greater than) # | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like 2.3.1~ynh4) # # Return 0 if the evaluation is true. 1 if false. @@ -519,35 +520,14 @@ ynh_compare_current_package_version() { # Check the syntax of the versions if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]] then - ynh_print_warn "Invalid agument for version." - return 1 + ynh_die "Invalid argument for version." fi - # If the version are identical, and the evaluation allows equal versions. - if [ $version == $current_version ] - then - if [ $comparison == ge ] || [ $comparison == le ]; then - return 0 - else - return 1 - fi + # Check validity of the comparator + if [[ ! $comparison =~ (lt|le|eq|ne|ge|gt) ]]; then + ynh_die "Invialid comparator must be : lt, le, eq, ne, ge, gt" fi - # Check if the current version is greater than the one given as argument - if [ $comparison == ge ] || [ $comparison == gt ] - then - if [ $(printf "$version\n$current_version" | sort --version-sort | tail -n 1) == $current_version ]; then - return 0 - else - return 1 - fi - - # Else if the current version is lower than the one given as argument - else - if [ $(printf "$version\n$current_version" | sort --version-sort | tail -n 1) == $version ]; then - return 0 - else - return 1 - fi - fi + # Return the return value of dpkg --compare-versions + dpkg --compare-versions $current_version $comparison $version } From 8dd3986cace2d771e3ad3e400331b89e6330c96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Wed, 15 Apr 2020 12:17:01 +0200 Subject: [PATCH 015/262] Fix rename variable --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 938984167..57e89e2a7 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -514,7 +514,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_UPGRADE_TYPE"] = upgrade_type env_dict["YNH_APP_MANIFEST_VERSION"] = app_new_version - env_dict["YNH_APP_CURRENT_VERSION"] = app_actual_version + env_dict["YNH_APP_CURRENT_VERSION"] = app_current_version # Start register change on system related_to = [('app', app_instance_name)] From 4f0d5cef964faf62164a1f7d8bca9426ded07314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Wed, 15 Apr 2020 16:25:35 +0200 Subject: [PATCH 016/262] Improve version management in '_app_upgradable' --- src/yunohost/app.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 57e89e2a7..bdfead85c 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -156,10 +156,18 @@ def app_info(app, full=False): def _app_upgradable(app_infos): + from packaging import version # Determine upgradability # In case there is neither update_time nor install_time, we assume the app can/has to be upgraded + # Firstly use the version to know if an upgrade is available + if app_infos["version"] != "-" and app_infos["from_catalog"]["manifest"].get("version", None): + if version.parse(app_infos["version"]) < version.parse(app_infos["from_catalog"]["manifest"].get("version", "-")): + return "yes" + else: + return "no" + if not app_infos.get("from_catalog", None): return "url_required" if not app_infos["from_catalog"].get("lastUpdate") or not app_infos["from_catalog"].get("git"): From a096a36e27c1606bc5cc27664c97a96335229c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Wed, 15 Apr 2020 16:30:11 +0200 Subject: [PATCH 017/262] Also manage downgrade --- src/yunohost/app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index bdfead85c..9305673d7 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -429,6 +429,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): url -- Git url to fetch for upgrade """ + from packaging import version from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.permission import permission_sync_to_user @@ -483,13 +484,15 @@ def app_upgrade(app=[], url=None, file=None, force=False): if manifest.get('integration', {}).get("upgrade_only_if_version_changes", None) is True: # do only the upgrade if there are a change - if app_current_version == app_new_version and not force: + if version.parse(app_current_version) >= version.parse(app_new_version) and not force: logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) # Save update time now = int(time.time()) app_setting(app_instance_name, 'update_time', now) app_setting(app_instance_name, 'current_revision', manifest.get('remote', {}).get('revision', "?")) continue + elif version.parse(app_current_version) > version.parse(app_new_version): + upgrade_type = "DOWNGRADE_FORCED" elif app_current_version == app_new_version: upgrade_type = "UPGRADE_FORCED" elif "~ynh" in app_current_version and "~ynh" in app_new_version: From 9389f4669cd061d6026ef6102ce8190d853488d1 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Wed, 15 Apr 2020 21:13:46 +0200 Subject: [PATCH 018/262] simplification --- data/helpers.d/utils | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 29eba2f07..b6f3e7071 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -489,8 +489,8 @@ ynh_check_app_version_changed () { # Compare the current package version against another version given as an argument. # This is really useful when we need to do some actions only for some old package versions. # -# example: ynh_compare_current_package_version --comparison gt --version 2.3.2~ynh1 -# This example will check if the installed version is greater than (gt) the version 2.3.2~ynh1 +# example: ynh_compare_current_package_version --comparison lt --version 2.3.2~ynh1 +# This example will check if the installed version is lower than (lt) the version 2.3.2~ynh1 # # Generally you might probably use it as follow in the upgrade script # @@ -500,7 +500,7 @@ ynh_check_app_version_changed () { # fi # # usage: ynh_compare_current_package_version --comparison lt|le|eq|ne|ge|gt -# | arg: --comparison - Comparison type. Could be : le (lower than), le (lower or equal), +# | arg: --comparison - Comparison type. Could be : lt (lower than), le (lower or equal), # | eq (equal), ne (not equal), ge (greater or equal), gt (greater than) # | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like 2.3.1~ynh4) # From e7970d8571495e0814914389ae3e6e6f0863b91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Thu, 23 Apr 2020 14:30:37 +0200 Subject: [PATCH 019/262] Check settings 'upgrade_only_if_version_changes' before to check update availability --- src/yunohost/app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 9305673d7..055ee8f29 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -162,7 +162,8 @@ def _app_upgradable(app_infos): # In case there is neither update_time nor install_time, we assume the app can/has to be upgraded # Firstly use the version to know if an upgrade is available - if app_infos["version"] != "-" and app_infos["from_catalog"]["manifest"].get("version", None): + if app_infos["manifest"].get('integration', {}).get("upgrade_only_if_version_changes", None) is True and \ + '~ynh' in app_infos["version"] and app_infos["from_catalog"]["manifest"].get("version", None): if version.parse(app_infos["version"]) < version.parse(app_infos["from_catalog"]["manifest"].get("version", "-")): return "yes" else: From 1826e3c5b66044940317e4296429e0b11b08035a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Thu, 23 Apr 2020 14:31:05 +0200 Subject: [PATCH 020/262] Make more robust version management in upgrade --- src/yunohost/app.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 055ee8f29..74a626597 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -483,28 +483,30 @@ def app_upgrade(app=[], url=None, file=None, force=False): app_current_version = app_dict.get("version", "?") if manifest.get('integration', {}).get("upgrade_only_if_version_changes", None) is True: - - # do only the upgrade if there are a change - if version.parse(app_current_version) >= version.parse(app_new_version) and not force: - logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) - # Save update time - now = int(time.time()) - app_setting(app_instance_name, 'update_time', now) - app_setting(app_instance_name, 'current_revision', manifest.get('remote', {}).get('revision', "?")) - continue - elif version.parse(app_current_version) > version.parse(app_new_version): - upgrade_type = "DOWNGRADE_FORCED" - elif app_current_version == app_new_version: - upgrade_type = "UPGRADE_FORCED" - elif "~ynh" in app_current_version and "~ynh" in app_new_version: - app_current_version_upstream, app_current_version_pkg = app_current_version.split("~ynh") - app_new_version_upstream, app_new_version_pkg = app_new_version.split("~ynh") - if app_current_version_upstream == app_new_version_upstream: - upgrade_type = "UPGRADE_PACKAGE" - elif app_current_version_pkg == app_new_version_pkg: - upgrade_type = "UPGRADE_APP" + if "~ynh" in app_current_version and "~ynh" in app_new_version: + if version.parse(app_current_version) >= version.parse(app_new_version) and not force: + # No new version available + logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) + # Save update time + now = int(time.time()) + app_setting(app_instance_name, 'update_time', now) + app_setting(app_instance_name, 'current_revision', manifest.get('remote', {}).get('revision', "?")) + continue + elif version.parse(app_current_version) > version.parse(app_new_version): + upgrade_type = "DOWNGRADE_FORCED" + elif app_current_version == app_new_version: + upgrade_type = "UPGRADE_FORCED" else: - upgrade_type = "UPGRADE_FULL" + app_current_version_upstream, app_current_version_pkg = app_current_version.split("~ynh") + app_new_version_upstream, app_new_version_pkg = app_new_version.split("~ynh") + if app_current_version_upstream == app_new_version_upstream: + upgrade_type = "UPGRADE_PACKAGE" + elif app_current_version_pkg == app_new_version_pkg: + upgrade_type = "UPGRADE_APP" + else: + upgrade_type = "UPGRADE_FULL" + else: + logger.warning("/!\\ Packagers ! You have enabled the setting 'upgrade_only_if_version_changes' but you haven't used the official way to define the package version") # Check requirements _check_manifest_requirements(manifest, app_instance_name=app_instance_name) From c34de0b792177676939c97a9d66f12e95bec3d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Fri, 24 Apr 2020 14:26:31 +0200 Subject: [PATCH 021/262] Improve version management in catalog --- src/yunohost/app.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 74a626597..5f0084f08 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -162,9 +162,13 @@ def _app_upgradable(app_infos): # In case there is neither update_time nor install_time, we assume the app can/has to be upgraded # Firstly use the version to know if an upgrade is available - if app_infos["manifest"].get('integration', {}).get("upgrade_only_if_version_changes", None) is True and \ - '~ynh' in app_infos["version"] and app_infos["from_catalog"]["manifest"].get("version", None): - if version.parse(app_infos["version"]) < version.parse(app_infos["from_catalog"]["manifest"].get("version", "-")): + app_is_in_catalog = bool(app_infos.get("from_catalog")) + upgrade_only_if_version_changes = app_infos["manifest"].get('integration', {}).get("upgrade_only_if_version_changes", None) is True + installed_version = version.parse(app_infos["version"]) + version_in_catalog = version.parse(app_infos.get("from_catalog", {}).get("manifest", {}).get("version", "0~ynh0")) + + if app_is_in_catalog and '~ynh' in app_infos["version"]: + if upgrade_only_if_version_changes and installed_version < version_in_catalog: return "yes" else: return "no" From ce6c33aa9000e838cf483ad42071461e315eea4f Mon Sep 17 00:00:00 2001 From: Josue-T Date: Mon, 27 Apr 2020 11:05:01 +0200 Subject: [PATCH 022/262] Fix version key in installed version Co-Authored-By: Alexandre Aubin --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5f0084f08..d23877035 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -164,7 +164,7 @@ def _app_upgradable(app_infos): # Firstly use the version to know if an upgrade is available app_is_in_catalog = bool(app_infos.get("from_catalog")) upgrade_only_if_version_changes = app_infos["manifest"].get('integration', {}).get("upgrade_only_if_version_changes", None) is True - installed_version = version.parse(app_infos["version"]) + installed_version = version.parse(app_infos.get("version", "0~ynh0")) version_in_catalog = version.parse(app_infos.get("from_catalog", {}).get("manifest", {}).get("version", "0~ynh0")) if app_is_in_catalog and '~ynh' in app_infos["version"]: From f2791c911f71cb253b1f6e4ea119d8a4e17e6339 Mon Sep 17 00:00:00 2001 From: Josue-T Date: Mon, 27 Apr 2020 11:08:36 +0200 Subject: [PATCH 023/262] Make more rebobut version check Co-Authored-By: Alexandre Aubin --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index d23877035..2d087a0d4 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -167,7 +167,7 @@ def _app_upgradable(app_infos): installed_version = version.parse(app_infos.get("version", "0~ynh0")) version_in_catalog = version.parse(app_infos.get("from_catalog", {}).get("manifest", {}).get("version", "0~ynh0")) - if app_is_in_catalog and '~ynh' in app_infos["version"]: + if app_is_in_catalog and '~ynh' in str(installed_version) and '~ynh' in str(version_in_catalog): if upgrade_only_if_version_changes and installed_version < version_in_catalog: return "yes" else: From 01d5c91e60c7cc08f12058ddd893b587d9d6ccb9 Mon Sep 17 00:00:00 2001 From: Josue-T Date: Mon, 27 Apr 2020 11:09:10 +0200 Subject: [PATCH 024/262] Simply code Co-Authored-By: Alexandre Aubin --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 2d087a0d4..3d17869f5 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -173,7 +173,7 @@ def _app_upgradable(app_infos): else: return "no" - if not app_infos.get("from_catalog", None): + if not app_is_in_catalog: return "url_required" if not app_infos["from_catalog"].get("lastUpdate") or not app_infos["from_catalog"].get("git"): return "url_required" From 72b412c6d3d9a18489b1d288425201a18078112a Mon Sep 17 00:00:00 2001 From: Josue-T Date: Mon, 27 Apr 2020 11:16:40 +0200 Subject: [PATCH 025/262] Cleanup code indentation --- src/yunohost/app.py | 58 +++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 3d17869f5..45cad35b8 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -480,37 +480,39 @@ def app_upgrade(app=[], url=None, file=None, force=False): logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) continue - # Manage upgrade type and avoid any upgrade if there are nothing to do +# Manage upgrade type and avoid any upgrade if there is nothing to do upgrade_type = "UNKNOWN" + upgrade_only_if_version_changes = manifest.get('integration', {}).get("upgrade_only_if_version_changes", None) is True # Get current_version and new version - app_new_version = manifest.get("version", "?") - app_current_version = app_dict.get("version", "?") - - if manifest.get('integration', {}).get("upgrade_only_if_version_changes", None) is True: - if "~ynh" in app_current_version and "~ynh" in app_new_version: - if version.parse(app_current_version) >= version.parse(app_new_version) and not force: - # No new version available - logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) - # Save update time - now = int(time.time()) - app_setting(app_instance_name, 'update_time', now) - app_setting(app_instance_name, 'current_revision', manifest.get('remote', {}).get('revision', "?")) - continue - elif version.parse(app_current_version) > version.parse(app_new_version): - upgrade_type = "DOWNGRADE_FORCED" - elif app_current_version == app_new_version: - upgrade_type = "UPGRADE_FORCED" - else: - app_current_version_upstream, app_current_version_pkg = app_current_version.split("~ynh") - app_new_version_upstream, app_new_version_pkg = app_new_version.split("~ynh") - if app_current_version_upstream == app_new_version_upstream: - upgrade_type = "UPGRADE_PACKAGE" - elif app_current_version_pkg == app_new_version_pkg: - upgrade_type = "UPGRADE_APP" - else: - upgrade_type = "UPGRADE_FULL" + app_new_version = version.parse(manifest.get("version", "?")) + app_current_version = version.parse(app_dict.get("version", "?")) + if "~ynh" not in str(app_current_version) or "~ynh" not in str(app_new_version): + logger.warning("/!\\ Packagers ! You have enabled the setting 'upgrade_only_if_version_changes' but you haven't used the official way to define the package version") + upgrade_only_if_version_changes = False + if upgrade_only_if_version_changes: + if app_current_version >= app_new_version and not force: + # In case of upgrade from file or custom repository + # No new version available + logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) + # Save update time + now = int(time.time()) + app_setting(app_instance_name, 'update_time', now) + app_setting(app_instance_name, 'current_revision', manifest.get('remote', {}).get('revision', "?")) + continue + elif app_current_version > app_new_version: + upgrade_type = "DOWNGRADE_FORCED" + elif app_current_version == app_new_version: + upgrade_type = "UPGRADE_FORCED" else: - logger.warning("/!\\ Packagers ! You have enabled the setting 'upgrade_only_if_version_changes' but you haven't used the official way to define the package version") + app_current_version_upstream, app_current_version_pkg = str(app_current_version).split("~ynh") + app_new_version_upstream, app_new_version_pkg = str(app_new_version).split("~ynh") + if app_current_version_upstream == app_new_version_upstream: + upgrade_type = "UPGRADE_PACKAGE" + elif app_current_version_pkg == app_new_version_pkg: + upgrade_type = "UPGRADE_APP" + else: + upgrade_type = "UPGRADE_FULL" + # Check requirements _check_manifest_requirements(manifest, app_instance_name=app_instance_name) From e01859ffc4b8aa6759adc75ff8920eff59d23026 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 27 Apr 2020 20:57:38 +0200 Subject: [PATCH 026/262] Fix tests --- src/yunohost/app.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 45cad35b8..2ab729a37 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -166,7 +166,7 @@ def _app_upgradable(app_infos): upgrade_only_if_version_changes = app_infos["manifest"].get('integration', {}).get("upgrade_only_if_version_changes", None) is True installed_version = version.parse(app_infos.get("version", "0~ynh0")) version_in_catalog = version.parse(app_infos.get("from_catalog", {}).get("manifest", {}).get("version", "0~ynh0")) - + if app_is_in_catalog and '~ynh' in str(installed_version) and '~ynh' in str(version_in_catalog): if upgrade_only_if_version_changes and installed_version < version_in_catalog: return "yes" @@ -480,7 +480,7 @@ def app_upgrade(app=[], url=None, file=None, force=False): logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) continue -# Manage upgrade type and avoid any upgrade if there is nothing to do + # Manage upgrade type and avoid any upgrade if there is nothing to do upgrade_type = "UNKNOWN" upgrade_only_if_version_changes = manifest.get('integration', {}).get("upgrade_only_if_version_changes", None) is True # Get current_version and new version @@ -533,8 +533,8 @@ def app_upgrade(app=[], url=None, file=None, force=False): env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_UPGRADE_TYPE"] = upgrade_type - env_dict["YNH_APP_MANIFEST_VERSION"] = app_new_version - env_dict["YNH_APP_CURRENT_VERSION"] = app_current_version + env_dict["YNH_APP_MANIFEST_VERSION"] = str(app_new_version) + env_dict["YNH_APP_CURRENT_VERSION"] = str(app_current_version) # Start register change on system related_to = [('app', app_instance_name)] From 5c6b4118b7d41f2fb2b25034eb92fb825d4d255c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Sat, 2 May 2020 11:20:27 +0200 Subject: [PATCH 027/262] Add comment about dependances --- src/yunohost/app.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 2ab729a37..f3af1daa0 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -156,6 +156,9 @@ def app_info(app, full=False): def _app_upgradable(app_infos): + # python-pkg-resources contains the packaging module + # yunohost depends of python-jinja2 and python-jinja2 depends of python-pkg-resources + # so packaging module should be available on all yunohost instances from packaging import version # Determine upgradability @@ -434,6 +437,9 @@ def app_upgrade(app=[], url=None, file=None, force=False): url -- Git url to fetch for upgrade """ + # python-pkg-resources contains the packaging module + # yunohost depends of python-jinja2 and python-jinja2 depends of python-pkg-resources + # so packaging module should be available on all yunohost instances from packaging import version from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.permission import permission_sync_to_user From 8d2bde84ec749770e50575780ee8a979bfb80a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Sun, 3 May 2020 19:56:14 +0200 Subject: [PATCH 028/262] Install python-packaging as dependance --- debian/control | 2 +- src/yunohost/app.py | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/debian/control b/debian/control index 5061ad4f2..db448f405 100644 --- a/debian/control +++ b/debian/control @@ -13,7 +13,7 @@ Architecture: all Depends: ${python:Depends}, ${misc:Depends} , moulinette (>= 3.7), ssowat (>= 3.7) , python-psutil, python-requests, python-dnspython, python-openssl - , python-apt, python-miniupnpc, python-dbus, python-jinja2 + , python-apt, python-miniupnpc, python-dbus, python-jinja2, python-packaging, , python-toml , apt, apt-transport-https , nginx, nginx-extras (>=1.6.2) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 452b4f7c4..edee217ce 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -183,9 +183,6 @@ def app_info(app, full=False): def _app_upgradable(app_infos): - # python-pkg-resources contains the packaging module - # yunohost depends of python-jinja2 and python-jinja2 depends of python-pkg-resources - # so packaging module should be available on all yunohost instances from packaging import version # Determine upgradability @@ -467,9 +464,6 @@ def app_upgrade(app=[], url=None, file=None, force=False): url -- Git url to fetch for upgrade """ - # python-pkg-resources contains the packaging module - # yunohost depends of python-jinja2 and python-jinja2 depends of python-pkg-resources - # so packaging module should be available on all yunohost instances from packaging import version from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.permission import permission_sync_to_user From 64066f85b0ba6da48945a6c842a1c49f84fdd6d3 Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 13 Aug 2019 22:49:01 +0200 Subject: [PATCH 029/262] [enh] Allow admin to specify an smtp relay --- data/hooks/conf_regen/19-postfix | 12 +++++++++++- data/templates/postfix/main.cf | 20 ++++++++++++++++++++ src/yunohost/settings.py | 4 ++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 68afe4bc9..235923b3d 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -23,7 +23,17 @@ do_pre_regen() { # Support different strategy for security configurations export compatibility="$(yunohost settings get 'security.postfix.compatibility')" - + + # Add possibility to specify a relay + # Could be useful with some isp with no 25 port open or more complex setup + export relay_host="$(yunohost settings get 'smtp.relay.host')" + if [ ! -z "${relay_host}" ]; then + export relay_port="$(yunohost settings get 'smtp.relay.port')" + export relay_user="$(yunohost settings get 'smtp.relay.user')" + relay_password="$(yunohost settings get 'smtp.relay.password')" + echo "[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" > /etc/postfix/sasl_passwd + postmap /etc/postfix/sasl_passwd + fi export main_domain export domain_list="$YNH_DOMAINS" ynh_render_template "main.cf" "${postfix_dir}/main.cf" diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index 61cbfa2e6..8121ad3d9 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -72,7 +72,11 @@ alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases mydomain = {{ main_domain }} mydestination = localhost +{% if relay_host == "" %} relayhost = +{% else %} +relayhost = [{{ relay_host }}]:{{ relay_port }} +{% endif %} mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_command = procmail -a "$EXTENSION" mailbox_size_limit = 0 @@ -178,3 +182,19 @@ default_destination_rate_delay = 5s # 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 + +{% if relay_user != "" %} +# Relay email through an other smtp account +# enable SASL authentication +smtp_sasl_auth_enable = yes +# disallow methods that allow anonymous authentication. +smtp_sasl_security_options = noanonymous +# where to find sasl_passwd +smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd +{% if relay_port == "587" %} +# Enable STARTTLS encryption +smtp_use_tls = yes +{% endif %} +# where to find CA certificates +smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt +{% endif %} diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index c1edadb93..f40bb61af 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -71,6 +71,10 @@ DEFAULTS = OrderedDict([ "choices": ["intermediate", "modern"]}), ("pop3.enabled", {"type": "bool", "default": False}), ("smtp.allow_ipv6", {"type": "bool", "default": True}), + ("smtp.relay.host", {"type": "string", "default": ""}), + ("smtp.relay.port", {"type": "int", "default": 587}), + ("smtp.relay.user", {"type": "string", "default": ""}), + ("smtp.relay.password", {"type": "string", "default": ""}), ]) From 3a0104861ed04c554abfc57c4e52c9b7f020fe51 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 29 Apr 2020 00:42:57 +0200 Subject: [PATCH 030/262] [fix] Don't modify directly files in regen conf --- data/hooks/conf_regen/19-postfix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 235923b3d..69790fd39 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -31,8 +31,8 @@ do_pre_regen() { export relay_port="$(yunohost settings get 'smtp.relay.port')" export relay_user="$(yunohost settings get 'smtp.relay.user')" relay_password="$(yunohost settings get 'smtp.relay.password')" - echo "[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" > /etc/postfix/sasl_passwd - postmap /etc/postfix/sasl_passwd + echo "[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" > ${postfix_dir}/sasl_passwd + postmap ${postfix_dir}/sasl_passwd fi export main_domain export domain_list="$YNH_DOMAINS" From fae6b3f3f474c7ba13d9e6f38ea8bb7270ec6ee6 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 29 Apr 2020 00:53:52 +0200 Subject: [PATCH 031/262] [fix] Unrelevant obsolete config params --- data/templates/postfix/main.cf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index 8121ad3d9..b15964241 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -191,10 +191,4 @@ smtp_sasl_auth_enable = yes smtp_sasl_security_options = noanonymous # where to find sasl_passwd smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd -{% if relay_port == "587" %} -# Enable STARTTLS encryption -smtp_use_tls = yes -{% endif %} -# where to find CA certificates -smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt {% endif %} From c1fddb312dec74c1d471279819a5bdc297bc8ea0 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 29 Apr 2020 01:11:25 +0200 Subject: [PATCH 032/262] [enh] Add settings description --- locales/en.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/locales/en.json b/locales/en.json index 25712e8cd..e81505efd 100644 --- a/locales/en.json +++ b/locales/en.json @@ -322,6 +322,10 @@ "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discard 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_setting_smtp_allow_ipv6": "Allow the use of IPv6 to receive and send mail", + "global_settings_setting_smtp_relay_host": "SMTP relay host to use in order to send mail instead of this yunohost instance. Useful if you are in one of this situation: your 25 port is blocked by your ISP or VPS provider, you have a residential IP listed on DUHL, you are not able to configure reverse DNS or this server is not directly exposed on the internet and you want use an other one to send mails.", + "global_settings_setting_smtp_relay_port": "SMTP relay port", + "global_settings_setting_smtp_relay_user": "SMTP relay user account", + "global_settings_setting_smtp_relay_password": "SMTP relay host password", "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it is not a type supported by the system.", "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to use a variation of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).", From 94eb9246bbed517d003c410aa4253da8b1e8ce64 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 29 Apr 2020 03:12:52 +0200 Subject: [PATCH 033/262] [fix] Avoid sasl account reachable from other users --- data/hooks/conf_regen/19-postfix | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 69790fd39..3a8117a61 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -31,8 +31,21 @@ do_pre_regen() { export relay_port="$(yunohost settings get 'smtp.relay.port')" export relay_user="$(yunohost settings get 'smtp.relay.user')" relay_password="$(yunohost settings get 'smtp.relay.password')" - echo "[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" > ${postfix_dir}/sasl_passwd + + # Avoid to display "Relay account paswword" to other users + touch ${postfix_dir}/sasl_passwd + chmod o=--- ${postfix_dir}/sasl_passwd + touch ${postfix_dir}/sasl_passwd.db + chmod o=--- ${postfix_dir}/sasl_passwd.db + # Avoid "postmap: warning: removing zero-length database file" + chown postfix ${pending_dir}/etc/postfix + chown postfix ${pending_dir}/etc/postfix/sasl_passwd + chown postfix ${pending_dir}/etc/postfix/sasl_passwd.db + + cat <<< "[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" > ${postfix_dir}/sasl_passwd postmap ${postfix_dir}/sasl_passwd + + fi export main_domain export domain_list="$YNH_DOMAINS" @@ -57,6 +70,8 @@ do_pre_regen() { do_post_regen() { regen_conf_files=$1 + chmod o=--- /etc/postfix/sasl_passwd + chmod o=--- /etc/postfix/sasl_passwd.db [[ -z "$regen_conf_files" ]] \ || { service postfix restart && service postsrsd restart; } From ff0a2192b9d29b072e00582dcf31062af3a0da70 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 29 Apr 2020 03:13:30 +0200 Subject: [PATCH 034/262] [enh] Automatic regenconf after editing smtp settings --- src/yunohost/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index f40bb61af..3dea458f1 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -326,6 +326,10 @@ def reconfigure_ssh(setting_name, old_value, new_value): service_regen_conf(names=['ssh']) @post_change_hook("smtp.allow_ipv6") +@post_change_hook("smtp.relay.host") +@post_change_hook("smtp.relay.port") +@post_change_hook("smtp.relay.user") +@post_change_hook("smtp.relay.password") @post_change_hook("security.postfix.compatibility") def reconfigure_postfix(setting_name, old_value, new_value): if old_value != new_value: From d51b126df85e7a74884a5dc4ebd9a4d7f9ca8001 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 29 Apr 2020 03:23:30 +0200 Subject: [PATCH 035/262] [fix] postmap: warning: removing zero-length database file --- data/hooks/conf_regen/19-postfix | 3 --- 1 file changed, 3 deletions(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 3a8117a61..1a1b88a25 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -35,12 +35,9 @@ do_pre_regen() { # Avoid to display "Relay account paswword" to other users touch ${postfix_dir}/sasl_passwd chmod o=--- ${postfix_dir}/sasl_passwd - touch ${postfix_dir}/sasl_passwd.db - chmod o=--- ${postfix_dir}/sasl_passwd.db # Avoid "postmap: warning: removing zero-length database file" chown postfix ${pending_dir}/etc/postfix chown postfix ${pending_dir}/etc/postfix/sasl_passwd - chown postfix ${pending_dir}/etc/postfix/sasl_passwd.db cat <<< "[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" > ${postfix_dir}/sasl_passwd postmap ${postfix_dir}/sasl_passwd From a5ecf52c30955ab3c1f8092a1d2f1952eec10131 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 29 Apr 2020 03:38:10 +0200 Subject: [PATCH 036/262] [fix] chown postfix to avoid warning --- data/hooks/conf_regen/19-postfix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 1a1b88a25..67ca22991 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -67,8 +67,8 @@ do_pre_regen() { do_post_regen() { regen_conf_files=$1 - chmod o=--- /etc/postfix/sasl_passwd - chmod o=--- /etc/postfix/sasl_passwd.db + chmod o=--- /etc/postfix/sasl_passwd* + chown postfix /etc/postfix/sasl_passwd* [[ -z "$regen_conf_files" ]] \ || { service postfix restart && service postsrsd restart; } From c7dd8817740a7af4bf2b267eb7fc3d6667775857 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 6 May 2020 21:41:54 +0200 Subject: [PATCH 037/262] Default 'ask' questions for common app manifest args --- locales/en.json | 5 ++++ src/yunohost/app.py | 59 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index 25712e8cd..486bc053c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -27,6 +27,11 @@ "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, '{domain}' is already in use by the other app '{other_app}'", "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps:s}", "app_manifest_invalid": "Something is wrong with the app manifest: {error}", + "app_manifest_install_ask_domain": "Choose the domain where this app should be installed", + "app_manifest_install_ask_path": "Choose the path where this app should be installed", + "app_manifest_install_ask_password": "Choose an administration password for this app", + "app_manifest_install_ask_admin": "Choose an administrator user for this app", + "app_manifest_install_ask_is_public": "Should this app be exposed to anonymous visitors?", "app_not_upgraded": "The app '{failed_app}' failed to upgrade, and as a consequence the following apps' upgrades have been cancelled: {apps}", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_installed": "Could not find the app '{app:s}' in the list of installed apps: {all_apps}", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index e2df6ba78..4a42a0484 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -91,6 +91,8 @@ def app_catalog(full=False, with_categories=False): "description": infos['manifest']['description'], "level": infos["level"], } + else: + infos["manifest"]["arguments"] = _set_default_ask_questions(infos["manifest"]["arguments"]) # Trim info for categories if not using --full for category in catalog["categories"]: @@ -110,7 +112,6 @@ def app_catalog(full=False, with_categories=False): return {"apps": catalog["apps"], "categories": catalog["categories"]} - # Old legacy function... def app_fetchlist(): logger.warning("'yunohost app fetchlist' is deprecated. Please use 'yunohost tools update --apps' instead") @@ -170,6 +171,7 @@ def app_info(app, full=False): return ret ret["manifest"] = local_manifest + ret["manifest"]["arguments"] = _set_default_ask_questions(ret["manifest"]["arguments"]) ret['settings'] = settings absolute_app_name = app if "__" not in app else app[:app.index('__')] # idk this is the name of the app even for multiinstance apps (so wordpress__2 -> wordpress) @@ -2071,12 +2073,63 @@ def _get_manifest_of_app(path): manifest["arguments"]["install"] = install_arguments - return manifest elif os.path.exists(os.path.join(path, "manifest.json")): - return read_json(os.path.join(path, "manifest.json")) + manifest = read_json(os.path.join(path, "manifest.json")) else: raise YunohostError("There doesn't seem to be any manifest file in %s ... It looks like an app was not correctly installed/removed." % path, raw_msg=True) + manifest["arguments"] = _set_default_ask_questions(manifest["arguments"]) + return manifest + + +def _set_default_ask_questions(arguments): + + # arguments is something like + # { "install": [ + # { "name": "domain", + # "type": "domain", + # .... + # }, + # { "name": "path", + # "type": "path" + # ... + # }, + # ... + # ], + # "upgrade": [ ... ] + # } + + # We set a default for any question with these matching (type, name) + # type namei + # N.B. : this is only for install script ... should be reworked for other + # scripts if we supports args for other scripts in the future... + questions_with_default = [("domain", "domain"), + ("path", "path"), + ("password", "password"), + ("user", "admin"), + ("boolean", "is_public")] + + for script_name, arg_list in arguments.items(): + + # We only support questions for install so far, and for other + if script_name != "install": + continue + + for arg in arg_list: + + # Do not override 'ask' field if provided by app ?... Or shall we ? + #if "ask" in arg: + # continue + + # If this arg corresponds to a question with default ask message... + if any((arg.get("type"), arg["name"]) == question for question in questions_with_default): + # The key is for example "app_manifest_install_ask_domain" + key = "app_manifest_%s_ask_%s" % (script_name, arg["name"]) + arg["ask"] = m18n.n(key) + + return arguments + + def _get_git_last_commit_hash(repository, reference='HEAD'): """ From 49c4324ee1af0c02f927afe3d326c0aca961b147 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 6 May 2020 22:17:39 +0200 Subject: [PATCH 038/262] During app installs, set default answer for user-type args to main user --- src/yunohost/app.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index e2df6ba78..289a03bd2 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2439,9 +2439,16 @@ def _parse_args_in_yunohost_format(args, action_args): elif arg_type == 'user': msignals.display(m18n.n('users_available')) - for user in user_list()['users'].keys(): + users = user_list()['users'] + for user in users.keys(): msignals.display("- {}".format(user)) + root_mail = "root@%s" % _get_maindomain() + for user in users.keys(): + if root_mail in user_info(user)["mail-aliases"]: + arg_default = user + ask_string += ' (default: {0})'.format(arg_default) + elif arg_type == 'password': msignals.display(m18n.n('good_practices_about_user_password')) From 882b003bd70718a181347971d1262d6167e25082 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 7 May 2020 00:07:10 +0200 Subject: [PATCH 039/262] Fix i18n string test --- src/yunohost/app.py | 13 ++++++------- tests/test_i18n_keys.py | 4 ++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 4a42a0484..0647e17d4 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2103,11 +2103,11 @@ def _set_default_ask_questions(arguments): # type namei # N.B. : this is only for install script ... should be reworked for other # scripts if we supports args for other scripts in the future... - questions_with_default = [("domain", "domain"), - ("path", "path"), - ("password", "password"), - ("user", "admin"), - ("boolean", "is_public")] + questions_with_default = [("domain", "domain"), # i18n: app_manifest_install_ask_domain + ("path", "path"), # i18n: app_manifest_install_ask_path + ("password", "password"), # i18n: app_manifest_install_ask_password + ("user", "admin"), # i18n: app_manifest_install_ask_admin + ("boolean", "is_public")] # i18n: app_manifest_install_ask_is_public for script_name, arg_list in arguments.items(): @@ -2118,7 +2118,7 @@ def _set_default_ask_questions(arguments): for arg in arg_list: # Do not override 'ask' field if provided by app ?... Or shall we ? - #if "ask" in arg: + # if "ask" in arg: # continue # If this arg corresponds to a question with default ask message... @@ -2130,7 +2130,6 @@ def _set_default_ask_questions(arguments): return arguments - def _get_git_last_commit_hash(repository, reference='HEAD'): """ Attempt to retrieve the last commit hash of a git repository diff --git a/tests/test_i18n_keys.py b/tests/test_i18n_keys.py index 874794e11..799db3de2 100644 --- a/tests/test_i18n_keys.py +++ b/tests/test_i18n_keys.py @@ -23,8 +23,10 @@ def find_expected_string_keys(): # Try to find : # m18n.n( "foo" # YunohostError("foo" + # # i18n: foo p1 = re.compile(r'm18n\.n\(\s*[\"\'](\w+)[\"\']') p2 = re.compile(r'YunohostError\([\'\"](\w+)[\'\"]') + p3 = re.compile(r'# i18n: [\'\"]?(\w+)[\'\"]?') python_files = glob.glob("src/yunohost/*.py") python_files.extend(glob.glob("src/yunohost/utils/*.py")) @@ -42,6 +44,8 @@ def find_expected_string_keys(): if m.endswith("_"): continue yield m + for m in p3.findall(content): + yield m # For each diagnosis, try to find strings like "diagnosis_stuff_foo" (c.f. diagnosis summaries) # Also we expect to have "diagnosis_description_" for each diagnosis From 94d0e253f7ca56b5fb742e18716c5587d14077b1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Apr 2020 20:24:04 +0200 Subject: [PATCH 040/262] Clean usr/bin/yunohost and yunohost-api ... --- bin/yunohost | 100 +++++++++++------------------------------ bin/yunohost-api | 115 +++++++++++------------------------------------ 2 files changed, 52 insertions(+), 163 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index b640c8c52..29a97e016 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -5,36 +5,13 @@ import os import sys import argparse -# Either we are in a development environment or not -IN_DEVEL = False - -# Level for which loggers will log -LOGGERS_LEVEL = 'DEBUG' -TTY_LOG_LEVEL = 'INFO' - -# Handlers that will be used by loggers -# - file: log to the file LOG_DIR/LOG_FILE -# - tty: log to current tty -LOGGERS_HANDLERS = ['file', 'tty'] - -# Directory and file to be used by logging -LOG_DIR = '/var/log/yunohost' -LOG_FILE = 'yunohost-cli.log' - -# Check and load - as needed - development environment -if not __file__.startswith('/usr/'): - IN_DEVEL = True -if IN_DEVEL: - basedir = os.path.abspath('%s/../' % os.path.dirname(__file__)) - if os.path.isdir(os.path.join(basedir, 'moulinette')): - sys.path.insert(0, basedir) - LOG_DIR = os.path.join(basedir, 'log') - - import moulinette from moulinette.actionsmap import ActionsMap from moulinette.interfaces.cli import colorize, get_locale +# Directory and file to be used by logging +LOG_DIR = '/var/log/yunohost' +LOG_FILE = 'yunohost-cli.log' # Initialization & helpers functions ----------------------------------- @@ -46,10 +23,6 @@ def _die(message, title='Error:'): def _parse_cli_args(): """Parse additional arguments for the cli""" parser = argparse.ArgumentParser(add_help=False) - parser.add_argument('--no-cache', - action='store_false', default=True, dest='use_cache', - help="Don't use actions map cache", - ) parser.add_argument('--output-as', choices=['json', 'plain', 'none'], default=None, help="Output result in another format", @@ -90,22 +63,13 @@ def _parse_cli_args(): def _init_moulinette(debug=False, quiet=False): """Configure logging and initialize the moulinette""" - # Define loggers handlers - handlers = set(LOGGERS_HANDLERS) - if quiet and 'tty' in handlers: - handlers.remove('tty') - elif 'tty' not in handlers: - handlers.append('tty') - root_handlers = set(handlers) - if not debug and 'tty' in root_handlers: - root_handlers.remove('tty') - - # Define loggers level - level = LOGGERS_LEVEL - tty_level = TTY_LOG_LEVEL - if debug: - tty_level = 'DEBUG' + # Create log directory + if not os.path.isdir(LOG_DIR): + try: + os.makedirs(LOG_DIR, 0750) + except os.error as e: + _die(str(e)) # Custom logging configuration logging = { @@ -126,7 +90,7 @@ def _init_moulinette(debug=False, quiet=False): }, 'handlers': { 'tty': { - 'level': tty_level, + 'level': 'DEBUG' if debug else 'INFO', 'class': 'moulinette.interfaces.cli.TTYHandler', 'formatter': 'tty-debug' if debug else '', }, @@ -139,45 +103,34 @@ def _init_moulinette(debug=False, quiet=False): }, 'loggers': { 'yunohost': { - 'level': level, - 'handlers': handlers, + 'level': 'DEBUG', + 'handlers': ['file', 'tty'] if not quiet else ['file'], 'propagate': False, }, 'moulinette': { - 'level': level, + 'level': 'DEBUG', 'handlers': [], 'propagate': True, }, 'moulinette.interface': { - 'level': level, - 'handlers': handlers, + 'level': 'DEBUG', + 'handlers': ['file', 'tty'] if not quiet else ['file'], 'propagate': False, }, }, 'root': { - 'level': level, - 'handlers': root_handlers, + 'level': 'DEBUG', + 'handlers': ['file', 'tty'] if debug else ['file'], }, } - # Create log directory - if not os.path.isdir(LOG_DIR): - try: - os.makedirs(LOG_DIR, 0750) - except os.error as e: - _die(str(e)) - # Initialize moulinette - moulinette.init(logging_config=logging, _from_source=IN_DEVEL) + moulinette.init(logging_config=logging) def _retrieve_namespaces(): """Return the list of namespaces to load""" - ret = ['yunohost'] - for n in ActionsMap.get_namespaces(): - # Append YunoHost modules - if n.startswith('ynh_'): - ret.append(n) - return ret + extensions = [n for n in ActionsMap.get_namespaces() if n.startswith('ynh_')] + return ['yunohost'] + extensions # Stupid PATH management because sometimes (e.g. some cron job) PATH is only /usr/bin:/bin ... default_path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" @@ -197,10 +150,9 @@ if __name__ == '__main__': _init_moulinette(opts.debug, opts.quiet) # Check that YunoHost is installed + allowed_if_not_installed = ['tools postinstall', 'backup restore', 'log display'] 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' and \ - args[0] +' '+ args[1] != 'log display')): + (len(args) < 2 or (args[0] +' '+ args[1] not in allowed_if_not_installed)): from moulinette import m18n # Init i18n @@ -212,9 +164,11 @@ if __name__ == '__main__': # Execute the action ret = moulinette.cli( - _retrieve_namespaces(), args, - use_cache=opts.use_cache, output_as=opts.output_as, - password=opts.password, parser_kwargs={'top_parser': parser}, + _retrieve_namespaces(), + args, + output_as=opts.output_as, + password=opts.password, timeout=opts.timeout, + parser_kwargs={'top_parser': parser}, ) sys.exit(ret) diff --git a/bin/yunohost-api b/bin/yunohost-api index e518c34b0..7503d28ad 100755 --- a/bin/yunohost-api +++ b/bin/yunohost-api @@ -5,42 +5,18 @@ import os import sys import argparse -# Either we are in a development environment or not -IN_DEVEL = False +import moulinette +from moulinette.actionsmap import ActionsMap +from moulinette.interfaces.cli import colorize # Default server configuration DEFAULT_HOST = 'localhost' DEFAULT_PORT = 6787 -# Level for which loggers will log -LOGGERS_LEVEL = 'DEBUG' -API_LOGGER_LEVEL = 'INFO' - -# Handlers that will be used by loggers -# - file: log to the file LOG_DIR/LOG_FILE -# - api: serve logs through the api -# - console: log to stderr -LOGGERS_HANDLERS = ['file', 'api'] - # Directory and file to be used by logging LOG_DIR = '/var/log/yunohost' LOG_FILE = 'yunohost-api.log' -# Check and load - as needed - development environment -if not __file__.startswith('/usr/'): - IN_DEVEL = True -if IN_DEVEL: - basedir = os.path.abspath('%s/../' % os.path.dirname(__file__)) - if os.path.isdir(os.path.join(basedir, 'moulinette')): - sys.path.insert(0, basedir) - LOG_DIR = os.path.join(basedir, 'log') - - -import moulinette -from moulinette.actionsmap import ActionsMap -from moulinette.interfaces.cli import colorize - - # Initialization & helpers functions ----------------------------------- def _die(message, title='Error:'): @@ -62,46 +38,26 @@ def _parse_api_args(): action='store', default=DEFAULT_PORT, type=int, help="Port to listen on (default: %d)" % DEFAULT_PORT, ) - srv_group.add_argument('--no-websocket', - action='store_true', default=True, dest='use_websocket', - help="Serve without WebSocket support, used to handle " - "asynchronous responses such as the messages", - ) glob_group = parser.add_argument_group('global arguments') - glob_group.add_argument('--no-cache', - action='store_false', default=True, dest='use_cache', - help="Don't use actions map cache", - ) glob_group.add_argument('--debug', action='store_true', default=False, help="Set log level to DEBUG", ) - glob_group.add_argument('--verbose', - action='store_true', default=False, - help="Be verbose in the output", - ) glob_group.add_argument('--help', action='help', help="Show this help message and exit", ) return parser.parse_args() -def _init_moulinette(use_websocket=True, debug=False, verbose=False): +def _init_moulinette(debug=False): """Configure logging and initialize the moulinette""" - # Define loggers handlers - handlers = set(LOGGERS_HANDLERS) - if not use_websocket and 'api' in handlers: - handlers.remove('api') - if verbose and 'console' not in handlers: - handlers.add('console') - root_handlers = handlers - set(['api']) - # Define loggers level - level = LOGGERS_LEVEL - api_level = API_LOGGER_LEVEL - if debug: - level = 'DEBUG' - api_level = 'DEBUG' + # Create log directory + if not os.path.isdir(LOG_DIR): + try: + os.makedirs(LOG_DIR, 0750) + except os.error as e: + _die(str(e)) # Custom logging configuration logging = { @@ -122,7 +78,7 @@ def _init_moulinette(use_websocket=True, debug=False, verbose=False): }, 'handlers': { 'api': { - 'level': api_level, + 'level': 'DEBUG' if debug else 'INFO', 'class': 'moulinette.interfaces.api.APIQueueHandler', }, 'file': { @@ -140,58 +96,36 @@ def _init_moulinette(use_websocket=True, debug=False, verbose=False): }, 'loggers': { 'yunohost': { - 'level': level, - 'handlers': handlers, + 'level': 'DEBUG', + 'handlers': ['file', 'api'] + ['console'] if debug else [], 'propagate': False, }, 'moulinette': { - 'level': level, + 'level': 'DEBUG', 'handlers': [], 'propagate': True, }, - 'gnupg': { - 'level': 'INFO', - 'handlers': [], - 'propagate': False, - }, }, 'root': { - 'level': level, - 'handlers': root_handlers, + 'level': 'DEBUG', + 'handlers': ['file'] + ['console'] if debug else [], }, } - # Create log directory - if not os.path.isdir(LOG_DIR): - try: - os.makedirs(LOG_DIR, 0750) - except os.error as e: - _die(str(e)) - # Initialize moulinette - moulinette.init(logging_config=logging, _from_source=IN_DEVEL) + moulinette.init(logging_config=logging) def _retrieve_namespaces(): """Return the list of namespaces to load""" - ret = ['yunohost'] - for n in ActionsMap.get_namespaces(): - # Append YunoHost modules - if n.startswith('ynh_'): - ret.append(n) - return ret + extensions = [n for n in ActionsMap.get_namespaces() if n.startswith('ynh_')] + return ['yunohost'] + extensions # Callbacks for additional routes -------------------------------------- def is_installed(): - """ - Check whether YunoHost is installed or not - - """ - installed = False - if os.path.isfile('/etc/yunohost/installed'): - installed = True - return { 'installed': installed } + """ Check whether YunoHost is installed or not """ + return { 'installed': os.path.isfile('/etc/yunohost/installed') } # Main action ---------------------------------------------------------- @@ -203,8 +137,9 @@ if __name__ == '__main__': # Run the server ret = moulinette.api( _retrieve_namespaces(), - host=opts.host, port=opts.port, routes={ - ('GET', '/installed'): is_installed, - }, use_cache=opts.use_cache, use_websocket=opts.use_websocket + host=opts.host, + port=opts.port, + routes={ ('GET', '/installed'): is_installed, }, + use_websocket=True ) sys.exit(ret) From 31e5f7e8b599fb25e27ebdd9b4a4e90f4a1fd62b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 8 Apr 2020 02:58:42 +0200 Subject: [PATCH 041/262] This arg ain't meaningful anymore? --- bin/yunohost | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index 29a97e016..4e0ece1e4 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -39,10 +39,6 @@ def _parse_cli_args(): type=int, default=None, help="Number of seconds before this command will timeout because it can't acquire the lock (meaning that another command is currently running), by default there is no timeout and the command will wait until it can get the lock", ) - parser.add_argument('--admin-password', - default=None, dest='password', metavar='PASSWORD', - help="The admin password to use to authenticate", - ) # deprecated arguments parser.add_argument('--plain', action='store_true', default=False, help=argparse.SUPPRESS @@ -167,7 +163,6 @@ if __name__ == '__main__': _retrieve_namespaces(), args, output_as=opts.output_as, - password=opts.password, timeout=opts.timeout, parser_kwargs={'top_parser': parser}, ) From 68c9244492166c94570fcc06e4c79c75b2d5186b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 22 Apr 2020 04:34:49 +0200 Subject: [PATCH 042/262] Remove _die, simplify creation of log dir, make linter a bit happier --- bin/yunohost | 24 ++++++++++-------------- bin/yunohost-api | 16 ++++------------ 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index 4e0ece1e4..b56666eb4 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -13,12 +13,12 @@ from moulinette.interfaces.cli import colorize, get_locale LOG_DIR = '/var/log/yunohost' LOG_FILE = 'yunohost-cli.log' +# Create log directory +if not os.path.isdir(LOG_DIR): + os.makedirs(LOG_DIR, 0750) + # Initialization & helpers functions ----------------------------------- -def _die(message, title='Error:'): - """Print error message and exit""" - print('%s %s' % (colorize(title, 'red'), message)) - sys.exit(1) def _parse_cli_args(): """Parse additional arguments for the cli""" @@ -57,16 +57,10 @@ def _parse_cli_args(): return (parser, opts, args) + def _init_moulinette(debug=False, quiet=False): """Configure logging and initialize the moulinette""" - # Create log directory - if not os.path.isdir(LOG_DIR): - try: - os.makedirs(LOG_DIR, 0750) - except os.error as e: - _die(str(e)) - # Custom logging configuration logging = { 'version': 1, @@ -123,6 +117,7 @@ def _init_moulinette(debug=False, quiet=False): # Initialize moulinette moulinette.init(logging_config=logging) + def _retrieve_namespaces(): """Return the list of namespaces to load""" extensions = [n for n in ActionsMap.get_namespaces() if n.startswith('ynh_')] @@ -138,7 +133,7 @@ if os.environ["PATH"] != default_path: if __name__ == '__main__': if os.geteuid() != 0: # since moulinette isn't initialized, we can't use m18n here - sys.stderr.write("\033[1;31mError:\033[0m yunohost command must be " \ + sys.stderr.write("\033[1;31mError:\033[0m yunohost command must be " "run as root or with sudo.\n") sys.exit(1) @@ -148,7 +143,7 @@ if __name__ == '__main__': # Check that YunoHost is installed allowed_if_not_installed = ['tools postinstall', 'backup restore', 'log display'] if not os.path.isfile('/etc/yunohost/installed') and \ - (len(args) < 2 or (args[0] +' '+ args[1] not in allowed_if_not_installed)): + (len(args) < 2 or (args[0] + ' ' + args[1] not in allowed_if_not_installed)): from moulinette import m18n # Init i18n @@ -156,7 +151,8 @@ if __name__ == '__main__': m18n.set_locale(get_locale()) # Print error and exit - _die(m18n.n('yunohost_not_installed'), m18n.g('error')) + print(colorize(m18n.g('error'), 'red') + " " + m18n.n('yunohost_not_installed')) + sys.exit(1) # Execute the action ret = moulinette.cli( diff --git a/bin/yunohost-api b/bin/yunohost-api index 7503d28ad..3185738f6 100755 --- a/bin/yunohost-api +++ b/bin/yunohost-api @@ -17,12 +17,11 @@ DEFAULT_PORT = 6787 LOG_DIR = '/var/log/yunohost' LOG_FILE = 'yunohost-api.log' -# Initialization & helpers functions ----------------------------------- +# Create log directory +if not os.path.isdir(LOG_DIR): + os.makedirs(LOG_DIR, 0750) -def _die(message, title='Error:'): - """Print error message and exit""" - print('%s %s' % (colorize(title, 'red'), message)) - sys.exit(1) +# Initialization & helpers functions ----------------------------------- def _parse_api_args(): """Parse main arguments for the api""" @@ -52,13 +51,6 @@ def _parse_api_args(): def _init_moulinette(debug=False): """Configure logging and initialize the moulinette""" - # Create log directory - if not os.path.isdir(LOG_DIR): - try: - os.makedirs(LOG_DIR, 0750) - except os.error as e: - _die(str(e)) - # Custom logging configuration logging = { 'version': 1, From f5c16737ebd44748225e7d17f60faf67c05d97db Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 22 Apr 2020 05:15:08 +0200 Subject: [PATCH 043/262] More stuff to reduce complexity --- bin/yunohost | 7 +++++-- bin/yunohost-api | 22 ++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index b56666eb4..173cbc1cb 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -4,9 +4,9 @@ import os import sys import argparse +import glob import moulinette -from moulinette.actionsmap import ActionsMap from moulinette.interfaces.cli import colorize, get_locale # Directory and file to be used by logging @@ -123,6 +123,7 @@ def _retrieve_namespaces(): extensions = [n for n in ActionsMap.get_namespaces() if n.startswith('ynh_')] return ['yunohost'] + extensions + # Stupid PATH management because sometimes (e.g. some cron job) PATH is only /usr/bin:/bin ... default_path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" if os.environ["PATH"] != default_path: @@ -154,9 +155,11 @@ if __name__ == '__main__': print(colorize(m18n.g('error'), 'red') + " " + m18n.n('yunohost_not_installed')) sys.exit(1) + extensions = [f.split('/')[-1][:-4] for f in glob.glob("/usr/share/moulinette/actionsmap/ynh_*.yml")] + # Execute the action ret = moulinette.cli( - _retrieve_namespaces(), + ['yunohost'] + extensions, args, output_as=opts.output_as, timeout=opts.timeout, diff --git a/bin/yunohost-api b/bin/yunohost-api index 3185738f6..7a2119a08 100755 --- a/bin/yunohost-api +++ b/bin/yunohost-api @@ -4,10 +4,9 @@ import os import sys import argparse +import glob import moulinette -from moulinette.actionsmap import ActionsMap -from moulinette.interfaces.cli import colorize # Default server configuration DEFAULT_HOST = 'localhost' @@ -23,6 +22,7 @@ if not os.path.isdir(LOG_DIR): # Initialization & helpers functions ----------------------------------- + def _parse_api_args(): """Parse main arguments for the api""" parser = argparse.ArgumentParser(add_help=False, @@ -48,6 +48,7 @@ def _parse_api_args(): return parser.parse_args() + def _init_moulinette(debug=False): """Configure logging and initialize the moulinette""" @@ -107,31 +108,28 @@ def _init_moulinette(debug=False): # Initialize moulinette moulinette.init(logging_config=logging) -def _retrieve_namespaces(): - """Return the list of namespaces to load""" - extensions = [n for n in ActionsMap.get_namespaces() if n.startswith('ynh_')] - return ['yunohost'] + extensions - - # Callbacks for additional routes -------------------------------------- + def is_installed(): """ Check whether YunoHost is installed or not """ - return { 'installed': os.path.isfile('/etc/yunohost/installed') } + return {'installed': os.path.isfile('/etc/yunohost/installed')} # Main action ---------------------------------------------------------- if __name__ == '__main__': opts = _parse_api_args() - _init_moulinette(opts.use_websocket, opts.debug, opts.verbose) + _init_moulinette(opts.debug) + + extensions = [f.split('/')[-1][:-4] for f in glob.glob("/usr/share/moulinette/actionsmap/ynh_*.yml")] # Run the server ret = moulinette.api( - _retrieve_namespaces(), + ['yunohost'] + extensions, host=opts.host, port=opts.port, - routes={ ('GET', '/installed'): is_installed, }, + routes={('GET', '/installed'): is_installed}, use_websocket=True ) sys.exit(ret) From 233c962710aec0e98f77936bde476fe662ca062f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 22 Apr 2020 16:49:35 +0200 Subject: [PATCH 044/262] Hmpf idk another iteration to cleaning attempt --- bin/yunohost | 26 +++++++++++--------------- bin/yunohost-api | 23 +++++++++-------------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index 173cbc1cb..8c4b10d8d 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -7,19 +7,13 @@ import argparse import glob import moulinette -from moulinette.interfaces.cli import colorize, get_locale # Directory and file to be used by logging LOG_DIR = '/var/log/yunohost' LOG_FILE = 'yunohost-cli.log' -# Create log directory -if not os.path.isdir(LOG_DIR): - os.makedirs(LOG_DIR, 0750) - # Initialization & helpers functions ----------------------------------- - def _parse_cli_args(): """Parse additional arguments for the cli""" parser = argparse.ArgumentParser(add_help=False) @@ -58,11 +52,13 @@ def _parse_cli_args(): return (parser, opts, args) -def _init_moulinette(debug=False, quiet=False): - """Configure logging and initialize the moulinette""" +def init(debug=False, quiet=False, logfile='%s/%s' % (LOG_DIR, LOG_FILE)): - # Custom logging configuration - logging = { + logdir = os.path.dirname(logfile) + if not os.path.isdir(logdir): + os.makedirs(logdir, 0750) + + moulinette.init(logging_config={ 'version': 1, 'disable_existing_loggers': True, 'formatters': { @@ -87,7 +83,7 @@ def _init_moulinette(debug=False, quiet=False): 'file': { 'class': 'logging.FileHandler', 'formatter': 'precise', - 'filename': '%s/%s' % (LOG_DIR, LOG_FILE), + 'filename': logfile, 'filters': ['action'], }, }, @@ -112,10 +108,8 @@ def _init_moulinette(debug=False, quiet=False): 'level': 'DEBUG', 'handlers': ['file', 'tty'] if debug else ['file'], }, - } + }) - # Initialize moulinette - moulinette.init(logging_config=logging) def _retrieve_namespaces(): @@ -139,7 +133,7 @@ if __name__ == '__main__': sys.exit(1) parser, opts, args = _parse_cli_args() - _init_moulinette(opts.debug, opts.quiet) + init(debug=opts.debug, quiet=opts.quiet) # Check that YunoHost is installed allowed_if_not_installed = ['tools postinstall', 'backup restore', 'log display'] @@ -147,6 +141,8 @@ if __name__ == '__main__': (len(args) < 2 or (args[0] + ' ' + args[1] not in allowed_if_not_installed)): from moulinette import m18n + from moulinette.interfaces.cli import colorize, get_locale + # Init i18n m18n.load_namespace('yunohost') m18n.set_locale(get_locale()) diff --git a/bin/yunohost-api b/bin/yunohost-api index 7a2119a08..0b6961a90 100755 --- a/bin/yunohost-api +++ b/bin/yunohost-api @@ -16,10 +16,6 @@ DEFAULT_PORT = 6787 LOG_DIR = '/var/log/yunohost' LOG_FILE = 'yunohost-api.log' -# Create log directory -if not os.path.isdir(LOG_DIR): - os.makedirs(LOG_DIR, 0750) - # Initialization & helpers functions ----------------------------------- @@ -49,11 +45,13 @@ def _parse_api_args(): return parser.parse_args() -def _init_moulinette(debug=False): - """Configure logging and initialize the moulinette""" +def init_api(debug=False, logfile='%s/%s' % (LOG_DIR, LOG_FILE)): - # Custom logging configuration - logging = { + logdir = os.path.dirname(logfile) + if not os.path.isdir(logdir): + os.makedirs(logdir, 0750) + + moulinette.init(logging_config={ 'version': 1, 'disable_existing_loggers': True, 'formatters': { @@ -77,7 +75,7 @@ def _init_moulinette(debug=False): 'file': { 'class': 'logging.handlers.WatchedFileHandler', 'formatter': 'precise', - 'filename': '%s/%s' % (LOG_DIR, LOG_FILE), + 'filename': logfile, 'filters': ['action'], }, 'console': { @@ -103,10 +101,7 @@ def _init_moulinette(debug=False): 'level': 'DEBUG', 'handlers': ['file'] + ['console'] if debug else [], }, - } - - # Initialize moulinette - moulinette.init(logging_config=logging) + }) # Callbacks for additional routes -------------------------------------- @@ -120,7 +115,7 @@ def is_installed(): if __name__ == '__main__': opts = _parse_api_args() - _init_moulinette(opts.debug) + init_api(opts.debug) extensions = [f.split('/')[-1][:-4] for f in glob.glob("/usr/share/moulinette/actionsmap/ynh_*.yml")] From dee08a16feb5358122bb424f0da3458952f03e85 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 22 Apr 2020 18:41:54 +0200 Subject: [PATCH 045/262] Move moulinette initialization and other stuff to src/yunohost/__init__.py --- bin/yunohost | 113 +++------------------- bin/yunohost-api | 92 +----------------- src/yunohost/__init__.py | 202 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+), 191 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index 8c4b10d8d..546d2d913 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -4,34 +4,29 @@ import os import sys import argparse -import glob -import moulinette +sys.path.insert(0, "/usr/lib/moulinette/") +import yunohost -# Directory and file to be used by logging -LOG_DIR = '/var/log/yunohost' -LOG_FILE = 'yunohost-cli.log' - -# Initialization & helpers functions ----------------------------------- def _parse_cli_args(): """Parse additional arguments for the cli""" parser = argparse.ArgumentParser(add_help=False) parser.add_argument('--output-as', choices=['json', 'plain', 'none'], default=None, - help="Output result in another format", + help="Output result in another format" ) parser.add_argument('--debug', action='store_true', default=False, - help="Log and print debug messages", + help="Log and print debug messages" ) parser.add_argument('--quiet', action='store_true', default=False, - help="Don't produce any output", + help="Don't produce any output" ) parser.add_argument('--timeout', type=int, default=None, - help="Number of seconds before this command will timeout because it can't acquire the lock (meaning that another command is currently running), by default there is no timeout and the command will wait until it can get the lock", + help="Number of seconds before this command will timeout because it can't acquire the lock (meaning that another command is currently running), by default there is no timeout and the command will wait until it can get the lock" ) # deprecated arguments parser.add_argument('--plain', @@ -52,72 +47,6 @@ def _parse_cli_args(): return (parser, opts, args) -def init(debug=False, quiet=False, logfile='%s/%s' % (LOG_DIR, LOG_FILE)): - - logdir = os.path.dirname(logfile) - if not os.path.isdir(logdir): - os.makedirs(logdir, 0750) - - moulinette.init(logging_config={ - 'version': 1, - 'disable_existing_loggers': True, - 'formatters': { - 'tty-debug': { - 'format': '%(relativeCreated)-4d %(fmessage)s' - }, - 'precise': { - 'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s' - }, - }, - 'filters': { - 'action': { - '()': 'moulinette.utils.log.ActionFilter', - }, - }, - 'handlers': { - 'tty': { - 'level': 'DEBUG' if debug else 'INFO', - 'class': 'moulinette.interfaces.cli.TTYHandler', - 'formatter': 'tty-debug' if debug else '', - }, - 'file': { - 'class': 'logging.FileHandler', - 'formatter': 'precise', - 'filename': logfile, - 'filters': ['action'], - }, - }, - 'loggers': { - 'yunohost': { - 'level': 'DEBUG', - 'handlers': ['file', 'tty'] if not quiet else ['file'], - 'propagate': False, - }, - 'moulinette': { - 'level': 'DEBUG', - 'handlers': [], - 'propagate': True, - }, - 'moulinette.interface': { - 'level': 'DEBUG', - 'handlers': ['file', 'tty'] if not quiet else ['file'], - 'propagate': False, - }, - }, - 'root': { - 'level': 'DEBUG', - 'handlers': ['file', 'tty'] if debug else ['file'], - }, - }) - - - -def _retrieve_namespaces(): - """Return the list of namespaces to load""" - extensions = [n for n in ActionsMap.get_namespaces() if n.startswith('ynh_')] - return ['yunohost'] + extensions - - # Stupid PATH management because sometimes (e.g. some cron job) PATH is only /usr/bin:/bin ... default_path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" if os.environ["PATH"] != default_path: @@ -127,38 +56,18 @@ if os.environ["PATH"] != default_path: if __name__ == '__main__': if os.geteuid() != 0: - # since moulinette isn't initialized, we can't use m18n here sys.stderr.write("\033[1;31mError:\033[0m yunohost command must be " "run as root or with sudo.\n") sys.exit(1) parser, opts, args = _parse_cli_args() - init(debug=opts.debug, quiet=opts.quiet) - - # Check that YunoHost is installed - allowed_if_not_installed = ['tools postinstall', 'backup restore', 'log display'] - if not os.path.isfile('/etc/yunohost/installed') and \ - (len(args) < 2 or (args[0] + ' ' + args[1] not in allowed_if_not_installed)): - - from moulinette import m18n - from moulinette.interfaces.cli import colorize, get_locale - - # Init i18n - m18n.load_namespace('yunohost') - m18n.set_locale(get_locale()) - - # Print error and exit - print(colorize(m18n.g('error'), 'red') + " " + m18n.n('yunohost_not_installed')) - sys.exit(1) - - extensions = [f.split('/')[-1][:-4] for f in glob.glob("/usr/share/moulinette/actionsmap/ynh_*.yml")] # Execute the action - ret = moulinette.cli( - ['yunohost'] + extensions, - args, + yunohost.cli( + debug=opts.debug, + quiet=opts.quiet, output_as=opts.output_as, timeout=opts.timeout, - parser_kwargs={'top_parser': parser}, + args=args, + parser=parser ) - sys.exit(ret) diff --git a/bin/yunohost-api b/bin/yunohost-api index 0b6961a90..cc849590a 100755 --- a/bin/yunohost-api +++ b/bin/yunohost-api @@ -1,23 +1,16 @@ #! /usr/bin/python # -*- coding: utf-8 -*- -import os import sys import argparse -import glob -import moulinette +sys.path.insert(0, "/usr/lib/moulinette/") +import yunohost # Default server configuration DEFAULT_HOST = 'localhost' DEFAULT_PORT = 6787 -# Directory and file to be used by logging -LOG_DIR = '/var/log/yunohost' -LOG_FILE = 'yunohost-api.log' - -# Initialization & helpers functions ----------------------------------- - def _parse_api_args(): """Parse main arguments for the api""" @@ -45,86 +38,7 @@ def _parse_api_args(): return parser.parse_args() -def init_api(debug=False, logfile='%s/%s' % (LOG_DIR, LOG_FILE)): - - logdir = os.path.dirname(logfile) - if not os.path.isdir(logdir): - os.makedirs(logdir, 0750) - - moulinette.init(logging_config={ - 'version': 1, - 'disable_existing_loggers': True, - 'formatters': { - 'console': { - 'format': '%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s' - }, - 'precise': { - 'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s' - }, - }, - 'filters': { - 'action': { - '()': 'moulinette.utils.log.ActionFilter', - }, - }, - 'handlers': { - 'api': { - 'level': 'DEBUG' if debug else 'INFO', - 'class': 'moulinette.interfaces.api.APIQueueHandler', - }, - 'file': { - 'class': 'logging.handlers.WatchedFileHandler', - 'formatter': 'precise', - 'filename': logfile, - 'filters': ['action'], - }, - 'console': { - 'class': 'logging.StreamHandler', - 'formatter': 'console', - 'stream': 'ext://sys.stdout', - 'filters': ['action'], - }, - }, - 'loggers': { - 'yunohost': { - 'level': 'DEBUG', - 'handlers': ['file', 'api'] + ['console'] if debug else [], - 'propagate': False, - }, - 'moulinette': { - 'level': 'DEBUG', - 'handlers': [], - 'propagate': True, - }, - }, - 'root': { - 'level': 'DEBUG', - 'handlers': ['file'] + ['console'] if debug else [], - }, - }) - -# Callbacks for additional routes -------------------------------------- - - -def is_installed(): - """ Check whether YunoHost is installed or not """ - return {'installed': os.path.isfile('/etc/yunohost/installed')} - - -# Main action ---------------------------------------------------------- - if __name__ == '__main__': opts = _parse_api_args() - init_api(opts.debug) - - extensions = [f.split('/')[-1][:-4] for f in glob.glob("/usr/share/moulinette/actionsmap/ynh_*.yml")] - # Run the server - ret = moulinette.api( - ['yunohost'] + extensions, - host=opts.host, - port=opts.port, - routes={('GET', '/installed'): is_installed}, - use_websocket=True - ) - sys.exit(ret) + yunohost.api(debug=opts.debug, host=opts.host, port=opts.port) diff --git a/src/yunohost/__init__.py b/src/yunohost/__init__.py index e69de29bb..06e3d773d 100644 --- a/src/yunohost/__init__.py +++ b/src/yunohost/__init__.py @@ -0,0 +1,202 @@ +#! /usr/bin/python +# -*- coding: utf-8 -*- + +import os +import sys +import glob + +import moulinette +from moulinette.utils.log import configure_logging + + +def is_installed(): + return os.path.isfile('/etc/yunohost/installed') + + +def cli(debug, quiet, output_as, timeout, args, parser): + + init_logging(interface="cli", debug=debug, quiet=quiet) + + # Check that YunoHost is installed + if not is_installed(): + check_command_is_valid_before_postinstall(args) + + ret = moulinette.cli( + ['yunohost'] + extensions(), + args, + output_as=output_as, + timeout=timeout, + parser_kwargs={'top_parser': parser}, + ) + sys.exit(ret) + + +def api(debug, host, port): + + init_logging(debug=debug) + + def is_installed_api(): + return {'installed': is_installed()} + + # FIXME : someday, maybe find a way to disable route /postinstall if + # postinstall already done ... + + ret = moulinette.api( + ['yunohost'] + extensions(), + host=host, + port=port, + routes={('GET', '/installed'): is_installed_api}, + use_websocket=True + ) + sys.exit(ret) + + +def extensions(): + # This is probably not used anywhere, but the actionsmap and code can be + # extended by creating such files that contain bits of actionmap... + return [f.split('/')[-1][:-4] for f in glob.glob("/usr/share/moulinette/actionsmap/ynh_*.yml")] + + +def check_command_is_valid_before_postinstall(args): + + allowed_if_not_postinstalled = ['tools postinstall', + 'tools versions', + 'backup list', + 'backup restore', + 'log display'] + + if (len(args) < 2 or (args[0] + ' ' + args[1] not in allowed_if_not_postinstalled)): + + # This function is called before m18n is initialized, so we only initialized + # the specific bit to be able to call m18n.n/g()... + from moulinette import m18n + from moulinette.interfaces.cli import colorize, get_locale + + # Init i18n + m18n.load_namespace('yunohost') + m18n.set_locale(get_locale()) + + print(colorize(m18n.g('error'), 'red') + " " + m18n.n('yunohost_not_installed')) + sys.exit(1) + + +def init_logging(interface="cli", + debug=False, + quiet=False, + logdir="/var/log/yunohost"): + + logfile = os.path.join(logdir, "yunohost-%s.log" % interface) + + if not os.path.isdir(logdir): + os.makedirs(logdir, 0750) + + # ####################################################################### # + # Logging configuration for CLI (or any other interface than api...) # + # ####################################################################### # + if interface != "api": + configure_logging({ + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'tty-debug': { + 'format': '%(relativeCreated)-4d %(fmessage)s' + }, + 'precise': { + 'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s' + }, + }, + 'filters': { + 'action': { + '()': 'moulinette.utils.log.ActionFilter', + }, + }, + 'handlers': { + 'tty': { + 'level': 'DEBUG' if debug else 'INFO', + 'class': 'moulinette.interfaces.cli.TTYHandler', + 'formatter': 'tty-debug' if debug else '', + }, + 'file': { + 'class': 'logging.FileHandler', + 'formatter': 'precise', + 'filename': logfile, + 'filters': ['action'], + }, + }, + 'loggers': { + 'yunohost': { + 'level': 'DEBUG', + 'handlers': ['file', 'tty'] if not quiet else ['file'], + 'propagate': False, + }, + 'moulinette': { + 'level': 'DEBUG', + 'handlers': [], + 'propagate': True, + }, + 'moulinette.interface': { + 'level': 'DEBUG', + 'handlers': ['file', 'tty'] if not quiet else ['file'], + 'propagate': False, + }, + }, + 'root': { + 'level': 'DEBUG', + 'handlers': ['file', 'tty'] if debug else ['file'], + }, + }) + # ####################################################################### # + # Logging configuration for API # + # ####################################################################### # + else: + configure_logging({ + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'console': { + 'format': '%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s' + }, + 'precise': { + 'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s' + }, + }, + 'filters': { + 'action': { + '()': 'moulinette.utils.log.ActionFilter', + }, + }, + 'handlers': { + 'api': { + 'level': 'DEBUG' if debug else 'INFO', + 'class': 'moulinette.interfaces.api.APIQueueHandler', + }, + 'file': { + 'class': 'logging.handlers.WatchedFileHandler', + 'formatter': 'precise', + 'filename': logfile, + 'filters': ['action'], + }, + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'console', + 'stream': 'ext://sys.stdout', + 'filters': ['action'], + }, + }, + 'loggers': { + 'yunohost': { + 'level': 'DEBUG', + 'handlers': ['file', 'api'] + ['console'] if debug else [], + 'propagate': False, + }, + 'moulinette': { + 'level': 'DEBUG', + 'handlers': [], + 'propagate': True, + }, + }, + 'root': { + 'level': 'DEBUG', + 'handlers': ['file'] + ['console'] if debug else [], + }, + }) From 69a520657c13aba8964c659963413cd8a1e6a951 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 22 Apr 2020 18:59:44 +0200 Subject: [PATCH 046/262] Add init_i18n for convenience when using yunohost as a lib... --- src/yunohost/__init__.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/yunohost/__init__.py b/src/yunohost/__init__.py index 06e3d773d..8a23f8e00 100644 --- a/src/yunohost/__init__.py +++ b/src/yunohost/__init__.py @@ -6,8 +6,9 @@ import sys import glob import moulinette +from moulinette import m18n from moulinette.utils.log import configure_logging - +from moulinette.interfaces.cli import colorize, get_locale def is_installed(): return os.path.isfile('/etc/yunohost/installed') @@ -66,20 +67,18 @@ def check_command_is_valid_before_postinstall(args): 'log display'] if (len(args) < 2 or (args[0] + ' ' + args[1] not in allowed_if_not_postinstalled)): - - # This function is called before m18n is initialized, so we only initialized - # the specific bit to be able to call m18n.n/g()... - from moulinette import m18n - from moulinette.interfaces.cli import colorize, get_locale - - # Init i18n - m18n.load_namespace('yunohost') - m18n.set_locale(get_locale()) - + init_i18n() print(colorize(m18n.g('error'), 'red') + " " + m18n.n('yunohost_not_installed')) sys.exit(1) +def init_i18n(): + # This should only be called when not willing to go through moulinette.cli + # or moulinette.api but still willing to call m18n.n/g... + m18n.load_namespace('yunohost') + m18n.set_locale(get_locale()) + + def init_logging(interface="cli", debug=False, quiet=False, From 7df8e8421df39bbed03f1fc7cb53102876a400a5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 28 Apr 2020 04:03:02 +0200 Subject: [PATCH 047/262] Forgot to set interface as api for init logging --- src/yunohost/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/__init__.py b/src/yunohost/__init__.py index 8a23f8e00..e2821f558 100644 --- a/src/yunohost/__init__.py +++ b/src/yunohost/__init__.py @@ -34,7 +34,7 @@ def cli(debug, quiet, output_as, timeout, args, parser): def api(debug, host, port): - init_logging(debug=debug) + init_logging(interface="api", debug=debug) def is_installed_api(): return {'installed': is_installed()} From 84e39a416a69c7974e36cf32af1d4e40e376bf03 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 29 Apr 2020 04:43:04 +0200 Subject: [PATCH 048/262] Add an 'init' helper for scripts/tests to initialize everything needed... --- src/yunohost/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/yunohost/__init__.py b/src/yunohost/__init__.py index e2821f558..3e7ddb496 100644 --- a/src/yunohost/__init__.py +++ b/src/yunohost/__init__.py @@ -72,6 +72,19 @@ def check_command_is_valid_before_postinstall(args): sys.exit(1) +def init(interface="cli", debug=False, quiet=False, logdir="/var/log/yunohost"): + """ + This is a small util function ONLY meant to be used to initialize a Yunohost + context when ran from tests or from scripts. + """ + init_logging(interface=interface, debug=debug, quiet=quiet, logdir=logdir) + init_i18n() + from moulinette.core import MoulinetteLock + lock = MoulinetteLock("yunohost", timeout=30) + lock.acquire() + return lock + + def init_i18n(): # This should only be called when not willing to go through moulinette.cli # or moulinette.api but still willing to call m18n.n/g... From 96e115c6091317d4d77e324e4f40f9e659648e03 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 29 Apr 2020 04:43:29 +0200 Subject: [PATCH 049/262] Use 'init' helper from tests! --- src/yunohost/tests/conftest.py | 67 ++-------------------------------- 1 file changed, 4 insertions(+), 63 deletions(-) diff --git a/src/yunohost/tests/conftest.py b/src/yunohost/tests/conftest.py index 073c880f8..69f8bcfed 100644 --- a/src/yunohost/tests/conftest.py +++ b/src/yunohost/tests/conftest.py @@ -1,12 +1,11 @@ import os import pytest import sys -import moulinette +import moulinette from moulinette import m18n from yunohost.utils.error import YunohostError from contextlib import contextmanager - sys.path.append("..") @@ -68,65 +67,7 @@ moulinette.core.Moulinette18n.n = new_m18nn def pytest_cmdline_main(config): - """Configure logging and initialize the moulinette""" - # Define loggers handlers - handlers = set(['tty']) - root_handlers = set(handlers) - # Define loggers level - level = 'DEBUG' - if config.option.yunodebug: - tty_level = 'DEBUG' - else: - tty_level = 'INFO' - - # Custom logging configuration - logging = { - 'version': 1, - 'disable_existing_loggers': True, - 'formatters': { - 'tty-debug': { - 'format': '%(relativeCreated)-4d %(fmessage)s' - }, - 'precise': { - 'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s' - }, - }, - 'filters': { - 'action': { - '()': 'moulinette.utils.log.ActionFilter', - }, - }, - 'handlers': { - 'tty': { - 'level': tty_level, - 'class': 'moulinette.interfaces.cli.TTYHandler', - 'formatter': '', - }, - }, - 'loggers': { - 'yunohost': { - 'level': level, - 'handlers': handlers, - 'propagate': False, - }, - 'moulinette': { - 'level': level, - 'handlers': [], - 'propagate': True, - }, - 'moulinette.interface': { - 'level': level, - 'handlers': handlers, - 'propagate': False, - }, - }, - 'root': { - 'level': level, - 'handlers': root_handlers, - }, - } - - # Initialize moulinette - moulinette.init(logging_config=logging, _from_source=False) - moulinette.m18n.load_namespace('yunohost') + sys.path.insert(0, "/usr/lib/moulinette/") + import yunohost + yunohost.init(debug=config.option.yunodebug) From d8be90165c2345ec956e24f789a7215f923679dd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 1 May 2020 06:11:53 +0200 Subject: [PATCH 050/262] Propagate changes from moulinette/simplify-interface-init --- src/yunohost/__init__.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/yunohost/__init__.py b/src/yunohost/__init__.py index 3e7ddb496..810d6127a 100644 --- a/src/yunohost/__init__.py +++ b/src/yunohost/__init__.py @@ -3,13 +3,13 @@ import os import sys -import glob import moulinette from moulinette import m18n from moulinette.utils.log import configure_logging from moulinette.interfaces.cli import colorize, get_locale + def is_installed(): return os.path.isfile('/etc/yunohost/installed') @@ -23,11 +23,10 @@ def cli(debug, quiet, output_as, timeout, args, parser): check_command_is_valid_before_postinstall(args) ret = moulinette.cli( - ['yunohost'] + extensions(), args, output_as=output_as, timeout=timeout, - parser_kwargs={'top_parser': parser}, + top_parser=parser ) sys.exit(ret) @@ -43,21 +42,13 @@ def api(debug, host, port): # postinstall already done ... ret = moulinette.api( - ['yunohost'] + extensions(), host=host, port=port, routes={('GET', '/installed'): is_installed_api}, - use_websocket=True ) sys.exit(ret) -def extensions(): - # This is probably not used anywhere, but the actionsmap and code can be - # extended by creating such files that contain bits of actionmap... - return [f.split('/')[-1][:-4] for f in glob.glob("/usr/share/moulinette/actionsmap/ynh_*.yml")] - - def check_command_is_valid_before_postinstall(args): allowed_if_not_postinstalled = ['tools postinstall', From 63ff02be5065efe845d101c29275adf2488e29fc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 24 Apr 2020 01:11:56 +0200 Subject: [PATCH 051/262] Keep track of 'parent' operation in operation loggers --- src/yunohost/log.py | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index de84280f0..20305b2c6 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -28,6 +28,7 @@ import os import re import yaml import collections +import glob from datetime import datetime from logging import FileHandler, getLogger, Formatter @@ -350,6 +351,8 @@ class OperationLogger(object): This class record logs and metadata like context or start time/end time. """ + _instances = [] + def __init__(self, operation, related_to=None, **kwargs): # TODO add a way to not save password on app installation self.operation = operation @@ -360,6 +363,8 @@ class OperationLogger(object): self.logger = None self._name = None self.data_to_redact = [] + self.parent = self.parent_logger() + self._instances.append(self) for filename in ["/etc/yunohost/mysql", "/etc/yunohost/psql"]: if os.path.exists(filename): @@ -370,6 +375,50 @@ class OperationLogger(object): if not os.path.exists(self.path): os.makedirs(self.path) + def parent_logger(self): + + # If there are other operation logger instances + for instance in reversed(self._instances): + # Is one of these operation logger started but not yet done ? + if instance.started_at is not None and instance.ended_at is None: + # We are a child of the first one we found + return instance.name + + locks = read_file("/var/run/moulinette_yunohost.lock").strip().split("\n") + # If we're the process with the lock, we're the root logger + if locks == [] or str(os.getpid()) in locks: + return None + + # If we get here, we are in a yunohost command called by a yunohost + # (maybe indirectly from an app script for example...) + # + # The strategy is : + # 1. list 20 most recent log files + # 2. iterate over the PID of parent processes + # 3. see if parent process has some log file open (being actively + # written in) + # 4. if among those file, there's an operation log file, we use the id + # of the most recent file + + recent_operation_logs = sorted(glob.iglob("/var/log/yunohost/categories/operation/*.log"), key=os.path.getctime, reverse=True)[:20] + + import psutil + proc = psutil.Process().parent() + while proc is not None: + # We use proc.open_files() to list files opened / actively used by this proc + # We only keep files matching a recent yunohost operation log + active_logs = sorted([f.path for f in proc.open_files() if f.path in recent_operation_logs], key=os.path.getctime, reverse=True) + if active_logs != []: + # extra the log if from the full path + return os.path.basename(active_logs[0])[:-4] + else: + proc = proc.parent() + continue + + # If nothing found, assume we're the root operation logger + return None + + def start(self): """ Start to record logs that change the system @@ -456,6 +505,7 @@ class OperationLogger(object): data = { 'started_at': self.started_at, 'operation': self.operation, + 'parent': self.parent, } if self.related_to is not None: data['related_to'] = self.related_to @@ -491,8 +541,10 @@ class OperationLogger(object): self.ended_at = datetime.utcnow() self._error = error self._success = error is None + if self.logger is not None: self.logger.removeHandler(self.file_handler) + self.file_handler.close() is_api = msettings.get('interface') == 'api' desc = _get_description_from_name(self.name) From 05aa54ac0f2f055aa5401a0d11d8069ce960a894 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Tue, 26 May 2020 06:03:49 +0200 Subject: [PATCH 052/262] [mod] refactor the whole argument parsing flow into a component oriented way --- src/yunohost/app.py | 293 +++++++++++------- .../tests/test_apps_arguments_parsing.py | 7 +- 2 files changed, 186 insertions(+), 114 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 0b16123cb..027a3a558 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2391,6 +2391,184 @@ def _parse_args_for_action(action, args={}): return _parse_args_in_yunohost_format(args, action_args) +class Question: + "empty class to store questions information" + + +class YunoHostArgumentFormatParser(object): + hide_user_input_in_prompt = False + + def parse_question(self, question, user_answers): + parsed_question = Question() + + parsed_question.name = question['name'] + parsed_question.default = question.get('default', None) + parsed_question.choices = question.get('choices', []) + parsed_question.optional = question.get('optional', False) + parsed_question.ask = question.get('ask') + parsed_question.value = user_answers.get(parsed_question.name) + + if parsed_question.ask is None: + parsed_question.ask = "Enter value for '%s':" % parsed_question.name + + return parsed_question + + def parse(self, question, user_answers): + question = self.parse_question(question, user_answers) + + if question.value is None: + text_for_user_input_in_cli = self._format_text_for_user_input_in_cli(question) + + try: + question.value = msignals.prompt(text_for_user_input_in_cli, self.hide_user_input_in_prompt) + except NotImplementedError: + question.value = None + + # we don't have an answer, check optional and default_value + if question.value is None: + if not question.optional and question.default is None: + raise YunohostError('app_argument_required', name=question.name) + else: + question.value = self.default_value if question.default is None else question.default + + # we have an answer, do some post checks + if question.value is not None: + if question.choices and question.value not in question.choices: + raise YunohostError('app_argument_choice_invalid', name=question.name, + choices=', '.join(question.choices)) + + # this is done to enforce a certain formating like for boolean + # by default it doesn't do anything + question.value = self._post_parse_value(question) + + return (question.value, self.argument_type) + + def _format_text_for_user_input_in_cli(self, question): + text_for_user_input_in_cli = _value_for_locale(question.ask) + + if question.default is not None: + text_for_user_input_in_cli += ' (default: {0})'.format(question.default) + + if question.choices: + text_for_user_input_in_cli += ' [{0}]'.format(' | '.join(question.choices)) + + return text_for_user_input_in_cli + + def _post_parse_value(self, question): + return question.value + + +class StringArgumentParser(YunoHostArgumentFormatParser): + argument_type = "string" + default_value = "" + + +class PasswordArgumentParser(YunoHostArgumentFormatParser): + hide_user_input_in_prompt = True + argument_type = "password" + default_value = "" + + +class PathArgumentParser(YunoHostArgumentFormatParser): + argument_type = "path" + default_value = "" + + +class BooleanArgumentParser(YunoHostArgumentFormatParser): + argument_type = "boolean" + default_value = False + + def parse_question(self, question, user_answers): + question = super(BooleanArgumentParser, self).parse_question(question, user_answers) + + if question.default is None: + question.default = False + + return question + + def _format_text_for_user_input_in_cli(self, question): + text_for_user_input_in_cli = _value_for_locale(question.ask) + + text_for_user_input_in_cli += " [yes | no]" + + if question.default is not None: + formatted_default = "yes" if question.default else "no" + text_for_user_input_in_cli += ' (default: {0})'.format(formatted_default) + + return text_for_user_input_in_cli + + def _post_parse_value(self, question): + if isinstance(question.value, bool): + return 1 if question.value else 0 + + if str(question.value).lower() in ["1", "yes", "y"]: + return 1 + + if str(question.value).lower() in ["0", "no", "n"]: + return 0 + + raise YunohostError('app_argument_choice_invalid', name=question.name, + choices='yes, no, y, n, 1, 0') + + +class DomainArgumentParser(YunoHostArgumentFormatParser): + argument_type = "domain" + + def parse_question(self, question, user_answers): + from yunohost.domain import domain_list, _get_maindomain + + question = super(DomainArgumentParser, self).parse_question(question, user_answers) + + if question.default is None: + question.default = _get_maindomain() + + question.choices = domain_list()["domains"] + + return question + + +class UserArgumentParser(YunoHostArgumentFormatParser): + argument_type = "user" + + def parse_question(self, question, user_answers): + from yunohost.user import user_list + + question = super(UserArgumentParser, self).parse_question(question, user_answers) + question.choices = user_list()["users"] + + return question + + +class AppArgumentParser(YunoHostArgumentFormatParser): + argument_type = "app" + + def parse_question(self, question, user_answers): + from yunohost.app import app_list + + question = super(AppArgumentParser, self).parse_question(question, user_answers) + question.choices = [x["id"] for x in app_list()["apps"]] + + return question + + +class DisplayTextArgumentParser(YunoHostArgumentFormatParser): + + def parse(self, question, user_answers): + print(question["ask"]) + + +ARGUMENTS_TYPE_PARSERS = { + "string": StringArgumentParser, + "password": PasswordArgumentParser, + "path": PathArgumentParser, + "boolean": BooleanArgumentParser, + "domain": DomainArgumentParser, + "user": UserArgumentParser, + "app": AppArgumentParser, + "display_text": DisplayTextArgumentParser, +} + + def _parse_args_in_yunohost_format(user_answers, argument_questions): """Parse arguments store in either manifest.json or actions.json or from a config panel against the user answers when they are present. @@ -2402,121 +2580,14 @@ def _parse_args_in_yunohost_format(user_answers, argument_questions): format from actions.json/toml, manifest.json/toml or config_panel.json/toml """ - from yunohost.domain import domain_list, _get_maindomain - from yunohost.user import user_list - parsed_answers_dict = OrderedDict() for question in argument_questions: - question_name = question['name'] - question_type = question.get('type', 'string') - question_default = question.get('default', None) - question_choices = question.get('choices', []) - question_value = None + parser = ARGUMENTS_TYPE_PARSERS[question.get("type", "string")]() - # Transpose default value for boolean type and set it to - # false if not defined. - if question_type == 'boolean': - question_default = 1 if question_default else 0 - - # do not print for webadmin - if question_type == 'display_text' and msettings.get('interface') != 'api': - print(_value_for_locale(question['ask'])) - continue - - # Attempt to retrieve argument value - if question_name in user_answers: - question_value = user_answers[question_name] - else: - if 'ask' in question: - # Retrieve proper ask string - text_for_user_input_in_cli = _value_for_locale(question['ask']) - - # Append extra strings - if question_type == 'boolean': - text_for_user_input_in_cli += ' [yes | no]' - elif question_choices: - text_for_user_input_in_cli += ' [{0}]'.format(' | '.join(question_choices)) - - if question_default is not None: - if question_type == 'boolean': - text_for_user_input_in_cli += ' (default: {0})'.format("yes" if question_default == 1 else "no") - else: - text_for_user_input_in_cli += ' (default: {0})'.format(question_default) - - # Check for a password argument - is_password = True if question_type == 'password' else False - - if question_type == 'domain': - question_default = _get_maindomain() - text_for_user_input_in_cli += ' (default: {0})'.format(question_default) - msignals.display(m18n.n('domains_available')) - for domain in domain_list()['domains']: - msignals.display("- {}".format(domain)) - - elif question_type == 'user': - msignals.display(m18n.n('users_available')) - for user in user_list()['users'].keys(): - msignals.display("- {}".format(user)) - - elif question_type == 'password': - msignals.display(m18n.n('good_practices_about_user_password')) - - try: - input_string = msignals.prompt(text_for_user_input_in_cli, is_password) - except NotImplementedError: - input_string = None - if (input_string == '' or input_string is None) \ - and question_default is not None: - question_value = question_default - else: - question_value = input_string - elif question_default is not None: - question_value = question_default - - # If the value is empty (none or '') - # then check if question is optional or not - if question_value is None or question_value == '': - if question.get("optional", False): - # Argument is optional, keep an empty value - # and that's all for this question! - parsed_answers_dict[question_name] = ('', question_type) - continue - else: - # The argument is required ! - raise YunohostError('app_argument_required', name=question_name) - - # Validate argument choice - if question_choices and question_value not in question_choices: - raise YunohostError('app_argument_choice_invalid', name=question_name, choices=', '.join(question_choices)) - - # Validate argument type - if question_type == 'domain': - if question_value not in domain_list()['domains']: - raise YunohostError('app_argument_invalid', name=question_name, error=m18n.n('domain_unknown')) - elif question_type == 'user': - if question_value not in user_list()["users"].keys(): - raise YunohostError('app_argument_invalid', name=question_name, error=m18n.n('user_unknown', user=question_value)) - elif question_type == 'app': - if not _is_installed(question_value): - raise YunohostError('app_argument_invalid', name=question_name, error=m18n.n('app_unknown')) - elif question_type == 'boolean': - if isinstance(question_value, bool): - question_value = 1 if question_value else 0 - else: - if str(question_value).lower() in ["1", "yes", "y"]: - question_value = 1 - elif str(question_value).lower() in ["0", "no", "n"]: - question_value = 0 - else: - raise YunohostError('app_argument_choice_invalid', name=question_name, choices='yes, no, y, n, 1, 0') - elif question_type == 'password': - forbidden_chars = "{}" - if any(char in question_value for char in forbidden_chars): - raise YunohostError('pattern_password_app', forbidden_chars=forbidden_chars) - from yunohost.utils.password import assert_password_is_strong_enough - assert_password_is_strong_enough('user', question_value) - parsed_answers_dict[question_name] = (question_value, question_type) + answer = parser.parse(question=question, user_answers=user_answers) + if answer is not None: + parsed_answers_dict[question["name"]] = answer return parsed_answers_dict diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index a3d5b7f09..c127cc414 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -705,15 +705,16 @@ def test_parse_args_in_yunohost_format_boolean_input_test_ask_with_default(): def test_parse_args_in_yunohost_format_domain_empty(): questions = [{"name": "some_domain", "type": "domain",}] + main_domain = "my_main_domain.com" + expected_result = OrderedDict({"some_domain": (main_domain, "domain")}) answers = {} with patch.object( domain, "_get_maindomain", return_value="my_main_domain.com" ), patch.object( - domain, "domain_list", return_value={"domains": ["my_main_domain.com"]} + domain, "domain_list", return_value={"domains": [main_domain]} ): - with pytest.raises(YunohostError): - _parse_args_in_yunohost_format(answers, questions) + assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_domain(): From 40dc8397c130581a73135f0c2fc6149fc30fa82a Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Mon, 1 Jun 2020 11:43:39 +0200 Subject: [PATCH 053/262] [mod] stop skipping tests that now works --- src/yunohost/tests/test_apps_arguments_parsing.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index c127cc414..5eed5bdfe 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -70,7 +70,6 @@ def test_parse_args_in_yunohost_format_string_input(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # that shit should work x( def test_parse_args_in_yunohost_format_string_input_no_ask(): questions = [{"name": "some_string",}] answers = {} @@ -96,7 +95,6 @@ def test_parse_args_in_yunohost_format_string_optional_with_input(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # this should work without ask def test_parse_args_in_yunohost_format_string_optional_with_input_without_ask(): questions = [{"name": "some_string", "optional": True,}] answers = {} @@ -237,7 +235,6 @@ def test_parse_args_in_yunohost_format_password_input(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # that shit should work x( def test_parse_args_in_yunohost_format_password_input_no_ask(): questions = [{"name": "some_password", "type": "password",}] answers = {} @@ -270,7 +267,6 @@ def test_parse_args_in_yunohost_format_password_optional_with_input(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # this should work without ask def test_parse_args_in_yunohost_format_password_optional_with_input_without_ask(): questions = [{"name": "some_password", "type": "password", "optional": True,}] answers = {} @@ -388,7 +384,6 @@ def test_parse_args_in_yunohost_format_path_input(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # that shit should work x( def test_parse_args_in_yunohost_format_path_input_no_ask(): questions = [{"name": "some_path", "type": "path",}] answers = {} @@ -416,7 +411,6 @@ def test_parse_args_in_yunohost_format_path_optional_with_input(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # this should work without ask def test_parse_args_in_yunohost_format_path_optional_with_input_without_ask(): questions = [{"name": "some_path", "type": "path", "optional": True,}] answers = {} @@ -604,11 +598,10 @@ def test_parse_args_in_yunohost_format_boolean_input(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # we should work def test_parse_args_in_yunohost_format_boolean_input_no_ask(): questions = [{"name": "some_boolean", "type": "boolean",}] answers = {} - expected_result = OrderedDict({"some_boolean": ("some_value", "boolean")}) + expected_result = OrderedDict({"some_boolean": (1, "boolean")}) with patch.object(msignals, "prompt", return_value="y"): assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -660,7 +653,6 @@ def test_parse_args_in_yunohost_format_boolean_no_input_default(): assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # we should raise def test_parse_args_in_yunohost_format_boolean_bad_default(): questions = [ { @@ -769,7 +761,6 @@ def test_parse_args_in_yunohost_format_domain_two_domains_wrong_answer(): _parse_args_in_yunohost_format(answers, questions) -@pytest.mark.skip # XXX should work def test_parse_args_in_yunohost_format_domain_two_domains_default_no_ask(): main_domain = "my_main_domain.com" other_domain = "some_other_domain.tld" From dc6a12f14b9ef3b1d27df97203cc925c73f5f79b Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Mon, 1 Jun 2020 12:02:04 +0200 Subject: [PATCH 054/262] [mod] password argument can't have a default value --- locales/en.json | 1 + src/yunohost/app.py | 8 ++++++++ src/yunohost/tests/test_apps_arguments_parsing.py | 1 - 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 851656532..5952e8b2a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -13,6 +13,7 @@ "app_already_up_to_date": "{app:s} is already up-to-date", "app_argument_choice_invalid": "Use one of these choices '{choices:s}' for the argument '{name:s}'", "app_argument_invalid": "Pick a valid value for the argument '{name:s}': {error:s}", + "app_argument_password_no_default": "Error while parsing password argument '{name}': password argument can't have a default value for security reason", "app_argument_required": "Argument '{name:s}' is required", "app_change_url_failed_nginx_reload": "Could not reload NGINX. Here is the output of 'nginx -t':\n{nginx_errors:s}", "app_change_url_identical_domains": "The old and new domain/url_path are identical ('{domain:s}{path:s}'), nothing to do.", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 027a3a558..4ec043687 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2468,6 +2468,14 @@ class PasswordArgumentParser(YunoHostArgumentFormatParser): argument_type = "password" default_value = "" + def parse_question(self, question, user_answers): + question = super(PasswordArgumentParser, self).parse_question(question, user_answers) + + if question.default is not None: + raise YunohostError('app_argument_password_no_default', name=question.name) + + return question + class PathArgumentParser(YunoHostArgumentFormatParser): argument_type = "path" diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index 5eed5bdfe..7b835c410 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -276,7 +276,6 @@ def test_parse_args_in_yunohost_format_password_optional_with_input_without_ask( assert _parse_args_in_yunohost_format(answers, questions) == expected_result -@pytest.mark.skip # this should raises def test_parse_args_in_yunohost_format_password_no_input_default(): questions = [ { From ecb52b4f1187e6e977ede50f2748c1ed009d3586 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Mon, 1 Jun 2020 12:37:10 +0200 Subject: [PATCH 055/262] [fix] allow optional app argument --- src/yunohost/app.py | 2 +- src/yunohost/tests/test_apps_arguments_parsing.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 4ec043687..4abaaecca 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2429,7 +2429,7 @@ class YunoHostArgumentFormatParser(object): if not question.optional and question.default is None: raise YunohostError('app_argument_required', name=question.name) else: - question.value = self.default_value if question.default is None else question.default + question.value = getattr(self, "default_value", None) if question.default is None else question.default # we have an answer, do some post checks if question.value is not None: diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index 7b835c410..1ea73cc0a 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -1011,14 +1011,14 @@ def test_parse_args_in_yunohost_format_app_no_apps(): _parse_args_in_yunohost_format(answers, questions) -@pytest.mark.skip # XXX should work def test_parse_args_in_yunohost_format_app_no_apps_optional(): apps = [] questions = [{"name": "some_app", "type": "app", "optional": True}] answers = {} + expected_result = OrderedDict({"some_app": (None, "app")}) with patch.object(app, "app_list", return_value={"apps": apps}): - assert _parse_args_in_yunohost_format(answers, questions) == [] + assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_app(): From 1a537039c70747d2aae39f2b16d604a717eff088 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 15 Jun 2020 22:23:26 +0200 Subject: [PATCH 056/262] Backup to uncompressed tar by default --- src/yunohost/backup.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 449b52bd8..0d97617c9 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1862,7 +1862,10 @@ class TarBackupMethod(BackupMethod): @property def _archive_file(self): """Return the compress archive path""" - return os.path.join(self.repo, self.name + '.tar.gz') + f = os.path.join(self.repo, self.name + '.tar') + if os.path.exists(f + ".gz"): + f += ".gz" + return f def backup(self): """ @@ -1885,7 +1888,7 @@ class TarBackupMethod(BackupMethod): # Open archive file for writing try: - tar = tarfile.open(self._archive_file, "w:gz") + tar = tarfile.open(self._archive_file, "w:gz" if self._archive_file.endswith(".gz") else "w") except: logger.debug("unable to open '%s' for writing", self._archive_file, exc_info=1) @@ -1909,7 +1912,7 @@ class TarBackupMethod(BackupMethod): # If backuped to a non-default location, keep a symlink of the archive # to that location - link = os.path.join(ARCHIVES_PATH, self.name + '.tar.gz') + link = os.path.join(ARCHIVES_PATH, self.name + '.tar') if not os.path.isfile(link): os.symlink(self._archive_file, link) @@ -1928,7 +1931,7 @@ class TarBackupMethod(BackupMethod): # Mount the tarball logger.debug(m18n.n("restore_extracting")) try: - tar = tarfile.open(self._archive_file, "r:gz") + tar = tarfile.open(self._archive_file, "r:gz" if self._archive_file.endswith(".gz") else "r") except: logger.debug("cannot open backup archive '%s'", self._archive_file, exc_info=1) @@ -1997,7 +2000,7 @@ class TarBackupMethod(BackupMethod): tar.close() def copy(self, file, target): - tar = tarfile.open(self._archive_file, "r:gz") + tar = tarfile.open(self._archive_file, "r:gz" if self._archive_file.endswith(".gz") else "r") file_to_extract = tar.getmember(file) # Remove the path file_to_extract.name = os.path.basename(file_to_extract.name) @@ -2284,9 +2287,14 @@ def backup_list(with_info=False, human_readable=False): """ # Get local archives sorted according to last modification time - archives = sorted(glob("%s/*.tar.gz" % ARCHIVES_PATH), key=lambda x: os.path.getctime(x)) + archives = sorted(glob("%s/*.tar.gz" % ARCHIVES_PATH) + glob("%s/*.tar" % ARCHIVES_PATH), key=lambda x: os.path.getctime(x)) # Extract only filename without the extension - archives = [os.path.basename(f)[:-len(".tar.gz")] for f in archives] + def remove_extension(f): + if f.endswith(".tar.gz"): + return os.path.basename(f)[:-len(".tar.gz")] + else: + return os.path.basename(f)[:-len(".tar")] + archives = [remove_extension(f) for f in archives] if with_info: d = OrderedDict() @@ -2314,11 +2322,13 @@ def backup_info(name, with_details=False, human_readable=False): human_readable -- Print sizes in human readable format """ - archive_file = '%s/%s.tar.gz' % (ARCHIVES_PATH, name) + archive_file = '%s/%s.tar' % (ARCHIVES_PATH, name) # Check file exist (even if it's a broken symlink) if not os.path.lexists(archive_file): - raise YunohostError('backup_archive_name_unknown', name=name) + archive_file += ".gz" + if not os.path.lexists(archive_file): + raise YunohostError('backup_archive_name_unknown', name=name) # If symlink, retrieve the real path if os.path.islink(archive_file): @@ -2332,7 +2342,7 @@ def backup_info(name, with_details=False, human_readable=False): info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name) if not os.path.exists(info_file): - tar = tarfile.open(archive_file, "r:gz") + tar = tarfile.open(archive_file, "r:gz" if archive_file.endswith(".gz") else "r") info_dir = info_file + '.d' try: @@ -2368,7 +2378,7 @@ def backup_info(name, with_details=False, human_readable=False): # Retrieve backup size size = info.get('size', 0) if not size: - tar = tarfile.open(archive_file, "r:gz") + tar = tarfile.open(archive_file, "r:gz" if archive_file.endswith(".gz") else "r") size = reduce(lambda x, y: getattr(x, 'size', x) + getattr(y, 'size', y), tar.getmembers()) tar.close() @@ -2428,7 +2438,9 @@ def backup_delete(name): hook_callback('pre_backup_delete', args=[name]) - archive_file = '%s/%s.tar.gz' % (ARCHIVES_PATH, name) + archive_file = '%s/%s.tar' % (ARCHIVES_PATH, name) + if os.path.exists(archive_file + ".gz"): + archive_file += '.gz' info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name) files_to_delete = [archive_file, info_file] From 3f1888a0417af23d330afdad24ac235aca546f61 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 15 Jun 2020 22:40:57 +0200 Subject: [PATCH 057/262] We expect .tar instead of .tar.gz now --- src/yunohost/tests/test_backuprestore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 5cffc186e..35a3e23f1 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -375,7 +375,7 @@ def test_backup_with_different_output_directory(mocker): output_directory="/opt/test_backup_output_directory", name="backup") - assert os.path.exists("/opt/test_backup_output_directory/backup.tar.gz") + assert os.path.exists("/opt/test_backup_output_directory/backup.tar") archives = backup_list()["archives"] assert len(archives) == 1 From 8f8be838b6ea0cd596d6e619ffc2d7b9f00f9811 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 21 Jun 2020 19:57:51 +0200 Subject: [PATCH 058/262] Enforce permissions on rspamd log directory --- data/hooks/conf_regen/31-rspamd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen/31-rspamd index 861549e27..6da4af643 100755 --- a/data/hooks/conf_regen/31-rspamd +++ b/data/hooks/conf_regen/31-rspamd @@ -42,6 +42,8 @@ do_post_regen() { chown _rspamd /etc/dkim/*.mail.key chmod 400 /etc/dkim/*.mail.key + [ ! -e /var/log/rspamd ] || chown _rspamd:_rspamd /var/log/rspamd + regen_conf_files=$1 [ -z "$regen_conf_files" ] && exit 0 From b373eb6076e25d995b73094a4e946f6562a0a317 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 21 Jun 2020 23:35:50 +0200 Subject: [PATCH 059/262] Fix parsing of unscd version... --- src/yunohost/data_migrations/0015_migrate_to_buster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/data_migrations/0015_migrate_to_buster.py b/src/yunohost/data_migrations/0015_migrate_to_buster.py index 836660701..bbbf8bf16 100644 --- a/src/yunohost/data_migrations/0015_migrate_to_buster.py +++ b/src/yunohost/data_migrations/0015_migrate_to_buster.py @@ -50,7 +50,7 @@ class MyMigration(Migration): # which for apt appears as a lower version (hence the --allow-downgrades and the hardcoded version number) unscd_version = check_output('dpkg -s unscd | grep "^Version: " | cut -d " " -f 2') if "yunohost" in unscd_version: - new_version = check_output("apt policy unscd 2>/dev/null | grep -v '\\*\\*\\*' | grep -A100 'Version table' | grep http -B1 | head -n 1 | awk '{print $1}'").strip() + new_version = check_output("LC_ALL=C apt policy unscd 2>/dev/null | grep -v '\\*\\*\\*' | grep http -B1 | head -n 1 | awk '{print $1}'").strip() if new_version: self.apt_install('unscd=%s --allow-downgrades' % new_version) else: From 649179338a4bc3ada9c8cf300ed6f128e8b4fbdb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 21 Jun 2020 23:38:42 +0200 Subject: [PATCH 060/262] Update changelog for 3.8.5.4 --- debian/changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/debian/changelog b/debian/changelog index 89f78b61a..8e3eea7d8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +yunohost (3.8.5.4) testing; urgency=low + + - [fix] Fix unscd version parsing *again* + - [fix] Enforce permissions on rspamd log directory + - [enh] Ignore stupid warnings about sudo-ldap that is already provided + + -- Alexandre Aubin Sun, 21 Jun 2020 23:37:09 +0200 + yunohost (3.8.5.3) testing; urgency=low - [fix] Fix the fix about unscd downgrade :/ From 82d59d59be7eaf3b87660e129aa0424aeaafccc7 Mon Sep 17 00:00:00 2001 From: ericgaspar Date: Tue, 23 Jun 2020 10:26:09 +0200 Subject: [PATCH 061/262] silence info: wasn't deleted because it doesn't exist. --- data/helpers.d/apt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index 5589830c3..377464786 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -467,8 +467,8 @@ ynh_remove_extra_repo () { ynh_secure_remove "/etc/apt/sources.list.d/$name.list" ynh_secure_remove "/etc/apt/preferences.d/$name" - ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.gpg" - ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.asc" + ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.gpg" > /dev/null + ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.asc" > /dev/null # Update the list of package to exclude the old repo ynh_package_update From 74ef4d1cf783f7b8cf1d73807495d2837c119e8a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Jun 2020 19:46:22 +0200 Subject: [PATCH 062/262] Remove useless robot protection code --- data/templates/nginx/yunohost_admin.conf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/data/templates/nginx/yunohost_admin.conf b/data/templates/nginx/yunohost_admin.conf index d13dbfe90..ff823ba30 100644 --- a/data/templates/nginx/yunohost_admin.conf +++ b/data/templates/nginx/yunohost_admin.conf @@ -28,12 +28,6 @@ server { } location /yunohost { - # Block crawlers bot - if ($http_user_agent ~ (crawl|Googlebot|Slurp|spider|bingbot|tracker|click|parser|spider|facebookexternalhit) ) { - return 403; - } - # X-Robots-Tag to precise the rules applied. - add_header X-Robots-Tag "nofollow, noindex, noarchive, nosnippet"; # Redirect most of 404 to maindomain.tld/yunohost/sso access_by_lua_file /usr/share/ssowat/access.lua; } From f1bfc521aec0b6687ec3af7e08652be3e4982734 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Jun 2020 20:04:11 +0200 Subject: [PATCH 063/262] Allow to extend the nginx default_server configuration --- data/templates/nginx/yunohost_admin.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/templates/nginx/yunohost_admin.conf b/data/templates/nginx/yunohost_admin.conf index d13dbfe90..d405192fc 100644 --- a/data/templates/nginx/yunohost_admin.conf +++ b/data/templates/nginx/yunohost_admin.conf @@ -9,6 +9,8 @@ server { location /yunohost/admin { return 301 https://$http_host$request_uri; } + + include /etc/nginx/conf.d/default.d/*.conf; } server { @@ -40,4 +42,5 @@ server { include /etc/nginx/conf.d/yunohost_admin.conf.inc; include /etc/nginx/conf.d/yunohost_api.conf.inc; + include /etc/nginx/conf.d/default.d/*.conf; } From ac9182d69fdabbdaffd7cde84b7c0485d0482e89 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Jun 2020 21:42:35 +0200 Subject: [PATCH 064/262] Move redirect to /yunohost/admin to a separate nginx conf file to allow customizing it more easily --- data/hooks/conf_regen/15-nginx | 5 +++++ data/templates/nginx/redirect_to_admin.conf | 3 +++ data/templates/nginx/yunohost_admin.conf | 12 ------------ 3 files changed, 8 insertions(+), 12 deletions(-) create mode 100644 data/templates/nginx/redirect_to_admin.conf diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx index 4924b6f91..a7574b070 100755 --- a/data/hooks/conf_regen/15-nginx +++ b/data/hooks/conf_regen/15-nginx @@ -26,6 +26,9 @@ do_init_regen() { ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc" ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf" + mkdir -p $nginx_conf_dir/default.d/ + cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/ + # Restart nginx if conf looks good, otherwise display error and exit unhappy nginx -t 2>/dev/null || { nginx -t; exit 1; } systemctl restart nginx || { journalctl --no-pager --lines=10 -u nginx >&2; exit 1; } @@ -77,6 +80,8 @@ do_pre_regen() { done ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf" + mkdir -p $nginx_conf_dir/default.d/ + cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/ # remove old domain conf files conf_files=$(ls -1 /etc/nginx/conf.d \ diff --git a/data/templates/nginx/redirect_to_admin.conf b/data/templates/nginx/redirect_to_admin.conf new file mode 100644 index 000000000..22748daa3 --- /dev/null +++ b/data/templates/nginx/redirect_to_admin.conf @@ -0,0 +1,3 @@ +location / { + return 302 https://$http_host/yunohost/admin; +} diff --git a/data/templates/nginx/yunohost_admin.conf b/data/templates/nginx/yunohost_admin.conf index d405192fc..b6d0b6d97 100644 --- a/data/templates/nginx/yunohost_admin.conf +++ b/data/templates/nginx/yunohost_admin.conf @@ -2,14 +2,6 @@ server { listen 80 default_server; listen [::]:80 default_server; - location / { - return 302 https://$http_host/yunohost/admin; - } - - location /yunohost/admin { - return 301 https://$http_host$request_uri; - } - include /etc/nginx/conf.d/default.d/*.conf; } @@ -25,10 +17,6 @@ server { more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload"; more_set_headers "Referrer-Policy : 'same-origin'"; - location / { - return 302 https://$http_host/yunohost/admin; - } - location /yunohost { # Block crawlers bot if ($http_user_agent ~ (crawl|Googlebot|Slurp|spider|bingbot|tracker|click|parser|spider|facebookexternalhit) ) { From d435889776680b64261eebdac20a097459db9d4e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 29 Jun 2020 22:04:04 +0200 Subject: [PATCH 065/262] Make sure to validate/upgrade that we don't have any active weak certificate used by nginx at the beginning of the buster migration, otherwise nginx will later miserably fail to start --- data/hooks/conf_regen/02-ssl | 5 +-- locales/en.json | 1 + .../data_migrations/0015_migrate_to_buster.py | 39 +++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl index a893b21e1..310a5d526 100755 --- a/data/hooks/conf_regen/02-ssl +++ b/data/hooks/conf_regen/02-ssl @@ -69,12 +69,11 @@ do_init_regen() { -out "${ssl_dir}/certs/yunohost_crt.pem" \ -batch >>$LOGFILE 2>&1 - last_cert=$(ls $ssl_dir/newcerts/*.pem | sort -V | tail -n 1) chmod 640 "${ssl_dir}/certs/yunohost_key.pem" - chmod 640 "$last_cert" + chmod 640 "${ssl_dir}/certs/yunohost_crt.pem" cp "${ssl_dir}/certs/yunohost_key.pem" "$ynh_key" - cp "$last_cert" "$ynh_crt" + cp "${ssl_dir}/certs/yunohost_crt.pem" "$ynh_crt" ln -sf "$ynh_crt" /etc/ssl/certs/yunohost_crt.pem ln -sf "$ynh_key" /etc/ssl/private/yunohost_key.pem fi diff --git a/locales/en.json b/locales/en.json index abc1c1092..7e8e51260 100644 --- a/locales/en.json +++ b/locales/en.json @@ -479,6 +479,7 @@ "migration_0015_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}", "migration_0015_specific_upgrade": "Starting upgrade of system packages that needs to be upgrade independently…", "migration_0015_cleaning_up": "Cleaning up cache and packages not useful anymore…", + "migration_0015_weak_certs": "The following certificates were found to still use weak signature algorithms and have to be upgraded to be compatible with the next version of nginx: {certs}", "migrations_already_ran": "Those migrations are already done: {ids}", "migrations_cant_reach_migration_file": "Could not access migrations files at the path '%s'", "migrations_dependencies_not_satisfied": "Run these migrations: '{dependencies_id}', before migration {id}.", diff --git a/src/yunohost/data_migrations/0015_migrate_to_buster.py b/src/yunohost/data_migrations/0015_migrate_to_buster.py index bbbf8bf16..4f7f8afeb 100644 --- a/src/yunohost/data_migrations/0015_migrate_to_buster.py +++ b/src/yunohost/data_migrations/0015_migrate_to_buster.py @@ -16,6 +16,7 @@ from yunohost.utils.packages import get_ynh_package_version, _list_upgradable_ap logger = getActionLogger('yunohost.migration') + class MyMigration(Migration): "Upgrade the system to Debian Buster and Yunohost 4.x" @@ -28,6 +29,13 @@ class MyMigration(Migration): logger.info(m18n.n("migration_0015_start")) + # + # Make sure certificates do not use weak signature hash algorithms (md5, sha1) + # otherwise nginx will later refuse to start which result in + # catastrophic situation + # + self.validate_and_upgrade_cert_if_necessary() + # # Patch sources.list # @@ -203,3 +211,34 @@ class MyMigration(Migration): logger.debug("Running: %s" % cmd) call_async_output(cmd, callbacks, shell=True) + + def validate_and_upgrade_cert_if_necessary(self): + + active_certs = set(check_output("grep -roh '/.*crt.pem' /etc/nginx/").strip().split("\n")) + + cmd = "LC_ALL=C openssl x509 -in %s -text -noout | grep -i 'Signature Algorithm:' | awk '{print $3}' | uniq" + + default_crt = '/etc/yunohost/certs/yunohost.org/crt.pem' + default_key = '/etc/yunohost/certs/yunohost.org/key.pem' + default_signature = check_output(cmd % default_crt).strip() if default_crt in active_certs else None + if default_signature is not None and (default_signature.startswith("md5") or default_signature.startswith("sha1")): + logger.warning("%s is using a pretty old certificate incompatible with newer versions of nginx ... attempting to regenerate a fresh one" % default_crt) + + os.system("mv %s %s.old" % (default_crt, default_crt)) + os.system("mv %s %s.old" % (default_key, default_key)) + ret = os.system("/usr/share/yunohost/hooks/conf_regen/02-ssl init") + + if ret != 0 or not os.path.exists(default_crt): + logger.error("Upgrading the certificate failed ... reverting") + os.system("mv %s.old %s" % (default_crt, default_crt)) + os.system("mv %s.old %s" % (default_key, default_key)) + + signatures = {cert: check_output(cmd % cert).strip() for cert in active_certs} + + def cert_is_weak(cert): + sig = signatures[cert] + return sig.startswith("md5") or sig.startswith("sha1") + + weak_certs = [cert for cert in signatures.keys() if cert_is_weak(cert)] + if weak_certs: + raise YunohostError("migration_0015_weak_certs", certs=", ".join(weak_certs)) From 7bfe564aab2300172b6aad68656dddbdd8933a27 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Thu, 25 Jun 2020 10:53:08 +0200 Subject: [PATCH 066/262] fix get_files_diff if {orig,new}_file is None --- src/yunohost/regenconf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/regenconf.py b/src/yunohost/regenconf.py index 6445248bd..a94f5023d 100644 --- a/src/yunohost/regenconf.py +++ b/src/yunohost/regenconf.py @@ -450,13 +450,13 @@ def _get_files_diff(orig_file, new_file, as_string=False, skip_header=True): """ - if os.path.exists(orig_file): + if orig_file and os.path.exists(orig_file): with open(orig_file, 'r') as orig_file: orig_file = orig_file.readlines() else: orig_file = [] - if os.path.exists(new_file): + if new_file and os.path.exists(new_file): with open(new_file, 'r') as new_file: new_file = new_file.readlines() else: From 49b91460654718c9637fdf6091220b433c46bbaf Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Fri, 3 Jul 2020 00:33:44 +0200 Subject: [PATCH 067/262] [fix] reintroduce custom exceptions for input fields type --- src/yunohost/app.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 4abaaecca..9c76e73eb 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2434,8 +2434,7 @@ class YunoHostArgumentFormatParser(object): # we have an answer, do some post checks if question.value is not None: if question.choices and question.value not in question.choices: - raise YunohostError('app_argument_choice_invalid', name=question.name, - choices=', '.join(question.choices)) + self._raise_invalide_answer(question) # this is done to enforce a certain formating like for boolean # by default it doesn't do anything @@ -2443,6 +2442,10 @@ class YunoHostArgumentFormatParser(object): return (question.value, self.argument_type) + def _raise_invalide_answer(self, question): + raise YunohostError('app_argument_choice_invalid', name=question.name, + choices=', '.join(question.choices)) + def _format_text_for_user_input_in_cli(self, question): text_for_user_input_in_cli = _value_for_locale(question.ask) @@ -2534,6 +2537,10 @@ class DomainArgumentParser(YunoHostArgumentFormatParser): return question + def _raise_invalide_answer(self, question): + raise YunohostError('app_argument_invalid', name=question.name, + error=m18n.n('domain_unknown')) + class UserArgumentParser(YunoHostArgumentFormatParser): argument_type = "user" @@ -2546,6 +2553,10 @@ class UserArgumentParser(YunoHostArgumentFormatParser): return question + def _raise_invalide_answer(self, question): + raise YunohostError('app_argument_invalid', name=question.name, + error=m18n.n('user_unknown', user=question.value)) + class AppArgumentParser(YunoHostArgumentFormatParser): argument_type = "app" @@ -2558,6 +2569,10 @@ class AppArgumentParser(YunoHostArgumentFormatParser): return question + def _raise_invalide_answer(self, question): + raise YunohostError('app_argument_invalid', name=question.name, + error=m18n.n('app_unknown')) + class DisplayTextArgumentParser(YunoHostArgumentFormatParser): From 2eb5f6b6da3d398f890c68240da2786852e01726 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Fri, 3 Jul 2020 00:49:14 +0200 Subject: [PATCH 068/262] [fix] re-put forbidding some chars in passwords --- src/yunohost/app.py | 7 +++++++ .../tests/test_apps_arguments_parsing.py | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 9c76e73eb..a3244abe1 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2470,6 +2470,7 @@ class PasswordArgumentParser(YunoHostArgumentFormatParser): hide_user_input_in_prompt = True argument_type = "password" default_value = "" + forbidden_chars = "{}" def parse_question(self, question, user_answers): question = super(PasswordArgumentParser, self).parse_question(question, user_answers) @@ -2479,6 +2480,12 @@ class PasswordArgumentParser(YunoHostArgumentFormatParser): return question + def _post_parse_value(self, question): + if any(char in question.value for char in self.forbidden_chars): + raise YunohostError('pattern_password_app', forbidden_chars=self.forbidden_chars) + + return super(PasswordArgumentParser, self)._post_parse_value(question) + class PathArgumentParser(YunoHostArgumentFormatParser): argument_type = "path" diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index 1ea73cc0a..9576ce0bf 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -8,7 +8,7 @@ from collections import OrderedDict from moulinette import msignals from yunohost import domain, user, app -from yunohost.app import _parse_args_in_yunohost_format +from yunohost.app import _parse_args_in_yunohost_format, PasswordArgumentParser from yunohost.utils.error import YunohostError @@ -359,6 +359,21 @@ def test_parse_args_in_yunohost_format_password_input_test_ask_with_help(): assert help_text in prompt.call_args[0][0] +def test_parse_args_in_yunohost_format_password_bad_chars(): + questions = [ + { + "name": "some_password", + "type": "password", + "ask": "some question", + "example": "some_value", + } + ] + + for i in PasswordArgumentParser.forbidden_chars: + with pytest.raises(YunohostError): + _parse_args_in_yunohost_format({"some_password": i * 8}, questions) + + def test_parse_args_in_yunohost_format_path(): questions = [{"name": "some_path", "type": "path",}] answers = {"some_path": "some_value"} From bcd2364d74378753b7d16bc5db9f7b2113706196 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Fri, 3 Jul 2020 00:57:09 +0200 Subject: [PATCH 069/262] [fix] re-put assert password is strong enough --- src/yunohost/app.py | 3 +++ .../tests/test_apps_arguments_parsing.py | 23 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index a3244abe1..1aba439e8 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2484,6 +2484,9 @@ class PasswordArgumentParser(YunoHostArgumentFormatParser): if any(char in question.value for char in self.forbidden_chars): raise YunohostError('pattern_password_app', forbidden_chars=self.forbidden_chars) + from yunohost.utils.password import assert_password_is_strong_enough + assert_password_is_strong_enough('user', question.value) + return super(PasswordArgumentParser, self)._post_parse_value(question) diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index 9576ce0bf..8224c443e 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -247,8 +247,9 @@ def test_parse_args_in_yunohost_format_password_input_no_ask(): def test_parse_args_in_yunohost_format_password_no_input_optional(): questions = [{"name": "some_password", "type": "password", "optional": True,}] answers = {} - expected_result = OrderedDict({"some_password": ("", "password")}) - assert _parse_args_in_yunohost_format(answers, questions) == expected_result + + with pytest.raises(YunohostError): + _parse_args_in_yunohost_format(answers, questions) def test_parse_args_in_yunohost_format_password_optional_with_input(): @@ -374,6 +375,24 @@ def test_parse_args_in_yunohost_format_password_bad_chars(): _parse_args_in_yunohost_format({"some_password": i * 8}, questions) +def test_parse_args_in_yunohost_format_password_strong_enough(): + questions = [ + { + "name": "some_password", + "type": "password", + "ask": "some question", + "example": "some_value", + } + ] + + with pytest.raises(YunohostError): + # too short + _parse_args_in_yunohost_format({"some_password": "a"}, questions) + + with pytest.raises(YunohostError): + _parse_args_in_yunohost_format({"some_password": "password"}, questions) + + def test_parse_args_in_yunohost_format_path(): questions = [{"name": "some_path", "type": "path",}] answers = {"some_path": "some_value"} From 95cbc23733c34aacd1ba3cfb32469ed7ae73547b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Gaspar?= <46165813+ericgaspar@users.noreply.github.com> Date: Sun, 19 Jul 2020 19:21:53 +0200 Subject: [PATCH 070/262] Update en.json --- locales/en.json | 114 ++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/locales/en.json b/locales/en.json index 7e8e51260..a76176dc1 100644 --- a/locales/en.json +++ b/locales/en.json @@ -29,26 +29,26 @@ "app_manifest_invalid": "Something is wrong with the app manifest: {error}", "app_not_upgraded": "The app '{failed_app}' failed to upgrade, and as a consequence the following apps' upgrades have been cancelled: {apps}", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", - "app_not_installed": "Could not find the app '{app:s}' in the list of installed apps: {all_apps}", + "app_not_installed": "Could not find {app:s} in the list of installed apps: {all_apps}", "app_not_properly_removed": "{app:s} has not been properly removed", "app_removed": "{app:s} removed", - "app_requirements_checking": "Checking required packages for {app}…", + "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_remove_after_failed_install": "Removing the app following the installation failure...", "app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?", - "app_start_install": "Installing the app '{app}'…", - "app_start_remove": "Removing the app '{app}'…", - "app_start_backup": "Collecting files to be backed up for the app '{app}'…", - "app_start_restore": "Restoring the app '{app}'…", + "app_start_install": "Installing {app}...", + "app_start_remove": "Removing {app}...", + "app_start_backup": "Collecting files to be backed up for {app}...", + "app_start_restore": "Restoring {app}...", "app_unknown": "Unknown app", "app_unsupported_remote_type": "Unsupported remote type used for the app", "app_upgrade_several_apps": "The following apps will be upgraded: {apps}", - "app_upgrade_app_name": "Now upgrading {app}…", + "app_upgrade_app_name": "Now upgrading {app}...", "app_upgrade_failed": "Could not upgrade {app:s}: {error}", "app_upgrade_script_failed": "An error occurred inside the app upgrade script", "app_upgrade_some_app_failed": "Some apps could not be upgraded", "app_upgraded": "{app:s} upgraded", - "app_packaging_format_not_supported": "This app cannot be installed because its packaging format is not supported by your Yunohost version. You should probably consider upgrading your system.", + "app_packaging_format_not_supported": "This app cannot be installed because its packaging format is not supported by your YunoHost version. You should probably consider upgrading your system.", "apps_already_up_to_date": "All apps are already up-to-date", "apps_catalog_init_success": "App catalog system initialized!", "apps_catalog_updating": "Updating application catalog…", @@ -64,22 +64,22 @@ "ask_new_path": "New path", "ask_password": "Password", "backup_abstract_method": "This backup method has yet to be implemented", - "backup_actually_backuping": "Creating a backup archive from the collected files…", - "backup_app_failed": "Could not back up the app '{app:s}'", - "backup_applying_method_borg": "Sending all files to backup into borg-backup repository…", - "backup_applying_method_copy": "Copying all files to backup…", - "backup_applying_method_custom": "Calling the custom backup method '{method:s}'…", - "backup_applying_method_tar": "Creating the backup TAR archive…", - "backup_archive_app_not_found": "Could not find the app '{app:s}' in the backup archive", + "backup_actually_backuping": "Creating a backup archive from the collected files...", + "backup_app_failed": "Could not back up {app:s}", + "backup_applying_method_borg": "Sending all files to backup into borg-backup repository...", + "backup_applying_method_copy": "Copying all files to backup...", + "backup_applying_method_custom": "Calling the custom backup method '{method:s}'...", + "backup_applying_method_tar": "Creating the backup TAR archive...", + "backup_archive_app_not_found": "Could not find {app:s} in the backup archive", "backup_archive_broken_link": "Could not access the backup archive (broken link to {path:s})", "backup_archive_name_exists": "A backup archive with this name already exists.", "backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'", "backup_archive_open_failed": "Could not open the backup archive", - "backup_archive_cant_retrieve_info_json": "Could not load infos for archive '{archive}' ... The info.json cannot be retrieved (or is not a valid json).", + "backup_archive_cant_retrieve_info_json": "Could not load infos for archive '{archive}'... The info.json cannot be retrieved (or is not a valid json).", "backup_archive_corrupted": "It looks like the backup archive '{archive}' is corrupted : {error}", "backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup", "backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'", - "backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s} MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)", + "backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s}MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)", "backup_borg_not_implemented": "The Borg backup method is not yet implemented", "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected", "backup_cleaning_failed": "Could not clean up the temporary backup folder", @@ -98,20 +98,20 @@ "backup_method_copy_finished": "Backup copy finalized", "backup_method_custom_finished": "Custom backup method '{method:s}' finished", "backup_method_tar_finished": "TAR backup archive created", - "backup_mount_archive_for_restore": "Preparing archive for restoration…", + "backup_mount_archive_for_restore": "Preparing archive for restoration...", "backup_no_uncompress_archive_dir": "There is no such uncompressed archive directory", "backup_nothings_done": "Nothing to save", "backup_output_directory_forbidden": "Pick a different output directory. Backups cannot be created in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var or /home/yunohost.backup/archives sub-folders", "backup_output_directory_not_empty": "You should pick an empty output directory", "backup_output_directory_required": "You must provide an output directory for the backup", "backup_output_symlink_dir_broken": "Your archive directory '{path:s}' is a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.", - "backup_permission": "Backup permission for app {app:s}", + "backup_permission": "Backup permission for {app:s}", "backup_php5_to_php7_migration_may_fail": "Could not convert your archive to support PHP 7, you may be unable to restore your PHP apps (reason: {error:s})", - "backup_running_hooks": "Running backup hooks…", + "backup_running_hooks": "Running backup hooks...", "backup_system_part_failed": "Could not backup the '{part:s}' system part", "backup_unable_to_organize_files": "Could not use the quick method to organize files in the archive", "backup_with_no_backup_script_for_app": "The app '{app:s}' has no backup script. Ignoring.", - "backup_with_no_restore_script_for_app": "The '{app:s}' has no restoration script, you will not be able to automatically restore the backup of this app.", + "backup_with_no_restore_script_for_app": "{app:s} has no restoration script, you will not be able to automatically restore the backup of this app.", "certmanager_acme_not_configured_for_domain": "The ACME challenge cannot be ran for {domain} right now because its nginx conf lacks the corresponding code snippet... Please make sure that your nginx configuration is up to date using `yunohost tools regen-conf nginx --dry-run --with-diff`.", "certmanager_attempt_to_renew_nonLE_cert": "The certificate for the domain '{domain:s}' is not issued by Let's Encrypt. Cannot renew it automatically!", "certmanager_attempt_to_renew_valid_cert": "The certificate for the domain '{domain:s}' is not about to expire! (You may use --force if you know what you're doing)", @@ -121,7 +121,7 @@ "certmanager_cert_install_success_selfsigned": "Self-signed certificate now installed for the domain '{domain:s}'", "certmanager_cert_renew_success": "Let's Encrypt certificate renewed for the domain '{domain:s}'", "certmanager_cert_signing_failed": "Could not sign the new certificate", - "certmanager_certificate_fetching_or_enabling_failed": "Trying to use the new certificate for {domain:s} did not work…", + "certmanager_certificate_fetching_or_enabling_failed": "Trying to use the new certificate for {domain:s} did not work...", "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_not_diagnosed_yet": "There is no diagnosis result for domain %s yet. Please re-run a diagnosis for categories 'DNS records' and 'Web' in the diagnosis section to check if the domain is ready for Let's Encrypt. (Or if you know what you are doing, use '--no-checks' to turn off those checks.)", "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' to do so.)", @@ -155,9 +155,9 @@ "diagnosis_everything_ok": "Everything looks good for {category}!", "diagnosis_failed": "Failed to fetch diagnosis result for category '{category}': {error}", "diagnosis_no_cache": "No diagnosis cache yet for category '{category}'", - "diagnosis_ip_connected_ipv4": "The server is connected to the Internet through IPv4 !", + "diagnosis_ip_connected_ipv4": "The server is connected to the Internet through IPv4!", "diagnosis_ip_no_ipv4": "The server does not have working IPv4.", - "diagnosis_ip_connected_ipv6": "The server is connected to the Internet through IPv6 !", + "diagnosis_ip_connected_ipv6": "The server is connected to the Internet through IPv6!", "diagnosis_ip_no_ipv6": "The server does not have working IPv6.", "diagnosis_ip_no_ipv6_tip": "Having a working IPv6 is not mandatory for your server to work, but it is better for the health of the Internet as a whole. IPv6 should usually be automatically configured by the system or your provider if it's available. Otherwise, you might need to configure a few things manually as explained in the documentation here: https://yunohost.org/#/ipv6. If you cannot enable IPv6 or if it seems too technical for you, you can also safely ignore this warning.", "diagnosis_ip_global": "Global IP: {global}", @@ -285,7 +285,7 @@ "dyndns_cron_removed": "DynDNS cron job removed", "dyndns_ip_update_failed": "Could not update IP address to DynDNS", "dyndns_ip_updated": "Updated your IP on DynDNS", - "dyndns_key_generating": "Generating DNS key… It may take a while.", + "dyndns_key_generating": "Generating DNS key... It may take a while.", "dyndns_key_not_found": "DNS key not found for the domain", "dyndns_no_domain_registered": "No domain registered with DynDNS", "dyndns_provider_unreachable": "Unable to reach DynDNS provider {provider}: either your YunoHost is not correctly connected to the internet or the dynette server is down.", @@ -293,9 +293,9 @@ "dyndns_registration_failed": "Could not register DynDNS domain: {error:s}", "dyndns_domain_not_provided": "DynDNS provider {provider:s} cannot provide domain {domain:s}.", "dyndns_unavailable": "The domain '{domain:s}' is unavailable.", - "executing_command": "Executing command '{command:s}'…", - "executing_script": "Executing script '{script:s}'…", - "extracting": "Extracting…", + "executing_command": "Executing command '{command:s}'...", + "executing_script": "Executing script '{script:s}'...", + "extracting": "Extracting...", "experimental_feature": "Warning: This feature is experimental and not considered stable, you should not use it unless you know what you are doing.", "field_invalid": "Invalid field '{:s}'", "file_does_not_exist": "The file {path:s} does not exist.", @@ -327,7 +327,7 @@ "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).", "group_already_exist": "Group {group} already exists", "group_already_exist_on_system": "Group {group} already exists in the system groups", - "group_already_exist_on_system_but_removing_it": "Group {group} already exists in the system groups, but YunoHost will remove it…", + "group_already_exist_on_system_but_removing_it": "Group {group} already exists in the system groups, but YunoHost will remove it...", "group_created": "Group '{group}' created", "group_creation_failed": "Could not create the group '{group}': {error}", "group_cannot_edit_all_users": "The group 'all_users' cannot be edited manually. It is a special group meant to contain all users registered in YunoHost", @@ -409,10 +409,10 @@ "migrate_tsig_end": "Migration to HMAC-SHA-512 finished", "migrate_tsig_failed": "Could not migrate the DynDNS domain '{domain}' to HMAC-SHA-512, rolling back. Error: {error_code}, {error}", "migrate_tsig_start": "Insufficiently secure key algorithm detected for TSIG signature of the domain '{domain}', initiating migration to the more secure HMAC-SHA-512", - "migrate_tsig_wait": "Waiting three minutes for the DynDNS server to take the new key into account…", - "migrate_tsig_wait_2": "2min…", - "migrate_tsig_wait_3": "1min…", - "migrate_tsig_wait_4": "30 seconds…", + "migrate_tsig_wait": "Waiting three minutes for the DynDNS server to take the new key into account...", + "migrate_tsig_wait_2": "2 min...", + "migrate_tsig_wait_3": "1 min...", + "migrate_tsig_wait_4": "30 seconds...", "migrate_tsig_not_needed": "You do not appear to use a DynDNS domain, so no migration is needed.", "migration_description_0001_change_cert_group_to_sslcert": "Change certificates group permissions from 'metronome' to 'ssl-cert'", "migration_description_0002_migrate_to_tsig_sha256": "Improve security of DynDNS TSIG updates by using SHA-512 instead of MD5", @@ -430,9 +430,9 @@ "migration_description_0014_remove_app_status_json": "Remove legacy status.json app files", "migration_description_0015_migrate_to_buster": "Upgrade the system to Debian Buster and YunoHost 4.x", "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", - "migration_0003_patching_sources_list": "Patching the sources.lists…", - "migration_0003_main_upgrade": "Starting main upgrade…", - "migration_0003_fail2ban_upgrade": "Starting the Fail2Ban upgrade…", + "migration_0003_patching_sources_list": "Patching the sources.lists...", + "migration_0003_main_upgrade": "Starting main upgrade...", + "migration_0003_fail2ban_upgrade": "Starting the Fail2Ban upgrade...", "migration_0003_restoring_origin_nginx_conf": "Your file /etc/nginx/nginx.conf was edited somehow. The migration is going to reset it to its original state first… The previous file will be available as {backup_dest}.", "migration_0003_yunohost_upgrade": "Starting the YunoHost package upgrade… The migration will end, but the actual upgrade will happen immediately afterwards. After the operation is complete, you might have to log in to the webadmin page again.", "migration_0003_not_jessie": "The current Debian distribution is not Jessie!", @@ -453,32 +453,32 @@ "migration_0008_dsa": "• The DSA key will be turned off. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server;", "migration_0008_warning": "If you understand those warnings and want YunoHost to override your current configuration, run the migration. Otherwise, you can also skip the migration, though it is not recommended.", "migration_0008_no_warning": "Overriding your SSH configuration should be safe, though this cannot be promised! Run the migration to override it. Otherwise, you can also skip the migration, though it is not recommended.", - "migration_0009_not_needed": "This migration already happened somehow… (?) Skipping.", + "migration_0009_not_needed": "This migration already happened somehow... (?) Skipping.", "migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.", "migration_0011_can_not_backup_before_migration": "The backup of the system could not be completed before the migration failed. Error: {error:s}", "migration_0011_create_group": "Creating a group for each user…", "migration_0011_done": "Migration completed. You are now able to manage usergroups.", "migration_0011_slapd_config_will_be_overwritten": "It looks like you manually edited the slapd configuration. For this critical migration, YunoHost needs to force the update of the slapd configuration. The original files will be backuped in {conf_backup_folder}.", "migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}", - "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP…", + "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP...", "migration_0011_migration_failed_trying_to_rollback": "Could not migrate… trying to roll back the system.", "migration_0011_rollback_success": "System rolled back.", - "migration_0011_update_LDAP_database": "Updating LDAP database…", - "migration_0011_update_LDAP_schema": "Updating LDAP schema…", + "migration_0011_update_LDAP_database": "Updating LDAP database...", + "migration_0011_update_LDAP_schema": "Updating LDAP schema...", "migration_0011_failed_to_remove_stale_object": "Could not remove stale object {dn}: {error}", "migration_0015_start" : "Starting migration to Buster", - "migration_0015_patching_sources_list": "Patching the sources.lists…", - "migration_0015_main_upgrade": "Starting main upgrade…", + "migration_0015_patching_sources_list": "Patching the sources.lists...", + "migration_0015_main_upgrade": "Starting main upgrade...", "migration_0015_still_on_stretch_after_main_upgrade": "Something went wrong during the main upgrade, the system appears to still be on Debian Stretch", - "migration_0015_yunohost_upgrade" : "Starting YunoHost core upgrade…", + "migration_0015_yunohost_upgrade" : "Starting YunoHost core upgrade...", "migration_0015_not_stretch" : "The current Debian distribution is not Stretch!", "migration_0015_not_enough_free_space" : "Free space is pretty low in /var/! You should have at least 1GB free to run this migration.", "migration_0015_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Buster.", "migration_0015_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.", "migration_0015_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from the YunoHost app catalog, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}", "migration_0015_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}", - "migration_0015_specific_upgrade": "Starting upgrade of system packages that needs to be upgrade independently…", - "migration_0015_cleaning_up": "Cleaning up cache and packages not useful anymore…", + "migration_0015_specific_upgrade": "Starting upgrade of system packages that needs to be upgrade independently...", + "migration_0015_cleaning_up": "Cleaning up cache and packages not useful anymore...", "migration_0015_weak_certs": "The following certificates were found to still use weak signature algorithms and have to be upgraded to be compatible with the next version of nginx: {certs}", "migrations_already_ran": "Those migrations are already done: {ids}", "migrations_cant_reach_migration_file": "Could not access migrations files at the path '%s'", @@ -486,7 +486,7 @@ "migrations_failed_to_load_migration": "Could not load migration {id}: {error}", "migrations_exclusive_options": "'--auto', '--skip', and '--force-rerun' are mutually exclusive options.", "migrations_list_conflict_pending_done": "You cannot use both '--previous' and '--done' at the same time.", - "migrations_loading_migration": "Loading migration {id}…", + "migrations_loading_migration": "Loading migration {id}...", "migrations_migration_has_failed": "Migration {id} did not complete, aborting. Error: {exception}", "migrations_must_provide_explicit_targets": "You must provide explicit targets when using '--skip' or '--force-rerun'", "migrations_need_to_accept_disclaimer": "To run the migration {id}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option '--accept-disclaimer'.", @@ -494,8 +494,8 @@ "migrations_no_such_migration": "There is no migration called '{id}'", "migrations_not_pending_cant_skip": "Those migrations are not pending, so cannot be skipped: {ids}", "migrations_pending_cant_rerun": "Those migrations are still pending, so cannot be run again: {ids}", - "migrations_running_forward": "Running migration {id}…", - "migrations_skip_migration": "Skipping migration {id}…", + "migrations_running_forward": "Running migration {id}...", + "migrations_skip_migration": "Skipping migration {id}...", "migrations_success_forward": "Migration {id} completed", "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.", "no_internet_connection": "The server is not connected to the Internet", @@ -549,11 +549,11 @@ "regenconf_would_be_updated": "The configuration would have been updated for category '{category}'", "regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for category '{category}'…", "regenconf_failed": "Could not regenerate the configuration for category(s): {categories}", - "regenconf_pending_applying": "Applying pending configuration for category '{category}'…", + "regenconf_pending_applying": "Applying pending configuration for category '{category}'...", "regenconf_need_to_explicitly_specify_ssh": "The ssh configuration has been manually modified, but you need to explicitly specify category 'ssh' with --force to actually apply the changes.", "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 the app '{app:s}'", + "restore_app_failed": "Could not restore {app:s}", "restore_cleaning_failed": "Could not clean up the temporary restoration directory", "restore_complete": "Restored", "restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]", @@ -616,7 +616,7 @@ "ssowat_conf_updated": "SSOwat configuration updated", "system_upgraded": "System upgraded", "system_username_exists": "Username already exists in the list of 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 apt install --fix-broken` and/or `sudo dpkg --configure -a`.", + "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 apt install --fix-broken` and/or `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": "Could not hold critical packages…", @@ -626,15 +626,15 @@ "tools_upgrade_special_packages": "Now upgrading 'special' (yunohost-related) packages…", "tools_upgrade_special_packages_explanation": "The special upgrade will continue in the background. Please don't start any other actions on your server for the next ~10 minutes (depending on hardware speed). After this, you may have to re-log in to the webadmin. The upgrade log will be available in Tools → Log (in the webadmin) or using 'yunohost log list' (from the 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", + "unbackup_app": "{app:s} will not be saved", "unexpected_error": "Something unexpected went wrong: {error}", "unlimit": "No quota", - "unrestore_app": "App '{app:s}' will not be restored", + "unrestore_app": "{app:s} will not be restored", "update_apt_cache_failed": "Could not to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}", "update_apt_cache_warning": "Something went wrong while updating the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}", - "updating_apt_cache": "Fetching available upgrades for system packages…", + "updating_apt_cache": "Fetching available upgrades for system packages...", "upgrade_complete": "Upgrade complete", - "upgrading_packages": "Upgrading packages…", + "upgrading_packages": "Upgrading packages...", "upnp_dev_not_found": "No UPnP device found", "upnp_disabled": "UPnP turned off", "upnp_enabled": "UPnP turned on", @@ -653,7 +653,7 @@ "yunohost_ca_creation_failed": "Could not create certificate authority", "yunohost_ca_creation_success": "Local certification authority created.", "yunohost_configured": "YunoHost is now configured", - "yunohost_installing": "Installing YunoHost…", + "yunohost_installing": "Installing YunoHost...", "yunohost_not_installed": "YunoHost is not correctly installed. Please run 'yunohost tools postinstall'", "yunohost_postinstall_end_tip": "The post-install completed! To finalize your setup, please consider:\n - adding a first user through the 'Users' section of the webadmin (or 'yunohost user create ' in command-line);\n - diagnose potential issues through the 'Diagnosis' section of the webadmin (or 'yunohost diagnosis run' in command-line);\n - reading the 'Finalizing your setup' and 'Getting to know Yunohost' parts in the admin documentation: https://yunohost.org/admindoc." } From 1a2f26dc34600e97c65eb0359d79b91c85a07eab Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 24 Jul 2020 00:52:46 +0200 Subject: [PATCH 071/262] [fix] Let's not redefine the value for the 'service' var ... --- src/yunohost/service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 278cea0a4..f12c0743c 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -97,8 +97,8 @@ def service_add(name, description=None, log=None, log_type=None, test_status=Non service["test_status"] = test_status else: # Try to get the description from systemd service - _, service = _get_service_information_from_systemd(name) - type_ = service.get("Type") if service is not None else "" + _, systemd_info = _get_service_information_from_systemd(name) + type_ = systemd_info.get("Type") if systemd_info is not None else "" if type_ == "oneshot": logger.warning("/!\\ Packagers! Please provide a --test_status when adding oneshot-type services in Yunohost, such that it has a reliable way to check if the service is running or not.") From bbbafaddd263e2000542d95a4075b3b61d857638 Mon Sep 17 00:00:00 2001 From: ppr Date: Fri, 26 Jun 2020 17:14:39 +0000 Subject: [PATCH 072/262] Translated using Weblate (French) Currently translated at 99.8% (655 of 656 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index b24f13544..4ef18e1e0 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -168,7 +168,7 @@ "certmanager_attempt_to_renew_valid_cert": "Le certificat pour le domaine {domain:s} n’est pas sur le point d’expirer ! (Vous pouvez utiliser --force si vous savez ce que vous faites)", "certmanager_domain_http_not_working": "Le domaine {domain:s} ne semble pas être accessible via HTTP. Merci de vérifier la catégorie 'Web' dans le diagnostic pour plus d'informations. (Ou si vous savez ce que vous faites, utilisez '--no-checks' pour désactiver la vérification.)", "certmanager_error_no_A_record": "Aucun enregistrement DNS 'A' n’a été trouvé pour {domain:s}. Vous devez faire pointer votre nom de domaine vers votre machine pour être en mesure d’installer un certificat Let’s Encrypt ! (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)", - "certmanager_domain_dns_ip_differs_from_public_ip": "L'enregistrement DNS du domaine {domain:s} est différent de l’adresse IP de ce serveur. Pour plus d'informations, veuillez consulter la catégorie \"Enregistrements DNS\" dans le diagnostic. Si vous avez récemment modifié votre enregistrement 'A', veuillez attendre sa propagation (des vérificateurs de propagation DNS sont disponibles en ligne). (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)", + "certmanager_domain_dns_ip_differs_from_public_ip": "L'enregistrement DNS du domaine {domain:s} est différent de l’adresse IP de ce serveur. Pour plus d'informations, veuillez consulter la catégorie \"Enregistrements DNS\" dans la section diagnostic. Si vous avez récemment modifié votre enregistrement 'A', veuillez attendre sa propagation (des vérificateurs de propagation DNS sont disponibles en ligne). (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)", "certmanager_cannot_read_cert": "Quelque chose s’est mal passé lors de la tentative d’ouverture du certificat actuel pour le domaine {domain:s} (fichier : {file:s}), la cause est : {reason:s}", "certmanager_cert_install_success_selfsigned": "Le certificat auto-signé est maintenant installé pour le domaine « {domain:s} »", "certmanager_cert_install_success": "Le certificat Let’s Encrypt est maintenant installé pour le domaine « {domain:s} »", @@ -392,7 +392,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 apt install --fix-broken` et/ou `sudo dpkg --configure -a`.", "app_action_cannot_be_ran_because_required_services_down": "Ces services requis doivent être en cours d’exécution pour exécuter cette action : {services}. Essayez de les redémarrer pour continuer (et éventuellement rechercher pourquoi ils sont en panne).", - "admin_password_too_long": "Veuillez choisir un mot de passe de moins de 127 caractères", + "admin_password_too_long": "Veuillez choisir un mot de passe comportant moins de 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}'", @@ -660,7 +660,7 @@ "migration_0015_system_not_fully_up_to_date": "Votre système n'est pas entièrement à jour. Veuillez effectuer une mise à jour normale avant de lancer la migration vers Buster.", "migration_0015_not_enough_free_space": "L'espace libre est très faible dans /var/ ! Vous devriez avoir au moins 1 Go de libre pour effectuer cette migration.", "migration_0015_not_stretch": "La distribution Debian actuelle n'est pas Stretch !", - "migration_0015_yunohost_upgrade": "Lancement de la mise à jour de YunoHost …", + "migration_0015_yunohost_upgrade": "Démarrage de la mise à jour de YunoHost …", "migration_0015_still_on_stretch_after_main_upgrade": "Quelque chose s'est mal passé lors de la mise à niveau, le système semble toujours être sous Debian Stretch", "migration_0015_main_upgrade": "Début de la mise à niveau générale …", "migration_0015_patching_sources_list": "Mise à jour du fichier sources.lists …", From fcdc1bf37bcaaf487d7de6e7154c74253bd90787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quent=C3=AD?= Date: Sun, 21 Jun 2020 12:53:03 +0000 Subject: [PATCH 073/262] Translated using Weblate (Occitan) Currently translated at 57.2% (375 of 656 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/oc/ --- locales/oc.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/locales/oc.json b/locales/oc.json index 90be0b561..7ba8525ce 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -580,5 +580,7 @@ "diagnosis_basesystem_hardware": "L’arquitectura del servidor es {virt} {arch}", "diagnosis_basesystem_hardware_board": "Lo modèl de carta del servidor es {model}", "backup_archive_corrupted": "Sembla que l’archiu de la salvagarda « {archive} » es corromput : {error}", - "diagnosis_domain_expires_in": "{domain} expiraà d’aquí {days} jorns." + "diagnosis_domain_expires_in": "{domain} expiraà d’aquí {days} jorns.", + "migration_0015_cleaning_up": "Netejatge de la memòria cache e dels paquets pas mai necessaris…", + "restore_already_installed_apps": "Restauracion impossibla de las aplicacions seguentas que son ja installadas : {apps}" } From df239f946e5a35d601676025c75cf811712dbb7f Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Tue, 30 Jun 2020 13:15:41 +0000 Subject: [PATCH 074/262] Translated using Weblate (Catalan) Currently translated at 100.0% (657 of 657 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/ca.json b/locales/ca.json index f29a65c43..254bae8d1 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -666,5 +666,6 @@ "migration_0015_patching_sources_list": "Apedaçament de source.lists…", "migration_0015_start": "Començant la migració a Buster", "migration_description_0015_migrate_to_buster": "Actualitza els sistema a Debian Buster i YunoHost 4.x", - "regenconf_need_to_explicitly_specify_ssh": "La configuració ssh ha estat modificada manualment, però heu d'especificar explícitament la categoria «ssh» amb --force per fer realment els canvis." + "regenconf_need_to_explicitly_specify_ssh": "La configuració ssh ha estat modificada manualment, però heu d'especificar explícitament la categoria «ssh» amb --force per fer realment els canvis.", + "migration_0015_weak_certs": "S'han trobat els següents certificats que encara utilitzen algoritmes de signatura febles i s'han d'actualitzar per a ser compatibles amb la propera versió de nginx: {certs}" } From ae38781714ecb3d991539026e2fb211c11908f03 Mon Sep 17 00:00:00 2001 From: ppr Date: Tue, 30 Jun 2020 20:03:07 +0000 Subject: [PATCH 075/262] Translated using Weblate (French) Currently translated at 99.8% (656 of 657 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 4ef18e1e0..4b229d470 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -662,10 +662,11 @@ "migration_0015_not_stretch": "La distribution Debian actuelle n'est pas Stretch !", "migration_0015_yunohost_upgrade": "Démarrage de la mise à jour de YunoHost …", "migration_0015_still_on_stretch_after_main_upgrade": "Quelque chose s'est mal passé lors de la mise à niveau, le système semble toujours être sous Debian Stretch", - "migration_0015_main_upgrade": "Début de la mise à niveau générale …", + "migration_0015_main_upgrade": "Démarrage de la mise à niveau générale …", "migration_0015_patching_sources_list": "Mise à jour du fichier sources.lists …", "migration_0015_start": "Démarrage de la migration vers Buster", "migration_description_0015_migrate_to_buster": "Mise à niveau du système vers Debian Buster et YunoHost 4.x", "diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par Yunohost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant yunohost dyndns update --force.", - "app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de Yunohost. Vous devriez probablement envisager de mettre à jour votre système." + "app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de Yunohost. Vous devriez probablement envisager de mettre à jour votre système.", + "migration_0015_weak_certs": "Il a été constaté que les certificats suivants utilisent encore des algorithmes de signature peu robustes et doivent être mis à jour pour être compatibles avec la prochaine version de nginx : {certs}" } From 6412959af48bcb93fffb18b0b0de4f5ebbd40894 Mon Sep 17 00:00:00 2001 From: Leandro Noferini Date: Sat, 11 Jul 2020 09:54:26 +0000 Subject: [PATCH 076/262] Translated using Weblate (Italian) Currently translated at 21.2% (139 of 657 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/it/ --- locales/it.json | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/locales/it.json b/locales/it.json index 19884b5bb..88968863c 100644 --- a/locales/it.json +++ b/locales/it.json @@ -27,16 +27,16 @@ "user_deleted": "L'utente è stato cancellato", "admin_password": "Password dell'amministrazione", "admin_password_change_failed": "Impossibile cambiare la password", - "admin_password_changed": "La password dell'amministrazione è stata cambiata", + "admin_password_changed": "La password d'amministrazione è stata cambiata", "app_install_files_invalid": "Questi file non possono essere installati", - "app_manifest_invalid": "Manifesto dell'applicazione non valido: {error}", + "app_manifest_invalid": "C'è qualcosa di scorretto nel manifesto dell'applicazione: {error}", "app_not_correctly_installed": "{app:s} sembra di non essere installata correttamente", "app_not_properly_removed": "{app:s} non è stata correttamente rimossa", "action_invalid": "L'azione '{action:s}' non è valida", - "app_removed": "{app:s} è stata rimossa", + "app_removed": "{app:s} rimossa", "app_sources_fetch_failed": "Impossibile riportare i file sorgenti, l'URL è corretto?", - "app_upgrade_failed": "Impossibile aggiornare {app:s}", - "app_upgraded": "{app:s} è stata aggiornata", + "app_upgrade_failed": "Impossibile aggiornare {app:s}: {error}", + "app_upgraded": "{app:s} aggiornata", "app_requirements_checking": "Controllo i pacchetti richiesti per {app}…", "app_requirements_unmeet": "Requisiti non soddisfatti per {app}, il pacchetto {pkgname} ({version}) deve essere {spec}", "ask_firstname": "Nome", @@ -175,11 +175,11 @@ "app_already_up_to_date": "{app:s} è già aggiornata", "app_change_url_failed_nginx_reload": "Non riesco a riavviare NGINX. Questo è il risultato di 'nginx -t':\n{nginx_errors:s}", "app_change_url_identical_domains": "Il vecchio ed il nuovo dominio/percorso_url sono identici ('{domain:s}{path:s}'), nessuna operazione necessaria.", - "app_change_url_no_script": "L'applicazione '{app_name:s}' non supporta ancora la modifica dell'URL. Forse dovresti aggiornare l'applicazione.", + "app_change_url_no_script": "L'applicazione '{app_name:s}' non supporta ancora la modifica dell'URL. Forse dovresti aggiornarla.", "app_change_url_success": "L'URL dell'applicazione {app:s} è stato cambiato in {domain:s}{path:s}", "app_make_default_location_already_used": "Impostazione dell'applicazione '{app}' come predefinita del dominio non riuscita perché il dominio {domain} è già in uso per l'altra applicazione '{other_app}'", "app_location_unavailable": "Questo URL non è più disponibile o va in conflitto con la/le applicazione/i già installata/e:\n{apps:s}", - "app_upgrade_app_name": "Aggiornando l'applicazione {app}…", + "app_upgrade_app_name": "Aggiornamento dell'applicazione {app}…", "app_upgrade_some_app_failed": "Impossibile aggiornare alcune applicazioni", "backup_abstract_method": "Questo metodo di backup non è ancora stato implementato", "backup_applying_method_borg": "Inviando tutti i file da salvare nel backup nel deposito borg-backup…", @@ -214,9 +214,9 @@ "admin_password_too_long": "Per favore scegli una password più corta di 127 caratteri", "app_not_upgraded": "Impossibile aggiornare le applicazioni '{failed_app}' e di conseguenza l'aggiornamento delle seguenti applicazione è stato cancellato: {apps}", "app_start_install": "Installando l'applicazione '{app}'…", - "app_start_remove": "Rimuovendo l'applicazione {app}…", - "app_start_backup": "Raccogliendo file da salvare nel backup per {app}…", - "app_start_restore": "Ripristinando l'applicazione {app}…", + "app_start_remove": "Rimozione dell'applicazione {app}…", + "app_start_backup": "Raccogliendo file da salvare nel backup per '{app}'…", + "app_start_restore": "Ripristino dell'applicazione '{app}'…", "app_upgrade_several_apps": "Le seguenti app saranno aggiornate : {apps}", "ask_new_domain": "Nuovo dominio", "ask_new_path": "Nuovo percorso", @@ -264,7 +264,7 @@ "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", - "already_up_to_date": "Niente da fare! Tutto è già aggiornato!", + "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)", @@ -339,5 +339,6 @@ "app_remove_after_failed_install": "Rimozione dell'applicazione a causa del fallimento dell'installazione…", "app_install_script_failed": "Si è verificato un errore nello script di installazione dell'applicazione", "app_install_failed": "Impossibile installare {app}:{error}", - "app_full_domain_unavailable": "Spiacente, questa app deve essere installata su un proprio dominio, ma altre applicazioni sono state installate sul dominio '{domain}'. Dovresti invece usare un sotto-dominio dedicato per questa app." + "app_full_domain_unavailable": "Spiacente, questa app deve essere installata su un proprio dominio, ma altre applicazioni sono state installate sul dominio '{domain}'. Dovresti invece usare un sotto-dominio dedicato per questa app.", + "app_upgrade_script_failed": "È stato trovato un errore nello script di aggiornamento dell'applicazione" } From fca1df68057e2beeb7117cbb050a163eedaf3fa8 Mon Sep 17 00:00:00 2001 From: Kayou Date: Mon, 27 Jul 2020 15:30:15 +0200 Subject: [PATCH 077/262] [FIX] patch sources list for armbian (#1028) * [FIX] patch sources list for armbian * Update src/yunohost/data_migrations/0015_migrate_to_buster.py Co-authored-by: Alexandre Aubin --- src/yunohost/data_migrations/0015_migrate_to_buster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/data_migrations/0015_migrate_to_buster.py b/src/yunohost/data_migrations/0015_migrate_to_buster.py index 4f7f8afeb..6af161c89 100644 --- a/src/yunohost/data_migrations/0015_migrate_to_buster.py +++ b/src/yunohost/data_migrations/0015_migrate_to_buster.py @@ -173,7 +173,7 @@ class MyMigration(Migration): command = "sed -i -e 's@ stretch @ buster @g' " \ "-e '/backports/ s@^#*@#@' " \ "-e 's@ stretch/updates @ buster/updates @g' " \ - "-e 's@ stretch-updates @ buster-updates @g' " \ + "-e 's@ stretch-@ buster-@g' " \ "{}".format(f) os.system(command) From 852dea07f3dc491b72cf8bd6754274ee58e151e4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 27 Jul 2020 17:12:55 +0200 Subject: [PATCH 078/262] Tweak custom disclaimer about the migration still being a bit touchy in preparation for stable release --- src/yunohost/data_migrations/0015_migrate_to_buster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/data_migrations/0015_migrate_to_buster.py b/src/yunohost/data_migrations/0015_migrate_to_buster.py index 6af161c89..2de5c7c08 100644 --- a/src/yunohost/data_migrations/0015_migrate_to_buster.py +++ b/src/yunohost/data_migrations/0015_migrate_to_buster.py @@ -150,7 +150,7 @@ class MyMigration(Migration): message = m18n.n("migration_0015_general_warning") - message = "THIS MIGRATION IS CURRENTLY IN ALPHA-STAGE TESTING. YOU SHOULD *NOT* RUN IT FOR NOW ON A PRODUCTION SERVER UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING OR HAVE A WAY TO ROLLBACK. MORE INFO ON THE FORUM!\n\n" + message + message = "N.B.: This migration has been tested by the community over the last few months but has only been declared stable recently. If your server hosts critical services and if you are not too confident with debugging possible issues, we recommend you to wait a little bit more while we gather more feedback and polish things up. If on the other hand you are relatively confident with debugging small issues that may arise, you are encouraged to run this migration ;)! You can read about remaining known issues and feedback from the community here: https://forum.yunohost.org/t/12195\n\n" + message if problematic_apps: message += "\n\n" + m18n.n("migration_0015_problematic_apps_warning", problematic_apps=problematic_apps) From 02385c58b0b35ec218d6adc6877efa968f50d5d0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 27 Jul 2020 19:13:28 +0200 Subject: [PATCH 079/262] Update changelog for 3.8.5.5 --- debian/changelog | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/debian/changelog b/debian/changelog index 8e3eea7d8..392ead86a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,21 @@ +yunohost (3.8.5.5) stable; urgency=low + + - [enh] Allow to extend the nginx default_server configuration (f1bfc521) + - [mod] Move redirect to /yunohost/admin to a separate nginx conf file to allow customizing it more easily (ac9182d6) + - [enh] Make sure to validate/upgrade that we don't have any active weak certificate used by nginx at the beginning of the buster migration, otherwise nginx will later miserably fail to start (d4358897) + - [fix] get_files_diff crashing if {orig,new}_file is None (7bfe564a) + - [enh] Remove some useless message about file that "wasn't deleted because it doesn't exist." (#1024) + - [mod] Remove useless robot protection code (#1026) + - [fix] Let's not redefine the value for the 'service' var ... (1a2f26dc) + - [fix] More general stretch->buster patching for sources.list (#1028) + - [mod] Tweak custom disclaimer about the migration still being a bit touchy in preparation for stable release (852dea07) + - [mod] Typo/wording in en.json (#1030) + - [i18n] Translations updated for Catalan, French, Italian, Occitan + + Thanks to all contributors <3 ! (É. Gaspar, Kay0u, L. Noferini, ppr, Quentí, xaloc33) + + -- Alexandre Aubin Mon, 27 Jul 2020 19:03:33 +0200 + yunohost (3.8.5.4) testing; urgency=low - [fix] Fix unscd version parsing *again* From 6b72fc5fc5fa42277bdb23e3d166e8c177b04033 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Apr 2020 00:23:36 +0200 Subject: [PATCH 080/262] It just make no sense to backup/restore the mysql password... --- data/hooks/backup/11-conf_ynh_mysql | 13 --------- data/hooks/restore/11-conf_ynh_mysql | 42 ---------------------------- 2 files changed, 55 deletions(-) delete mode 100755 data/hooks/backup/11-conf_ynh_mysql delete mode 100644 data/hooks/restore/11-conf_ynh_mysql diff --git a/data/hooks/backup/11-conf_ynh_mysql b/data/hooks/backup/11-conf_ynh_mysql deleted file mode 100755 index 031707337..000000000 --- a/data/hooks/backup/11-conf_ynh_mysql +++ /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/mysql" - -# Save MySQL root password -ynh_backup "/etc/yunohost/mysql" "${backup_dir}/root_pwd" diff --git a/data/hooks/restore/11-conf_ynh_mysql b/data/hooks/restore/11-conf_ynh_mysql deleted file mode 100644 index f54641d6f..000000000 --- a/data/hooks/restore/11-conf_ynh_mysql +++ /dev/null @@ -1,42 +0,0 @@ -backup_dir="$1/conf/ynh/mysql" -MYSQL_PKG="$(dpkg --list | sed -ne 's/^ii \(mariadb-server-[[:digit:].]\+\) .*$/\1/p')" - -. /usr/share/yunohost/helpers - -# ensure that mysql is running -service mysql status >/dev/null 2>&1 \ - || service mysql start - -# retrieve current and new password -[ -f /etc/yunohost/mysql ] \ - && curr_pwd=$(cat /etc/yunohost/mysql) -new_pwd=$(cat "${backup_dir}/root_pwd" || cat "${backup_dir}/mysql") -[ -z "$curr_pwd" ] && curr_pwd="yunohost" -[ -z "$new_pwd" ] && { - new_pwd=$(ynh_string_random 10) -} - -# attempt to change it -mysqladmin -s -u root -p"$curr_pwd" password "$new_pwd" || { - - echo "It seems that you have already configured MySQL." \ - "YunoHost needs to have a root access to MySQL to runs its" \ - "applications, and is going to reset the MySQL root password." \ - "You can find this new password in /etc/yunohost/mysql." >&2 - - # set new password with debconf - debconf-set-selections << EOF -$MYSQL_PKG mysql-server/root_password password $new_pwd -$MYSQL_PKG mysql-server/root_password_again password $new_pwd -EOF - - # reconfigure Debian package - dpkg-reconfigure -freadline -u "$MYSQL_PKG" 2>&1 -} - -# store new root password -echo "$new_pwd" | tee /etc/yunohost/mysql -chmod 400 /etc/yunohost/mysql - -# reload the grant tables -mysqladmin -s -u root -p"$new_pwd" reload From bc19bef59e41d0227eb95b2a2c67d81c7841c77c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 12 Apr 2020 18:57:35 +0200 Subject: [PATCH 081/262] Workaround for custom backup/restore hook issue --- data/hooks/restore/11-conf_ynh_mysql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/hooks/restore/11-conf_ynh_mysql diff --git a/data/hooks/restore/11-conf_ynh_mysql b/data/hooks/restore/11-conf_ynh_mysql new file mode 100644 index 000000000..11353425a --- /dev/null +++ b/data/hooks/restore/11-conf_ynh_mysql @@ -0,0 +1,5 @@ +# We don't backup/restore mysql password anymore +# c.f. https://github.com/YunoHost/yunohost/pull/912 + +# This is a dummy empty file as a workaround for +# https://github.com/YunoHost/issues/issues/1553 until it is fixed From 8d5422d13f33c65a4ae321dc6592079dbb83e0f1 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Sun, 8 Mar 2020 00:18:48 +0100 Subject: [PATCH 082/262] [fix] Fix getopts and helpers --- data/helpers.d/getopts | 8 ++++---- data/helpers.d/mysql | 15 +++++++++++++-- data/helpers.d/postgresql | 14 ++++++++++++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/data/helpers.d/getopts b/data/helpers.d/getopts index 10c06930c..a4bbe20e6 100644 --- a/data/helpers.d/getopts +++ b/data/helpers.d/getopts @@ -80,10 +80,10 @@ ynh_handle_getopts_args () { arguments[arg]="${arguments[arg]//--${args_array[$option_flag]}-/--${args_array[$option_flag]}\\TOBEREMOVED\\-}" # And replace long option (value of the option_flag) by the short option, the option_flag itself # (e.g. for [u]=user, --user will be -u) - # Replace long option with = - arguments[arg]="${arguments[arg]//--${args_array[$option_flag]}/-${option_flag} }" - # And long option without = - arguments[arg]="${arguments[arg]//--${args_array[$option_flag]%=}/-${option_flag}}" + # Replace long option with = (match the beginning of the argument) + arguments[arg]="$(echo "${arguments[arg]}" | sed "s/^--${args_array[$option_flag]}/-${option_flag} /")" + # And long option without = (match the whole line) + arguments[arg]="$(echo "${arguments[arg]}" | sed "s/^--${args_array[$option_flag]%=}$/-${option_flag} /")" done done diff --git a/data/helpers.d/mysql b/data/helpers.d/mysql index 84acc1029..05f75e0a2 100644 --- a/data/helpers.d/mysql +++ b/data/helpers.d/mysql @@ -44,8 +44,13 @@ ynh_mysql_execute_as_root() { ynh_handle_getopts_args "$@" database="${database:-}" + if [ -n "$database" ] + then + database="--database=$database" + fi + ynh_mysql_connect_as --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" \ - --database="$database" <<< "$sql" + $database <<< "$sql" } # Execute a command from a file as root user @@ -65,8 +70,14 @@ ynh_mysql_execute_file_as_root() { ynh_handle_getopts_args "$@" database="${database:-}" + if [ -n "$database" ] + then + database="--database=$database" + fi + + ynh_mysql_connect_as --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" \ - --database="$database" < "$file" + $database < "$file" } # Create a database and grant optionnaly privilegies to a user diff --git a/data/helpers.d/postgresql b/data/helpers.d/postgresql index 284c02d3e..7eb4e7289 100644 --- a/data/helpers.d/postgresql +++ b/data/helpers.d/postgresql @@ -46,8 +46,13 @@ ynh_psql_execute_as_root() { ynh_handle_getopts_args "$@" database="${database:-}" + if [ -n "$database" ] + then + database="--database=$database" + fi + ynh_psql_connect_as --user="postgres" --password="$(cat $PSQL_ROOT_PWD_FILE)" \ - --database="$database" <<<"$sql" + $database <<<"$sql" } # Execute a command from a file as root user @@ -67,8 +72,13 @@ ynh_psql_execute_file_as_root() { ynh_handle_getopts_args "$@" database="${database:-}" + if [ -n "$database" ] + then + database="--database=$database" + fi + ynh_psql_connect_as --user="postgres" --password="$(cat $PSQL_ROOT_PWD_FILE)" \ - --database="$database" <"$file" + $database <"$file" } # Create a database and grant optionnaly privilegies to a user From 834b76700062784de8f2cec5bbbf6318fba39367 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 12 Aug 2019 11:43:49 +0200 Subject: [PATCH 083/262] [fix] Explicitly create home using mkhomedir_helper instead of obscure pam rule that doesn't work anymore --- debian/conf/pam/mkhomedir | 6 ------ debian/install | 1 - debian/postinst | 5 ----- src/yunohost/firewall.py | 6 +----- src/yunohost/user.py | 3 +-- 5 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 debian/conf/pam/mkhomedir diff --git a/debian/conf/pam/mkhomedir b/debian/conf/pam/mkhomedir deleted file mode 100644 index eedc8b745..000000000 --- a/debian/conf/pam/mkhomedir +++ /dev/null @@ -1,6 +0,0 @@ -Name: Create home directory during login -Default: yes -Priority: 900 -Session-Type: Additional -Session: - required pam_mkhomedir.so umask=0022 skel=/etc/skel diff --git a/debian/install b/debian/install index 777d7973e..1691a4849 100644 --- a/debian/install +++ b/debian/install @@ -13,7 +13,6 @@ data/other/* /usr/share/yunohost/yunohost-config/moulinette/ data/templates/* /usr/share/yunohost/templates/ data/helpers /usr/share/yunohost/ data/helpers.d/* /usr/share/yunohost/helpers.d/ -debian/conf/pam/* /usr/share/pam-configs/ lib/metronome/modules/* /usr/lib/metronome/modules/ locales/* /usr/lib/moulinette/yunohost/locales/ src/yunohost /usr/lib/moulinette diff --git a/debian/postinst b/debian/postinst index 9c78c8432..4b43b2506 100644 --- a/debian/postinst +++ b/debian/postinst @@ -29,11 +29,6 @@ do_configure() { # Yunoprompt systemctl enable yunoprompt.service - - # remove old PAM config and update it - [[ ! -f /usr/share/pam-configs/my_mkhomedir ]] \ - || rm /usr/share/pam-configs/my_mkhomedir - pam-auth-update --package } # summary of how this script can be called: diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index 9d209dbb8..4c3cfb0f7 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -26,11 +26,7 @@ import os import sys import yaml -try: - import miniupnpc -except ImportError: - sys.stderr.write('Error: Yunohost CLI Require miniupnpc lib\n') - sys.exit(1) +import miniupnpc from moulinette import m18n from yunohost.utils.error import YunohostError diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 7f8f2dc35..ed2a44670 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -208,8 +208,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, try: # Attempt to create user home folder - subprocess.check_call( - ['su', '-', username, '-c', "''"]) + subprocess.check_call(["mkhomedir_helper", username]) except subprocess.CalledProcessError: if not os.path.isdir('/home/{0}'.format(username)): logger.warning(m18n.n('user_home_creation_failed'), From 0a9f4d59cb7cd6438376035ebc4e4326981d0213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Mollier?= Date: Wed, 14 Aug 2019 16:28:40 +0200 Subject: [PATCH 084/262] [fix] Ldap interface seems to expect lists everywhere now? --- src/yunohost/app.py | 1 + src/yunohost/ssh.py | 4 ++-- src/yunohost/tools.py | 18 +++++++++--------- src/yunohost/user.py | 40 ++++++++++++++++++++-------------------- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 6af892b03..afa81127d 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -865,6 +865,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu os.path.join(extracted_app_folder, 'scripts/remove'), args=[app_instance_name], env=env_dict_remove )[0] + # Here again, calling hook_exec could fail miserably, or get # manually interrupted (by mistake or because script was stuck) # In that case we still want to proceed with the rest of the diff --git a/src/yunohost/ssh.py b/src/yunohost/ssh.py index f0110b34e..be876ce16 100644 --- a/src/yunohost/ssh.py +++ b/src/yunohost/ssh.py @@ -25,7 +25,7 @@ def user_ssh_allow(username): from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() - ldap.update('uid=%s,ou=users' % username, {'loginShell': '/bin/bash'}) + 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 ? @@ -46,7 +46,7 @@ def user_ssh_disallow(username): from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() - ldap.update('uid=%s,ou=users' % username, {'loginShell': '/bin/false'}) + 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 ? diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index d5fab2fb1..75a27aa66 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -88,15 +88,15 @@ def tools_ldapinit(): logger.warn("Error when trying to inject '%s' -> '%s' into ldap: %s" % (rdn, attr_dict, e)) admin_dict = { - 'cn': 'admin', - 'uid': 'admin', - 'description': 'LDAP Administrator', - 'gidNumber': '1007', - 'uidNumber': '1007', - 'homeDirectory': '/home/admin', - 'loginShell': '/bin/bash', + 'cn': ['admin'], + 'uid': ['admin'], + 'description': ['LDAP Administrator'], + 'gidNumber': ['1007'], + 'uidNumber': ['1007'], + 'homeDirectory': ['/home/admin'], + 'loginShell': ['/bin/bash'], 'objectClass': ['organizationalRole', 'posixAccount', 'simpleSecurityObject'], - 'userPassword': 'yunohost' + 'userPassword': ['yunohost'] } ldap.update('cn=admin', admin_dict) @@ -140,7 +140,7 @@ def tools_adminpw(new_password, check_strength=True): ldap = _get_ldap_interface() try: - ldap.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') diff --git a/src/yunohost/user.py b/src/yunohost/user.py index ed2a44670..d19da177c 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -178,19 +178,19 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, fullname = '%s %s' % (firstname, lastname) attr_dict = { 'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh'], - 'givenName': firstname, - 'sn': lastname, - 'displayName': fullname, - 'cn': fullname, - 'uid': username, - 'mail': mail, - 'maildrop': username, - 'mailuserquota': mailbox_quota, - 'userPassword': _hash_user_password(password), - 'gidNumber': uid, - 'uidNumber': uid, - 'homeDirectory': '/home/' + username, - 'loginShell': '/bin/false' + 'givenName': [firstname], + 'sn': [lastname], + 'displayName': [fullname], + 'cn': [fullname], + 'uid': [username], + 'mail': mail, # NOTE: this one seems to be already a list + 'maildrop': [username], + 'mailuserquota': [mailbox_quota], + 'userPassword': [_hash_user_password(password)], + 'gidNumber': [uid], + 'uidNumber': [uid], + 'homeDirectory': ['/home/' + username], + 'loginShell': ['/bin/false'] } # If it is the first user, add some aliases @@ -316,21 +316,21 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail= # Get modifications from arguments new_attr_dict = {} if firstname: - new_attr_dict['givenName'] = firstname # TODO: Validate - new_attr_dict['cn'] = new_attr_dict['displayName'] = firstname + ' ' + user['sn'][0] + new_attr_dict['givenName'] = [firstname] # TODO: Validate + new_attr_dict['cn'] = new_attr_dict['displayName'] = [firstname + ' ' + user['sn'][0]] if lastname: - new_attr_dict['sn'] = lastname # TODO: Validate - new_attr_dict['cn'] = new_attr_dict['displayName'] = user['givenName'][0] + ' ' + lastname + new_attr_dict['sn'] = [lastname] # TODO: Validate + new_attr_dict['cn'] = new_attr_dict['displayName'] = [user['givenName'][0] + ' ' + lastname] if lastname and firstname: - new_attr_dict['cn'] = new_attr_dict['displayName'] = firstname + ' ' + lastname + new_attr_dict['cn'] = new_attr_dict['displayName'] = [firstname + ' ' + lastname] if change_password: # Ensure sufficiently complex password assert_password_is_strong_enough("user", change_password) - new_attr_dict['userPassword'] = _hash_user_password(change_password) + new_attr_dict['userPassword'] = [_hash_user_password(change_password)] if mail: main_domain = _get_maindomain() @@ -395,7 +395,7 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail= new_attr_dict['maildrop'] = user['maildrop'] if mailbox_quota is not None: - new_attr_dict['mailuserquota'] = mailbox_quota + new_attr_dict['mailuserquota'] = [mailbox_quota] operation_logger.start() From 4f9216b07c939e57074ff2d3a9fd00a1b9820d70 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 7 Feb 2020 21:17:14 +0100 Subject: [PATCH 085/262] [deb] Tmp version for debian builds during experimental/alpha dev for Buster --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 392ead86a..7f1e50190 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +yunohost (4.0.0~alpha) unstable; urgency=low + + - Tmp version for debian builds during experimental/alpha dev for Buster + + -- Alexandre Aubin Fri, 7 Feb 2020 21:15:00 +0000 + yunohost (3.8.5.5) stable; urgency=low - [enh] Allow to extend the nginx default_server configuration (f1bfc521) From e9d5abf5e6efe0430a2992fbe133cae79f489718 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 7 Feb 2020 22:21:08 +0100 Subject: [PATCH 086/262] [deb] Clean control file, remove some legacy Conflicts and Replaces -- it is safe to do so because 3.x instances already have these Conflicts / Replaces, so it's okay to remove them for 4.x --- debian/control | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/debian/control b/debian/control index 96b5bbb08..7cb1761b8 100644 --- a/debian/control +++ b/debian/control @@ -14,9 +14,8 @@ Depends: ${python:Depends}, ${misc:Depends} , moulinette (>= 3.7), ssowat (>= 3.7) , python-psutil, python-requests, python-dnspython, python-openssl , python-miniupnpc, python-dbus, python-jinja2 - , python-toml, python-packaging + , python-toml, python-packaging, python-publicsuffix , apt, apt-transport-https, dirmngr - , nginx, nginx-extras (>=1.6.2) , php-fpm, php-ldap, php-intl , mariadb-server, php-mysql | php-mysqlnd , openssh-server, iptables, fail2ban, dnsutils, bind9utils @@ -25,10 +24,10 @@ Depends: ${python:Depends}, ${misc:Depends} , dnsmasq, avahi-daemon, libnss-mdns, resolvconf, libnss-myhostname , postfix, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre , dovecot-core, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved, dovecot-antispam - , rspamd (>= 1.6.0), opendkim-tools, postsrsd, procmail, mailutils + , rspamd, opendkim-tools, postsrsd, procmail, mailutils , redis-server , metronome (>=3.14.0) - , git, curl, wget, cron, unzip, jq + , git, curl, wget, cron, unzip, jq, bc , lsb-release, haveged, fake-hwclock, equivs, lsof, whois, python-publicsuffix Recommends: yunohost-admin , ntp, inetutils-ping | iputils-ping @@ -39,17 +38,8 @@ Recommends: yunohost-admin , libdbd-ldap-perl, libnet-dns-perl Suggests: htop, vim, rsync, acpi-support-base, udisks2 Conflicts: iptables-persistent - , moulinette-yunohost, yunohost-config - , yunohost-config-others, yunohost-config-postfix - , yunohost-config-dovecot, yunohost-config-slapd - , yunohost-config-nginx, yunohost-config-amavis - , yunohost-config-mysql, yunohost-predepends - , apache2, bind9 -Replaces: moulinette-yunohost, yunohost-config - , yunohost-config-others, yunohost-config-postfix - , yunohost-config-dovecot, yunohost-config-slapd - , yunohost-config-nginx, yunohost-config-amavis - , yunohost-config-mysql, yunohost-predepends + , apache2 + , bind9 Description: manageable and configured self-hosting server YunoHost aims to make self-hosting accessible to everyone. It configures an email, Web and IM server alongside a LDAP base. It also provides From d935ab64f6141f36a386b709c13635bf11520c01 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 2 May 2020 04:37:09 +0200 Subject: [PATCH 087/262] [deb] Add conflicts with versions from backports for critical dependencies --- debian/control | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/control b/debian/control index 7cb1761b8..6abb4c0c4 100644 --- a/debian/control +++ b/debian/control @@ -40,6 +40,13 @@ Suggests: htop, vim, rsync, acpi-support-base, udisks2 Conflicts: iptables-persistent , apache2 , bind9 + , nginx-extras (>= 1.16) + , openssl (>= 1.1.1g) + , slapd (>= 2.4.49) + , dovecot-core (>= 1:2.3.7) + , redis-server (>= 5:5.0.7) + , fail2ban (>= 0.11) + , iptables (>= 1.8.3) Description: manageable and configured self-hosting server YunoHost aims to make self-hosting accessible to everyone. It configures an email, Web and IM server alongside a LDAP base. It also provides From 04894b1b87884516a9f2da1217cb0cf5b76103c1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 7 Feb 2020 22:32:55 +0100 Subject: [PATCH 088/262] [deb] Update moulinette and ssowat required version to >= 4.0.0~alpha --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 6abb4c0c4..cd6b6e495 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Package: yunohost Essential: yes Architecture: all Depends: ${python:Depends}, ${misc:Depends} - , moulinette (>= 3.7), ssowat (>= 3.7) + , moulinette (>= 4.0.0~alpha), ssowat (>= 4.0.0~alpha) , python-psutil, python-requests, python-dnspython, python-openssl , python-miniupnpc, python-dbus, python-jinja2 , python-toml, python-packaging, python-publicsuffix From 60dd1fc8481c424e3d639c051e2949e9dbd98d8b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Mar 2020 22:52:19 +0100 Subject: [PATCH 089/262] [cleanup] Remove legacy migrations and code snippets which won't be needed anymore on buster --- locales/en.json | 48 +-- src/yunohost/app.py | 6 - .../0001_change_cert_group_to_sslcert.py | 15 - .../0002_migrate_to_tsig_sha256.py | 86 ---- .../0003_migrate_to_stretch.py | 379 ------------------ .../0004_php5_to_php7_pools.py | 99 ----- .../0005_postgresql_9p4_to_9p6.py | 41 -- .../0006_sync_admin_and_root_passwords.py | 78 ---- ...0007_ssh_conf_managed_by_yunohost_step1.py | 70 ---- ...0008_ssh_conf_managed_by_yunohost_step2.py | 105 ----- .../0009_decouple_regenconf_from_services.py | 39 -- .../0010_migrate_to_apps_json.py | 13 - ...stgresql_password_to_md5_authentication.py | 16 - .../0013_futureproof_apps_catalog_system.py | 51 --- .../0014_remove_app_status_json.py | 31 -- src/yunohost/dyndns.py | 16 - src/yunohost/regenconf.py | 10 - src/yunohost/tests/test_appscatalog.py | 55 --- 18 files changed, 1 insertion(+), 1157 deletions(-) delete mode 100644 src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py delete mode 100644 src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py delete mode 100644 src/yunohost/data_migrations/0003_migrate_to_stretch.py delete mode 100644 src/yunohost/data_migrations/0004_php5_to_php7_pools.py delete mode 100644 src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py delete mode 100644 src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py delete mode 100644 src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py delete mode 100644 src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py delete mode 100644 src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py delete mode 100644 src/yunohost/data_migrations/0010_migrate_to_apps_json.py delete mode 100644 src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py delete mode 100644 src/yunohost/data_migrations/0013_futureproof_apps_catalog_system.py delete mode 100644 src/yunohost/data_migrations/0014_remove_app_status_json.py diff --git a/locales/en.json b/locales/en.json index a76176dc1..c0f79436f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -406,54 +406,8 @@ "mail_unavailable": "This e-mail address is reserved and shall be automatically allocated to the very first user", "main_domain_change_failed": "Unable to change the main domain", "main_domain_changed": "The main domain has been changed", - "migrate_tsig_end": "Migration to HMAC-SHA-512 finished", - "migrate_tsig_failed": "Could not migrate the DynDNS domain '{domain}' to HMAC-SHA-512, rolling back. Error: {error_code}, {error}", - "migrate_tsig_start": "Insufficiently secure key algorithm detected for TSIG signature of the domain '{domain}', initiating migration to the more secure HMAC-SHA-512", - "migrate_tsig_wait": "Waiting three minutes for the DynDNS server to take the new key into account...", - "migrate_tsig_wait_2": "2 min...", - "migrate_tsig_wait_3": "1 min...", - "migrate_tsig_wait_4": "30 seconds...", - "migrate_tsig_not_needed": "You do not appear to use a DynDNS domain, so no migration is needed.", - "migration_description_0001_change_cert_group_to_sslcert": "Change certificates group permissions from 'metronome' to 'ssl-cert'", - "migration_description_0002_migrate_to_tsig_sha256": "Improve security of DynDNS TSIG updates by using SHA-512 instead of MD5", - "migration_description_0003_migrate_to_stretch": "Upgrade the system to Debian Stretch and YunoHost 3.0", - "migration_description_0004_php5_to_php7_pools": "Reconfigure the PHP pools to use PHP 7 instead of 5", - "migration_description_0005_postgresql_9p4_to_9p6": "Migrate databases from PostgreSQL 9.4 to 9.6", - "migration_description_0006_sync_admin_and_root_passwords": "Synchronize admin and root passwords", - "migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Let the SSH configuration be managed by YunoHost (step 1, automatic)", - "migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)", - "migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services", - "migration_description_0010_migrate_to_apps_json": "Remove deprecated apps catalogs and use the new unified 'apps.json' list instead (outdated, replaced by migration 13)", - "migration_description_0011_setup_group_permission": "Set up user groups and permissions for apps and services", - "migration_description_0012_postgresql_password_to_md5_authentication": "Force PostgreSQL authentication to use MD5 for local connections", - "migration_description_0013_futureproof_apps_catalog_system": "Migrate to the new future-proof apps catalog system", - "migration_description_0014_remove_app_status_json": "Remove legacy status.json app files", + "migration_description_0011_setup_group_permission": "Set up user group and set up permission for apps and services", "migration_description_0015_migrate_to_buster": "Upgrade the system to Debian Buster and YunoHost 4.x", - "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", - "migration_0003_patching_sources_list": "Patching the sources.lists...", - "migration_0003_main_upgrade": "Starting main upgrade...", - "migration_0003_fail2ban_upgrade": "Starting the Fail2Ban upgrade...", - "migration_0003_restoring_origin_nginx_conf": "Your file /etc/nginx/nginx.conf was edited somehow. The migration is going to reset it to its original state first… The previous file will be available as {backup_dest}.", - "migration_0003_yunohost_upgrade": "Starting the YunoHost package upgrade… The migration will end, but the actual upgrade will happen immediately afterwards. After the operation is complete, you might have to log in to the webadmin page again.", - "migration_0003_not_jessie": "The current Debian distribution is not Jessie!", - "migration_0003_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Stretch.", - "migration_0003_still_on_jessie_after_main_upgrade": "Something went wrong during the main upgrade: Is the system still on Jessie‽ To investigate the issue, please look at {log}:s…", - "migration_0003_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.\n\nAdditionally, the port for SMTP, used by external e-mail clients (like Thunderbird or K9-Mail) was changed from 465 (SSL/TLS) to 587 (STARTTLS). The old port (465) will automatically be closed, and the new port (587) will be opened in the firewall. You and your users *will* have to adapt the configuration of your e-mail clients accordingly.", - "migration_0003_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from an app catalog, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}", - "migration_0003_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}", - "migration_0005_postgresql_94_not_installed": "PostgreSQL was not installed on your system. Nothing to do.", - "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 is installed, but not postgresql 9.6‽ Something weird might have happened on your system :(…", - "migration_0005_not_enough_space": "Make sufficient space available in {path} to run the migration.", - "migration_0006_disclaimer": "YunoHost now expects the admin and root passwords to be synchronized. This migration replaces your root password with the admin password.", - "migration_0007_cancelled": "Could not improve the way your SSH configuration is managed.", - "migration_0007_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.", - "migration_0008_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH setup differs from the recommendation. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change thusly:", - "migration_0008_port": "• You will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it;", - "migration_0008_root": "• You will not be able to connect as root through SSH. Instead you should use the admin user;", - "migration_0008_dsa": "• The DSA key will be turned off. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server;", - "migration_0008_warning": "If you understand those warnings and want YunoHost to override your current configuration, run the migration. Otherwise, you can also skip the migration, though it is not recommended.", - "migration_0008_no_warning": "Overriding your SSH configuration should be safe, though this cannot be promised! Run the migration to override it. Otherwise, you can also skip the migration, though it is not recommended.", - "migration_0009_not_needed": "This migration already happened somehow... (?) Skipping.", "migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.", "migration_0011_can_not_backup_before_migration": "The backup of the system could not be completed before the migration failed. Error: {error:s}", "migration_0011_create_group": "Creating a group for each user…", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index afa81127d..032536247 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2683,12 +2683,6 @@ def _read_apps_catalog_list(): Read the json corresponding to the list of apps catalogs """ - # Legacy code - can be removed after moving to buster (if the migration got merged before buster) - if os.path.exists('/etc/yunohost/appslists.json'): - from yunohost.tools import _get_migration_by_name - migration = _get_migration_by_name("futureproof_apps_catalog_system") - migration.run() - try: list_ = read_yaml(APPS_CATALOG_CONF) # Support the case where file exists but is empty diff --git a/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py b/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py deleted file mode 100644 index 5670f3329..000000000 --- a/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py +++ /dev/null @@ -1,15 +0,0 @@ -import subprocess -import glob -from yunohost.tools import Migration -from moulinette.utils.filesystem import chown - - -class MyMigration(Migration): - - "Change certificates group permissions from 'metronome' to 'ssl-cert'" - - all_certificate_files = glob.glob("/etc/yunohost/certs/*/*.pem") - - def run(self): - for filename in self.all_certificate_files: - chown(filename, uid="root", gid="ssl-cert") diff --git a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py deleted file mode 100644 index 65158ba2c..000000000 --- a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py +++ /dev/null @@ -1,86 +0,0 @@ -import glob -import os -import requests -import base64 -import time -import json - -from moulinette import m18n -from yunohost.utils.error import YunohostError -from moulinette.utils.log import getActionLogger - -from yunohost.tools import Migration -from yunohost.dyndns import _guess_current_dyndns_domain - -logger = getActionLogger('yunohost.migration') - - -class MyMigration(Migration): - - "Migrate Dyndns stuff from MD5 TSIG to SHA512 TSIG" - - def run(self, dyn_host="dyndns.yunohost.org", domain=None, private_key_path=None): - - if domain is None or private_key_path is None: - try: - (domain, private_key_path) = _guess_current_dyndns_domain(dyn_host) - assert "+157" in private_key_path - except (YunohostError, AssertionError): - logger.info(m18n.n("migrate_tsig_not_needed")) - return - - logger.info(m18n.n('migrate_tsig_start', domain=domain)) - public_key_path = private_key_path.rsplit(".private", 1)[0] + ".key" - public_key_md5 = open(public_key_path).read().strip().split(' ')[-1] - - os.system('cd /etc/yunohost/dyndns && ' - 'dnssec-keygen -a hmac-sha512 -b 512 -r /dev/urandom -n USER %s' % domain) - os.system('chmod 600 /etc/yunohost/dyndns/*.key /etc/yunohost/dyndns/*.private') - - # +165 means that this file store a hmac-sha512 key - new_key_path = glob.glob('/etc/yunohost/dyndns/*+165*.key')[0] - public_key_sha512 = open(new_key_path).read().strip().split(' ', 6)[-1] - - try: - r = requests.put('https://%s/migrate_key_to_sha512/' % (dyn_host), - data={ - 'public_key_md5': base64.b64encode(public_key_md5), - 'public_key_sha512': base64.b64encode(public_key_sha512), - }, timeout=30) - except requests.ConnectionError: - raise YunohostError('no_internet_connection') - - if r.status_code != 201: - try: - error = json.loads(r.text)['error'] - except Exception: - # failed to decode json - error = r.text - - import traceback - from StringIO import StringIO - stack = StringIO() - traceback.print_stack(file=stack) - logger.error(stack.getvalue()) - - # Migration didn't succeed, so we rollback and raise an exception - os.system("mv /etc/yunohost/dyndns/*+165* /tmp") - - raise YunohostError('migrate_tsig_failed', domain=domain, - error_code=str(r.status_code), error=error) - - # remove old certificates - os.system("mv /etc/yunohost/dyndns/*+157* /tmp") - - # sleep to wait for dyndns cache invalidation - logger.info(m18n.n('migrate_tsig_wait')) - time.sleep(60) - logger.info(m18n.n('migrate_tsig_wait_2')) - time.sleep(60) - logger.info(m18n.n('migrate_tsig_wait_3')) - time.sleep(30) - logger.info(m18n.n('migrate_tsig_wait_4')) - time.sleep(30) - - logger.info(m18n.n('migrate_tsig_end')) - return diff --git a/src/yunohost/data_migrations/0003_migrate_to_stretch.py b/src/yunohost/data_migrations/0003_migrate_to_stretch.py deleted file mode 100644 index e916b1ae8..000000000 --- a/src/yunohost/data_migrations/0003_migrate_to_stretch.py +++ /dev/null @@ -1,379 +0,0 @@ -import glob -import os -from shutil import copy2 - -from moulinette import m18n, msettings -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_file - -from yunohost.tools import Migration -from yunohost.app import unstable_apps -from yunohost.service import _run_service_command -from yunohost.regenconf import (manually_modified_files, - manually_modified_files_compared_to_debian_default) -from yunohost.utils.filesystem import free_space_in_directory -from yunohost.utils.packages import get_ynh_package_version -from yunohost.utils.network import get_network_interfaces -from yunohost.firewall import firewall_allow, firewall_disallow - -logger = getActionLogger('yunohost.migration') - -YUNOHOST_PACKAGES = ["yunohost", "yunohost-admin", "moulinette", "ssowat"] - - -class MyMigration(Migration): - - "Upgrade the system to Debian Stretch and Yunohost 3.0" - - mode = "manual" - - def run(self): - - self.logfile = "/var/log/yunohost/{}.log".format(self.name) - - self.check_assertions() - - logger.info(m18n.n("migration_0003_start", logfile=self.logfile)) - - # Preparing the upgrade - self.restore_original_nginx_conf_if_needed() - - logger.info(m18n.n("migration_0003_patching_sources_list")) - self.patch_apt_sources_list() - self.backup_files_to_keep() - self.apt_update() - apps_packages = self.get_apps_equivs_packages() - self.unhold(["metronome"]) - self.hold(YUNOHOST_PACKAGES + apps_packages + ["fail2ban"]) - - # Main dist-upgrade - logger.info(m18n.n("migration_0003_main_upgrade")) - _run_service_command("stop", "mysql") - self.apt_dist_upgrade(conf_flags=["old", "miss", "def"]) - _run_service_command("start", "mysql") - if self.debian_major_version() == 8: - raise YunohostError("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile) - - # Specific upgrade for fail2ban... - logger.info(m18n.n("migration_0003_fail2ban_upgrade")) - self.unhold(["fail2ban"]) - # Don't move this if folder already exists. If it does, we probably are - # running this script a 2nd, 3rd, ... time but /etc/fail2ban will - # be re-created only for the first dist-upgrade of fail2ban - if not os.path.exists("/etc/fail2ban.old"): - os.system("mv /etc/fail2ban /etc/fail2ban.old") - self.apt_dist_upgrade(conf_flags=["new", "miss", "def"]) - _run_service_command("restart", "fail2ban") - - self.disable_predicable_interface_names() - - # Clean the mess - os.system("apt autoremove --assume-yes") - os.system("apt clean --assume-yes") - - # We moved to port 587 for SMTP - # https://busylog.net/smtp-tls-ssl-25-465-587/ - firewall_allow("Both", 587) - firewall_disallow("Both", 465) - - # Upgrade yunohost packages - logger.info(m18n.n("migration_0003_yunohost_upgrade")) - self.restore_files_to_keep() - self.unhold(YUNOHOST_PACKAGES + apps_packages) - self.upgrade_yunohost_packages() - - def debian_major_version(self): - # The python module "platform" and lsb_release are not reliable because - # on some setup, they still return Release=8 even after upgrading to - # stretch ... (Apparently this is related to OVH overriding some stuff - # with /etc/lsb-release for instance -_-) - # Instead, we rely on /etc/os-release which should be the raw info from - # the distribution... - return int(check_output("grep VERSION_ID /etc/os-release | head -n 1 | tr '\"' ' ' | cut -d ' ' -f2")) - - def yunohost_major_version(self): - return int(get_ynh_package_version("yunohost")["version"].split('.')[0]) - - def check_assertions(self): - - # Be on jessie (8.x) and yunohost 2.x - # NB : we do both check to cover situations where the upgrade crashed - # in the middle and debian version could be >= 9.x but yunohost package - # would still be in 2.x... - if not self.debian_major_version() == 8 \ - and not self.yunohost_major_version() == 2: - raise YunohostError("migration_0003_not_jessie") - - # Have > 1 Go free space on /var/ ? - if free_space_in_directory("/var/") / (1024**3) < 1.0: - raise YunohostError("There is not enough free space in /var/ to run the migration. You need at least 1GB free space") - - # Check system is up to date - # (but we don't if 'stretch' is already in the sources.list ... - # which means maybe a previous upgrade crashed and we're re-running it) - if " stretch " not in read_file("/etc/apt/sources.list"): - self.apt_update() - apt_list_upgradable = check_output("apt list --upgradable -a") - if "upgradable" in apt_list_upgradable: - raise YunohostError("migration_0003_system_not_fully_up_to_date") - - @property - def disclaimer(self): - - # Avoid having a super long disclaimer + uncessary check if we ain't - # on jessie / yunohost 2.x anymore - # NB : we do both check to cover situations where the upgrade crashed - # in the middle and debian version could be >= 9.x but yunohost package - # would still be in 2.x... - if not self.debian_major_version() == 8 \ - and not self.yunohost_major_version() == 2: - return None - - # Get list of problematic apps ? I.e. not official or community+working - problematic_apps = unstable_apps() - problematic_apps = "".join(["\n - " + app for app in problematic_apps]) - - # Manually modified files ? (c.f. yunohost service regen-conf) - modified_files = manually_modified_files() - # We also have a specific check for nginx.conf which some people - # modified and needs to be upgraded... - if "/etc/nginx/nginx.conf" in manually_modified_files_compared_to_debian_default(): - modified_files.append("/etc/nginx/nginx.conf") - modified_files = "".join(["\n - " + f for f in modified_files]) - - message = m18n.n("migration_0003_general_warning") - - if problematic_apps: - message += "\n\n" + m18n.n("migration_0003_problematic_apps_warning", problematic_apps=problematic_apps) - - if modified_files: - message += "\n\n" + m18n.n("migration_0003_modified_files", manually_modified_files=modified_files) - - return message - - def patch_apt_sources_list(self): - - sources_list = glob.glob("/etc/apt/sources.list.d/*.list") - sources_list.append("/etc/apt/sources.list") - - # This : - # - replace single 'jessie' occurence by 'stretch' - # - comments lines containing "backports" - # - replace 'jessie/updates' by 'strech/updates' (or same with a -) - # - switch yunohost's repo to forge - for f in sources_list: - command = "sed -i -e 's@ jessie @ stretch @g' " \ - "-e '/backports/ s@^#*@#@' " \ - "-e 's@ jessie/updates @ stretch/updates @g' " \ - "-e 's@ jessie-updates @ stretch-updates @g' " \ - "-e 's@repo.yunohost@forge.yunohost@g' " \ - "{}".format(f) - os.system(command) - - def get_apps_equivs_packages(self): - - command = "dpkg --get-selections" \ - " | grep -v deinstall" \ - " | awk '{print $1}'" \ - " | { grep 'ynh-deps$' || true; }" - - output = check_output(command).strip() - - return output.split('\n') if output else [] - - def hold(self, packages): - for package in packages: - os.system("apt-mark hold {}".format(package)) - - def unhold(self, packages): - for package in packages: - os.system("apt-mark unhold {}".format(package)) - - def apt_update(self): - - command = "apt-get update" - logger.debug("Running apt command :\n{}".format(command)) - command += " 2>&1 | tee -a {}".format(self.logfile) - - os.system(command) - - def upgrade_yunohost_packages(self): - - # - # Here we use a dirty hack to run a command after the current - # "yunohost tools migrations migrate", because the upgrade of - # yunohost will also trigger another "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...) - # - - MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock" - - upgrade_command = "" - upgrade_command += " DEBIAN_FRONTEND=noninteractive" - upgrade_command += " APT_LISTCHANGES_FRONTEND=none" - upgrade_command += " apt-get install" - upgrade_command += " --assume-yes " - upgrade_command += " ".join(YUNOHOST_PACKAGES) - # We also install php-zip and php7.0-acpu to fix an issue with - # nextcloud and kanboard that need it when on stretch. - upgrade_command += " php-zip php7.0-apcu" - upgrade_command += " 2>&1 | tee -a {}".format(self.logfile) - - wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK) - - command = "({} && {}; echo 'Migration complete!') &".format(wait_until_end_of_yunohost_command, - upgrade_command) - - logger.debug("Running command :\n{}".format(command)) - - os.system(command) - - def apt_dist_upgrade(self, conf_flags): - - # Make apt-get happy - os.system("echo 'libc6 libraries/restart-without-asking boolean true' | debconf-set-selections") - # Don't send an email to root about the postgresql migration. It should be handled automatically after. - os.system("echo 'postgresql-common postgresql-common/obsolete-major seen true' | debconf-set-selections") - - command = "" - command += " DEBIAN_FRONTEND=noninteractive" - command += " APT_LISTCHANGES_FRONTEND=none" - command += " apt-get" - command += " --fix-broken --show-upgraded --assume-yes" - for conf_flag in conf_flags: - command += ' -o Dpkg::Options::="--force-conf{}"'.format(conf_flag) - command += " dist-upgrade" - - logger.debug("Running apt command :\n{}".format(command)) - - command += " 2>&1 | tee -a {}".format(self.logfile) - - is_api = msettings.get('interface') == 'api' - if is_api: - callbacks = ( - lambda l: logger.info(l.rstrip()), - lambda l: logger.warning(l.rstrip()), - ) - call_async_output(command, callbacks, shell=True) - else: - # We do this when running from the cli to have the output of the - # command showing in the terminal, since 'info' channel is only - # enabled if the user explicitly add --verbose ... - os.system(command) - - # Those are files that should be kept and restored before the final switch - # to yunohost 3.x... They end up being modified by the various dist-upgrades - # (or need to be taken out momentarily), which then blocks the regen-conf - # as they are flagged as "manually modified"... - files_to_keep = [ - "/etc/mysql/my.cnf", - "/etc/nslcd.conf", - "/etc/postfix/master.cf", - "/etc/fail2ban/filter.d/yunohost.conf" - ] - - def backup_files_to_keep(self): - - logger.debug("Backuping specific files to keep ...") - - # Create tmp directory if it does not exists - tmp_dir = os.path.join("/tmp/", self.name) - if not os.path.exists(tmp_dir): - os.mkdir(tmp_dir, 0o700) - - for f in self.files_to_keep: - dest_file = f.strip('/').replace("/", "_") - - # If the file is already there, we might be re-running the migration - # because it previously crashed. Hence we keep the existing file. - if os.path.exists(os.path.join(tmp_dir, dest_file)): - continue - - copy2(f, os.path.join(tmp_dir, dest_file)) - - def restore_files_to_keep(self): - - logger.debug("Restoring specific files to keep ...") - - tmp_dir = os.path.join("/tmp/", self.name) - - for f in self.files_to_keep: - dest_file = f.strip('/').replace("/", "_") - copy2(os.path.join(tmp_dir, dest_file), f) - - # On some setups, /etc/nginx/nginx.conf got edited. But this file needs - # to be upgraded because of the way the new module system works for nginx. - # (in particular, having the line that include the modules at the top) - # - # So here, if it got edited, we force the restore of the original conf - # *before* starting the actual upgrade... - # - # An alternative strategy that was attempted was to hold the nginx-common - # package and have a specific upgrade for it like for fail2ban, but that - # leads to apt complaining about not being able to upgrade for shitty - # reasons >.> - def restore_original_nginx_conf_if_needed(self): - if "/etc/nginx/nginx.conf" not in manually_modified_files_compared_to_debian_default(): - return - - if not os.path.exists("/etc/nginx/nginx.conf"): - return - - # If stretch is in the sources.list, we already started migrating on - # stretch so we don't re-do this - if " stretch " in read_file("/etc/apt/sources.list"): - return - - backup_dest = "/home/yunohost.conf/backup/nginx.conf.bkp_before_stretch" - - logger.warning(m18n.n("migration_0003_restoring_origin_nginx_conf", - backup_dest=backup_dest)) - - os.system("mv /etc/nginx/nginx.conf %s" % backup_dest) - - command = "" - command += " DEBIAN_FRONTEND=noninteractive" - command += " APT_LISTCHANGES_FRONTEND=none" - command += " apt-get" - command += " --fix-broken --show-upgraded --assume-yes" - command += ' -o Dpkg::Options::="--force-confmiss"' - command += " install --reinstall" - command += " nginx-common" - - logger.debug("Running apt command :\n{}".format(command)) - - command += " 2>&1 | tee -a {}".format(self.logfile) - - is_api = msettings.get('interface') == 'api' - if is_api: - callbacks = ( - lambda l: logger.info(l.rstrip()), - lambda l: logger.warning(l.rstrip()), - ) - call_async_output(command, callbacks, shell=True) - else: - # We do this when running from the cli to have the output of the - # command showing in the terminal, since 'info' channel is only - # enabled if the user explicitly add --verbose ... - os.system(command) - - def disable_predicable_interface_names(self): - - # Try to see if currently used interface names are predictable ones or not... - # If we ain't using "eth0" or "wlan0", assume we are using predictable interface - # names and therefore they shouldnt be disabled - network_interfaces = get_network_interfaces().keys() - if "eth0" not in network_interfaces and "wlan0" not in network_interfaces: - return - - interfaces_config = read_file("/etc/network/interfaces") - if "eth0" not in interfaces_config and "wlan0" not in interfaces_config: - return - - # Disable predictive interface names - # c.f. https://unix.stackexchange.com/a/338730 - os.system("ln -s /dev/null /etc/systemd/network/99-default.link") diff --git a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py deleted file mode 100644 index 1b90c4ff0..000000000 --- a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import glob -from shutil import copy2 - -from moulinette.utils.log import getActionLogger - -from yunohost.tools import Migration -from yunohost.service import _run_service_command - -logger = getActionLogger('yunohost.migration') - -PHP5_POOLS = "/etc/php5/fpm/pool.d" -PHP7_POOLS = "/etc/php/7.0/fpm/pool.d" - -PHP5_SOCKETS_PREFIX = "/var/run/php5-fpm" -PHP7_SOCKETS_PREFIX = "/run/php/php7.0-fpm" - -MIGRATION_COMMENT = "; YunoHost note : this file was automatically moved from {}".format(PHP5_POOLS) - - -class MyMigration(Migration): - - "Migrate php5-fpm 'pool' conf files to php7 stuff" - - dependencies = ["migrate_to_stretch"] - - def run(self): - # Get list of php5 pool files - php5_pool_files = glob.glob("{}/*.conf".format(PHP5_POOLS)) - - # Keep only basenames - php5_pool_files = [os.path.basename(f) for f in php5_pool_files] - - # Ignore the "www.conf" (default stuff, probably don't want to touch it ?) - php5_pool_files = [f for f in php5_pool_files if f != "www.conf"] - - for f in php5_pool_files: - - # Copy the files to the php7 pool - src = "{}/{}".format(PHP5_POOLS, f) - dest = "{}/{}".format(PHP7_POOLS, f) - copy2(src, dest) - - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, dest) - os.system(c) - - # Also add a comment that it was automatically moved from php5 - # (for human traceability and backward migration) - c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest) - os.system(c) - - # Some old comments starting with '#' instead of ';' are not - # compatible in php7 - c = "sed -i 's/^#/;#/g' {}".format(dest) - os.system(c) - - # Reload/restart the php pools - _run_service_command("restart", "php7.0-fpm") - _run_service_command("enable", "php7.0-fpm") - os.system("systemctl stop php5-fpm") - os.system("systemctl disable php5-fpm") - os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy - - # Get list of nginx conf file - nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") - for f in nginx_conf_files: - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, f) - os.system(c) - - # Reload nginx - _run_service_command("reload", "nginx") - - def backward(self): - - # Get list of php7 pool files - php7_pool_files = glob.glob("{}/*.conf".format(PHP7_POOLS)) - - # Keep only files which have the migration comment - php7_pool_files = [f for f in php7_pool_files if open(f).readline().strip() == MIGRATION_COMMENT] - - # Delete those files - for f in php7_pool_files: - os.remove(f) - - # Reload/restart the php pools - _run_service_command("stop", "php7.0-fpm") - os.system("systemctl start php5-fpm") - - # Get list of nginx conf file - nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") - for f in nginx_conf_files: - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP7_SOCKETS_PREFIX, PHP5_SOCKETS_PREFIX, f) - os.system(c) - - # Reload nginx - _run_service_command("reload", "nginx") diff --git a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py deleted file mode 100644 index 3127f2c65..000000000 --- a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py +++ /dev/null @@ -1,41 +0,0 @@ -import subprocess - -from moulinette import m18n -from yunohost.utils.error import YunohostError -from moulinette.utils.log import getActionLogger - -from yunohost.tools import Migration -from yunohost.utils.filesystem import free_space_in_directory, space_used_by_directory - -logger = getActionLogger('yunohost.migration') - - -class MyMigration(Migration): - - "Migrate DBs from Postgresql 9.4 to 9.6 after migrating to Stretch" - - dependencies = ["migrate_to_stretch"] - - def run(self): - - if not self.package_is_installed("postgresql-9.4"): - logger.warning(m18n.n("migration_0005_postgresql_94_not_installed")) - return - - if not self.package_is_installed("postgresql-9.6"): - raise YunohostError("migration_0005_postgresql_96_not_installed") - - if not space_used_by_directory("/var/lib/postgresql/9.4") > free_space_in_directory("/var/lib/postgresql"): - raise YunohostError("migration_0005_not_enough_space", path="/var/lib/postgresql/") - - subprocess.check_call("service postgresql stop", shell=True) - subprocess.check_call("pg_dropcluster --stop 9.6 main", shell=True) - subprocess.check_call("pg_upgradecluster -m upgrade 9.4 main", shell=True) - subprocess.check_call("pg_dropcluster --stop 9.4 main", shell=True) - subprocess.check_call("service postgresql start", shell=True) - - def package_is_installed(self, package_name): - - p = subprocess.Popen("dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name), shell=True) - p.communicate() - return p.returncode == 0 diff --git a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py deleted file mode 100644 index 844e3028c..000000000 --- a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py +++ /dev/null @@ -1,78 +0,0 @@ -import spwd -import crypt -import random -import string -import subprocess - -from moulinette import m18n -from yunohost.utils.error import YunohostError -from moulinette.utils.log import getActionLogger -from moulinette.utils.process import run_commands, check_output -from moulinette.utils.filesystem import append_to_file -from moulinette.authenticators.ldap import Authenticator -from yunohost.tools import Migration - -logger = getActionLogger('yunohost.migration') -SMALL_PWD_LIST = ["yunohost", "olinuxino", "olinux", "raspberry", "admin", "root", "test", "rpi"] - - -class MyMigration(Migration): - - "Synchronize admin and root passwords" - - def run(self): - - new_hash = self._get_admin_hash() - self._replace_root_hash(new_hash) - - logger.info(m18n.n("root_password_replaced_by_admin_password")) - - @property - def mode(self): - - # If the root password is still a "default" value, - # then this is an emergency and migration shall - # be applied automatically - # - # Otherwise, as playing with root password is touchy, - # we set this as a manual migration. - return "auto" if self._is_root_pwd_listed(SMALL_PWD_LIST) else "manual" - - @property - def disclaimer(self): - if self._is_root_pwd_listed(SMALL_PWD_LIST): - return None - - return m18n.n("migration_0006_disclaimer") - - def _get_admin_hash(self): - """ - Fetch the admin hash from the LDAP db using slapcat - """ - admin_hash = check_output("slapcat \ - | grep 'dn: cn=admin,dc=yunohost,dc=org' -A20 \ - | grep userPassword -A2 \ - | tr -d '\n ' \ - | tr ':' ' ' \ - | awk '{print $2}' \ - | base64 -d \ - | sed 's/{CRYPT}//g'") - return admin_hash - - def _replace_root_hash(self, new_hash): - hash_root = spwd.getspnam("root").sp_pwd - - with open('/etc/shadow', 'r') as before_file: - before = before_file.read() - - with open('/etc/shadow', 'w') as after_file: - after_file.write(before.replace("root:" + hash_root, - "root:" + new_hash)) - - def _is_root_pwd_listed(self, pwd_list): - hash_root = spwd.getspnam("root").sp_pwd - - for password in pwd_list: - if hash_root == crypt.crypt(password, hash_root): - return True - return False diff --git a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py deleted file mode 100644 index 624288210..000000000 --- a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py +++ /dev/null @@ -1,70 +0,0 @@ -import os -import re - -from shutil import copyfile - -from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import mkdir, rm - -from yunohost.tools import Migration -from yunohost.service import _run_service_command -from yunohost.regenconf import regen_conf -from yunohost.settings import settings_set -from yunohost.utils.error import YunohostError - -logger = getActionLogger('yunohost.migration') - -SSHD_CONF = '/etc/ssh/sshd_config' - - -class MyMigration(Migration): - - """ - This is the first step of a couple of migrations that ensure SSH conf is - managed by YunoHost (even if the "from_script" flag is present, which was - previously preventing it from being managed by YunoHost) - - The goal of this first (automatic) migration is to make sure that the - sshd_config is managed by the regen-conf mechanism. - - If the from_script flag exists, then we keep the current SSH conf such that it - will appear as "manually modified" to the regenconf. - - In step 2 (manual), the admin will be able to choose wether or not to actually - use the recommended configuration, with an appropriate disclaimer. - """ - - def run(self): - - # Check if deprecated DSA Host Key is in config - dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$' - dsa = False - for line in open(SSHD_CONF): - if re.match(dsa_rgx, line) is not None: - dsa = True - break - if dsa: - settings_set("service.ssh.allow_deprecated_dsa_hostkey", True) - - # Here, we make it so that /etc/ssh/sshd_config is managed - # by the regen conf (in particular in the case where the - # from_script flag is present - in which case it was *not* - # managed by the regenconf) - # But because we can't be sure the user wants to use the - # recommended conf, we backup then restore the /etc/ssh/sshd_config - # right after the regenconf, such that it will appear as - # "manually modified". - if os.path.exists('/etc/yunohost/from_script'): - rm('/etc/yunohost/from_script') - copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp') - regen_conf(names=['ssh'], force=True) - copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) - - # Restart ssh and rollback if it failed - if not _run_service_command('restart', 'ssh'): - # We don't rollback completely but it should be enough - copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) - if not _run_service_command('restart', 'ssh'): - raise YunohostError("migration_0007_cannot_restart") - else: - raise YunohostError("migration_0007_cancelled") diff --git a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py deleted file mode 100644 index ecc8cfdcb..000000000 --- a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py +++ /dev/null @@ -1,105 +0,0 @@ -import os -import re - -from moulinette import m18n -from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import chown - -from yunohost.tools import Migration -from yunohost.regenconf import _get_conf_hashes, _calculate_hash -from yunohost.regenconf import regen_conf -from yunohost.settings import settings_set, settings_get -from yunohost.utils.error import YunohostError -from yunohost.backup import ARCHIVES_PATH - - -logger = getActionLogger('yunohost.migration') - -SSHD_CONF = '/etc/ssh/sshd_config' - - -class MyMigration(Migration): - - """ - In this second step, the admin is asked if it's okay to use - the recommended SSH configuration - which also implies - disabling deprecated DSA key. - - This has important implications in the way the user may connect - to its server (key change, and a spooky warning might be given - by SSH later) - - A disclaimer explaining the various things to be aware of is - shown - and the user may also choose to skip this migration. - """ - - dependencies = ["ssh_conf_managed_by_yunohost_step1"] - - def run(self): - settings_set("service.ssh.allow_deprecated_dsa_hostkey", False) - regen_conf(names=['ssh'], force=True) - - # Update local archives folder permissions, so that - # admin can scp archives out of the server - if os.path.isdir(ARCHIVES_PATH): - chown(ARCHIVES_PATH, uid="admin", gid="root") - - @property - def mode(self): - - # If the conf is already up to date - # and no DSA key is used, then we're good to go - # and the migration can be done automatically - # (basically nothing shall change) - ynh_hash = _get_conf_hashes('ssh').get(SSHD_CONF, None) - current_hash = _calculate_hash(SSHD_CONF) - dsa = settings_get("service.ssh.allow_deprecated_dsa_hostkey") - if ynh_hash == current_hash and not dsa: - return "auto" - - return "manual" - - @property - def disclaimer(self): - - if self.mode == "auto": - return None - - # Detect key things to be aware of before enabling the - # recommended configuration - dsa_key_enabled = False - ports = [] - root_login = [] - port_rgx = r'^[ \t]*Port[ \t]+(\d+)[ \t]*(?:#.*)?$' - root_rgx = r'^[ \t]*PermitRootLogin[ \t]([^# \t]*)[ \t]*(?:#.*)?$' - dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$' - for line in open(SSHD_CONF): - - ports = ports + re.findall(port_rgx, line) - - root_login = root_login + re.findall(root_rgx, line) - - if not dsa_key_enabled and re.match(dsa_rgx, line) is not None: - dsa_key_enabled = True - - custom_port = ports != ['22'] and ports != [] - root_login_enabled = root_login and root_login[-1] != 'no' - - # Build message - message = m18n.n("migration_0008_general_disclaimer") - - if custom_port: - message += "\n\n" + m18n.n("migration_0008_port") - - if root_login_enabled: - message += "\n\n" + m18n.n("migration_0008_root") - - if dsa_key_enabled: - message += "\n\n" + m18n.n("migration_0008_dsa") - - if custom_port or root_login_enabled or dsa_key_enabled: - message += "\n\n" + m18n.n("migration_0008_warning") - else: - message += "\n\n" + m18n.n("migration_0008_no_warning") - - return message diff --git a/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py b/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py deleted file mode 100644 index c190e2aaa..000000000 --- a/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py +++ /dev/null @@ -1,39 +0,0 @@ -import os - -from moulinette import m18n -from moulinette.utils.log import getActionLogger - -from moulinette.utils.filesystem import read_file -from yunohost.service import _get_services, _save_services -from yunohost.regenconf import _update_conf_hashes, REGEN_CONF_FILE - -from yunohost.tools import Migration - -logger = getActionLogger('yunohost.migration') - - -class MyMigration(Migration): - """ - Decouple the regen conf mechanism from the concept of services - """ - - def run(self): - - if "conffiles" not in read_file("/etc/yunohost/services.yml") \ - or os.path.exists(REGEN_CONF_FILE): - logger.warning(m18n.n("migration_0009_not_needed")) - return - - # For all services - services = _get_services() - for service, infos in services.items(): - # If there are some conffiles (file hashes) - if "conffiles" in infos.keys(): - # Save them using the new regen conf thingy - _update_conf_hashes(service, infos["conffiles"]) - # And delete the old conffile key from the service infos - del services[service]["conffiles"] - - # (Actually save the modification of services) - _save_services(services) - diff --git a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py deleted file mode 100644 index e5ce65608..000000000 --- a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py +++ /dev/null @@ -1,13 +0,0 @@ -from moulinette.utils.log import getActionLogger -from yunohost.tools import Migration - -logger = getActionLogger('yunohost.migration') - - -class MyMigration(Migration): - - "Migrate from official.json to apps.json (outdated, replaced by migration 13)" - - def run(self): - logger.info("This migration is oudated and doesn't do anything anymore. The migration 13 will handle this instead.") - pass diff --git a/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py b/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py deleted file mode 100644 index 636b4f12b..000000000 --- a/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py +++ /dev/null @@ -1,16 +0,0 @@ -import glob -import re -from yunohost.tools import Migration -from moulinette.utils.filesystem import read_file, write_to_file - - -class MyMigration(Migration): - - "Force authentication in md5 for local connexions" - - all_hba_files = glob.glob("/etc/postgresql/*/*/pg_hba.conf") - - def run(self): - for filename in self.all_hba_files: - pg_hba_in = read_file(filename) - write_to_file(filename, re.sub(r"local(\s*)all(\s*)all(\s*)password", "local\\1all\\2all\\3md5", pg_hba_in)) diff --git a/src/yunohost/data_migrations/0013_futureproof_apps_catalog_system.py b/src/yunohost/data_migrations/0013_futureproof_apps_catalog_system.py deleted file mode 100644 index ff4925183..000000000 --- a/src/yunohost/data_migrations/0013_futureproof_apps_catalog_system.py +++ /dev/null @@ -1,51 +0,0 @@ - -import os -import shutil - -from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_json - -from yunohost.tools import Migration -from yunohost.app import (_initialize_apps_catalog_system, - _update_apps_catalog, - APPS_CATALOG_CACHE, - APPS_CATALOG_CONF) - -logger = getActionLogger('yunohost.migration') - -LEGACY_APPS_CATALOG_CONF = '/etc/yunohost/appslists.json' -LEGACY_APPS_CATALOG_CONF_BACKUP = LEGACY_APPS_CATALOG_CONF + ".old" - - -class MyMigration(Migration): - - "Migrate to the new future-proof apps catalog system" - - def run(self): - - if not os.path.exists(LEGACY_APPS_CATALOG_CONF): - logger.info("No need to do anything") - - # Destroy old lecacy cache - if os.path.exists(APPS_CATALOG_CACHE): - shutil.rmtree(APPS_CATALOG_CACHE) - - # and legacy cron - if os.path.exists("/etc/cron.daily/yunohost-fetch-appslists"): - os.remove("/etc/cron.daily/yunohost-fetch-appslists") - - # Backup the legacy file - try: - legacy_catalogs = read_json(LEGACY_APPS_CATALOG_CONF) - # If there's only one catalog, we assume it's just the old official catalog - # Otherwise, warn the (power-?)users that they should migrate their old catalogs manually - if len(legacy_catalogs) > 1: - logger.warning("It looks like you had additional apps_catalog in the configuration file %s! YunoHost now uses %s instead, but it won't migrate your custom apps_catalog. You should do this manually. The old file has been backuped in %s." % (LEGACY_APPS_CATALOG_CONF, APPS_CATALOG_CONF, LEGACY_APPS_CATALOG_CONF_BACKUP)) - except Exception as e: - logger.warning("Unable to parse the legacy conf %s (error : %s) ... migrating anyway" % (LEGACY_APPS_CATALOG_CONF, str(e))) - - if os.path.exists(LEGACY_APPS_CATALOG_CONF): - os.rename(LEGACY_APPS_CATALOG_CONF, LEGACY_APPS_CATALOG_CONF_BACKUP) - - _initialize_apps_catalog_system() - _update_apps_catalog() diff --git a/src/yunohost/data_migrations/0014_remove_app_status_json.py b/src/yunohost/data_migrations/0014_remove_app_status_json.py deleted file mode 100644 index 1cb5bc002..000000000 --- a/src/yunohost/data_migrations/0014_remove_app_status_json.py +++ /dev/null @@ -1,31 +0,0 @@ -import os - -from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_json - -from yunohost.tools import Migration -from yunohost.app import app_setting, APPS_SETTING_PATH - -logger = getActionLogger('yunohost.migration') - -class MyMigration(Migration): - - """Remove legacy app status.json files""" - - def run(self): - - apps = os.listdir(APPS_SETTING_PATH) - - for app in apps: - status_file = os.path.join(APPS_SETTING_PATH, app, "status.json") - if not os.path.exists(status_file): - continue - - try: - status = read_json(status_file) - current_revision = status.get("remote", {}).get("revision", "?") - app_setting(app, 'current_revision', current_revision) - except Exception as e: - logger.warning("Could not migrate status.json from app %s: %s", (app, str(e))) - else: - os.system("rm %s" % status_file) diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index efa25f23f..7dcc33cbf 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -205,22 +205,6 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, key = keys[0] - # This mean that hmac-md5 is used - # (Re?)Trigger the migration to sha256 and return immediately. - # The actual update will be done in next run. - if "+157" in key: - from yunohost.tools import _get_migration_by_name - migration = _get_migration_by_name("migrate_to_tsig_sha256") - try: - migration.run(dyn_host, domain, key) - except Exception as e: - logger.error(m18n.n('migrations_migration_has_failed', - exception=e, - number=migration.number, - name=migration.name), - exc_info=1) - return - # Extract 'host', e.g. 'nohost.me' from 'foo.nohost.me' host = domain.split('.')[1:] host = '.'.join(host) diff --git a/src/yunohost/regenconf.py b/src/yunohost/regenconf.py index a94f5023d..6c9b10aac 100644 --- a/src/yunohost/regenconf.py +++ b/src/yunohost/regenconf.py @@ -62,16 +62,6 @@ def regen_conf(operation_logger, names=[], with_diff=False, force=False, dry_run """ - # Legacy code to automatically run the migration - # This is required because regen_conf is called before the migration call - # in debian's postinst script - if os.path.exists("/etc/yunohost/installed") \ - and ("conffiles" in read_file("/etc/yunohost/services.yml") \ - or not os.path.exists(REGEN_CONF_FILE)): - from yunohost.tools import _get_migration_by_name - migration = _get_migration_by_name("decouple_regenconf_from_services") - migration.run() - result = {} # Return the list of pending conf diff --git a/src/yunohost/tests/test_appscatalog.py b/src/yunohost/tests/test_appscatalog.py index c3ece7907..40cf1489f 100644 --- a/src/yunohost/tests/test_appscatalog.py +++ b/src/yunohost/tests/test_appscatalog.py @@ -315,58 +315,3 @@ def test_apps_catalog_load_with_oudated_api_version(mocker): for cache_file in glob.glob(APPS_CATALOG_CACHE + "/*"): cache_json = read_json(cache_file) assert cache_json["from_api_version"] == APPS_CATALOG_API_VERSION - - - -def test_apps_catalog_migrate_legacy_explicitly(): - - open("/etc/yunohost/appslists.json", "w").write('{"yunohost": {"yolo":"swag"}}') - mkdir(APPS_CATALOG_CACHE, 0o750, parents=True) - open(APPS_CATALOG_CACHE+"/yunohost_old.json", "w").write('{"foo":{}, "bar": {}}') - open(APPS_CATALOG_CRON_PATH, "w").write("# Some old cron") - - from yunohost.tools import _get_migration_by_name - migration = _get_migration_by_name("futureproof_apps_catalog_system") - - with requests_mock.Mocker() as m: - - # Mock the server response with a dummy apps catalog - m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG) - migration.run() - - # Old conf shouldnt be there anymore (got renamed to .old) - assert not os.path.exists("/etc/yunohost/appslists.json") - # Old cache should have been removed - assert not os.path.exists(APPS_CATALOG_CACHE+"/yunohost_old.json") - # Cron should have been changed - assert "/bin/bash" in open(APPS_CATALOG_CRON_PATH, "r").read() - assert cron_job_is_there() - - # Reading the apps_catalog should work - app_dict = _load_apps_catalog()["apps"] - assert "foo" in app_dict.keys() - assert "bar" in app_dict.keys() - - -def test_apps_catalog_migrate_legacy_implicitly(): - - open("/etc/yunohost/appslists.json", "w").write('{"yunohost": {"yolo":"swag"}}') - mkdir(APPS_CATALOG_CACHE, 0o750, parents=True) - open(APPS_CATALOG_CACHE+"/yunohost_old.json", "w").write('{"old_foo":{}, "old_bar": {}}') - open(APPS_CATALOG_CRON_PATH, "w").write("# Some old cron") - - with requests_mock.Mocker() as m: - m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG) - app_dict = _load_apps_catalog()["apps"] - - assert "foo" in app_dict.keys() - assert "bar" in app_dict.keys() - - # Old conf shouldnt be there anymore (got renamed to .old) - assert not os.path.exists("/etc/yunohost/appslists.json") - # Old cache should have been removed - assert not os.path.exists(APPS_CATALOG_CACHE+"/yunohost_old.json") - # Cron should have been changed - assert "/bin/bash" in open(APPS_CATALOG_CRON_PATH, "r").read() - assert cron_job_is_there() - From 06c5f23c2b9d2d044ad6ca1f052b405890c19486 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Mar 2020 22:56:15 +0100 Subject: [PATCH 090/262] [cleanup] These arent used anywhere --- data/templates/unattended/02periodic | 5 --- .../unattended/50unattended-upgrades | 36 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 data/templates/unattended/02periodic delete mode 100644 data/templates/unattended/50unattended-upgrades diff --git a/data/templates/unattended/02periodic b/data/templates/unattended/02periodic deleted file mode 100644 index f16105466..000000000 --- a/data/templates/unattended/02periodic +++ /dev/null @@ -1,5 +0,0 @@ -# https://wiki.debian.org/UnattendedUpgrades#automatic_call_via_.2Fetc.2Fapt.2Fapt.conf.d.2F02periodic -APT::Periodic::Enable "1"; -APT::Periodic::Update-Package-Lists "1"; -APT::Periodic::Unattended-Upgrade "1"; -APT::Periodic::Verbose "1"; diff --git a/data/templates/unattended/50unattended-upgrades b/data/templates/unattended/50unattended-upgrades deleted file mode 100644 index 49b600a3b..000000000 --- a/data/templates/unattended/50unattended-upgrades +++ /dev/null @@ -1,36 +0,0 @@ -// Automatically upgrade packages from these (origin, archive) pairs -Unattended-Upgrade::Allowed-Origins { - "${distro_id} stable"; - "${distro_id} testing"; - "Depot-Debian testing"; - "${distro_id} ${distro_codename}-security"; - "${distro_id} ${distro_codename}-updates"; -// "${distro_id} ${distro_codename}-proposed-updates"; -}; - -// List of packages to not update -Unattended-Upgrade::Package-Blacklist { -// "vim"; -// "libc6"; -// "libc6-dev"; -// "libc6-i686"; -}; - -// Send email to this address for problems or packages upgrades -// If empty or unset then no email is sent, make sure that you -// have a working mail setup on your system. The package 'mailx' -// must be installed or anything that provides /usr/bin/mail. -//Unattended-Upgrade::Mail "root@localhost"; - -// Do automatic removal of new unused dependencies after the upgrade -// (equivalent to apt-get autoremove) -Unattended-Upgrade::Remove-Unused-Dependencies "true"; - -// Automatically reboot *WITHOUT CONFIRMATION* if a -// the file /var/run/reboot-required is found after the upgrade -Unattended-Upgrade::Automatic-Reboot "false"; - - -// Use apt bandwidth limit feature, this example limits the download -// speed to 70kb/sec -//Acquire::http::Dl-Limit "70"; From bde5dab71d218d8f85c3ce0ca1a205272447eb91 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 1 Apr 2020 02:34:20 +0200 Subject: [PATCH 091/262] [cleanup] Fix typo in string name, remove stale string --- locales/en.json | 1 - 1 file changed, 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index c0f79436f..a851543dc 100644 --- a/locales/en.json +++ b/locales/en.json @@ -452,7 +452,6 @@ "migrations_skip_migration": "Skipping migration {id}...", "migrations_success_forward": "Migration {id} completed", "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.", - "no_internet_connection": "The server is not connected to the Internet", "not_enough_disk_space": "Not enough free space on '{path:s}'", "operation_interrupted": "The operation was manually interrupted?", "packages_upgrade_failed": "Could not upgrade all the packages", From fa5130a7fc3a37a7faef58ca2350c19257c53539 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 19 May 2020 19:50:48 +0200 Subject: [PATCH 092/262] [cleanup] Move migration 0011 code to legacy.py --- locales/en.json | 10 +- src/yunohost/backup.py | 10 +- .../legacy.py} | 91 +++---------------- 3 files changed, 19 insertions(+), 92 deletions(-) rename src/yunohost/{data_migrations/0011_setup_group_permission.py => utils/legacy.py} (52%) diff --git a/locales/en.json b/locales/en.json index a851543dc..3f17b576a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -406,17 +406,13 @@ "mail_unavailable": "This e-mail address is reserved and shall be automatically allocated to the very first user", "main_domain_change_failed": "Unable to change the main domain", "main_domain_changed": "The main domain has been changed", - "migration_description_0011_setup_group_permission": "Set up user group and set up permission for apps and services", "migration_description_0015_migrate_to_buster": "Upgrade the system to Debian Buster and YunoHost 4.x", - "migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.", - "migration_0011_can_not_backup_before_migration": "The backup of the system could not be completed before the migration failed. Error: {error:s}", + "migration_description_0016_php70_to_php73_pools": "Migrate php7.0-fpm 'pool' conf files to php7.3", + "migration_description_0017_postgresql_9p6_to_11": "Migrate databases from PostgreSQL 9.6 to 11", + "migration_description_0018_xtable_to_nftable": "Migrate old network traffic rules to the new nftable system", "migration_0011_create_group": "Creating a group for each user…", - "migration_0011_done": "Migration completed. You are now able to manage usergroups.", - "migration_0011_slapd_config_will_be_overwritten": "It looks like you manually edited the slapd configuration. For this critical migration, YunoHost needs to force the update of the slapd configuration. The original files will be backuped in {conf_backup_folder}.", "migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}", "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP...", - "migration_0011_migration_failed_trying_to_rollback": "Could not migrate… trying to roll back the system.", - "migration_0011_rollback_success": "System rolled back.", "migration_0011_update_LDAP_database": "Updating LDAP database...", "migration_0011_update_LDAP_schema": "Updating LDAP schema...", "migration_0011_failed_to_remove_stale_object": "Could not remove stale object {dn}: {error}", diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 449b52bd8..8a6ce4e7f 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1244,12 +1244,11 @@ class RestoreManager(): # # Legacy code if not "all_users" in user_group_list()["groups"].keys(): - from yunohost.tools import _get_migration_by_name - setup_group_permission = _get_migration_by_name("setup_group_permission") + from yunohost.utils.legacy import SetupGroupPermissions # Update LDAP schema restart slapd logger.info(m18n.n("migration_0011_update_LDAP_schema")) regen_conf(names=['slapd'], force=True) - setup_group_permission.migrate_LDAP_db() + SetupGroupPermissions.migrate_LDAP_db() # Remove all permission for all app which is still in the LDAP for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys(): @@ -1389,9 +1388,8 @@ class RestoreManager(): else: # Otherwise, we need to migrate the legacy permissions of this # app (included in its settings.yml) - from yunohost.tools import _get_migration_by_name - setup_group_permission = _get_migration_by_name("setup_group_permission") - setup_group_permission.migrate_app_permission(app=app_instance_name) + from yunohost.utils.legacy import SetupGroupPermissions + SetupGroupPermissions.migrate_app_permission(app=app_instance_name) # Prepare env. var. to pass to script env_dict = self._get_env_var(app_instance_name) diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/utils/legacy.py similarity index 52% rename from src/yunohost/data_migrations/0011_setup_group_permission.py rename to src/yunohost/utils/legacy.py index c55e33cab..b7052b438 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/utils/legacy.py @@ -1,34 +1,19 @@ -import time -import os - from moulinette import m18n from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_yaml -from yunohost.tools import Migration from yunohost.user import user_list, user_group_create, user_group_update from yunohost.app import app_setting, _installed_apps -from yunohost.regenconf import regen_conf, BACKUP_CONF_DIR from yunohost.permission import permission_create, user_permission_update, permission_sync_to_user -logger = getActionLogger('yunohost.migration') - -################################################### -# Tools used also for restoration -################################################### +logger = getActionLogger('yunohost.legacy') -class MyMigration(Migration): - """ - Update the LDAP DB to be able to store the permission - Create a group for each yunohost user - Migrate app permission from apps setting to LDAP - """ +class SetupGroupPermissions(): - required = True - - def remove_if_exists(self, target): + @staticmethod + def remove_if_exists(target): from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() @@ -50,7 +35,8 @@ class MyMigration(Migration): except Exception as e: raise YunohostError("migration_0011_failed_to_remove_stale_object", dn=dn, error=e) - def migrate_LDAP_db(self): + @staticmethod + def migrate_LDAP_db(): logger.info(m18n.n("migration_0011_update_LDAP_database")) @@ -60,8 +46,8 @@ class MyMigration(Migration): ldap_map = read_yaml('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') try: - self.remove_if_exists("ou=permission") - self.remove_if_exists('ou=groups') + SetupGroupPermissions.remove_if_exists("ou=permission") + SetupGroupPermissions.remove_if_exists('ou=groups') attr_dict = ldap_map['parents']['ou=permission'] ldap.add('ou=permission', attr_dict) @@ -93,7 +79,8 @@ class MyMigration(Migration): user_group_create(username, gid=user_info['uidNumber'][0], primary_group=True, sync_perm=False) user_group_update(groupname='all_users', add=username, force=True, sync_perm=False) - def migrate_app_permission(self, app=None): + @staticmethod + def migrate_app_permission(app=None): logger.info(m18n.n("migration_0011_migrate_permission")) apps = _installed_apps() @@ -116,66 +103,12 @@ class MyMigration(Migration): allowed = [user for user in permission.split(',') if user in known_users] else: allowed = ["all_users"] - permission_create(app+".main", url=url, allowed=allowed, sync_perm=False) + permission_create(app + ".main", url=url, allowed=allowed, sync_perm=False) app_setting(app, 'allowed_users', delete=True) # Migrate classic public app still using the legacy unprotected_uris if app_setting(app, "unprotected_uris") == "/" or app_setting(app, "skipped_uris") == "/": - user_permission_update(app+".main", add="visitors", sync_perm=False) + user_permission_update(app + ".main", add="visitors", sync_perm=False) permission_sync_to_user() - - def run(self): - - # FIXME : what do we really want to do here ... - # Imho we should just force-regen the conf in all case, and maybe - # just display a warning if we detect that the conf was manually modified - - # Check if the migration can be processed - ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True) - # By this we check if the have been customized - if ldap_regen_conf_status and ldap_regen_conf_status['slapd']['pending']: - logger.warning(m18n.n("migration_0011_slapd_config_will_be_overwritten", conf_backup_folder=BACKUP_CONF_DIR)) - - # Backup LDAP and the apps settings before to do the migration - logger.info(m18n.n("migration_0011_backup_before_migration")) - try: - backup_folder = "/home/yunohost.backup/premigration/" + time.strftime('%Y%m%d-%H%M%S', time.gmtime()) - os.makedirs(backup_folder, 0o750) - os.system("systemctl stop slapd") - os.system("cp -r --preserve /etc/ldap %s/ldap_config" % backup_folder) - os.system("cp -r --preserve /var/lib/ldap %s/ldap_db" % backup_folder) - os.system("cp -r --preserve /etc/yunohost/apps %s/apps_settings" % backup_folder) - except Exception as e: - raise YunohostError("migration_0011_can_not_backup_before_migration", error=e) - finally: - os.system("systemctl start slapd") - - try: - # Update LDAP schema restart slapd - logger.info(m18n.n("migration_0011_update_LDAP_schema")) - regen_conf(names=['slapd'], force=True) - - # Update LDAP database - self.migrate_LDAP_db() - - # Migrate permission - self.migrate_app_permission() - - permission_sync_to_user() - except Exception as e: - logger.warn(m18n.n("migration_0011_migration_failed_trying_to_rollback")) - os.system("systemctl stop slapd") - os.system("rm -r /etc/ldap/slapd.d") # To be sure that we don't keep some part of the old config - os.system("cp -r --preserve %s/ldap_config/. /etc/ldap/" % backup_folder) - os.system("cp -r --preserve %s/ldap_db/. /var/lib/ldap/" % backup_folder) - os.system("cp -r --preserve %s/apps_settings/. /etc/yunohost/apps/" % backup_folder) - os.system("systemctl start slapd") - os.system("rm -r " + backup_folder) - logger.info(m18n.n("migration_0011_rollback_success")) - raise - else: - os.system("rm -r " + backup_folder) - - logger.info(m18n.n("migration_0011_done")) From fa59ad35a9c2b983f7b7a1e7dc89d350ef142fe6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 8 Feb 2020 18:27:44 +0100 Subject: [PATCH 093/262] [conf] Automatically disable/stop systemd-resolved that conflicts with dnsmasq on fresh setups ... --- data/hooks/conf_regen/43-dnsmasq | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq index 8cddec1be..75d74b09c 100755 --- a/data/hooks/conf_regen/43-dnsmasq +++ b/data/hooks/conf_regen/43-dnsmasq @@ -69,8 +69,16 @@ do_post_regen() { short_hostname=$(hostname -s) grep -q "127.0.0.1.*$short_hostname" /etc/hosts || echo -e "\n127.0.0.1\t$short_hostname" >>/etc/hosts - [[ -z "$regen_conf_files" ]] \ - || service dnsmasq restart + [[ -n "$regen_conf_files" ]] || return + + # Remove / disable services likely to conflict with dnsmasq + for SERVICE in systemd-resolved bind9 + do + systemctl is-enabled $SERVICE &>/dev/null && systemctl disable $SERVICE 2>/dev/null + systemctl is-active $SERVICE &>/dev/null && systemctl stop $SERVICE + done + + systemctl restart dnsmasq } FORCE=${2:-0} From 16ce8bf693c9aa4ecd011e597e23f5a4a44b5b3d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 18 Feb 2020 00:48:19 +0100 Subject: [PATCH 094/262] [conf] Remove deprecated option in sshd conf, c.f. https://patchwork.openembedded.org/patch/139981/ --- data/templates/ssh/sshd_config | 3 --- 1 file changed, 3 deletions(-) diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index 8dc0e8dfc..bd3efdef3 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -27,9 +27,6 @@ HostKey {{ key }}{% endfor %} MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com {% endif %} -# Use kernel sandbox mechanisms where possible in unprivileged processes -UsePrivilegeSeparation sandbox - # LogLevel VERBOSE logs user's key fingerprint on login. # Needed to have a clear audit track of which key was using to log in. SyslogFacility AUTH From 01ccd5dd8cab28794bc1ee41fc5ea1e4f18ea816 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 2 May 2020 03:27:07 +0200 Subject: [PATCH 095/262] [conf] Small tweak in dovecot conf (deprecated settings) --- data/templates/dovecot/dovecot.conf | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/data/templates/dovecot/dovecot.conf b/data/templates/dovecot/dovecot.conf index 8fc0e75ae..d64b15356 100644 --- a/data/templates/dovecot/dovecot.conf +++ b/data/templates/dovecot/dovecot.conf @@ -14,18 +14,16 @@ mail_plugins = $mail_plugins quota ############################################################################### -# generated 2020-04-03, Mozilla Guideline v5.4, Dovecot 2.2.27, OpenSSL 1.1.0l, intermediate configuration -# https://ssl-config.mozilla.org/#server=dovecot&version=2.2.27&config=intermediate&openssl=1.1.0l&guideline=5.4 +# generated 2020-05-02, Mozilla Guideline v5.4, Dovecot 2.3.4.1, OpenSSL 1.1.1d, intermediate configuration +# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.4.1&config=intermediate&openssl=1.1.1d&guideline=5.4 ssl = required ssl_cert = Date: Sat, 7 Mar 2020 20:16:03 +0100 Subject: [PATCH 096/262] [conf] Update nslcd and nsswitch stuff using new Buster's default configs + get rid of nslcd service, only keep the regen-conf part --- data/templates/nslcd/nslcd.conf | 12 ++++++++++++ data/templates/nsswitch/nsswitch.conf | 10 +++------- data/templates/yunohost/services.yml | 9 +++++---- locales/en.json | 3 +-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/data/templates/nslcd/nslcd.conf b/data/templates/nslcd/nslcd.conf index 091ecb7cc..7cfe73e07 100644 --- a/data/templates/nslcd/nslcd.conf +++ b/data/templates/nslcd/nslcd.conf @@ -15,6 +15,18 @@ base dc=yunohost,dc=org # The LDAP protocol version to use. #ldap_version 3 +# The DN to bind with for normal lookups. +#binddn cn=annonymous,dc=example,dc=net +#bindpw secret + +# The DN used for password modifications by root. +#rootpwmoddn cn=admin,dc=example,dc=com + +# SSL options +#ssl off +#tls_reqcert never +tls_cacertfile /etc/ssl/certs/ca-certificates.crt + # The search scope. #scope sub diff --git a/data/templates/nsswitch/nsswitch.conf b/data/templates/nsswitch/nsswitch.conf index b55e01b02..8f46e4f5d 100644 --- a/data/templates/nsswitch/nsswitch.conf +++ b/data/templates/nsswitch/nsswitch.conf @@ -1,12 +1,8 @@ # /etc/nsswitch.conf -# -# Example configuration of GNU Name Service Switch functionality. -# If you have the `glibc-doc-reference' and `info' packages installed, try: -# `info libc "Name Service Switch"' for information about this file. -passwd: compat ldap -group: compat ldap -shadow: compat ldap +passwd: files systemd ldap +group: files systemd ldap +shadow: files ldap gshadow: files hosts: files myhostname mdns4_minimal [NOTFOUND=return] dns diff --git a/data/templates/yunohost/services.yml b/data/templates/yunohost/services.yml index e1dd57e55..73ae9403e 100644 --- a/data/templates/yunohost/services.yml +++ b/data/templates/yunohost/services.yml @@ -20,10 +20,9 @@ nginx: test_conf: nginx -t needs_exposed_ports: [80, 443] category: web -nslcd: {} -php7.0-fpm: - log: /var/log/php7.0-fpm.log - test_conf: php-fpm7.0 --test +php7.3-fpm: + log: /var/log/php7.3-fpm.log + test_conf: php-fpm7.3 --test category: web postfix: log: [/var/log/mail.log,/var/log/mail.err] @@ -64,3 +63,5 @@ postgrey: null spamassassin: null rmilter: null php5-fpm: null +php7.0-fpm: null +nslcd: null diff --git a/locales/en.json b/locales/en.json index 3f17b576a..f3daed3ca 100644 --- a/locales/en.json +++ b/locales/en.json @@ -534,8 +534,7 @@ "service_description_metronome": "Manage XMPP instant messaging accounts", "service_description_mysql": "Stores app data (SQL database)", "service_description_nginx": "Serves or provides access to all the websites hosted on your server", - "service_description_nslcd": "Handles YunoHost user shell connection", - "service_description_php7.0-fpm": "Runs apps written in PHP with NGINX", + "service_description_php7.3-fpm": "Runs apps written in PHP with NGINX", "service_description_postfix": "Used to send and receive e-mails", "service_description_redis-server": "A specialized database used for rapid data access, task queue, and communication between programs", "service_description_rspamd": "Filters spam, and other e-mail related features", From 5930b6ddf2619f895c9b2e382f28890b4775a295 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 8 Feb 2020 20:11:31 +0100 Subject: [PATCH 097/262] [php] Now use php7.3 instead of php7.0 + autopatch app scripts like we did for php5 --- data/helpers.d/php | 2 +- locales/en.json | 1 - src/yunohost/app.py | 31 ++++++++++++++++++++----------- src/yunohost/backup.py | 39 +++++++++++++++++---------------------- 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 4902e3292..7ff671317 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -1,6 +1,6 @@ #!/bin/bash -readonly YNH_DEFAULT_PHP_VERSION=7.0 +readonly YNH_DEFAULT_PHP_VERSION=7.3 # Declare the actual php version to use. # A packager willing to use another version of php can override the variable into its _common.sh. YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION} diff --git a/locales/en.json b/locales/en.json index f3daed3ca..da6c759b9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -106,7 +106,6 @@ "backup_output_directory_required": "You must provide an output directory for the backup", "backup_output_symlink_dir_broken": "Your archive directory '{path:s}' is a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.", "backup_permission": "Backup permission for {app:s}", - "backup_php5_to_php7_migration_may_fail": "Could not convert your archive to support PHP 7, you may be unable to restore your PHP apps (reason: {error:s})", "backup_running_hooks": "Running backup hooks...", "backup_system_part_failed": "Could not backup the '{part:s}' system part", "backup_unable_to_organize_files": "Could not use the quick method to organize files in the archive", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 032536247..56574acf6 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -521,7 +521,7 @@ def app_upgrade(app=[], url=None, file=None): _patch_legacy_helpers(extracted_app_folder) # Apply dirty patch to make php5 apps compatible with php7 - _patch_php5(extracted_app_folder) + _patch_legacy_php_versions(extracted_app_folder) # Start register change on system related_to = [('app', app_instance_name)] @@ -736,7 +736,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu _patch_legacy_helpers(extracted_app_folder) # Apply dirty patch to make php5 apps compatible with php7 - _patch_php5(extracted_app_folder) + _patch_legacy_php_versions(extracted_app_folder) # Prepare env. var. to pass to script env_dict = _make_environment_dict(args_odict) @@ -1033,7 +1033,7 @@ def app_remove(operation_logger, app): # Apply dirty patch to make php5 apps compatible with php7 (e.g. the remove # script might date back from jessie install) - _patch_php5(app_setting_path) + _patch_legacy_php_versions(app_setting_path) manifest = _get_manifest_of_app(app_setting_path) @@ -2839,8 +2839,8 @@ def _assert_system_is_sane_for_app(manifest, when): # Some apps use php-fpm or php5-fpm which is now php7.0-fpm def replace_alias(service): - if service in ["php-fpm", "php5-fpm"]: - return "php7.0-fpm" + if service in ["php-fpm", "php5-fpm", "php7.0-fpm"]: + return "php7.3-fpm" else: return service services = [replace_alias(s) for s in services] @@ -2848,7 +2848,7 @@ def _assert_system_is_sane_for_app(manifest, when): # We only check those, mostly to ignore "custom" services # (added by apps) and because those are the most popular # services - service_filter = ["nginx", "php7.0-fpm", "mysql", "postfix"] + service_filter = ["nginx", "php7.3-fpm", "mysql", "postfix"] services = [str(s) for s in services if s in service_filter] if "nginx" not in services: @@ -2873,7 +2873,16 @@ def _assert_system_is_sane_for_app(manifest, when): raise YunohostError("this_action_broke_dpkg") -def _patch_php5(app_folder): +LEGACY_PHP_VERSION_REPLACEMENTS = [ + ("/etc/php5", "/etc/php/7.3"), + ("/etc/php/7.0", "/etc/php/7.3"), + ("/var/run/php5-fpm", "/var/run/php/php7.3-fpm"), + ("/var/run/php/php7.0-fpm", "/var/run/php/php7.3-fpm"), + ("php5", "php7.3"), + ("php7.0", "php7.3") +] + +def _patch_legacy_php_versions(app_folder): files_to_patch = [] files_to_patch.extend(glob.glob("%s/conf/*" % app_folder)) @@ -2888,12 +2897,12 @@ def _patch_php5(app_folder): if not os.path.isfile(filename): continue - c = "sed -i -e 's@/etc/php5@/etc/php/7.0@g' " \ - "-e 's@/var/run/php5-fpm@/var/run/php/php7.0-fpm@g' " \ - "-e 's@php5@php7.0@g' " \ - "%s" % filename + c = "sed -i " \ + + "".join("-e 's@{pattern}@{replace}@g' ".format(pattern=p, replace=r) for p, r in LEGACY_PHP_VERSION_REPLACEMENTS) \ + + "%s" % filename os.system(c) + def _patch_legacy_helpers(app_folder): files_to_patch = [] diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 8a6ce4e7f..8ba8f2610 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -43,7 +43,7 @@ from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml from yunohost.app import ( - app_info, _is_installed, _parse_app_instance_name, _patch_php5, dump_app_log_extract_for_debugging, _patch_legacy_helpers + app_info, _is_installed, _parse_app_instance_name, _patch_legacy_php_versions, dump_app_log_extract_for_debugging, _patch_legacy_helpers, LEGACY_PHP_VERSION_REPLACEMENTS ) from yunohost.hook import ( hook_list, hook_info, hook_callback, hook_exec, CUSTOM_HOOK_FOLDER @@ -1141,7 +1141,7 @@ class RestoreManager(): self._postinstall_if_needed() # Apply dirty patch to redirect php5 file on php7 - self._patch_backup_csv_file() + self._patch_legacy_php_versions_in_csv_file() self._restore_system() self._restore_apps() @@ -1150,9 +1150,9 @@ class RestoreManager(): finally: self.clean() - def _patch_backup_csv_file(self): + def _patch_legacy_php_versions_in_csv_file(self): """ - Apply dirty patch to redirect php5 file on php7 + Apply dirty patch to redirect php5 and php7.0 files to php7.3 """ backup_csv = os.path.join(self.work_dir, 'backup.csv') @@ -1160,32 +1160,27 @@ class RestoreManager(): if not os.path.isfile(backup_csv): return - contains_php5 = False + replaced_something = False with open(backup_csv) as csvfile: reader = csv.DictReader(csvfile, fieldnames=['source', 'dest']) newlines = [] for row in reader: - if 'php5' in row['source']: - contains_php5 = True - row['source'] = row['source'].replace('/etc/php5', '/etc/php/7.0') \ - .replace('/var/run/php5-fpm', '/var/run/php/php7.0-fpm') \ - .replace('php5', 'php7') + for pattern, replace in LEGACY_PHP_VERSION_REPLACEMENTS: + if pattern in row['source']: + replaced_something = True + row['source'] = row['source'].replace(pattern, replace) newlines.append(row) - if not contains_php5: + if not replaced_something: return - try: - with open(backup_csv, 'w') as csvfile: - writer = csv.DictWriter(csvfile, - fieldnames=['source', 'dest'], - quoting=csv.QUOTE_ALL) - for row in newlines: - writer.writerow(row) - except (IOError, OSError, csv.Error) as e: - logger.warning(m18n.n('backup_php5_to_php7_migration_may_fail', - error=str(e))) + with open(backup_csv, 'w') as csvfile: + writer = csv.DictWriter(csvfile, + fieldnames=['source', 'dest'], + quoting=csv.QUOTE_ALL) + for row in newlines: + writer.writerow(row) def _restore_system(self): """ Restore user and system parts """ @@ -1333,7 +1328,7 @@ class RestoreManager(): _patch_legacy_helpers(app_settings_in_archive) # Apply dirty patch to make php5 apps compatible with php7 - _patch_php5(app_settings_in_archive) + _patch_legacy_php_versions(app_settings_in_archive) # Delete _common.sh file in backup common_file = os.path.join(app_backup_in_archive, '_common.sh') From 2c7b3a1fd1e9f0d28231b0f6675260f124fac7d9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 8 Mar 2020 22:43:52 +0100 Subject: [PATCH 098/262] [php] Add migration procedure for php7.0 -> php7.3 pool files --- .../0016_php70_to_php73_pools.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/yunohost/data_migrations/0016_php70_to_php73_pools.py diff --git a/src/yunohost/data_migrations/0016_php70_to_php73_pools.py b/src/yunohost/data_migrations/0016_php70_to_php73_pools.py new file mode 100644 index 000000000..099c01a8a --- /dev/null +++ b/src/yunohost/data_migrations/0016_php70_to_php73_pools.py @@ -0,0 +1,70 @@ +import os +import glob +from shutil import copy2 + +from moulinette.utils.log import getActionLogger + +from yunohost.tools import Migration +from yunohost.service import _run_service_command + +logger = getActionLogger('yunohost.migration') + +PHP70_POOLS = "/etc/php/7.0/fpm/pool.d" +PHP73_POOLS = "/etc/php/7.3/fpm/pool.d" + +PHP70_SOCKETS_PREFIX = "/run/php/php7.0-fpm" +PHP73_SOCKETS_PREFIX = "/run/php/php7.3-fpm" + +MIGRATION_COMMENT = "; YunoHost note : this file was automatically moved from {}".format(PHP70_POOLS) + + +class MyMigration(Migration): + + "Migrate php7.0-fpm 'pool' conf files to php7.3" + + dependencies = ["migrate_to_buster"] + + def run(self): + # Get list of php7.0 pool files + php70_pool_files = glob.glob("{}/*.conf".format(PHP70_POOLS)) + + # Keep only basenames + php70_pool_files = [os.path.basename(f) for f in php70_pool_files] + + # Ignore the "www.conf" (default stuff, probably don't want to touch it ?) + php70_pool_files = [f for f in php70_pool_files if f != "www.conf"] + + for f in php70_pool_files: + + # Copy the files to the php7.3 pool + src = "{}/{}".format(PHP70_POOLS, f) + dest = "{}/{}".format(PHP73_POOLS, f) + copy2(src, dest) + + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP70_SOCKETS_PREFIX, PHP73_SOCKETS_PREFIX, dest) + os.system(c) + + # Also add a comment that it was automatically moved from php7.0 + # (for human traceability and backward migration) + c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest) + os.system(c) + + # Reload/restart the php pools + _run_service_command("restart", "php7.3-fpm") + _run_service_command("enable", "php7.3-fpm") + os.system("systemctl stop php7.0-fpm") + os.system("systemctl disable php7.0-fpm") + os.system("rm /etc/logrotate.d/php7.0-fpm") # We remove this otherwise the logrotate cron will be unhappy + + # Get list of nginx conf file + nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") + for f in nginx_conf_files: + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP70_SOCKETS_PREFIX, PHP73_SOCKETS_PREFIX, f) + os.system(c) + # FIXME : should tweak the checksum setting in settings.yml of the app so that the file ain't considered manually modified + # Aslo gotta tweak the settings fpm_service and fpm_config_dir ... + + # Reload nginx + _run_service_command("reload", "nginx") From 9dbf626b36d6ff401d15c39bf721fceaeb2aeed1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 9 Mar 2020 20:36:44 +0100 Subject: [PATCH 099/262] [php] More ugly hack for php helpers shipped by apps that assume php 7.0 by default --- src/yunohost/app.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 56574acf6..8faf587ff 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2879,7 +2879,9 @@ LEGACY_PHP_VERSION_REPLACEMENTS = [ ("/var/run/php5-fpm", "/var/run/php/php7.3-fpm"), ("/var/run/php/php7.0-fpm", "/var/run/php/php7.3-fpm"), ("php5", "php7.3"), - ("php7.0", "php7.3") + ("php7.0", "php7.3"), + ('phpversion="${phpversion:-7.0}"', 'phpversion="${phpversion:-7.3}"'), # Many helpers like the composer ones use 7.0 by default ... + ('"$phpversion" == "7.0"', '$(bc <<< "$phpversion >= 7.3") -eq 1') # patch ynh_install_php to refuse installing/removing php <= 7.3 ] def _patch_legacy_php_versions(app_folder): From b747c17369adb6a8f359059c45d965e7defaca2a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 21 Mar 2020 18:41:06 +0100 Subject: [PATCH 100/262] [php] Also patch stuff in subfolers inside scripts/ (some apps have an experimental_helpers/ folder) --- src/yunohost/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 8faf587ff..b198ab3b3 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2889,6 +2889,7 @@ def _patch_legacy_php_versions(app_folder): files_to_patch = [] files_to_patch.extend(glob.glob("%s/conf/*" % app_folder)) files_to_patch.extend(glob.glob("%s/scripts/*" % app_folder)) + files_to_patch.extend(glob.glob("%s/scripts/*/*" % app_folder)) files_to_patch.extend(glob.glob("%s/scripts/.*" % app_folder)) files_to_patch.append("%s/manifest.json" % app_folder) files_to_patch.append("%s/manifest.toml" % app_folder) From a90540ddeb7cfd6327857d1cf610658fdc22f9cb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 8 Apr 2020 13:01:35 +0200 Subject: [PATCH 101/262] [php] php-mcrypt doesn't exist anymore in buster --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index cd6b6e495..1f2c24678 100644 --- a/debian/control +++ b/debian/control @@ -32,7 +32,7 @@ Depends: ${python:Depends}, ${misc:Depends} Recommends: yunohost-admin , ntp, inetutils-ping | iputils-ping , bash-completion, rsyslog - , php-gd, php-curl, php-gettext, php-mcrypt + , php-gd, php-curl, php-gettext , python-pip , unattended-upgrades , libdbd-ldap-perl, libnet-dns-perl From b6d1bb790174f892c82726a7f936f2b57babaad8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 17 May 2020 18:26:18 +0200 Subject: [PATCH 102/262] [php] Also migrate app settings to fix inconsistencies (e.g. during remove of migrated apps) --- .../0016_php70_to_php73_pools.py | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/yunohost/data_migrations/0016_php70_to_php73_pools.py b/src/yunohost/data_migrations/0016_php70_to_php73_pools.py index 099c01a8a..fa2e04a4a 100644 --- a/src/yunohost/data_migrations/0016_php70_to_php73_pools.py +++ b/src/yunohost/data_migrations/0016_php70_to_php73_pools.py @@ -4,6 +4,7 @@ from shutil import copy2 from moulinette.utils.log import getActionLogger +from yunohost.app import _is_installed, _get_app_settings, _set_app_settings from yunohost.tools import Migration from yunohost.service import _run_service_command @@ -50,21 +51,44 @@ class MyMigration(Migration): c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest) os.system(c) + app_id = os.path.basename(f)[:-len(".conf")] + self.migrate_app_settings(app_id) + + nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/%s.conf" % app_id) + for f in nginx_conf_files: + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP70_SOCKETS_PREFIX, PHP73_SOCKETS_PREFIX, f) + os.system(c) + + os.system("rm /etc/logrotate.d/php7.0-fpm") # We remove this otherwise the logrotate cron will be unhappy + # Reload/restart the php pools _run_service_command("restart", "php7.3-fpm") _run_service_command("enable", "php7.3-fpm") os.system("systemctl stop php7.0-fpm") os.system("systemctl disable php7.0-fpm") - os.system("rm /etc/logrotate.d/php7.0-fpm") # We remove this otherwise the logrotate cron will be unhappy - - # Get list of nginx conf file - nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") - for f in nginx_conf_files: - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP70_SOCKETS_PREFIX, PHP73_SOCKETS_PREFIX, f) - os.system(c) - # FIXME : should tweak the checksum setting in settings.yml of the app so that the file ain't considered manually modified - # Aslo gotta tweak the settings fpm_service and fpm_config_dir ... # Reload nginx _run_service_command("reload", "nginx") + + def migrate_app_settings(self, app_id): + + if not _is_installed(app_id): + return + + settings = _get_app_settings(app_id) + + if settings.get("fpm_config_dir") == "/etc/php/7.0/fpm": + settings["fpm_config_dir"] = "/etc/php/7.3/fpm" + if settings.get("fpm_service") == "php7.0-fpm": + settings["fpm_service"] = "php7.3-fpm" + if settings.get("phpversion") == "7.0": + settings["phpversion"] = "7.3" + + # We delete these checksums otherwise the file will appear as manually modified + list_to_remove = ["checksum__etc_php_7.0_fpm_pool", + "checksum__etc_nginx_conf.d"] + settings = {k: v for k, v in settings.items() + if not any(k.startswith(to_remove) for to_remove in list_to_remove)} + + _set_app_settings(app_id, settings) From 73356eed75033762eb28e3aff1ff1290f01fff13 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 19 May 2020 16:30:36 +0200 Subject: [PATCH 103/262] [php] Also patch php7.0 settings on the fly during restore --- src/yunohost/app.py | 21 +++++++++++++++ src/yunohost/backup.py | 9 ++++++- .../0016_php70_to_php73_pools.py | 27 +++---------------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index b198ab3b3..00472744f 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2884,6 +2884,7 @@ LEGACY_PHP_VERSION_REPLACEMENTS = [ ('"$phpversion" == "7.0"', '$(bc <<< "$phpversion >= 7.3") -eq 1') # patch ynh_install_php to refuse installing/removing php <= 7.3 ] + def _patch_legacy_php_versions(app_folder): files_to_patch = [] @@ -2906,6 +2907,26 @@ def _patch_legacy_php_versions(app_folder): os.system(c) +def _patch_legacy_php_versions_in_settings(app_folder): + + settings = read_yaml(os.path.join(app_folder, 'settings.yml')) + + if settings.get("fpm_config_dir") == "/etc/php/7.0/fpm": + settings["fpm_config_dir"] = "/etc/php/7.3/fpm" + if settings.get("fpm_service") == "php7.0-fpm": + settings["fpm_service"] = "php7.3-fpm" + if settings.get("phpversion") == "7.0": + settings["phpversion"] = "7.3" + + # We delete these checksums otherwise the file will appear as manually modified + list_to_remove = ["checksum__etc_php_7.0_fpm_pool", + "checksum__etc_nginx_conf.d"] + settings = {k: v for k, v in settings.items() + if not any(k.startswith(to_remove) for to_remove in list_to_remove)} + + write_to_yaml(app_folder + '/settings.yml', settings) + + def _patch_legacy_helpers(app_folder): files_to_patch = [] diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 8ba8f2610..4b55ee83d 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -43,7 +43,13 @@ from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml from yunohost.app import ( - app_info, _is_installed, _parse_app_instance_name, _patch_legacy_php_versions, dump_app_log_extract_for_debugging, _patch_legacy_helpers, LEGACY_PHP_VERSION_REPLACEMENTS + app_info, _is_installed, + _parse_app_instance_name, + dump_app_log_extract_for_debugging, + _patch_legacy_helpers, + _patch_legacy_php_versions, + _patch_legacy_php_versions_in_settings, + LEGACY_PHP_VERSION_REPLACEMENTS ) from yunohost.hook import ( hook_list, hook_info, hook_callback, hook_exec, CUSTOM_HOOK_FOLDER @@ -1329,6 +1335,7 @@ class RestoreManager(): # Apply dirty patch to make php5 apps compatible with php7 _patch_legacy_php_versions(app_settings_in_archive) + _patch_legacy_php_versions_in_settings(app_settings_in_archive) # Delete _common.sh file in backup common_file = os.path.join(app_backup_in_archive, '_common.sh') diff --git a/src/yunohost/data_migrations/0016_php70_to_php73_pools.py b/src/yunohost/data_migrations/0016_php70_to_php73_pools.py index fa2e04a4a..1cdb2bc4d 100644 --- a/src/yunohost/data_migrations/0016_php70_to_php73_pools.py +++ b/src/yunohost/data_migrations/0016_php70_to_php73_pools.py @@ -4,7 +4,7 @@ from shutil import copy2 from moulinette.utils.log import getActionLogger -from yunohost.app import _is_installed, _get_app_settings, _set_app_settings +from yunohost.app import _is_installed, _get_app_settings, _set_app_settings, _patch_legacy_php_versions_in_settings from yunohost.tools import Migration from yunohost.service import _run_service_command @@ -52,7 +52,8 @@ class MyMigration(Migration): os.system(c) app_id = os.path.basename(f)[:-len(".conf")] - self.migrate_app_settings(app_id) + if _is_installed(app_id): + _patch_legacy_php_versions_in_settings("/etc/yunohost/apps/%s/" % app_id) nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/%s.conf" % app_id) for f in nginx_conf_files: @@ -70,25 +71,3 @@ class MyMigration(Migration): # Reload nginx _run_service_command("reload", "nginx") - - def migrate_app_settings(self, app_id): - - if not _is_installed(app_id): - return - - settings = _get_app_settings(app_id) - - if settings.get("fpm_config_dir") == "/etc/php/7.0/fpm": - settings["fpm_config_dir"] = "/etc/php/7.3/fpm" - if settings.get("fpm_service") == "php7.0-fpm": - settings["fpm_service"] = "php7.3-fpm" - if settings.get("phpversion") == "7.0": - settings["phpversion"] = "7.3" - - # We delete these checksums otherwise the file will appear as manually modified - list_to_remove = ["checksum__etc_php_7.0_fpm_pool", - "checksum__etc_nginx_conf.d"] - settings = {k: v for k, v in settings.items() - if not any(k.startswith(to_remove) for to_remove in list_to_remove)} - - _set_app_settings(app_id, settings) From 70fab24247786a9331b02df1c3090c3137e9ba73 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 9 Mar 2020 18:32:09 +0100 Subject: [PATCH 104/262] [psql] Upgrade postgresql helper to use version 11 --- data/helpers.d/postgresql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/postgresql b/data/helpers.d/postgresql index 7eb4e7289..11b9c0fed 100644 --- a/data/helpers.d/postgresql +++ b/data/helpers.d/postgresql @@ -1,7 +1,7 @@ #!/bin/bash PSQL_ROOT_PWD_FILE=/etc/yunohost/psql -PSQL_VERSION=9.6 +PSQL_VERSION=11 # Open a connection as a user # From 50f1e9a6819f79f6fcfb42495303bdd95df9a81c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Mar 2020 20:12:12 +0100 Subject: [PATCH 105/262] [psql] Add migration for Postgresql cluster upgrade from 9.6 to 11 --- locales/en.json | 4 ++ .../0017_postgresql_9p6_to_11.py | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py diff --git a/locales/en.json b/locales/en.json index da6c759b9..7813e4410 100644 --- a/locales/en.json +++ b/locales/en.json @@ -429,6 +429,10 @@ "migration_0015_specific_upgrade": "Starting upgrade of system packages that needs to be upgrade independently...", "migration_0015_cleaning_up": "Cleaning up cache and packages not useful anymore...", "migration_0015_weak_certs": "The following certificates were found to still use weak signature algorithms and have to be upgraded to be compatible with the next version of nginx: {certs}", + "migration_description_0017_postgresql_9p6_to_11": "Migrate databases from PostgreSQL 9.6 to 11", + "migration_0017_postgresql_96_not_installed": "PostgreSQL was not installed on your system. Nothing to do.", + "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 is installed, but not postgresql 11‽ Something weird might have happened on your system :(...", + "migration_0017_not_enough_space": "Make sufficient space available in {path} to run the migration.", "migrations_already_ran": "Those migrations are already done: {ids}", "migrations_cant_reach_migration_file": "Could not access migrations files at the path '%s'", "migrations_dependencies_not_satisfied": "Run these migrations: '{dependencies_id}', before migration {id}.", diff --git a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py new file mode 100644 index 000000000..f03cd9c8c --- /dev/null +++ b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py @@ -0,0 +1,41 @@ +import subprocess + +from moulinette import m18n +from yunohost.utils.error import YunohostError +from moulinette.utils.log import getActionLogger + +from yunohost.tools import Migration +from yunohost.utils.filesystem import free_space_in_directory, space_used_by_directory + +logger = getActionLogger('yunohost.migration') + + +class MyMigration(Migration): + + "Migrate DBs from Postgresql 9.6 to 11 after migrating to Buster" + + dependencies = ["migrate_to_buster"] + + def run(self): + + if not self.package_is_installed("postgresql-9.6"): + logger.warning(m18n.n("migration_0017_postgresql_96_not_installed")) + return + + if not self.package_is_installed("postgresql-11"): + raise YunohostError("migration_0017_postgresql_11_not_installed") + + if not space_used_by_directory("/var/lib/postgresql/9.6") > free_space_in_directory("/var/lib/postgresql"): + raise YunohostError("migration_0017_not_enough_space", path="/var/lib/postgresql/") + + subprocess.check_call("systemctl stop postgresql", shell=True) + subprocess.check_call("pg_dropcluster --stop 11 main", shell=True) + subprocess.check_call("pg_upgradecluster -m upgrade 9.6 main", shell=True) + subprocess.check_call("pg_dropcluster --stop 9.6 main", shell=True) + subprocess.check_call("systemctl start postgresql", shell=True) + + def package_is_installed(self, package_name): + + p = subprocess.Popen("dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name), shell=True) + p.communicate() + return p.returncode == 0 From 56a5b5aae2a64d56e1bde6ecd9d69940bd308cd8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 18 May 2020 02:11:12 +0200 Subject: [PATCH 106/262] [psql] Fix hardcoded fix for postgresql status check --- src/yunohost/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index f12c0743c..2eeb078bc 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -618,7 +618,7 @@ def _get_services(): if "postgresql" in services: if "description" in services["postgresql"]: del services["postgresql"]["description"] - services["postgresql"]["actual_systemd_service"] = "postgresql@9.6-main" + services["postgresql"]["actual_systemd_service"] = "postgresql@11-main" return services From bbb6a4ce91e90d083c99176130e400d695208a43 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 17 May 2020 16:41:54 +0200 Subject: [PATCH 107/262] [firewall] Add draft of migration for xtable->nftable --- locales/en.json | 3 +- .../data_migrations/0018_xtable_to_nftable.py | 92 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/yunohost/data_migrations/0018_xtable_to_nftable.py diff --git a/locales/en.json b/locales/en.json index 7813e4410..d40fcfb14 100644 --- a/locales/en.json +++ b/locales/en.json @@ -429,10 +429,11 @@ "migration_0015_specific_upgrade": "Starting upgrade of system packages that needs to be upgrade independently...", "migration_0015_cleaning_up": "Cleaning up cache and packages not useful anymore...", "migration_0015_weak_certs": "The following certificates were found to still use weak signature algorithms and have to be upgraded to be compatible with the next version of nginx: {certs}", - "migration_description_0017_postgresql_9p6_to_11": "Migrate databases from PostgreSQL 9.6 to 11", "migration_0017_postgresql_96_not_installed": "PostgreSQL was not installed on your system. Nothing to do.", "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 is installed, but not postgresql 11‽ Something weird might have happened on your system :(...", "migration_0017_not_enough_space": "Make sufficient space available in {path} to run the migration.", + "migration_0018_failed_to_migrate_iptables_rules": "Failed to migrate legacy iptables rules to nftables: {error}", + "migration_0018_failed_to_reset_legacy_rules": "Failed to reset legacy iptables rules: {error}", "migrations_already_ran": "Those migrations are already done: {ids}", "migrations_cant_reach_migration_file": "Could not access migrations files at the path '%s'", "migrations_dependencies_not_satisfied": "Run these migrations: '{dependencies_id}', before migration {id}.", diff --git a/src/yunohost/data_migrations/0018_xtable_to_nftable.py b/src/yunohost/data_migrations/0018_xtable_to_nftable.py new file mode 100644 index 000000000..7d3810518 --- /dev/null +++ b/src/yunohost/data_migrations/0018_xtable_to_nftable.py @@ -0,0 +1,92 @@ +import os +import subprocess + +from moulinette import m18n +from yunohost.utils.error import YunohostError +from moulinette.utils.log import getActionLogger + +from yunohost.firewall import firewall_reload +from yunohost.service import service_restart +from yunohost.tools import Migration + +logger = getActionLogger('yunohost.migration') + + +class MyMigration(Migration): + + "Migrate legacy iptables rules from stretch that relied on xtable and should now rely on nftable" + + dependencies = ["migrate_to_buster"] + + def run(self): + + self.do_ipv4 = os.system("iptables -w -L >/dev/null") == 0 + self.do_ipv6 = os.system("ip6tables -w -L >/dev/null") == 0 + + if not self.do_ipv4: + logger.warning(m18n.n('iptables_unavailable')) + if not self.do_ipv6: + logger.warning(m18n.n('ip6tables_unavailable')) + + backup_folder = "/home/yunohost.backup/premigration/xtable_to_nftable/" + if not os.path.exists(backup_folder): + os.makedirs(backup_folder, 0o750) + self.backup_rules_ipv4 = os.path.join(backup_folder, "legacy_rules_ipv4") + self.backup_rules_ipv6 = os.path.join(backup_folder, "legacy_rules_ipv6") + + # Backup existing legacy rules to be able to rollback + if self.do_ipv4 and not os.path.exists(self.backup_rules_ipv4): + os.system("iptables-legacy -L >/dev/null") # For some reason if we don't do this, iptables-legacy-save is empty ? + subprocess.check_call("iptables-legacy-save > %s" % self.backup_rules_ipv4, shell=True) + assert subprocess.check_output("cat %s" % self.backup_rules_ipv4, shell=True).strip(), "Uhoh backup of legacy ipv4 rules is empty !?" + if self.do_ipv6 and not os.path.exists(self.backup_rules_ipv6): + os.system("ip6tables-legacy -L >/dev/null") # For some reason if we don't do this, iptables-legacy-save is empty ? + subprocess.check_call("ip6tables-legacy-save > %s" % self.backup_rules_ipv6, shell=True) + assert subprocess.check_output("cat %s" % self.backup_rules_ipv6, shell=True).strip(), "Uhoh backup of legacy ipv6 rules is empty !?" + + # We inject the legacy rules (iptables-legacy) into the new iptable (just "iptables") + try: + if self.do_ipv4: + subprocess.check_call("iptables-legacy-save | iptables-restore", shell=True) + if self.do_ipv6: + subprocess.check_call("ip6tables-legacy-save | ip6tables-restore", shell=True) + except Exception as e: + self.rollback() + raise YunohostError("migration_0018_failed_to_migrate_iptables_rules", error=e) + + # Reset everything in iptables-legacy + # Stolen from https://serverfault.com/a/200642 + try: + if self.do_ipv4: + subprocess.check_call( + "iptables-legacy-save | awk '/^[*]/ { print $1 }" # Keep lines like *raw, *filter and *nat + " /^:[A-Z]+ [^-]/ { print $1 \" ACCEPT\" ; }" # Turn all policies to accept + " /COMMIT/ { print $0; }'" # Keep the line COMMIT + " | iptables-legacy-restore", + shell=True) + if self.do_ipv6: + subprocess.check_call( + "ip6tables-legacy-save | awk '/^[*]/ { print $1 }" # Keep lines like *raw, *filter and *nat + " /^:[A-Z]+ [^-]/ { print $1 \" ACCEPT\" ; }" # Turn all policies to accept + " /COMMIT/ { print $0; }'" # Keep the line COMMIT + " | ip6tables-legacy-restore", + shell=True) + except Exception as e: + self.rollback() + raise YunohostError("migration_0018_failed_to_reset_legacy_rules", error=e) + + # You might be wondering "uh but is it really useful to + # iptables-legacy-save | iptables-restore considering firewall_reload() + # flush/resets everything anyway ?" + # But the answer is : firewall_reload() only resets the *filter table. + # On more complex setups (e.g. internet cube or docker) you will also + # have rules in the *nat (or maybe *raw?) sections of iptables. + firewall_reload() + service_restart("fail2ban") + + def rollback(self): + + if self.do_ipv4: + subprocess.check_call("iptables-legacy-restore < %s" % self.backup_rules_ipv4, shell=True) + if self.do_ipv6: + subprocess.check_call("iptables-legacy-restore < %s" % self.backup_rules_ipv6, shell=True) From 591474095760771f4a06f0e8f878f233bef23915 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 17 May 2020 16:55:41 +0200 Subject: [PATCH 108/262] [firewall] nftables is the new iptables-persistent and it should conflict with yunohost-firewall --- data/hooks/conf_regen/01-yunohost | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 61951fe5f..1081def17 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -75,7 +75,16 @@ EOF ConditionCapability=CAP_SYS_TIME ConditionVirtualization=!container " > ${pending_dir}/etc/systemd/system/ntp.service.d/ynh-override.conf - + + # Make nftable conflict with yunohost-firewall + mkdir -p ${pending_dir}/etc/systemd/system/nftables.service.d/ + cat > ${pending_dir}/etc/systemd/system/nftables.service.d/ynh-override.conf << EOF +[Unit] +# yunohost-firewall and nftables conflict with each other +Conflicts=yunohost-firewall.service +ConditionFileIsExecutable=!/etc/init.d/yunohost-firewall +ConditionPathExists=!/etc/systemd/system/multi-user.target.wants/yunohost-firewall.service +EOF } do_post_regen() { @@ -100,7 +109,9 @@ do_post_regen() { [[ ! -e /etc/yunohost/hooks.d ]] || (chown root /etc/yunohost/hooks.d && chmod 700 /etc/yunohost/hooks.d) [[ ! -e /etc/yunohost/apps ]] || (chown root /etc/yunohost/apps && chmod 700 /etc/yunohost/apps) + # Propagates changes in systemd service config overrides [[ ! "$regen_conf_files" =~ "ntp.service.d/ynh-override.conf" ]] || { systemctl daemon-reload; systemctl restart ntp; } + [[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || { systemctl daemon-reload; systemctl disable nftables -q; } } _update_services() { From ca20eda9caf3849a9b7391e0f0fc8d4eba751874 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 17 May 2020 20:09:50 +0200 Subject: [PATCH 109/262] [firewall] Let's not disable nftables that makes the script crash if it's not installed and we don't really need to disable it anyway since it's already configured to conflict --- data/hooks/conf_regen/01-yunohost | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 1081def17..c4120d487 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -111,7 +111,7 @@ do_post_regen() { # Propagates changes in systemd service config overrides [[ ! "$regen_conf_files" =~ "ntp.service.d/ynh-override.conf" ]] || { systemctl daemon-reload; systemctl restart ntp; } - [[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || { systemctl daemon-reload; systemctl disable nftables -q; } + [[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || systemctl daemon-reload } _update_services() { From 70997503239a0e725d44a2d43f31b2ddc6f09008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Thu, 7 May 2020 00:22:53 +0200 Subject: [PATCH 110/262] Change file type of LDAP schema from 'schema' to ldif --- .../{mailserver.schema => mailserver.ldif} | 42 +++++++++------- .../{yunohost.schema => permission.ldif} | 22 ++++---- .../slapd/{sudo.schema => sudo.ldif} | 50 ++++++++++--------- 3 files changed, 61 insertions(+), 53 deletions(-) rename data/templates/slapd/{mailserver.schema => mailserver.ldif} (79%) rename data/templates/slapd/{yunohost.schema => permission.ldif} (55%) rename data/templates/slapd/{sudo.schema => sudo.ldif} (72%) diff --git a/data/templates/slapd/mailserver.schema b/data/templates/slapd/mailserver.ldif similarity index 79% rename from data/templates/slapd/mailserver.schema rename to data/templates/slapd/mailserver.ldif index 23d0d24bd..849d1d9e1 100644 --- a/data/templates/slapd/mailserver.schema +++ b/data/templates/slapd/mailserver.ldif @@ -2,58 +2,62 @@ ## Version 0.1 ## Adrien Beudin +dn: cn=mailserver,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: mailserver +# # Attributes -attributetype ( 1.3.6.1.4.1.40328.1.20.2.1 +olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.1 NAME 'maildrop' DESC 'Mail addresses where mails are forwarded -- ie forwards' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) - -attributetype ( 1.3.6.1.4.1.40328.1.20.2.2 +# +olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.2 NAME 'mailalias' DESC 'Mail addresses accepted by this account -- ie aliases' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) - -attributetype ( 1.3.6.1.4.1.40328.1.20.2.3 +# +olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.3 NAME 'mailenable' DESC 'Mail Account validity' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{8}) - -attributetype ( 1.3.6.1.4.1.40328.1.20.2.4 +# +olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.4 NAME 'mailbox' DESC 'Mailbox path where mails are delivered' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) - -attributetype ( 1.3.6.1.4.1.40328.1.20.2.5 +# +olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.5 NAME 'virtualdomain' DESC 'A mail domain name' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) - -attributetype ( 1.3.6.1.4.1.40328.1.20.2.6 +# +olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.6 NAME 'virtualdomaindescription' DESC 'Virtual domain description' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) - -attributetype ( 1.3.6.1.4.1.40328.1.20.2.7 +# +olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.7 NAME 'mailuserquota' DESC 'Mailbox quota for a user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{16} SINGLE-VALUE ) - +# # Mail Account Objectclass -objectclass ( 1.3.6.1.4.1.40328.1.1.2.1 +olcObjectClasses: ( 1.3.6.1.4.1.40328.1.1.2.1 NAME 'mailAccount' DESC 'Mail Account' SUP top @@ -65,9 +69,9 @@ objectclass ( 1.3.6.1.4.1.40328.1.1.2.1 mailalias $ maildrop $ mailenable $ mailbox $ mailuserquota ) ) - +# # Mail Domain Objectclass -objectclass ( 1.3.6.1.4.1.40328.1.1.2.2 +olcObjectClasses: ( 1.3.6.1.4.1.40328.1.1.2.2 NAME 'mailDomain' DESC 'Domain mail entry' SUP top @@ -79,9 +83,9 @@ objectclass ( 1.3.6.1.4.1.40328.1.1.2.2 virtualdomaindescription $ mailuserquota ) ) - +# # Mail Group Objectclass -objectclass ( 1.3.6.1.4.1.40328.1.1.2.3 +olcObjectClasses: ( 1.3.6.1.4.1.40328.1.1.2.3 NAME 'mailGroup' SUP top AUXILIARY DESC 'Mail Group' MUST ( mail ) diff --git a/data/templates/slapd/yunohost.schema b/data/templates/slapd/permission.ldif similarity index 55% rename from data/templates/slapd/yunohost.schema rename to data/templates/slapd/permission.ldif index 7da60a20c..a97249d07 100644 --- a/data/templates/slapd/yunohost.schema +++ b/data/templates/slapd/permission.ldif @@ -1,33 +1,35 @@ -#dn: cn=yunohost,cn=schema,cn=config -#objectClass: olcSchemaConfig -#cn: yunohost +# Yunohost schema for group and permission support + +dn: cn=yunohost,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: yunohost # ATTRIBUTES # For Permission -attributetype ( 1.3.6.1.4.1.17953.9.1.1 NAME 'permission' +olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.1 NAME 'permission' DESC 'Yunohost permission on user and group side' SUP distinguishedName ) -attributetype ( 1.3.6.1.4.1.17953.9.1.2 NAME 'groupPermission' +olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.2 NAME 'groupPermission' DESC 'Yunohost permission for a group on permission side' SUP distinguishedName ) -attributetype ( 1.3.6.1.4.1.17953.9.1.3 NAME 'inheritPermission' +olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.3 NAME 'inheritPermission' DESC 'Yunohost permission for user on permission side' SUP distinguishedName ) -attributetype ( 1.3.6.1.4.1.17953.9.1.4 NAME 'URL' +olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.4 NAME 'URL' DESC 'Yunohost application URL' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} ) # OBJECTCLASS # For Applications -objectclass ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh' +olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh' DESC 'Yunohost user group' SUP top AUXILIARY MAY ( member $ businessCategory $ seeAlso $ owner $ ou $ o $ permission ) ) -objectclass ( 1.3.6.1.4.1.17953.9.2.2 NAME 'permissionYnh' +olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.2 NAME 'permissionYnh' DESC 'a Yunohost application' SUP top AUXILIARY MUST cn MAY ( groupPermission $ inheritPermission $ URL ) ) # For User -objectclass ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh' +olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh' DESC 'a Yunohost application' SUP top AUXILIARY MAY ( permission ) ) diff --git a/data/templates/slapd/sudo.schema b/data/templates/slapd/sudo.ldif similarity index 72% rename from data/templates/slapd/sudo.schema rename to data/templates/slapd/sudo.ldif index d3e95e00c..a7088c855 100644 --- a/data/templates/slapd/sudo.schema +++ b/data/templates/slapd/sudo.ldif @@ -1,76 +1,78 @@ # # OpenLDAP schema file for Sudo -# Save as /etc/openldap/schema/sudo.schema +# Save as /etc/openldap/schema/sudo.ldif # -attributetype ( 1.3.6.1.4.1.15953.9.1.1 +dn: cn=sudo,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: sudo +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.1 NAME 'sudoUser' DESC 'User(s) who may run sudo' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) - -attributetype ( 1.3.6.1.4.1.15953.9.1.2 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.2 NAME 'sudoHost' DESC 'Host(s) who may run sudo' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) - -attributetype ( 1.3.6.1.4.1.15953.9.1.3 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.3 NAME 'sudoCommand' DESC 'Command(s) to be executed by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) - -attributetype ( 1.3.6.1.4.1.15953.9.1.4 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.4 NAME 'sudoRunAs' DESC 'User(s) impersonated by sudo (deprecated)' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) - -attributetype ( 1.3.6.1.4.1.15953.9.1.5 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.5 NAME 'sudoOption' DESC 'Options(s) followed by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) - -attributetype ( 1.3.6.1.4.1.15953.9.1.6 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.6 NAME 'sudoRunAsUser' DESC 'User(s) impersonated by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) - -attributetype ( 1.3.6.1.4.1.15953.9.1.7 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.7 NAME 'sudoRunAsGroup' DESC 'Group(s) impersonated by sudo' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) - -attributetype ( 1.3.6.1.4.1.15953.9.1.8 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.8 NAME 'sudoNotBefore' DESC 'Start of time interval for which the entry is valid' EQUALITY generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 ) - -attributetype ( 1.3.6.1.4.1.15953.9.1.9 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.9 NAME 'sudoNotAfter' DESC 'End of time interval for which the entry is valid' EQUALITY generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 ) - -attributeTypes ( 1.3.6.1.4.1.15953.9.1.10 +# +olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.10 NAME 'sudoOrder' DESC 'an integer to order the sudoRole entries' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) - -objectclass ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL +# +olcObjectClasses: ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL DESC 'Sudoer Entries' MUST ( cn ) - MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoRunAsUser $ sudoRunAsGroup $ sudoOption $ sudoOrder $ sudoNotBefore $ sudoNotAfter $ - description ) + MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoRunAsUser $ sudoRunAsGroup $ sudoOption $ sudoOrder $ sudoNotBefore $ sudoNotAfter $ description ) ) From d73a71fa610fb28488a4af19cde08a8bc8cd0f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Thu, 7 May 2020 00:24:53 +0200 Subject: [PATCH 111/262] Rewrite LDAP configuration in LDIF format --- data/templates/slapd/slapd.conf | 154 --------------------- data/templates/slapd/slapd.ldif | 229 ++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 154 deletions(-) delete mode 100644 data/templates/slapd/slapd.conf create mode 100644 data/templates/slapd/slapd.ldif diff --git a/data/templates/slapd/slapd.conf b/data/templates/slapd/slapd.conf deleted file mode 100644 index 0750b43aa..000000000 --- a/data/templates/slapd/slapd.conf +++ /dev/null @@ -1,154 +0,0 @@ -# This is the main slapd configuration file. See slapd.conf(5) for more -# info on the configuration options. - -####################################################################### -# Global Directives: - -# Features to permit -#allow bind_v2 - -# Schema and objectClass definitions -include /etc/ldap/schema/core.schema -include /etc/ldap/schema/cosine.schema -include /etc/ldap/schema/nis.schema -include /etc/ldap/schema/inetorgperson.schema -include /etc/ldap/schema/mailserver.schema -include /etc/ldap/schema/sudo.schema -include /etc/ldap/schema/yunohost.schema - -# Where the pid file is put. The init.d script -# will not stop the server if you change this. -pidfile /var/run/slapd/slapd.pid - -# List of arguments that were passed to the server -argsfile /var/run/slapd/slapd.args - -# Read slapd.conf(5) for possible values -loglevel none - -# Hashes to be used in generation of user passwords -password-hash {SSHA} - -# Where the dynamically loaded modules are stored -modulepath /usr/lib/ldap -moduleload back_mdb -moduleload memberof - -# The maximum number of entries that is returned for a search operation -sizelimit 500 - -# The tool-threads parameter sets the actual amount of cpu's that is used -# for indexing. -tool-threads 1 - -# TLS Support -TLSCertificateFile /etc/yunohost/certs/yunohost.org/crt.pem -TLSCertificateKeyFile /etc/yunohost/certs/yunohost.org/key.pem - -####################################################################### -# Specific Backend Directives for mdb: -# Backend specific directives apply to this backend until another -# 'backend' directive occurs -backend mdb - -####################################################################### -# Specific Directives for database #1, of type mdb: -# Database specific directives apply to this databasse until another -# 'database' directive occurs -database mdb - -# The base of your directory in database #1 -suffix "dc=yunohost,dc=org" - -# rootdn directive for specifying a superuser on the database. This is needed -# for syncrepl. -# rootdn "cn=admin,dc=yunohost,dc=org" - -# Where the database file are physically stored for database #1 -directory "/var/lib/ldap" - -# Indexing options for database #1 -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 -index permission eq - -# Save the time that the entry gets modified, for database #1 -lastmod on - -# Checkpoint the BerkeleyDB database periodically in case of system -# failure and to speed slapd shutdown. -checkpoint 512 30 - -# The userPassword by default can be changed -# by the entry owning it if they are authenticated. -# Others should not be able to see it, except the -# admin entry below -# 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 - -# Personnal information can be changed by the entry -# owning it if they are authenticated. -# 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 - -# Ensure read access to the base for things like -# supportedSASLMechanisms. Without this you may -# have problems with SASL not knowing what -# mechanisms are available and the like. -# Note that this is covered by the 'access to *' -# ACL below too but if you change that as people -# are wont to do you'll still need this if you -# want SASL (and possible other things) to work -# happily. -access to dn.base="" by * read - -# The admin dn has full write access, everyone else -# 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 - -# Configure Memberof Overlay (used for Yunohost permission) - -# Link user <-> group -#dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config -overlay memberof -memberof-group-oc groupOfNamesYnh -memberof-member-ad member -memberof-memberof-ad memberOf -memberof-dangling error -memberof-refint TRUE - -# Link permission <-> groupes -#dn: olcOverlay={1}memberof,olcDatabase={1}mdb,cn=config -overlay memberof -memberof-group-oc permissionYnh -memberof-member-ad groupPermission -memberof-memberof-ad permission -memberof-dangling error -memberof-refint TRUE - -# Link permission <-> user -#dn: olcOverlay={2}memberof,olcDatabase={1}mdb,cn=config -overlay memberof -memberof-group-oc permissionYnh -memberof-member-ad inheritPermission -memberof-memberof-ad permission -memberof-dangling error -memberof-refint TRUE diff --git a/data/templates/slapd/slapd.ldif b/data/templates/slapd/slapd.ldif new file mode 100644 index 000000000..c9b6c581f --- /dev/null +++ b/data/templates/slapd/slapd.ldif @@ -0,0 +1,229 @@ +# OpenLDAP server configuration for Yunohost +# ------------------------------------------ +# +# By the Yunohost regen-conf tools it's NOT possible to edit the config database by a LDAP request. +# The way to to edit the config database is to edit THIS file +# and after update the config database based on this file. +# +# Config database customization: +# 1. Edit this file as you want. +# 2. Apply your modifications. For this just run this following command in a shell: +# $ /usr/share/yunohost/hooks/conf_regen/06-slapd apply_config +# +# Note that if you customize this file after all modifications my Yunohost won't be applied. + +# +# Main configuration +# +dn: cn=config +objectClass: olcGlobal +cn: config +olcConfigFile: /etc/ldap/slapd.conf +olcConfigDir: /etc/ldap/slapd.d/ +# List of arguments that were passed to the server +olcArgsFile: /var/run/slapd/slapd.args +# +olcAttributeOptions: lang- +olcAuthzPolicy: none +olcConcurrency: 0 +olcConnMaxPending: 100 +olcConnMaxPendingAuth: 1000 +olcIdleTimeout: 0 +olcIndexSubstrIfMaxLen: 4 +olcIndexSubstrIfMinLen: 2 +olcIndexSubstrAnyLen: 4 +olcIndexSubstrAnyStep: 2 +olcIndexIntLen: 4 +olcListenerThreads: 1 +olcLocalSSF: 71 +# Read slapd.conf(5) for possible values +olcLogLevel: None +# Where the pid file is put. The init.d script +# will not stop the server if you change this. +olcPidFile: /var/run/slapd/slapd.pid +olcReverseLookup: FALSE +olcThreads: 16 +# TLS Support +olcTLSCertificateFile: /etc/yunohost/certs/yunohost.org/crt.pem +olcTLSCertificateKeyFile: /etc/yunohost/certs/yunohost.org/key.pem +olcTLSVerifyClient: never +olcTLSProtocolMin: 0.0 +# The tool-threads parameter sets the actual amount of cpu's that is used +# for indexing. +olcToolThreads: 1 +structuralObjectClass: olcGlobal + +# +# Schema and objectClass definitions +# +dn: cn=schema,cn=config +objectClass: olcSchemaConfig +cn: schema + +include: file:///etc/ldap/schema/core.ldif +include: file:///etc/ldap/schema/cosine.ldif +include: file:///etc/ldap/schema/nis.ldif +include: file:///etc/ldap/schema/inetorgperson.ldif +include: file:///etc/ldap/schema/mailserver.ldif +include: file:///etc/ldap/schema/sudo.ldif +include: file:///etc/ldap/schema/permission.ldif + +# +# Module management +# +dn: cn=module{0},cn=config +objectClass: olcModuleList +cn: module{0} +# Where the dynamically loaded modules are stored +olcModulePath: /usr/lib/ldap +olcModuleLoad: {0}back_mdb +olcModuleLoad: {1}memberof +structuralObjectClass: olcModuleList + +# +# Frontend database +# +dn: olcDatabase={-1}frontend,cn=config +objectClass: olcDatabaseConfig +objectClass: olcFrontendConfig +olcDatabase: {-1}frontend +olcAddContentAcl: FALSE +olcLastMod: TRUE +olcSchemaDN: cn=Subschema +# Hashes to be used in generation of user passwords +olcPasswordHash: {SSHA} +structuralObjectClass: olcDatabaseConfig + +# +# Config database Configuration (#0) +# +dn: olcDatabase={0}config,cn=config +objectClass: olcDatabaseConfig +olcDatabase: {0}config +# Give access to root user. +# This give the possiblity to the admin to customize the LDAP configuration +olcAccess: {0}to * by * none +olcAddContentAcl: TRUE +olcLastMod: TRUE +olcRootDN: cn=config +structuralObjectClass: olcDatabaseConfig + +# +# Main database Configuration (#1) +# +dn: olcDatabase={1}mdb,cn=config +objectClass: olcDatabaseConfig +objectClass: olcMdbConfig +olcDatabase: {1}mdb +# The base of your directory in database #1 +olcSuffix: dc=yunohost,dc=org +# +# The userPassword by default can be changed +# by the entry owning it if they are authenticated. +# Others should not be able to see it, except the +# admin entry below +# These access lines apply to database #1 only +olcAccess: {0}to attrs=userPassword,shadowLastChange + by dn.base="cn=admin,dc=yunohost,dc=org" write + by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write + by anonymous auth + by self write + by * none +# +# Personnal information can be changed by the entry +# owning it if they are authenticated. +# Others should be able to see it. +olcAccess: {1}to attrs=cn,gecos,givenName,mail,maildrop,displayName,sn + by dn.base="cn=admin,dc=yunohost,dc=org" write + by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write + by self write + by * read +# +# Ensure read access to the base for things like +# supportedSASLMechanisms. Without this you may +# have problems with SASL not knowing what +# mechanisms are available and the like. +# Note that this is covered by the 'access to *' +# ACL below too but if you change that as people +# are wont to do you'll still need this if you +# want SASL (and possible other things) to work +# happily. +olcAccess: {2}to dn.base="" + by * read +# +# The admin dn has full write access, everyone else +# can read everything. +olcAccess: {3}to * + by dn.base="cn=admin,dc=yunohost,dc=org" write + by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write + by group/groupOfNames/member.exact="cn=admin,ou=groups,dc=yunohost,dc=org" write + by * read +# +olcAddContentAcl: FALSE +# Save the time that the entry gets modified, for database #1 +olcLastMod: TRUE +# Where the database file are physically stored for database #1 +olcDbDirectory: /var/lib/ldap +# Checkpoint the BerkeleyDB database periodically in case of system +# failure and to speed slapd shutdown. +olcDbCheckpoint: 512 30 +olcDbNoSync: FALSE +# Indexing options for database #1 +olcDbIndex: objectClass eq +olcDbIndex: entryUUID eq +olcDbIndex: entryCSN eq +olcDbIndex: cn eq +olcDbIndex: uid eq,sub +olcDbIndex: uidNumber eq +olcDbIndex: gidNumber eq +olcDbIndex: sudoUser eq,sub +olcDbIndex: member eq +olcDbIndex: mail eq +olcDbIndex: memberUid eq +olcDbIndex: uniqueMember eq +olcDbIndex: virtualdomain eq +olcDbMaxSize: 10485760 +structuralObjectClass: olcMdbConfig + +# +# Configure Memberof Overlay (used for Yunohost permission) +# + +# Link user <-> group +dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config +objectClass: olcOverlayConfig +objectClass: olcMemberOf +olcOverlay: {0}memberof +olcMemberOfDangling: error +olcMemberOfDanglingError: constraintViolation +olcMemberOfRefInt: TRUE +olcMemberOfGroupOC: groupOfNamesYnh +olcMemberOfMemberAD: member +olcMemberOfMemberOfAD: memberOf +structuralObjectClass: olcMemberOf + +# Link permission <-> groupes +dn: olcOverlay={1}memberof,olcDatabase={1}mdb,cn=config +objectClass: olcOverlayConfig +objectClass: olcMemberOf +olcOverlay: {1}memberof +olcMemberOfDangling: error +olcMemberOfDanglingError: constraintViolation +olcMemberOfRefInt: TRUE +olcMemberOfGroupOC: permissionYnh +olcMemberOfMemberAD: groupPermission +olcMemberOfMemberOfAD: permission +structuralObjectClass: olcMemberOf + +# Link permission <-> user +dn: olcOverlay={2}memberof,olcDatabase={1}mdb,cn=config +objectClass: olcOverlayConfig +objectClass: olcMemberOf +olcOverlay: {2}memberof +olcMemberOfDangling: error +olcMemberOfDanglingError: constraintViolation +olcMemberOfRefInt: TRUE +olcMemberOfGroupOC: permissionYnh +olcMemberOfMemberAD: inheritPermission +olcMemberOfMemberOfAD: permission +structuralObjectClass: olcMemberOf From 9bb8c0437dd6059effe6bc4e698518d1a57dfb30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Thu, 7 May 2020 00:26:21 +0200 Subject: [PATCH 112/262] Rewrite slapd regen-conf for new config file --- data/hooks/conf_regen/06-slapd | 118 ++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 53 deletions(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index c8fba52fc..26ff325f5 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -14,19 +14,25 @@ do_init_regen() { systemctl daemon-reload - # fix some permissions - chown root:openldap /etc/ldap/slapd.conf - chown -R openldap:openldap /etc/ldap/schema/ - usermod -aG ssl-cert openldap + # Because slaptest can't test the LDAP config file + # we need to regenerate the new config and after validate it + # regenerate LDAP config directory from slapd.ldif + rm -Rf /etc/ldap/slapd_new.d + mkdir /etc/ldap/slapd_new.d + slapadd -n0 -l /etc/ldap/slapd.ldif -F /etc/ldap/slapd_new.d/ 2>&1 # check the slapd config file at first - slaptest -Q -u -f /etc/ldap/slapd.conf + slaptest -Q -u -F /etc/ldap/slapd_new.d - # regenerate LDAP config directory from slapd.conf + # Move to the new config rm -Rf /etc/ldap/slapd.d - mkdir /etc/ldap/slapd.d - slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1 + mv /etc/ldap/slapd_new.d /etc/ldap/slapd.d + + # fix some permissions + chown root:openldap /etc/ldap/slapd.ldif + chown -R openldap:openldap /etc/ldap/schema/ chown -R openldap:openldap /etc/ldap/slapd.d/ + usermod -aG ssl-cert openldap service slapd restart } @@ -34,6 +40,28 @@ do_init_regen() { do_pre_regen() { pending_dir=$1 + # remove temporary backup file + rm -f "$tmp_backup_dir_file" + + # Define if we need to migrate from hdb to mdb + curr_backend=$(grep '^database' /etc/ldap/slapd.conf 2>/dev/null | awk '{print $2}') + if [ -e /etc/ldap/slapd.conf ] && [ -n "$curr_backend" ] && \ + [ $curr_backend != 'mdb' ]; then + backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)" + mkdir -p "$backup_dir" + slapcat -b dc=yunohost,dc=org \ + -l "${backup_dir}/dc=yunohost-dc=org.ldif" + echo "$backup_dir" > "$tmp_backup_dir_file" + fi + + # remove legacy configuration file + [ ! -f /etc/ldap/slapd-yuno.conf ] \ + || touch "${pending_dir}/etc/ldap/slapd-yuno.conf" + [ ! -f /etc/ldap/slapd.conf ] \ + || touch "${pending_dir}/etc/ldap/slapd.conf" + [ ! -f /etc/ldap/schema/yunohost.schema ] \ + || touch "${pending_dir}/etc/ldap/schema/yunohost.schema" + cd /usr/share/yunohost/templates/slapd # create needed directories @@ -41,29 +69,9 @@ do_pre_regen() { schema_dir="${ldap_dir}/schema" mkdir -p "$ldap_dir" "$schema_dir" - # remove legacy configuration file - [ ! -f /etc/ldap/slapd-yuno.conf ] \ - || touch "${pending_dir}/etc/ldap/slapd-yuno.conf" - - # remove temporary backup file - rm -f "$tmp_backup_dir_file" - - # retrieve current and new backends - curr_backend=$(grep '^database' /etc/ldap/slapd.conf 2>/dev/null | awk '{print $2}') - new_backend=$(grep '^database' slapd.conf | awk '{print $2}') - - # save current database before any conf changes - if [[ -n "$curr_backend" && "$curr_backend" != "$new_backend" ]]; then - backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)" - mkdir -p "$backup_dir" - slapcat -b dc=yunohost,dc=org \ - -l "${backup_dir}/dc=yunohost-dc=org.ldif" - echo "$backup_dir" > "$tmp_backup_dir_file" - fi - # copy configuration files - cp -a ldap.conf slapd.conf "$ldap_dir" - cp -a sudo.schema mailserver.schema yunohost.schema "$schema_dir" + cp -a ldap.conf slapd.ldif "$ldap_dir" + cp -a sudo.ldif mailserver.ldif permission.ldif "$schema_dir" mkdir -p ${pending_dir}/etc/systemd/system/slapd.service.d/ cp systemd-override.conf ${pending_dir}/etc/systemd/system/slapd.service.d/ynh-override.conf @@ -74,14 +82,11 @@ do_pre_regen() { do_post_regen() { regen_conf_files=$1 - # ensure that slapd.d exists - mkdir -p /etc/ldap/slapd.d - # fix some permissions echo "Making sure we have the right permissions needed ..." # penldap user should be in the ssl-cert group to let it access the certificate for TLS usermod -aG ssl-cert openldap - chown root:openldap /etc/ldap/slapd.conf + chown root:openldap /etc/ldap/slapd.ldif chown -R openldap:openldap /etc/ldap/schema/ chown -R openldap:openldap /etc/ldap/slapd.d/ @@ -94,29 +99,33 @@ do_post_regen() { [ -z "$regen_conf_files" ] && exit 0 - # check the slapd config file at first - slaptest -Q -u -f /etc/ldap/slapd.conf - # check if a backup should be restored backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true) + + # regenerate LDAP config directory from slapd.conf + echo "Regenerate LDAP config directory from slapd.conf" + + # Because slaptest can't test the LDAP config file + # we need to regenerate the new config and after validate it + # regenerate LDAP config directory from slapd.ldif + rm -Rf /etc/ldap/slapd_new.d + mkdir /etc/ldap/slapd_new.d + slapadd -n0 -l /etc/ldap/slapd.ldif -F /etc/ldap/slapd_new.d/ 2>&1 + + # check the slapd config file at first + slaptest -Q -u -F /etc/ldap/slapd_new.d + + # Move to the new config + rm -Rf /etc/ldap/slapd.d + mv /etc/ldap/slapd_new.d /etc/ldap/slapd.d + chown -R openldap:openldap /etc/ldap/slapd.d/ + if [[ -n "$backup_dir" && -f "${backup_dir}/dc=yunohost-dc=org.ldif" ]]; then # regenerate LDAP config directory and import database as root - # since the admin user may be unavailable - echo "Regenerate LDAP config directory and import the database using slapadd" - sh -c "rm -Rf /etc/ldap/slapd.d; - mkdir /etc/ldap/slapd.d; - slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d; - chown -R openldap:openldap /etc/ldap/slapd.d; - slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \ - -l '${backup_dir}/dc=yunohost-dc=org.ldif'; - chown -R openldap:openldap /var/lib/ldap" 2>&1 - else - # regenerate LDAP config directory from slapd.conf - echo "Regenerate LDAP config directory from slapd.conf" - rm -Rf /etc/ldap/slapd.d - mkdir /etc/ldap/slapd.d - slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1 - chown -R openldap:openldap /etc/ldap/slapd.d/ + echo "Import the database using slapadd" + slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \ + -l '${backup_dir}/dc=yunohost-dc=org.ldif' + chown -R openldap:openldap /var/lib/ldap 2>&1 fi echo "Running slapdindex" @@ -156,6 +165,9 @@ case "$1" in init) do_init_regen ;; + apply_config) + do_post_regen /etc/ldap/slapd.ldif + ;; *) echo "hook called with unknown argument \`$1'" >&2 exit 1 From e6a52f09bd6acd3833f0901830fad4e322ab8b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Thu, 7 May 2020 00:40:24 +0200 Subject: [PATCH 113/262] [fix] Clean old files --- data/hooks/conf_regen/06-slapd | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 26ff325f5..6759cef1a 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -55,12 +55,18 @@ do_pre_regen() { fi # remove legacy configuration file - [ ! -f /etc/ldap/slapd-yuno.conf ] \ - || touch "${pending_dir}/etc/ldap/slapd-yuno.conf" - [ ! -f /etc/ldap/slapd.conf ] \ - || touch "${pending_dir}/etc/ldap/slapd.conf" - [ ! -f /etc/ldap/schema/yunohost.schema ] \ - || touch "${pending_dir}/etc/ldap/schema/yunohost.schema" + if [ -f /etc/ldap/slapd-yuno.conf ]; then + mkdir -p ${pending_dir}/etc/ldap + touch "${pending_dir}/etc/ldap/slapd-yuno.conf" + fi + if [ -f /etc/ldap/slapd.conf ]; then + mkdir -p ${pending_dir}/etc/ldap + touch "${pending_dir}/etc/ldap/slapd.conf" + fi + if [ -f /etc/ldap/schema/yunohost.schema ]; then + mkdir -p ${pending_dir}/etc/ldap/schema + touch "${pending_dir}/etc/ldap/schema/yunohost.schema" + fi cd /usr/share/yunohost/templates/slapd From c0d3a361e362e83c52fe7d1e1707254b4da26271 Mon Sep 17 00:00:00 2001 From: Josue-T Date: Thu, 7 May 2020 10:48:49 +0200 Subject: [PATCH 114/262] Update data/hooks/conf_regen/06-slapd Co-authored-by: Alexandre Aubin --- data/hooks/conf_regen/06-slapd | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 6759cef1a..38d3adfc5 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -54,27 +54,18 @@ do_pre_regen() { echo "$backup_dir" > "$tmp_backup_dir_file" fi - # remove legacy configuration file - if [ -f /etc/ldap/slapd-yuno.conf ]; then - mkdir -p ${pending_dir}/etc/ldap - touch "${pending_dir}/etc/ldap/slapd-yuno.conf" - fi - if [ -f /etc/ldap/slapd.conf ]; then - mkdir -p ${pending_dir}/etc/ldap - touch "${pending_dir}/etc/ldap/slapd.conf" - fi - if [ -f /etc/ldap/schema/yunohost.schema ]; then - mkdir -p ${pending_dir}/etc/ldap/schema - touch "${pending_dir}/etc/ldap/schema/yunohost.schema" - fi - - cd /usr/share/yunohost/templates/slapd - # create needed directories ldap_dir="${pending_dir}/etc/ldap" schema_dir="${ldap_dir}/schema" mkdir -p "$ldap_dir" "$schema_dir" + # remove legacy configuration file + [ ! -f /etc/ldap/slapd-yuno.conf ] || touch "${ldap_dir}/slapd-yuno.conf" + [ ! -f /etc/ldap/slapd.conf ] || touch "${ldap_dir}/slapd.conf" + [ ! -f /etc/ldap/schema/yunohost.schema ] || touch "${schema_dir}/yunohost.schema" + + cd /usr/share/yunohost/templates/slapd + # copy configuration files cp -a ldap.conf slapd.ldif "$ldap_dir" cp -a sudo.ldif mailserver.ldif permission.ldif "$schema_dir" From 4eb93d62e90953771acc86505e4ff4696b69f7de Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 27 May 2020 21:43:21 +0200 Subject: [PATCH 115/262] Improve comment about editing slapd.ldif --- data/templates/slapd/slapd.ldif | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/data/templates/slapd/slapd.ldif b/data/templates/slapd/slapd.ldif index c9b6c581f..44dde192e 100644 --- a/data/templates/slapd/slapd.ldif +++ b/data/templates/slapd/slapd.ldif @@ -1,16 +1,21 @@ # OpenLDAP server configuration for Yunohost # ------------------------------------------ # -# By the Yunohost regen-conf tools it's NOT possible to edit the config database by a LDAP request. -# The way to to edit the config database is to edit THIS file -# and after update the config database based on this file. +# Because of the Yunohost's regen-conf mechanism, it is NOT POSSIBLE to +# edit the config database using an LDAP request. +# +# If you wish to edit the config database, you should edit THIS file +# and update the config database based on this file. # # Config database customization: # 1. Edit this file as you want. # 2. Apply your modifications. For this just run this following command in a shell: # $ /usr/share/yunohost/hooks/conf_regen/06-slapd apply_config # -# Note that if you customize this file after all modifications my Yunohost won't be applied. +# Note that if you customize this file, YunoHost's regen-conf will NOT +# overwrite this file. But that also means that you should be careful about +# upgrades, because they may ship important/necessary changes to this +# configuration that you will have to propagate yourself. # # Main configuration From a179e9107a60d8219b3c883eb7275796c4bd9607 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 27 May 2020 21:44:38 +0200 Subject: [PATCH 116/262] Misc comment improvements? --- data/hooks/conf_regen/06-slapd | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 38d3adfc5..0f465e50d 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -14,21 +14,21 @@ do_init_regen() { systemctl daemon-reload - # Because slaptest can't test the LDAP config file - # we need to regenerate the new config and after validate it - # regenerate LDAP config directory from slapd.ldif + # Validate the new slapd config + # To do so, we have to use the .ldif to generate the config directory + # so we use a temporary directory slapd_new.d rm -Rf /etc/ldap/slapd_new.d mkdir /etc/ldap/slapd_new.d slapadd -n0 -l /etc/ldap/slapd.ldif -F /etc/ldap/slapd_new.d/ 2>&1 - - # check the slapd config file at first + # Actual validation (-Q is for quiet, -u is for dry-run) slaptest -Q -u -F /etc/ldap/slapd_new.d - # Move to the new config + # "Commit" / apply the new config (meaning we delete the old one and replace + # it with the new one) rm -Rf /etc/ldap/slapd.d mv /etc/ldap/slapd_new.d /etc/ldap/slapd.d - # fix some permissions + # Enforce permissions chown root:openldap /etc/ldap/slapd.ldif chown -R openldap:openldap /etc/ldap/schema/ chown -R openldap:openldap /etc/ldap/slapd.d/ @@ -50,7 +50,7 @@ do_pre_regen() { backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)" mkdir -p "$backup_dir" slapcat -b dc=yunohost,dc=org \ - -l "${backup_dir}/dc=yunohost-dc=org.ldif" + -l "${backup_dir}/dc=yunohost-dc=org.ldif" echo "$backup_dir" > "$tmp_backup_dir_file" fi @@ -80,7 +80,7 @@ do_post_regen() { regen_conf_files=$1 # fix some permissions - echo "Making sure we have the right permissions needed ..." + echo "Enforce permissions on ldap/slapd directories and certs ..." # penldap user should be in the ssl-cert group to let it access the certificate for TLS usermod -aG ssl-cert openldap chown root:openldap /etc/ldap/slapd.ldif @@ -100,16 +100,15 @@ do_post_regen() { backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true) # regenerate LDAP config directory from slapd.conf - echo "Regenerate LDAP config directory from slapd.conf" + echo "Regenerate LDAP config directory from slapd.ldif" - # Because slaptest can't test the LDAP config file - # we need to regenerate the new config and after validate it - # regenerate LDAP config directory from slapd.ldif + # Validate the new slapd config + # To do so, we have to use the .ldif to generate the config directory + # so we use a temporary directory slapd_new.d rm -Rf /etc/ldap/slapd_new.d mkdir /etc/ldap/slapd_new.d slapadd -n0 -l /etc/ldap/slapd.ldif -F /etc/ldap/slapd_new.d/ 2>&1 - - # check the slapd config file at first + # Actual validation (-Q is for quiet, -u is for dry-run) slaptest -Q -u -F /etc/ldap/slapd_new.d # Move to the new config From 2bbff1d26468f3b52c69cbf0e4d377711f157c43 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 27 May 2020 21:53:21 +0200 Subject: [PATCH 117/262] Factorize the actual slapd regeneration --- data/hooks/conf_regen/06-slapd | 36 +++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 0f465e50d..8d79335a2 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -13,6 +13,18 @@ do_init_regen() { do_pre_regen "" systemctl daemon-reload + + _regenerate_slapd_conf + + # Enforce permissions + chown root:openldap /etc/ldap/slapd.ldif + chown -R openldap:openldap /etc/ldap/schema/ + usermod -aG ssl-cert openldap + + service slapd restart +} + +_regenerate_slapd_conf() { # Validate the new slapd config # To do so, we have to use the .ldif to generate the config directory @@ -28,13 +40,7 @@ do_init_regen() { rm -Rf /etc/ldap/slapd.d mv /etc/ldap/slapd_new.d /etc/ldap/slapd.d - # Enforce permissions - chown root:openldap /etc/ldap/slapd.ldif - chown -R openldap:openldap /etc/ldap/schema/ chown -R openldap:openldap /etc/ldap/slapd.d/ - usermod -aG ssl-cert openldap - - service slapd restart } do_pre_regen() { @@ -49,8 +55,7 @@ do_pre_regen() { [ $curr_backend != 'mdb' ]; then backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)" mkdir -p "$backup_dir" - slapcat -b dc=yunohost,dc=org \ - -l "${backup_dir}/dc=yunohost-dc=org.ldif" + slapcat -b dc=yunohost,dc=org -l "${backup_dir}/dc=yunohost-dc=org.ldif" echo "$backup_dir" > "$tmp_backup_dir_file" fi @@ -101,20 +106,7 @@ do_post_regen() { # regenerate LDAP config directory from slapd.conf echo "Regenerate LDAP config directory from slapd.ldif" - - # Validate the new slapd config - # To do so, we have to use the .ldif to generate the config directory - # so we use a temporary directory slapd_new.d - rm -Rf /etc/ldap/slapd_new.d - mkdir /etc/ldap/slapd_new.d - slapadd -n0 -l /etc/ldap/slapd.ldif -F /etc/ldap/slapd_new.d/ 2>&1 - # Actual validation (-Q is for quiet, -u is for dry-run) - slaptest -Q -u -F /etc/ldap/slapd_new.d - - # Move to the new config - rm -Rf /etc/ldap/slapd.d - mv /etc/ldap/slapd_new.d /etc/ldap/slapd.d - chown -R openldap:openldap /etc/ldap/slapd.d/ + _regenerate_slapd_conf if [[ -n "$backup_dir" && -f "${backup_dir}/dc=yunohost-dc=org.ldif" ]]; then # regenerate LDAP config directory and import database as root From e28c618e677d491b99fc1e00d2fe9e09d4f99e53 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 27 May 2020 21:54:07 +0200 Subject: [PATCH 118/262] Those single quotes were probably meant to be double quotes? --- data/hooks/conf_regen/06-slapd | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 8d79335a2..9f808b58e 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -101,18 +101,16 @@ do_post_regen() { [ -z "$regen_conf_files" ] && exit 0 - # check if a backup should be restored - backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true) - # regenerate LDAP config directory from slapd.conf echo "Regenerate LDAP config directory from slapd.ldif" _regenerate_slapd_conf + # If there's a backup, re-import its data + backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true) if [[ -n "$backup_dir" && -f "${backup_dir}/dc=yunohost-dc=org.ldif" ]]; then # regenerate LDAP config directory and import database as root echo "Import the database using slapadd" - slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \ - -l '${backup_dir}/dc=yunohost-dc=org.ldif' + slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org -l "${backup_dir}/dc=yunohost-dc=org.ldif" chown -R openldap:openldap /var/lib/ldap 2>&1 fi From 8af56c82efc06843c6c6b5a5f13927ae064c888e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Thu, 28 May 2020 21:10:15 +0200 Subject: [PATCH 119/262] slapd.conf is no more used, but ldap.conf need to be saved/restored --- data/hooks/backup/05-conf_ldap | 2 +- data/hooks/restore/05-conf_ldap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/hooks/backup/05-conf_ldap b/data/hooks/backup/05-conf_ldap index 75b4c2075..b28ea39ca 100755 --- a/data/hooks/backup/05-conf_ldap +++ b/data/hooks/backup/05-conf_ldap @@ -10,7 +10,7 @@ source /usr/share/yunohost/helpers backup_dir="${1}/conf/ldap" # Backup the configuration -ynh_backup "/etc/ldap/slapd.conf" "${backup_dir}/slapd.conf" +ynh_backup "/etc/ldap/ldap.conf" "${backup_dir}/ldap.conf" slapcat -b cn=config -l "${backup_dir}/cn=config.master.ldif" # Backup the database diff --git a/data/hooks/restore/05-conf_ldap b/data/hooks/restore/05-conf_ldap index 74093136d..23cf98887 100644 --- a/data/hooks/restore/05-conf_ldap +++ b/data/hooks/restore/05-conf_ldap @@ -39,7 +39,7 @@ else # Restore the configuration mv /etc/ldap/slapd.d "$TMPDIR" mkdir -p /etc/ldap/slapd.d - cp -a "${backup_dir}/slapd.conf" /etc/ldap/slapd.conf + cp -a "${backup_dir}/ldap.conf" /etc/ldap/ldap.conf slapadd -F /etc/ldap/slapd.d -b cn=config \ -l "${backup_dir}/cn=config.master.ldif" \ || die 1 "Unable to restore LDAP configuration" From 7c10ea6b9331a30a25ec7a9a287254f212ad08ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Fri, 29 May 2020 10:59:51 +0200 Subject: [PATCH 120/262] Fix index config --- data/templates/slapd/slapd.ldif | 1 + 1 file changed, 1 insertion(+) diff --git a/data/templates/slapd/slapd.ldif b/data/templates/slapd/slapd.ldif index 44dde192e..d3ed2e053 100644 --- a/data/templates/slapd/slapd.ldif +++ b/data/templates/slapd/slapd.ldif @@ -187,6 +187,7 @@ olcDbIndex: mail eq olcDbIndex: memberUid eq olcDbIndex: uniqueMember eq olcDbIndex: virtualdomain eq +olcDbIndex: permission eq olcDbMaxSize: 10485760 structuralObjectClass: olcMdbConfig From f22f64103abed25a119329f48729d9a4159c86be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Fri, 29 May 2020 13:38:29 +0200 Subject: [PATCH 121/262] Backup/restore also slapd.ldif We need this because the regen-conf need to get the state of the slapd config database if it is customized or not and if some update are need. --- data/hooks/backup/05-conf_ldap | 1 + data/hooks/restore/05-conf_ldap | 3 +++ 2 files changed, 4 insertions(+) diff --git a/data/hooks/backup/05-conf_ldap b/data/hooks/backup/05-conf_ldap index b28ea39ca..e3e8e455d 100755 --- a/data/hooks/backup/05-conf_ldap +++ b/data/hooks/backup/05-conf_ldap @@ -11,6 +11,7 @@ backup_dir="${1}/conf/ldap" # Backup the configuration ynh_backup "/etc/ldap/ldap.conf" "${backup_dir}/ldap.conf" +ynh_backup "/etc/ldap/slapd.ldif" "${backup_dir}/slapd.ldif" slapcat -b cn=config -l "${backup_dir}/cn=config.master.ldif" # Backup the database diff --git a/data/hooks/restore/05-conf_ldap b/data/hooks/restore/05-conf_ldap index 23cf98887..bdc1ebcdf 100644 --- a/data/hooks/restore/05-conf_ldap +++ b/data/hooks/restore/05-conf_ldap @@ -40,6 +40,9 @@ else 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" From 5a6dca897ba94a2cd777fc6707e7e2667df75243 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 5 Jun 2020 03:35:41 +0200 Subject: [PATCH 122/262] Update changelog for 4.0.0-alpha1 --- debian/changelog | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 7f1e50190..d87b0b669 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,24 @@ -yunohost (4.0.0~alpha) unstable; urgency=low +yunohost (4.0.0-alpha1) testing; urgency=low - - Tmp version for debian builds during experimental/alpha dev for Buster + - [fix] It just make no sense to backup/restore the mysql password... (#911) + - [fix] Fix getopts and helpers (#885, #886) + - [fix] Explicitly create home using mkhomedir_helper instead of obscure pam rule that doesn't work anymore (b67ff314) + - [fix] Ldap interface seems to expect lists everywhere now? (fb8c2b7b) + - [deb] Clean control file, remove some legacy Conflicts and Replaces (ca0d4933) + - [deb] Add conflicts with versions from backports for critical dependencies (#967) + - [cleanup] Stale / legacy code (217aaa36, d77da6a0, af047468, 82d468a3) + - [conf] Automatically disable/stop systemd-resolved that conflicts with dnsmasq on fresh setups ... (e7214b37) + - [conf] Remove deprecated option in sshd conf, c.f. https://patchwork.openembedded.org/patch/139981/ (2723d245) + - [conf] Small tweak in dovecot conf (deprecated settings) (dc0481e2) + - [conf] Update nslcd and nsswitch stuff using new Buster's default configs + get rid of nslcd service, only keep the regen-conf part (6ef3520f) + - [php] Migrate from php7.0 to php7.3 (3374e653, 9be10506, dd9564d3, 9679c291, 212a15e4, 25fcaa19, c4ad66f5) + - [psql] Migrate from psql 11 to 9.6 (e88aed72, 4920d4f9, c70b0ae4) + - [firewall] Migrate from xtable to nftable (05fb58f2, 2c4a8b73, 625d5372) + - [slapd] Rework slapd regenconf to use new backend (#984) - -- Alexandre Aubin Fri, 7 Feb 2020 21:15:00 +0000 + Thanks to all contributors <3 ! (Étienne M., Josué, Kay0u) + + -- Alexandre Aubin Fri, 05 Jun 2020 03:10:09 +0200 yunohost (3.8.5.5) stable; urgency=low From c5ae774e8d8b6751da8ebae531f4314581b97f17 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 5 Jun 2020 04:02:16 +0200 Subject: [PATCH 123/262] Funny jokes with char meanings in deb versions, apparently using a dash makes everything explode suddently >.> --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index d87b0b669..5e585731a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -yunohost (4.0.0-alpha1) testing; urgency=low +yunohost (4.0.0~alpha1) testing; urgency=low - [fix] It just make no sense to backup/restore the mysql password... (#911) - [fix] Fix getopts and helpers (#885, #886) From 1f2061c9ee0060b73d66b89b496d49465adb3b71 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 5 Jun 2020 17:01:03 +0200 Subject: [PATCH 124/262] Fix version number >.> --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 5e585731a..f8f76c3a8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -yunohost (4.0.0~alpha1) testing; urgency=low +yunohost (4.0.1~alpha) testing; urgency=low - [fix] It just make no sense to backup/restore the mysql password... (#911) - [fix] Fix getopts and helpers (#885, #886) From 509d417070b37b1d090775f423673ae2454505f6 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Wed, 17 Jun 2020 22:50:02 +0200 Subject: [PATCH 125/262] create admin home folder --- src/yunohost/tools.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 75a27aa66..e341c76f1 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -104,6 +104,14 @@ def tools_ldapinit(): # Force nscd to refresh cache to take admin creation into account subprocess.call(['nscd', '-i', 'passwd']) + try: + # Attempt to create user home folder + subprocess.check_call(["mkhomedir_helper", "admin"]) + except subprocess.CalledProcessError: + if not os.path.isdir('/home/{0}'.format("admin")): + logger.warning(m18n.n('user_home_creation_failed'), + exc_info=1) + # Check admin actually exists now try: pwd.getpwnam("admin") From c2422d8c3eadba4ba655be98d57028d34ac7dc74 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 18 Jun 2020 19:14:30 +0200 Subject: [PATCH 126/262] Let's create the home after we validate that admin actually does exists --- src/yunohost/tools.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index e341c76f1..b6d45b03c 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -104,14 +104,6 @@ def tools_ldapinit(): # Force nscd to refresh cache to take admin creation into account subprocess.call(['nscd', '-i', 'passwd']) - try: - # Attempt to create user home folder - subprocess.check_call(["mkhomedir_helper", "admin"]) - except subprocess.CalledProcessError: - if not os.path.isdir('/home/{0}'.format("admin")): - logger.warning(m18n.n('user_home_creation_failed'), - exc_info=1) - # Check admin actually exists now try: pwd.getpwnam("admin") @@ -119,6 +111,14 @@ def tools_ldapinit(): logger.error(m18n.n('ldap_init_failed_to_create_admin')) raise YunohostError('installation_failed') + try: + # Attempt to create user home folder + subprocess.check_call(["mkhomedir_helper", "admin"]) + except subprocess.CalledProcessError: + if not os.path.isdir('/home/{0}'.format("admin")): + logger.warning(m18n.n('user_home_creation_failed'), + exc_info=1) + logger.success(m18n.n('ldap_initialized')) From 6a9a7b1cf6c7b6f448ac3116622278ebe8b5d54c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 19 Jun 2020 15:16:54 +0200 Subject: [PATCH 127/262] Update changelog for 4.0.2~beta --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index f8f76c3a8..cc41be2d3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +yunohost (4.0.2~beta) testing; urgency=low + + - [mod] Rebase on stretch-unstable to include recent changes + - [fix] Create admin's home during postinstall (#1021) + + Thanks to all contributors <3 ! (Kay0u) + + -- Alexandre Aubin Fri, 19 Jun 2020 15:16:26 +0200 + yunohost (4.0.1~alpha) testing; urgency=low - [fix] It just make no sense to backup/restore the mysql password... (#911) From b4ced44ad4068c85e67db4d7f507b72aa9dd5c7a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 29 Jul 2020 17:10:41 +0200 Subject: [PATCH 128/262] Update changelog for 4.0.3 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index cc41be2d3..01db3b109 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +yunohost (4.0.3) stable; urgency=low + + - Bump version number for stable release + + -- Alexandre Aubin Wed, 29 Jul 2020 17:00:00 +0200 + yunohost (4.0.2~beta) testing; urgency=low - [mod] Rebase on stretch-unstable to include recent changes From fed5c2c1e01cc8ae65c3f6291031615d532b1f2a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 29 Jul 2020 18:49:39 +0200 Subject: [PATCH 129/262] Stupid fr wording ~_~ --- locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 4b229d470..72eec2458 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -89,7 +89,7 @@ "mail_domain_unknown": "Le domaine '{domain:s}' de cette adresse de courriel n’est pas valide. Merci d’utiliser un domaine administré par ce serveur.", "mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'", "main_domain_change_failed": "Impossible de modifier le domaine principal", - "main_domain_changed": "Le domaine principal modifié", + "main_domain_changed": "Le domaine principal a été modifié", "no_internet_connection": "Le serveur n’est pas connecté à Internet", "not_enough_disk_space": "L’espace disque est insuffisant sur '{path:s}'", "package_unknown": "Le paquet '{pkgname}' est inconnu", @@ -188,7 +188,7 @@ "certmanager_http_check_timeout": "Expiration du délai lorsque le serveur a essayé de se contacter lui-même via HTTP en utilisant l’adresse IP public {ip:s} du domaine {domain:s}. Vous rencontrez peut-être un problème d’hairpinning ou alors le pare-feu/routeur en amont de votre serveur est mal configuré.", "certmanager_couldnt_fetch_intermediate_cert": "Expiration du délai lors de la tentative de récupération du certificat intermédiaire depuis Let’s Encrypt. L’installation ou le renouvellement du certificat a été annulé. Veuillez réessayer plus tard.", "domain_hostname_failed": "Échec de l’utilisation d’un nouveau nom d’hôte. Cela pourrait causer des soucis plus tard (cela n’en causera peut-être pas).", - "yunohost_ca_creation_success": "L’autorité de certification locale créée.", + "yunohost_ca_creation_success": "L'autorité de certification locale a été créée.", "app_already_installed_cant_change_url": "Cette application est déjà installée. L’URL ne peut pas être changé simplement par cette fonction. Vérifiez si cela est disponible avec `app changeurl`.", "app_change_url_failed_nginx_reload": "Le redémarrage de Nginx a échoué. Voici la sortie de 'nginx -t' :\n{nginx_errors:s}", "app_change_url_identical_domains": "L’ancien et le nouveau couple domaine/chemin_de_l’URL sont identiques pour ('{domain:s}{path:s}'), rien à faire.", From 05ff84e5af4228f762e99a8c1c01a6fee33c81ff Mon Sep 17 00:00:00 2001 From: ericgaspar Date: Tue, 4 Aug 2020 18:36:19 +0200 Subject: [PATCH 130/262] Update php --- data/helpers.d/php | 106 ++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 7ff671317..0fe118fde 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -1,37 +1,37 @@ #!/bin/bash readonly YNH_DEFAULT_PHP_VERSION=7.3 -# Declare the actual php version to use. -# A packager willing to use another version of php can override the variable into its _common.sh. +# Declare the actual PHP version to use. +# A packager willing to use another version of PHP can override the variable into its _common.sh. YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION} -# Create a dedicated php-fpm config +# Create a dedicated PHP-FPM config # # usage 1: ynh_add_fpm_config [--phpversion=7.X] [--use_template] [--package=packages] [--dedicated_service] -# | arg: -v, --phpversion= - Version of php to use. +# | arg: -v, --phpversion= - Version of PHP to use. # | arg: -t, --use_template - Use this helper in template mode. -# | arg: -p, --package= - Additionnal php packages to install -# | arg: -d, --dedicated_service - Use a dedicated php-fpm service instead of the common one. +# | arg: -p, --package= - Additionnal PHP packages to install +# | arg: -d, --dedicated_service - Use a dedicated PHP-FPM service instead of the common one. # # ----------------------------------------------------------------------------- # # usage 2: ynh_add_fpm_config [--phpversion=7.X] --usage=usage --footprint=footprint [--package=packages] [--dedicated_service] -# | arg: -v, --phpversion= - Version of php to use. +# | arg: -v, --phpversion= - Version of PHP to use. # | arg: -f, --footprint= - Memory footprint of the service (low/medium/high). -# low - Less than 20Mb of ram by pool. -# medium - Between 20Mb and 40Mb of ram by pool. -# high - More than 40Mb of ram by pool. -# Or specify exactly the footprint, the load of the service as Mb by pool instead of having a standard value. +# low - Less than 20 MB of RAM by pool. +# medium - Between 20 MB and 40 MB of RAM by pool. +# high - More than 40 MB of RAM by pool. +# Or specify exactly the footprint, the load of the service as MB by pool instead of having a standard value. # To have this value, use the following command and stress the service. # watch -n0.5 ps -o user,cmd,%cpu,rss -u APP # # | arg: -u, --usage= - Expected usage of the service (low/medium/high). -# low - Personal usage, behind the sso. +# low - Personal usage, behind the SSO. # medium - Low usage, few people or/and publicly accessible. # high - High usage, frequently visited website. # -# | arg: -p, --package= - Additionnal php packages to install for a specific version of php -# | arg: -d, --dedicated_service - Use a dedicated php-fpm service instead of the common one. +# | arg: -p, --package= - Additionnal PHP packages to install for a specific version of PHP +# | arg: -d, --dedicated_service - Use a dedicated PHP-FPM service instead of the common one. # # # The footprint of the service will be used to defined the maximum footprint we can allow, which is half the maximum RAM. @@ -85,7 +85,7 @@ ynh_add_fpm_config () { # Set the default PHP-FPM version by default phpversion="${phpversion:-$YNH_PHP_VERSION}" - # If the requested php version is not the default version for YunoHost + # If the requested PHP version is not the default version for YunoHost if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ] then # If the argument --package is used, add the packages to ynh_install_php to install them from sury @@ -95,7 +95,7 @@ ynh_add_fpm_config () { else local additionnal_packages="" fi - # Install this specific version of php. + # Install this specific version of PHP. ynh_install_php --phpversion="$phpversion" "$additionnal_packages" elif [ -n "$package" ] then @@ -118,7 +118,7 @@ ynh_add_fpm_config () { fpm_service="php5-fpm" fi - # Create the directory for fpm pools + # Create the directory for FPM pools mkdir --parents "$fpm_config_dir/pool.d" ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir" @@ -127,7 +127,7 @@ ynh_add_fpm_config () { ynh_app_setting_set --app=$app --key=phpversion --value=$phpversion finalphpconf="$fpm_config_dir/pool.d/$app.conf" - # Migrate from mutual php service to dedicated one. + # Migrate from mutual PHP service to dedicated one. if [ $dedicated_service -eq 1 ] then local old_fpm_config_dir="/etc/php/$phpversion/fpm" @@ -137,9 +137,9 @@ ynh_add_fpm_config () { ynh_print_info --message="Migrate to a dedicated php-fpm service for $app." # Create a backup of the old file before migration ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf" - # Remove the old php config file + # Remove the old PHP config file ynh_secure_remove --file="$old_fpm_config_dir/pool.d/$app.conf" - # Reload php to release the socket and allow the dedicated service to use it + # Reload PHP to release the socket and allow the dedicated service to use it ynh_systemd_action --service_name=php${phpversion}-fpm --action=reload fi fi @@ -151,10 +151,10 @@ ynh_add_fpm_config () { # Usage 1, use the template in conf/php-fpm.conf local phpfpm_path="../conf/php-fpm.conf" if [ ! -e "$phpfpm_path" ]; then - phpfpm_path="../settings/conf/php-fpm.conf" # Into the restore script, the php-fpm template is not at the same place + phpfpm_path="../settings/conf/php-fpm.conf" # Into the restore script, the PHP-FPM template is not at the same place fi # Make sure now that the template indeed exists - [ -e "$phpfpm_path" ] || ynh_die --message="Unable to find template to configure php-fpm." + [ -e "$phpfpm_path" ] || ynh_die --message="Unable to find template to configure PHP-FPM." cp "$phpfpm_path" "$finalphpconf" ynh_replace_string --match_string="__NAMETOCHANGE__" --replace_string="$app" --target_file="$finalphpconf" ynh_replace_string --match_string="__FINALPATH__" --replace_string="$final_path" --target_file="$finalphpconf" @@ -162,13 +162,13 @@ ynh_add_fpm_config () { ynh_replace_string --match_string="__PHPVERSION__" --replace_string="$phpversion" --target_file="$finalphpconf" else - # Usage 2, generate a php-fpm config file with ynh_get_scalable_phpfpm + # Usage 2, generate a PHP-FPM config file with ynh_get_scalable_phpfpm # Store settings ynh_app_setting_set --app=$app --key=fpm_footprint --value=$footprint ynh_app_setting_set --app=$app --key=fpm_usage --value=$usage - # Define the values to use for the configuration of php. + # Define the values to use for the configuration of PHP. ynh_get_scalable_phpfpm --usage=$usage --footprint=$footprint # Copy the default file @@ -181,7 +181,7 @@ ynh_add_fpm_config () { ynh_replace_string --match_string="^group = .*" --replace_string="group = $app" --target_file="$finalphpconf" ynh_replace_string --match_string=".*chdir = .*" --replace_string="chdir = $final_path" --target_file="$finalphpconf" - # Configure fpm children + # Configure FPM children ynh_replace_string --match_string=".*pm = .*" --replace_string="pm = $php_pm" --target_file="$finalphpconf" ynh_replace_string --match_string=".*pm.max_children = .*" --replace_string="pm.max_children = $php_max_children" --target_file="$finalphpconf" ynh_replace_string --match_string=".*pm.max_requests = .*" --replace_string="pm.max_requests = 500" --target_file="$finalphpconf" @@ -238,7 +238,7 @@ ynh_add_fpm_config () { ynh_replace_string --match_string="^[; ]*syslog.ident *=.*" --replace_string="syslog.ident = php-fpm-$app" --target_file="$globalphpconf" ynh_replace_string --match_string="^[; ]*include *=.*" --replace_string="include = $finalphpconf" --target_file="$globalphpconf" - # Create a config for a dedicated php-fpm service for the app + # Create a config for a dedicated PHP-FPM service for the app echo "[Unit] Description=PHP $phpversion FastCGI Process Manager for $app After=network.target @@ -253,7 +253,7 @@ ExecReload=/bin/kill -USR2 \$MAINPID WantedBy=multi-user.target " > ../conf/$fpm_service - # Create this dedicated php-fpm service + # Create this dedicated PHP-FPM service ynh_add_systemd_config --service=$fpm_service --template=$fpm_service # Integrate the service in YunoHost admin panel yunohost service add $fpm_service --log /var/log/php/fpm-php.$app.log --log_type file --description "Php-fpm dedicated to $app" @@ -262,12 +262,12 @@ WantedBy=multi-user.target # Restart the service, as this service is either stopped or only for this app ynh_systemd_action --service_name=$fpm_service --action=restart else - # Reload php, to not impact other parts of the system using php + # Reload PHP, to not impact other parts of the system using PHP ynh_systemd_action --service_name=$fpm_service --action=reload fi } -# Remove the dedicated php-fpm config +# Remove the dedicated PHP-FPM config # # usage: ynh_remove_fpm_config # @@ -277,13 +277,13 @@ ynh_remove_fpm_config () { local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service) local dedicated_service=$(ynh_app_setting_get --app=$app --key=fpm_dedicated_service) dedicated_service=${dedicated_service:-0} - # Get the version of php used by this app + # Get the version of PHP used by this app local phpversion=$(ynh_app_setting_get $app phpversion) # Assume default PHP-FPM version by default phpversion="${phpversion:-$YNH_DEFAULT_PHP_VERSION}" - # Assume default php files if not set + # Assume default PHP files if not set if [ -z "$fpm_config_dir" ] then fpm_config_dir="/etc/php/$YNH_DEFAULT_PHP_VERSION/fpm" @@ -292,11 +292,11 @@ ynh_remove_fpm_config () { if [ $dedicated_service -eq 1 ] then - # Remove the dedicated service php-fpm service for the app + # Remove the dedicated service PHP-FPM service for the app ynh_remove_systemd_config --service=$fpm_service - # Remove the global php-fpm conf + # Remove the global PHP-FPM conf ynh_secure_remove --file="$fpm_config_dir/php-fpm-$app.conf" - # Remove the service from the list of services known by Yunohost + # Remove the service from the list of services known by YunoHost yunohost service remove $fpm_service elif ynh_package_is_installed --package="php${phpversion}-fpm"; then ynh_systemd_action --service_name=$fpm_service --action=reload @@ -308,21 +308,21 @@ ynh_remove_fpm_config () { ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini" fi - # If the php version used is not the default version for YunoHost + # If the PHP version used is not the default version for YunoHost if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ] then - # Remove this specific version of php + # Remove this specific version of PHP ynh_remove_php fi } -# Install another version of php. +# Install another version of PHP. # # [internal] # # usage: ynh_install_php --phpversion=phpversion [--package=packages] -# | arg: -v, --phpversion= - Version of php to install. -# | arg: -p, --package= - Additionnal php packages to install +# | arg: -v, --phpversion= - Version of PHP to install. +# | arg: -p, --package= - Additionnal PHP packages to install # # Requires YunoHost version 3.8.1 or higher. ynh_install_php () { @@ -349,7 +349,7 @@ ynh_install_php () { # Do not add twice the same line if ! grep --quiet "$YNH_APP_INSTANCE_NAME:" "/etc/php/ynh_app_version" then - # Store the ID of this app and the version of php requested for it + # Store the ID of this app and the version of PHP requested for it echo "$YNH_APP_INSTANCE_NAME:$phpversion" | tee --append "/etc/php/ynh_app_version" fi @@ -357,11 +357,11 @@ ynh_install_php () { ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --priority=995 --name=extra_php_version --priority=600 # Install requested dependencies from this extra repository. - # Install php-fpm first, otherwise php will install apache as a dependency. + # Install PHP-FPM first, otherwise PHP will install apache as a dependency. ynh_add_app_dependencies --package="php${phpversion}-fpm" ynh_add_app_dependencies --package="php$phpversion php${phpversion}-common $package" - # Set the default php version back as the default version for php-cli. + # Set the default PHP version back as the default version for php-cli. update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION # Pin this extra repository after packages are installed to prevent sury of doing shit @@ -374,7 +374,7 @@ ynh_install_php () { yunohost service add php${phpversion}-fpm --log "/var/log/php${phpversion}-fpm.log" } -# Remove the specific version of php used by the app. +# Remove the specific version of PHP used by the app. # # [internal] # @@ -382,7 +382,7 @@ ynh_install_php () { # # Requires YunoHost version 3.8.1 or higher. ynh_remove_php () { - # Get the version of php used by this app + # Get the version of PHP used by this app local phpversion=$(ynh_app_setting_get $app phpversion) if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ] || [ -z "$phpversion" ] @@ -400,7 +400,7 @@ ynh_remove_php () { # Remove the line for this app sed --in-place "/$YNH_APP_INSTANCE_NAME:$phpversion/d" "/etc/php/ynh_app_version" - # If no other app uses this version of php, remove it. + # If no other app uses this version of PHP, remove it. if ! grep --quiet "$phpversion" "/etc/php/ynh_app_version" then # Remove the service from the admin panel @@ -408,26 +408,26 @@ ynh_remove_php () { yunohost service remove php${phpversion}-fpm fi - # Purge php dependencies for this version. + # Purge PHP dependencies for this version. ynh_package_autopurge "php$phpversion php${phpversion}-fpm php${phpversion}-common" fi } -# Define the values to configure php-fpm +# Define the values to configure PHP-FPM # # [internal] # # usage: ynh_get_scalable_phpfpm --usage=usage --footprint=footprint [--print] # | arg: -f, --footprint= - Memory footprint of the service (low/medium/high). -# low - Less than 20Mb of ram by pool. -# medium - Between 20Mb and 40Mb of ram by pool. -# high - More than 40Mb of ram by pool. -# Or specify exactly the footprint, the load of the service as Mb by pool instead of having a standard value. +# low - Less than 20 MB of RAM by pool. +# medium - Between 20 MB and 40 MB of RAM by pool. +# high - More than 40 MB of RAM by pool. +# Or specify exactly the footprint, the load of the service as MB by pool instead of having a standard value. # To have this value, use the following command and stress the service. # watch -n0.5 ps -o user,cmd,%cpu,rss -u APP # # | arg: -u, --usage= - Expected usage of the service (low/medium/high). -# low - Personal usage, behind the sso. +# low - Personal usage, behind the SSO. # medium - Low usage, few people or/and publicly accessible. # high - High usage, frequently visited website. # @@ -498,7 +498,7 @@ ynh_get_scalable_phpfpm () { # Define pm.max_children # The value of pm.max_children is the total amount of ram divide by 2 and divide again by the footprint of a pool for this app. - # So if php-fpm start the maximum of children, it won't exceed half of the ram. + # So if PHP-FPM start the maximum of children, it won't exceed half of the ram. php_max_children=$(( $max_ram / 2 / $footprint )) # If process manager is set as static, use half less children. # Used as static, there's always as many children as the value of pm.max_children From 53bc87149a920b3b72df877482e94a067fbcd3ab Mon Sep 17 00:00:00 2001 From: Kayou Date: Mon, 10 Aug 2020 11:40:53 +0200 Subject: [PATCH 131/262] Update lint.gitlab-ci.yml --- .gitlab/ci/lint.gitlab-ci.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitlab/ci/lint.gitlab-ci.yml b/.gitlab/ci/lint.gitlab-ci.yml index c6967d5a5..a7dbe3aab 100644 --- a/.gitlab/ci/lint.gitlab-ci.yml +++ b/.gitlab/ci/lint.gitlab-ci.yml @@ -17,8 +17,9 @@ invalidcode: script: - tox -e invalidcode -# Disabled, waiting for buster -#format-check: -# extends: .lint-stage -# script: -# - black --check --diff +format-check: + stage: lint + image: "before-install" + needs: [] + script: + - black --check --diff From e35d806ae1e89062250fd72b91625ce5880c1989 Mon Sep 17 00:00:00 2001 From: Kayou Date: Mon, 10 Aug 2020 12:04:34 +0200 Subject: [PATCH 132/262] Update lint.gitlab-ci.yml --- .gitlab/ci/lint.gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab/ci/lint.gitlab-ci.yml b/.gitlab/ci/lint.gitlab-ci.yml index a7dbe3aab..9a2d1d95f 100644 --- a/.gitlab/ci/lint.gitlab-ci.yml +++ b/.gitlab/ci/lint.gitlab-ci.yml @@ -1,6 +1,7 @@ ######################################## # LINTER ######################################## +# later we must fix lint and format-check jobs and remove "allow_failure" lint: stage: lint @@ -21,5 +22,6 @@ format-check: stage: lint image: "before-install" needs: [] + allow_failure: true script: - - black --check --diff + - black --check --diff src doc data tests From accc2da4e3e3b80f8471ba4c57cf0c31da4886da Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Aug 2020 16:55:54 +0200 Subject: [PATCH 133/262] Improve debugging of postgresql 9.6 -> 11 migration --- .../0017_postgresql_9p6_to_11.py | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py index f03cd9c8c..b0b527a73 100644 --- a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py +++ b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py @@ -28,14 +28,29 @@ class MyMigration(Migration): if not space_used_by_directory("/var/lib/postgresql/9.6") > free_space_in_directory("/var/lib/postgresql"): raise YunohostError("migration_0017_not_enough_space", path="/var/lib/postgresql/") - subprocess.check_call("systemctl stop postgresql", shell=True) - subprocess.check_call("pg_dropcluster --stop 11 main", shell=True) - subprocess.check_call("pg_upgradecluster -m upgrade 9.6 main", shell=True) - subprocess.check_call("pg_dropcluster --stop 9.6 main", shell=True) - subprocess.check_call("systemctl start postgresql", shell=True) + self.runcmd("systemctl stop postgresql") + self.runcmd("pg_dropcluster --stop 11 main") + self.runcmd("pg_upgradecluster -m upgrade 9.6 main") + self.runcmd("pg_dropcluster --stop 9.6 main") + self.runcmd("systemctl start postgresql") def package_is_installed(self, package_name): - p = subprocess.Popen("dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name), shell=True) - p.communicate() - return p.returncode == 0 + (returncode, out, err) = self.runcmd("dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name), raise_on_errors=False) + return returncode == 0 + + def runcmd(self, cmd, raise_on_errors=True): + p = subprocess.Popen(cmd, + shell=True, + executable='/bin/bash', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + out, err = p.communicate() + returncode = p.returncode + if raise_on_errors and returncode != 0: + raise YunohostError("Failed to run command '{}'.\nreturncode: {}\nstdout:\n{}\nstderr:\n{}\n".format(cmd, returncode, out, err)) + + out = out.strip().split("\n") + return (returncode, out, err) + From 59bd7d6664217968faec74ac339c228e745a1ab3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Aug 2020 17:12:21 +0200 Subject: [PATCH 134/262] Improve robustness of postgresql 9.6 -> 11 migration (in particular dropcluster in case cluster 11 doesn't exists yet) --- .../data_migrations/0017_postgresql_9p6_to_11.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py index b0b527a73..e39cd84ad 100644 --- a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py +++ b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py @@ -25,11 +25,18 @@ class MyMigration(Migration): if not self.package_is_installed("postgresql-11"): raise YunohostError("migration_0017_postgresql_11_not_installed") + # Make sure there's a 9.6 cluster + try: + self.runcmd("pg_lsclusters | grep -q '^9.6 '") + except Exception as e: + logger.warning("It looks like there's not active 9.6 cluster, so probably don't need to run this migration") + return + if not space_used_by_directory("/var/lib/postgresql/9.6") > free_space_in_directory("/var/lib/postgresql"): raise YunohostError("migration_0017_not_enough_space", path="/var/lib/postgresql/") self.runcmd("systemctl stop postgresql") - self.runcmd("pg_dropcluster --stop 11 main") + self.runcmd("pg_dropcluster --stop 11 main || true") # We do not trigger an exception if the command fails because that probably means cluster 11 doesn't exists, which is fine because it's created during the pg_upgradecluster) self.runcmd("pg_upgradecluster -m upgrade 9.6 main") self.runcmd("pg_dropcluster --stop 9.6 main") self.runcmd("systemctl start postgresql") From 4cb6f7fddcda08c9fb5d32b01fbfce1b4980c377 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Aug 2020 17:21:32 +0200 Subject: [PATCH 135/262] Improve debugging for xtables -> nftables migration --- .../data_migrations/0018_xtable_to_nftable.py | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/src/yunohost/data_migrations/0018_xtable_to_nftable.py b/src/yunohost/data_migrations/0018_xtable_to_nftable.py index 7d3810518..ed9f7cf89 100644 --- a/src/yunohost/data_migrations/0018_xtable_to_nftable.py +++ b/src/yunohost/data_migrations/0018_xtable_to_nftable.py @@ -36,20 +36,20 @@ class MyMigration(Migration): # Backup existing legacy rules to be able to rollback if self.do_ipv4 and not os.path.exists(self.backup_rules_ipv4): - os.system("iptables-legacy -L >/dev/null") # For some reason if we don't do this, iptables-legacy-save is empty ? - subprocess.check_call("iptables-legacy-save > %s" % self.backup_rules_ipv4, shell=True) - assert subprocess.check_output("cat %s" % self.backup_rules_ipv4, shell=True).strip(), "Uhoh backup of legacy ipv4 rules is empty !?" + self.runcmd("iptables-legacy -L >/dev/null") # For some reason if we don't do this, iptables-legacy-save is empty ? + self.runcmd("iptables-legacy-save > %s" % self.backup_rules_ipv4) + assert open(self.backup_rules_ipv4).read().strip(), "Uhoh backup of legacy ipv4 rules is empty !?" if self.do_ipv6 and not os.path.exists(self.backup_rules_ipv6): - os.system("ip6tables-legacy -L >/dev/null") # For some reason if we don't do this, iptables-legacy-save is empty ? - subprocess.check_call("ip6tables-legacy-save > %s" % self.backup_rules_ipv6, shell=True) - assert subprocess.check_output("cat %s" % self.backup_rules_ipv6, shell=True).strip(), "Uhoh backup of legacy ipv6 rules is empty !?" + self.runcmd("ip6tables-legacy -L >/dev/null") # For some reason if we don't do this, iptables-legacy-save is empty ? + self.runcmd("ip6tables-legacy-save > %s" % self.backup_rules_ipv6) + assert open(self.backup_rules_ipv6).read().strip(), "Uhoh backup of legacy ipv6 rules is empty !?" # We inject the legacy rules (iptables-legacy) into the new iptable (just "iptables") try: if self.do_ipv4: - subprocess.check_call("iptables-legacy-save | iptables-restore", shell=True) + self.runcmd("iptables-legacy-save | iptables-restore") if self.do_ipv6: - subprocess.check_call("ip6tables-legacy-save | ip6tables-restore", shell=True) + self.runcmd("ip6tables-legacy-save | ip6tables-restore") except Exception as e: self.rollback() raise YunohostError("migration_0018_failed_to_migrate_iptables_rules", error=e) @@ -58,19 +58,17 @@ class MyMigration(Migration): # Stolen from https://serverfault.com/a/200642 try: if self.do_ipv4: - subprocess.check_call( + self.runcmd( "iptables-legacy-save | awk '/^[*]/ { print $1 }" # Keep lines like *raw, *filter and *nat " /^:[A-Z]+ [^-]/ { print $1 \" ACCEPT\" ; }" # Turn all policies to accept " /COMMIT/ { print $0; }'" # Keep the line COMMIT - " | iptables-legacy-restore", - shell=True) + " | iptables-legacy-restore") if self.do_ipv6: - subprocess.check_call( + self.runcmd( "ip6tables-legacy-save | awk '/^[*]/ { print $1 }" # Keep lines like *raw, *filter and *nat " /^:[A-Z]+ [^-]/ { print $1 \" ACCEPT\" ; }" # Turn all policies to accept " /COMMIT/ { print $0; }'" # Keep the line COMMIT - " | ip6tables-legacy-restore", - shell=True) + " | ip6tables-legacy-restore") except Exception as e: self.rollback() raise YunohostError("migration_0018_failed_to_reset_legacy_rules", error=e) @@ -87,6 +85,22 @@ class MyMigration(Migration): def rollback(self): if self.do_ipv4: - subprocess.check_call("iptables-legacy-restore < %s" % self.backup_rules_ipv4, shell=True) + self.runcmd("iptables-legacy-restore < %s" % self.backup_rules_ipv4) if self.do_ipv6: - subprocess.check_call("iptables-legacy-restore < %s" % self.backup_rules_ipv6, shell=True) + self.runcmd("iptables-legacy-restore < %s" % self.backup_rules_ipv6) + + def runcmd(self, cmd, raise_on_errors=True): + p = subprocess.Popen(cmd, + shell=True, + executable='/bin/bash', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + out, err = p.communicate() + returncode = p.returncode + if raise_on_errors and returncode != 0: + raise YunohostError("Failed to run command '{}'.\nreturncode: {}\nstdout:\n{}\nstderr:\n{}\n".format(cmd, returncode, out, err)) + + out = out.strip().split("\n") + return (returncode, out, err) + From 4b14402c5af83f2b342aafcd94d3c05a3bcfcf0d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Aug 2020 18:13:15 +0200 Subject: [PATCH 136/262] Moaaar debug --- src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py | 3 +++ src/yunohost/data_migrations/0018_xtable_to_nftable.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py index e39cd84ad..955393c5b 100644 --- a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py +++ b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py @@ -47,6 +47,9 @@ class MyMigration(Migration): return returncode == 0 def runcmd(self, cmd, raise_on_errors=True): + + logger.debug("Running command: " + cmd) + p = subprocess.Popen(cmd, shell=True, executable='/bin/bash', diff --git a/src/yunohost/data_migrations/0018_xtable_to_nftable.py b/src/yunohost/data_migrations/0018_xtable_to_nftable.py index ed9f7cf89..2f931a6ef 100644 --- a/src/yunohost/data_migrations/0018_xtable_to_nftable.py +++ b/src/yunohost/data_migrations/0018_xtable_to_nftable.py @@ -90,6 +90,9 @@ class MyMigration(Migration): self.runcmd("iptables-legacy-restore < %s" % self.backup_rules_ipv6) def runcmd(self, cmd, raise_on_errors=True): + + logger.debug("Running command: " + cmd) + p = subprocess.Popen(cmd, shell=True, executable='/bin/bash', From 1697943afcbe4586ec1e667ffe586752e59dbe96 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Aug 2020 18:15:32 +0200 Subject: [PATCH 137/262] Update changelog for 4.0.4 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 01db3b109..0fc8ef27b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +yunohost (4.0.4) stable; urgency=low + + - Debugging and robustness improvements for postgresql 9.6 -> 11 and xtables->nftables migrations (accc2da4, 59bd7d66, 4cb6f7fd, 4b14402c) + + -- Alexandre Aubin Wed, 12 Aug 2020 18:14:00 +0200 + yunohost (4.0.3) stable; urgency=low - Bump version number for stable release From 7b1f02e01f42116a038f0bcbcb221b263fce97ee Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 14 Aug 2020 13:27:07 +0200 Subject: [PATCH 138/262] Fix ref to variable in i18n string (c.f. issue 1647) --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index d40fcfb14..3240cffe7 100644 --- a/locales/en.json +++ b/locales/en.json @@ -122,7 +122,7 @@ "certmanager_cert_signing_failed": "Could not sign the new certificate", "certmanager_certificate_fetching_or_enabling_failed": "Trying to use the new certificate for {domain:s} did not work...", "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_not_diagnosed_yet": "There is no diagnosis result for domain %s yet. Please re-run a diagnosis for categories 'DNS records' and 'Web' in the diagnosis section to check if the domain is ready for Let's Encrypt. (Or if you know what you are doing, use '--no-checks' to turn off those checks.)", + "certmanager_domain_not_diagnosed_yet": "There is no diagnosis result for domain {domain} yet. Please re-run a diagnosis for categories 'DNS records' and 'Web' in the diagnosis section to check if the domain is ready for Let's Encrypt. (Or if you know what you are doing, use '--no-checks' to turn off those checks.)", "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' to do so.)", "certmanager_domain_dns_ip_differs_from_public_ip": "The DNS records for domain '{domain:s}' is different from this server's IP. Please check the 'DNS records' (basic) category in the diagnosis for more info. If you recently modified your A record, please wait for it to propagate (some DNS propagation checkers are available online). (If you know what you are doing, use '--no-checks' to turn off those checks.)", "certmanager_domain_http_not_working": "Domain {domain:s} does not seem to be accessible through HTTP. Please check the 'Web' category in the diagnosis for more info. (If you know what you are doing, use '--no-checks' to turn off those checks.)", From f3a4334a13aec8e73af7e00f85400eab1db9b3a5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 14 Aug 2020 14:50:54 +0200 Subject: [PATCH 139/262] Upgrade nginx configuration according to Mozilla guidelines now that we're on Buster --- data/templates/nginx/security.conf.inc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/data/templates/nginx/security.conf.inc b/data/templates/nginx/security.conf.inc index 4c6461506..4b4f3fe5b 100644 --- a/data/templates/nginx/security.conf.inc +++ b/data/templates/nginx/security.conf.inc @@ -2,15 +2,16 @@ ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; # about 200000 sessions ssl_session_tickets off; -# nginx 1.10 in stretch doesn't support TLS1.3 and Mozilla doesn't have any -# "modern" config recommendation with it. -# So until buster the modern conf is same as intermediate -{% if compatibility == "modern" %} {% else %} {% endif %} - +{% if compatibility == "modern" %} +# generated 2020-08-14, Mozilla Guideline v5.6, nginx 1.14.2, OpenSSL 1.1.1d, modern configuration +# https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=modern&openssl=1.1.1d&guideline=5.6 +ssl_protocols TLSv1.3; +ssl_prefer_server_ciphers off; +{% else %} # Ciphers with intermediate compatibility -# generated 2020-04-03, Mozilla Guideline v5.4, nginx 1.10.3, OpenSSL 1.1.0l, intermediate configuration -# https://ssl-config.mozilla.org/#server=nginx&version=1.10.3&config=intermediate&openssl=1.1.0l&guideline=5.4 -ssl_protocols TLSv1.2; +# generated 2020-08-14, Mozilla Guideline v5.6, nginx 1.14.2, OpenSSL 1.1.1d, intermediate configuration +# https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=intermediate&openssl=1.1.1d&guideline=5.6 +ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; @@ -18,6 +19,8 @@ ssl_prefer_server_ciphers off; # From https://ssl-config.mozilla.org/ffdhe2048.txt # https://security.stackexchange.com/a/149818 ssl_dhparam /usr/share/yunohost/other/ffdhe2048.pem; +{% endif %} + # Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners # https://wiki.mozilla.org/Security/Guidelines/Web_Security From ca0a42f2b42628805aa41bca7095e234c6365a52 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Fri, 14 Aug 2020 18:08:51 +0200 Subject: [PATCH 140/262] git clone with depth during CI --- .gitlab/ci/build.gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/ci/build.gitlab-ci.yml b/.gitlab/ci/build.gitlab-ci.yml index 67232ba1f..5e25bcd1f 100644 --- a/.gitlab/ci/build.gitlab-ci.yml +++ b/.gitlab/ci/build.gitlab-ci.yml @@ -38,7 +38,7 @@ build-ssowat: variables: PACKAGE: "ssowat" script: - - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE + - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script @@ -47,6 +47,6 @@ build-moulinette: variables: PACKAGE: "moulinette" script: - - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE + - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script From 485c65a9a5bb59d5d4e657ec15c967324bf0dbe8 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Sat, 15 Aug 2020 11:05:59 +0200 Subject: [PATCH 141/262] apt-daily is now disabled by the CI --- .gitlab/ci/install.gitlab-ci.yml | 8 -------- .gitlab/ci/test.gitlab-ci.yml | 8 -------- 2 files changed, 16 deletions(-) diff --git a/.gitlab/ci/install.gitlab-ci.yml b/.gitlab/ci/install.gitlab-ci.yml index eb5187e11..1df4fc4b9 100644 --- a/.gitlab/ci/install.gitlab-ci.yml +++ b/.gitlab/ci/install.gitlab-ci.yml @@ -26,12 +26,4 @@ install-postinstall: script: - apt-get update -o Acquire::Retries=3 - DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb - - systemctl -q stop apt-daily.timer - - systemctl -q stop apt-daily-upgrade.timer - - systemctl -q stop apt-daily.service - - systemctl -q stop apt-daily-upgrade.service - - systemctl -q disable apt-daily.timer - - systemctl -q disable apt-daily-upgrade.timer - - systemctl -q disable apt-daily.service - - systemctl -q disable apt-daily-upgrade.service - yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns diff --git a/.gitlab/ci/test.gitlab-ci.yml b/.gitlab/ci/test.gitlab-ci.yml index 12379342a..3d88354b6 100644 --- a/.gitlab/ci/test.gitlab-ci.yml +++ b/.gitlab/ci/test.gitlab-ci.yml @@ -1,12 +1,4 @@ .install_debs: &install_debs - - systemctl -q stop apt-daily.timer - - systemctl -q stop apt-daily-upgrade.timer - - systemctl -q stop apt-daily.service - - systemctl -q stop apt-daily-upgrade.service - - systemctl -q disable apt-daily.timer - - systemctl -q disable apt-daily-upgrade.timer - - systemctl -q disable apt-daily.service - - systemctl -q disable apt-daily-upgrade.service - apt-get update -o Acquire::Retries=3 - DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb From 0fb5b139242f482467950fe1bb1ea7cdd74d5b94 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Mon, 17 Aug 2020 19:51:32 +0200 Subject: [PATCH 142/262] trying to fix the ci --- .gitlab/ci/test.gitlab-ci.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.gitlab/ci/test.gitlab-ci.yml b/.gitlab/ci/test.gitlab-ci.yml index 3d88354b6..ef21731f3 100644 --- a/.gitlab/ci/test.gitlab-ci.yml +++ b/.gitlab/ci/test.gitlab-ci.yml @@ -36,7 +36,7 @@ full-tests: - *install_debs - yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns script: - - pytest --cov=yunohost tests/ src/yunohost/tests/ --junitxml=report.xml + - python -m pytest --cov=yunohost tests/ src/yunohost/tests/ --junitxml=report.xml needs: - job: build-yunohost artifacts: true @@ -51,70 +51,70 @@ full-tests: root-tests: extends: .test-stage script: - - py.test tests + - python -m pytest tests test-apps: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_apps.py + - python -m pytest tests/test_apps.py test-appscatalog: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_appscatalog.py + - python -m pytest tests/test_appscatalog.py test-appurl: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_appurl.py + - python -m pytest tests/test_appurl.py test-apps-arguments-parsing: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_apps_arguments_parsing.py + - python -m pytest tests/test_apps_arguments_parsing.py test-backuprestore: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_backuprestore.py + - python -m pytest tests/test_backuprestore.py test-changeurl: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_changeurl.py + - python -m pytest tests/test_changeurl.py test-permission: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_permission.py + - python -m pytest tests/test_permission.py test-settings: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_settings.py + - python -m pytest tests/test_settings.py test-user-group: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_user-group.py + - python -m pytest tests/test_user-group.py test-regenconf: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_regenconf.py + - python -m pytest tests/test_regenconf.py test-service: extends: .test-stage script: - cd src/yunohost - - py.test tests/test_service.py + - python -m pytest tests/test_service.py From 93bff46bc3a68a30af1e71ab6c75de02dd620062 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Mon, 17 Aug 2020 23:54:20 +0200 Subject: [PATCH 143/262] imove tox and add python3 linter --- .gitlab/ci/lint.gitlab-ci.yml | 26 +++++++++++++++++++++----- tox.ini | 25 ++++++------------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/.gitlab/ci/lint.gitlab-ci.yml b/.gitlab/ci/lint.gitlab-ci.yml index 9a2d1d95f..8db1ee756 100644 --- a/.gitlab/ci/lint.gitlab-ci.yml +++ b/.gitlab/ci/lint.gitlab-ci.yml @@ -3,20 +3,36 @@ ######################################## # later we must fix lint and format-check jobs and remove "allow_failure" -lint: +lint27: stage: lint image: "before-install" needs: [] allow_failure: true script: - - tox -e lint + - tox -e py27-lint -invalidcode: +lint37: + stage: lint + image: "before-install" + needs: [] + allow_failure: true + script: + - tox -e py37-lint + +invalidcode27: stage: lint image: "before-install" needs: [] script: - - tox -e invalidcode + - tox -e py27-invalidcode + +invalidcode37: + stage: lint + image: "before-install" + allow_failure: true + needs: [] + script: + - tox -e py37-invalidcode format-check: stage: lint @@ -24,4 +40,4 @@ format-check: needs: [] allow_failure: true script: - - black --check --diff src doc data tests + - tox -e py37-black diff --git a/tox.ini b/tox.ini index 4598ad3d3..0c0b01a5e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,25 +1,12 @@ [tox] -envlist = - py27 - lint -skipdist = True +envlist = py{27,37}-{lint,invalidcode},py37-black [testenv] skip_install=True deps = - pytest >= 4.6.3, < 5.0 - pyyaml >= 5.1.2, < 6.0 - flake8 >= 3.7.9, < 3.8 - urllib3 + py{27,37}-{lint,invalidcode}: flake8 + py37-black: black commands = - pytest {posargs} - -[testenv:lint] -skip_install=True -commands = flake8 src doc data tests -deps = flake8 - -[testenv:invalidcode] -skip_install=True -commands = flake8 src data --exclude src/yunohost/tests --select F --ignore F401,F841 -deps = flake8 + py{27,37}-lint: flake8 src doc data tests + py{27,37}-invalidcode: flake8 src data --exclude src/yunohost/tests --select F --ignore F401,F841 + py37-black: black --check --diff src doc data tests \ No newline at end of file From 0c9b6eec98ad223a98a1662b6d234c63a89f14e5 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Tue, 18 Aug 2020 00:02:08 +0200 Subject: [PATCH 144/262] fix travis --- .travis.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8674d4d03..9a0f40674 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,18 @@ language: python matrix: allow_failures: - - env: TOXENV=lint + - env: TOXENV=py27-lint + - env: TOXENV=py37-lint + - env: TOXENV=py37-invalidcode include: - python: 2.7 - env: TOXENV=py27 + env: TOXENV=py27-lint - python: 2.7 - env: TOXENV=lint + env: TOXENV=py27-invalidcode + - python: 3.7 + env: TOXENV=py37-lint + - python: 3.7 + env: TOXENV=py37-invalidcode install: - pip install tox From 89bcf1ba6d84d64267c3c37df69e518991c29420 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 18 Aug 2020 13:25:38 +0200 Subject: [PATCH 145/262] Update postfix configuration now that we're on Buster + we have DH param --- data/templates/postfix/main.cf | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index 18e457a76..02b119cdc 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -19,34 +19,35 @@ readme_directory = no # -- TLS for incoming connections ############################################################################### -# generated 2020-04-03, Mozilla Guideline v5.4, Postfix 3.1.14, OpenSSL 1.1.0l, intermediate configuration -# https://ssl-config.mozilla.org/#server=postfix&version=3.1.14&config=intermediate&openssl=1.1.0l&guideline=5.4 - -# (No modern conf support until we're on buster...) -# {% if compatibility == "intermediate" %} {% else %} {% endif %} - smtpd_use_tls = yes smtpd_tls_security_level = may smtpd_tls_auth_only = yes smtpd_tls_cert_file = /etc/yunohost/certs/{{ main_domain }}/crt.pem smtpd_tls_key_file = /etc/yunohost/certs/{{ main_domain }}/key.pem + +{% if compatibility == "intermediate" %} +# generated 2020-08-18, Mozilla Guideline v5.6, Postfix 3.4.14, OpenSSL 1.1.1d, intermediate configuration +# https://ssl-config.mozilla.org/#server=postfix&version=3.4.14&config=intermediate&openssl=1.1.1d&guideline=5.6 + smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 -# smtpd_tls_mandatory_ciphers = medium # (c.f. below) +smtpd_tls_mandatory_ciphers = medium # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam.pem # not actually 1024 bits, this applies to all DHE >= 1024 bits -# smtpd_tls_dh1024_param_file = /path/to/dhparam.pem +smtpd_tls_dh1024_param_file = /usr/share/yunohost/other/ffdhe2048.pem; + +tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 +{% else %} +# generated 2020-08-18, Mozilla Guideline v5.6, Postfix 3.4.14, OpenSSL 1.1.1d, modern configuration +# https://ssl-config.mozilla.org/#server=postfix&version=3.4.14&config=modern&openssl=1.1.1d&guideline=5.6 + +smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2 +smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2 +{% else %} -# This custom medium cipherlist recommendation only works if we have a DH ... which we don't, c.f. https://github.com/YunoHost/issues/issues/93 -# On the other hand, the postfix doc strongly discourage tweaking this list ... So whatever, let's keep the mandatory_ciphers to high like we did before applying the Mozilla recommendation ... -#tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 tls_preempt_cipherlist = no - -# Custom Yunohost stuff ... because we can't use the recommendation about medium cipher list ... -smtpd_tls_mandatory_ciphers=high -smtpd_tls_eecdh_grade = ultra ############################################################################### smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtpd_tls_loglevel=1 From 2d661737b33ae2471ffaf31ebb8d1aeb64698e3e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 18 Aug 2020 13:28:46 +0200 Subject: [PATCH 146/262] Update dovecot configuration now that we have a DH param --- data/templates/dovecot/dovecot.conf | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/data/templates/dovecot/dovecot.conf b/data/templates/dovecot/dovecot.conf index d64b15356..2e17ff163 100644 --- a/data/templates/dovecot/dovecot.conf +++ b/data/templates/dovecot/dovecot.conf @@ -14,14 +14,17 @@ mail_plugins = $mail_plugins quota ############################################################################### -# generated 2020-05-02, Mozilla Guideline v5.4, Dovecot 2.3.4.1, OpenSSL 1.1.1d, intermediate configuration -# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.4.1&config=intermediate&openssl=1.1.1d&guideline=5.4 +# generated 2020-08-18, Mozilla Guideline v5.6, Dovecot 2.3.4, OpenSSL 1.1.1d, intermediate configuration +# https://ssl-config.mozilla.org/#server=dovecot&version=2.3.4&config=intermediate&openssl=1.1.1d&guideline=5.6 ssl = required ssl_cert = /path/to/dhparam +ssl_dh = /usr/share/yunohost/other/ffdhe2048.pem; + # intermediate configuration ssl_min_protocol = TLSv1.2 ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 From 8454f2ec12a96bb019d855c6f4256cd38d46baff Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 21 Aug 2020 20:40:55 +0200 Subject: [PATCH 147/262] Recursively enforce ownership for rspamd --- data/hooks/conf_regen/31-rspamd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen/31-rspamd index 6da4af643..87ed722a7 100755 --- a/data/hooks/conf_regen/31-rspamd +++ b/data/hooks/conf_regen/31-rspamd @@ -42,7 +42,7 @@ do_post_regen() { chown _rspamd /etc/dkim/*.mail.key chmod 400 /etc/dkim/*.mail.key - [ ! -e /var/log/rspamd ] || chown _rspamd:_rspamd /var/log/rspamd + [ ! -e /var/log/rspamd ] || chown -R _rspamd:_rspamd /var/log/rspamd regen_conf_files=$1 [ -z "$regen_conf_files" ] && exit 0 From 6ec0e7b6af442e4c166e6e90060f234f9a7b9e03 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 24 Aug 2020 15:41:41 +0200 Subject: [PATCH 148/262] Fix stupid encoding issue when fetching service description --- src/yunohost/service.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 2eeb078bc..522395718 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -346,16 +346,20 @@ def _get_and_format_service_status(service, infos): 'configuration': "unknown", } - translation_key = "service_description_%s" % service + # Try to get description directly from services.yml description = infos.get("description") + + # If no description was there, try to get it from the .json locales if not description: + translation_key = "service_description_%s" % service description = m18n.n(translation_key) - # that mean that we don't have a translation for this string - # that's the only way to test for that for now - # if we don't have it, uses the one provided by systemd - if description.decode('utf-8') == translation_key: - description = str(raw_status.get("Description", "")) + # If descrption is still equal to the translation key, + # that mean that we don't have a translation for this string + # that's the only way to test for that for now + # if we don't have it, uses the one provided by systemd + if description.decode('utf-8') == translation_key: + description = str(raw_status.get("Description", "")) output = { 'status': str(raw_status.get("SubState", "unknown")), From d491b3208b6b60fe90a0e8118e037b567c6df5e8 Mon Sep 17 00:00:00 2001 From: Kayou Date: Mon, 24 Aug 2020 23:25:40 +0200 Subject: [PATCH 149/262] Update acme_tiny to 4.1.0 (#1037) --- src/yunohost/vendor/acme_tiny/acme_tiny.py | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/yunohost/vendor/acme_tiny/acme_tiny.py b/src/yunohost/vendor/acme_tiny/acme_tiny.py index ba04e37ad..6d1d085c6 100644 --- a/src/yunohost/vendor/acme_tiny/acme_tiny.py +++ b/src/yunohost/vendor/acme_tiny/acme_tiny.py @@ -48,7 +48,7 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check # helper function - make signed requests def _send_signed_request(url, payload, err_msg, depth=0): - payload64 = _b64(json.dumps(payload).encode('utf8')) + payload64 = "" if payload is None else _b64(json.dumps(payload).encode('utf8')) new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce'] protected = {"url": url, "alg": alg, "nonce": new_nonce} protected.update({"jwk": jwk} if acct_headers is None else {"kid": acct_headers['Location']}) @@ -63,12 +63,12 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check # helper function - poll until complete def _poll_until_not(url, pending_statuses, err_msg): - while True: - result, _, _ = _do_request(url, err_msg=err_msg) - if result['status'] in pending_statuses: - time.sleep(2) - continue - return result + result, t0 = None, time.time() + while result is None or result['status'] in pending_statuses: + assert (time.time() - t0 < 3600), "Polling timeout" # 1 hour timeout + time.sleep(0 if result is None else 2) + result, _, _ = _send_signed_request(url, None, err_msg) + return result # parse account key to get public key log.info("Parsing account key...") @@ -93,7 +93,7 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check common_name = re.search(r"Subject:.*? CN\s?=\s?([^\s,;/]+)", out.decode('utf8')) if common_name is not None: domains.add(common_name.group(1)) - subject_alt_names = re.search(r"X509v3 Subject Alternative Name: \n +([^\n]+)\n", out.decode('utf8'), re.MULTILINE|re.DOTALL) + subject_alt_names = re.search(r"X509v3 Subject Alternative Name: (?:critical)?\n +([^\n]+)\n", out.decode('utf8'), re.MULTILINE|re.DOTALL) if subject_alt_names is not None: for san in subject_alt_names.group(1).split(", "): if san.startswith("DNS:"): @@ -123,7 +123,7 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check # get the authorizations that need to be completed for auth_url in order['authorizations']: - authorization, _, _ = _do_request(auth_url, err_msg="Error getting challenges") + authorization, _, _ = _send_signed_request(auth_url, None, "Error getting challenges") domain = authorization['identifier']['value'] log.info("Verifying {0}...".format(domain)) @@ -138,9 +138,8 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check # check that the file is in place try: wellknown_url = "http://{0}/.well-known/acme-challenge/{1}".format(domain, token) - assert(disable_check or _do_request(wellknown_url)[0] == keyauthorization) + assert (disable_check or _do_request(wellknown_url)[0] == keyauthorization) except (AssertionError, ValueError) as e: - os.remove(wellknown_path) raise ValueError("Wrote file to {0}, but couldn't download {1}: {2}".format(wellknown_path, wellknown_url, e)) # say the challenge is done @@ -148,6 +147,7 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check authorization = _poll_until_not(auth_url, ["pending"], "Error checking challenge status for {0}".format(domain)) if authorization['status'] != "valid": raise ValueError("Challenge did not pass for {0}: {1}".format(domain, authorization)) + os.remove(wellknown_path) log.info("{0} verified!".format(domain)) # finalize the order with the csr @@ -161,7 +161,7 @@ def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, disable_check raise ValueError("Order failed: {0}".format(order)) # download the certificate - certificate_pem, _, _ = _do_request(order['certificate'], err_msg="Certificate download failed") + certificate_pem, _, _ = _send_signed_request(order['certificate'], None, "Certificate download failed") log.info("Certificate signed!") return certificate_pem From e5e26c2b81a324cdc2bbb65c5c95124a4d7b33b7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 25 Aug 2020 16:46:02 +0200 Subject: [PATCH 150/262] Typo in postfix's conf template --- 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 02b119cdc..b4b645138 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -45,7 +45,7 @@ tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA25 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2 smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2 -{% else %} +{% endif %} tls_preempt_cipherlist = no ############################################################################### From f9c075097c021efeda1f28127d8ff175dc76f7cc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 25 Aug 2020 20:09:38 +0200 Subject: [PATCH 151/262] Update changelog for 4.0.5 --- debian/changelog | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/debian/changelog b/debian/changelog index 0fc8ef27b..92d5b6410 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,16 @@ +yunohost (4.0.5) testing; urgency=low + + - [enh] Update postfix, dovecot, nginx configuration according to Mozilla guidelines (Buster + DH params) (f3a4334a, 89bcf1ba, 2d661737) + - [enh] Update acme_tiny to 4.1.0 (#1037) + - [fix] ref to variable in i18n string (c.f. issue 1647) (7b1f02e0) + - [fix] Recursively enforce ownership for rspamd (8454f2ec) + - [fix] Stupid encoding issue when fetching service description (6ec0e7b6) + - [fix] Misc fixes for CI (ca0a42f2, 485c65a9, #1038, a891d20a) + + Thanks to all contributors <3 ! (Eric G., Kay0u) + + -- Alexandre Aubin Tue, 25 Aug 2020 19:32:27 +0200 + yunohost (4.0.4) stable; urgency=low - Debugging and robustness improvements for postgresql 9.6 -> 11 and xtables->nftables migrations (accc2da4, 59bd7d66, 4cb6f7fd, 4b14402c) From 5f242aea18227004cd7500e1e21562048938e7a1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 27 Aug 2020 17:54:44 +0200 Subject: [PATCH 152/262] Fix code that was half broken because of undefined vars :| --- src/yunohost/app.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index eb30f796b..b7ae4f6ba 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2477,7 +2477,7 @@ def _parse_args_in_yunohost_format(user_answers, argument_questions): or config_panel.json/toml """ from yunohost.domain import domain_list, _get_maindomain - from yunohost.user import user_list + from yunohost.user import user_list, user_info parsed_answers_dict = OrderedDict() @@ -2503,27 +2503,9 @@ def _parse_args_in_yunohost_format(user_answers, argument_questions): question_value = user_answers[question_name] else: if 'ask' in question: - # Retrieve proper ask string - text_for_user_input_in_cli = _value_for_locale(question['ask']) - - # Append extra strings - if question_type == 'boolean': - text_for_user_input_in_cli += ' [yes | no]' - elif question_choices: - text_for_user_input_in_cli += ' [{0}]'.format(' | '.join(question_choices)) - - if question_default is not None: - if question_type == 'boolean': - text_for_user_input_in_cli += ' (default: {0})'.format("yes" if question_default == 1 else "no") - else: - text_for_user_input_in_cli += ' (default: {0})'.format(question_default) - - # Check for a password argument - is_password = True if question_type == 'password' else False if question_type == 'domain': question_default = _get_maindomain() - text_for_user_input_in_cli += ' (default: {0})'.format(question_default) msignals.display(m18n.n('domains_available')) for domain in domain_list()['domains']: msignals.display("- {}".format(domain)) @@ -2537,12 +2519,30 @@ def _parse_args_in_yunohost_format(user_answers, argument_questions): root_mail = "root@%s" % _get_maindomain() for user in users.keys(): if root_mail in user_info(user)["mail-aliases"]: - arg_default = user - ask_string += ' (default: {0})'.format(arg_default) + question_default = user + break elif question_type == 'password': msignals.display(m18n.n('good_practices_about_user_password')) + # Retrieve proper ask string + text_for_user_input_in_cli = _value_for_locale(question['ask']) + + # Append extra strings + if question_type == 'boolean': + text_for_user_input_in_cli += ' [yes | no]' + elif question_choices: + text_for_user_input_in_cli += ' [{0}]'.format(' | '.join(question_choices)) + + + if question_default is not None: + if question_type == 'boolean': + text_for_user_input_in_cli += ' (default: {0})'.format("yes" if question_default == 1 else "no") + else: + text_for_user_input_in_cli += ' (default: {0})'.format(question_default) + + is_password = True if question_type == 'password' else False + try: input_string = msignals.prompt(text_for_user_input_in_cli, is_password) except NotImplementedError: From 77ce657ae1809ef0d62bfedc44799144e141e3ff Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 27 Aug 2020 19:22:09 +0200 Subject: [PATCH 153/262] Ugly hack to have the name of the main logger, otherwise error/exception messages ain't displayed --- src/yunohost/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yunohost/__init__.py b/src/yunohost/__init__.py index 810d6127a..ce505ffd9 100644 --- a/src/yunohost/__init__.py +++ b/src/yunohost/__init__.py @@ -99,6 +99,7 @@ def init_logging(interface="cli", if interface != "api": configure_logging({ 'version': 1, + 'main_logger': "yunohost", 'disable_existing_loggers': True, 'formatters': { 'tty-debug': { From 2ee08189bad784953486a88e9db3b66b912c0b7e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 28 Aug 2020 15:06:28 +0200 Subject: [PATCH 154/262] Don't log helper loading because hmpf --- data/helpers | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/helpers b/data/helpers index a56a6a57a..5ec562f61 100644 --- a/data/helpers +++ b/data/helpers @@ -1,7 +1,8 @@ # -*- shell-script -*- -# TODO : use --regex to validate against a namespace +set +x for helper in $(run-parts --list /usr/share/yunohost/helpers.d 2>/dev/null) ; do [ -r $helper ] && . $helper || true done +set -x From f5acbffb5cb62de21095656b65ba18572e52831c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 28 Aug 2020 17:12:07 +0200 Subject: [PATCH 155/262] Add a diagnosis of processes rencently killed by oom_reaper --- data/hooks/diagnosis/50-systemresources.py | 46 ++++++++++++++++++++++ locales/en.json | 1 + 2 files changed, 47 insertions(+) diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index 682fb897f..6960b9bf0 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -1,6 +1,9 @@ #!/usr/bin/env python import os import psutil +import subprocess +import datetime +import re from yunohost.diagnosis import Diagnoser @@ -96,6 +99,49 @@ class SystemResourcesDiagnoser(Diagnoser): yield item + # + # Recent kills by oom_reaper + # + + kills_count = self.recent_kills_by_oom_reaper() + if kills_count: + kills_summary = "\n".join(["%s (x%s)" % (proc, count) for proc, count in kills_count]) + + yield dict(meta={"test": "oom_reaper"}, + status="WARNING", + summary="diagnosis_processes_killed_by_oom_reaper", + data={"kills_summary": kills_summary}) + + + def recent_kills_by_oom_reaper(self): + if not os.path.exists("/var/log/kern.log"): + return [] + + def analyzed_kern_log(): + + cmd = 'tail -n 10000 /var/log/kern.log | grep "oom_reaper: reaped process"' + out = subprocess.check_output(cmd, shell=True) + lines = out.strip().split("\n") + + now = datetime.datetime.now() + + for line in reversed(lines): + # Lines look like : + # Aug 25 18:48:21 yolo kernel: [ 9623.613667] oom_reaper: reaped process 11509 (uwsgi), now anon-rss:0kB, file-rss:0kB, shmem-rss:328kB + date_str = str(now.year) + " " + " ".join(line.split()[:3]) + date = datetime.datetime.strptime(date_str, '%Y %b %d %H:%M:%S') + diff = now - date + if diff.days >= 1: + break + process_killed = re.search(r"\(.*\)", line).group().strip("()") + yield process_killed + + processes = list(analyzed_kern_log()) + kills_count = [(p, len([p_ for p_ in processes if p_ == p])) for p in set(processes)] + kills_count = sorted(kills_count, key=lambda p: p[1], reverse=True) + + return kills_count + def human_size(bytes_): # Adapted from https://stackoverflow.com/a/1094933 diff --git a/locales/en.json b/locales/en.json index e180bf828..83cc84442 100644 --- a/locales/en.json +++ b/locales/en.json @@ -262,6 +262,7 @@ "diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the difference with the command line using yunohost tools regen-conf nginx --dry-run --with-diff and if you're ok, apply the changes with yunohost tools regen-conf nginx --force.", "diagnosis_unknown_categories": "The following categories are unknown: {categories}", "diagnosis_never_ran_yet": "It looks like this server was setup recently and there's no diagnosis report to show yet. You should start by running a full diagnosis, either from the webadmin or using 'yunohost diagnosis run' from the command line.", + "diagnosis_processes_killed_by_oom_reaper": "Some processes were recently killed by the system because it ran out of memory. This is typically symptomatic of a lack of memory on the system or of a process that ate up to much memory. Summary of the processes killed:\n{kills_summary}", "domain_cannot_remove_main": "You cannot remove '{domain:s}' since it's the main domain, you first need to set another domain as the main domain using 'yunohost domain main-domain -n '; here is the list of candidate domains: {other_domains:s}", "domain_cannot_add_xmpp_upload": "You cannot add domains starting with 'xmpp-upload.'. This kind of name is reserved for the XMPP upload feature integrated in YunoHost.", "domain_cannot_remove_main_add_new_one": "You cannot remove '{domain:s}' since it's the main domain and your only domain, you need to first add another domain using 'yunohost domain add ', then set is as the main domain using 'yunohost domain main-domain -n ' and then you can remove the domain '{domain:s}' using 'yunohost domain remove {domain:s}'.'", From f6a25dcbb3ae3d6f179844c08b5a98c79e3d7fd3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 28 Aug 2020 17:27:56 +0200 Subject: [PATCH 156/262] Bump version number to fix the CI --- debian/changelog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/debian/changelog b/debian/changelog index 92d5b6410..5adf01293 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,7 @@ +yunohost (4.1.0) testing; urgency=low + + - Tmp bump of the version number to fix CI (c.f. Breaks: yunohost(<<4.1) in moulinette) + yunohost (4.0.5) testing; urgency=low - [enh] Update postfix, dovecot, nginx configuration according to Mozilla guidelines (Buster + DH params) (f3a4334a, 89bcf1ba, 2d661737) From ed528d43bfba8e045e808d8bf409c2b0cd0bec29 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 28 Aug 2020 17:55:02 +0200 Subject: [PATCH 157/262] Fix stupid typo in postfix conf T_T --- 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 b4b645138..cfadde356 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -36,7 +36,7 @@ smtpd_tls_mandatory_ciphers = medium # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam.pem # not actually 1024 bits, this applies to all DHE >= 1024 bits -smtpd_tls_dh1024_param_file = /usr/share/yunohost/other/ffdhe2048.pem; +smtpd_tls_dh1024_param_file = /usr/share/yunohost/other/ffdhe2048.pem tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 {% else %} From 5a4c09d2c7f9167015f5175a2ab8f744b96efaf3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 28 Aug 2020 17:58:16 +0200 Subject: [PATCH 158/262] Yunohost 4.1 requires moulinette 4.1 --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 1f2c24678..cf0138d55 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Package: yunohost Essential: yes Architecture: all Depends: ${python:Depends}, ${misc:Depends} - , moulinette (>= 4.0.0~alpha), ssowat (>= 4.0.0~alpha) + , moulinette (>= 4.1), ssowat (>= 4.0) , python-psutil, python-requests, python-dnspython, python-openssl , python-miniupnpc, python-dbus, python-jinja2 , python-toml, python-packaging, python-publicsuffix From 683c06308f1b0cd3a9fb146f0ed340aae3e3ecde Mon Sep 17 00:00:00 2001 From: Titus PiJean Date: Thu, 27 Aug 2020 08:38:10 +0000 Subject: [PATCH 159/262] Translated using Weblate (French) Currently translated at 90.3% (550 of 609 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 72eec2458..ee9ba7b16 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -522,7 +522,7 @@ "diagnosis_display_tip_cli": "Vous pouvez exécuter 'yunohost diagnosis show --issues' pour afficher les problèmes détectés.", "diagnosis_failed_for_category": "Échec du diagnostic pour la catégorie '{category}': {error}", "diagnosis_cache_still_valid": "(Le cache est encore valide pour le diagnostic {category}. Il ne sera pas re-diagnostiqué pour le moment!)", - "diagnosis_ignored_issues": "(+ {nb_ignored} problèmes ignorée(s))", + "diagnosis_ignored_issues": "(+ {nb_ignored} problème(s) ignoré(s))", "diagnosis_found_warnings": "Trouvé {warnings} objet(s) pouvant être amélioré(s) pour {category}.", "diagnosis_everything_ok": "Tout semble bien pour {category} !", "diagnosis_failed": "Échec de la récupération du résultat du diagnostic pour la catégorie '{category}' : {error}", From 74afd229e5b87312ffa1b2e94a14011b782273e0 Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Sat, 22 Aug 2020 15:56:57 +0000 Subject: [PATCH 160/262] Translated using Weblate (Catalan) Currently translated at 100.0% (609 of 609 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 105 ++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 48 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index 254bae8d1..63426c7a6 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -20,15 +20,15 @@ "app_location_unavailable": "Aquesta URL no està disponible o entra en conflicte amb aplicacions ja instal·lades:\n{apps:s}", "app_manifest_invalid": "Hi ha algun error amb el manifest de l'aplicació: {error}", "app_not_correctly_installed": "{app:s} sembla estar mal instal·lada", - "app_not_installed": "No s'ha trobat l'aplicació «{app:s}» en la llista d'aplicacions instal·lades: {all_apps}", + "app_not_installed": "No s'ha trobat {app:s} en la llista d'aplicacions instal·lades: {all_apps}", "app_not_properly_removed": "{app:s} no s'ha pogut suprimir correctament", "app_removed": "{app:s} ha estat suprimida", - "app_requirements_checking": "Verificació dels paquets requerits per {app}…", + "app_requirements_checking": "Verificació dels paquets requerits per {app}...", "app_requirements_unmeet": "No es compleixen els requeriments per {app}, el paquet {pkgname} ({version}) ha de ser {spec}", "app_sources_fetch_failed": "No s'han pogut carregar els fitxers font, l'URL és correcta?", "app_unknown": "Aplicació desconeguda", "app_unsupported_remote_type": "El tipus remot utilitzat per l'aplicació no està suportat", - "app_upgrade_app_name": "Actualitzant {app}…", + "app_upgrade_app_name": "Actualitzant {app}...", "app_upgrade_failed": "No s'ha pogut actualitzar {app:s}: {error}", "app_upgrade_some_app_failed": "No s'han pogut actualitzar algunes aplicacions", "app_upgraded": "S'ha actualitzat {app:s}", @@ -39,19 +39,19 @@ "ask_new_admin_password": "Nova contrasenya d'administrador", "ask_password": "Contrasenya", "backup_abstract_method": "Encara està per implementar aquest mètode de còpia de seguretat", - "backup_app_failed": "No s'ha pogut fer la còpia de seguretat de l'aplicació \"{app:s}\"", - "backup_applying_method_borg": "Enviant tots els fitxers de la còpia de seguretat al repositori borg-backup…", - "backup_applying_method_copy": "Còpia de tots els fitxers a la còpia de seguretat…", - "backup_applying_method_custom": "Crida del mètode de còpia de seguretat personalitzat \"{method:s}\"…", - "backup_applying_method_tar": "Creació de l'arxiu TAR de la còpia de seguretat…", - "backup_archive_app_not_found": "No s'ha pogut trobar l'aplicació «{app:s}» dins l'arxiu de la còpia de seguretat", + "backup_app_failed": "No s'ha pogut fer la còpia de seguretat de {app:s}", + "backup_applying_method_borg": "Enviant tots els fitxers de la còpia de seguretat al repositori borg-backup...", + "backup_applying_method_copy": "Còpia de tots els fitxers a la còpia de seguretat...", + "backup_applying_method_custom": "Crida del mètode de còpia de seguretat personalitzat \"{method:s}\"...", + "backup_applying_method_tar": "Creació de l'arxiu TAR de la còpia de seguretat...", + "backup_archive_app_not_found": "No s'ha pogut trobar {app:s} en l'arxiu de la còpia de seguretat", "backup_archive_broken_link": "No s'ha pogut accedir a l'arxiu de la còpia de seguretat (enllaç invàlid cap a {path:s})", "backup_archive_name_exists": "Ja hi ha una còpia de seguretat amb aquest nom.", "backup_archive_name_unknown": "Còpia de seguretat local \"{name:s}\" desconeguda", "backup_archive_open_failed": "No s'ha pogut obrir l'arxiu de la còpia de seguretat", "backup_archive_system_part_not_available": "La part «{part:s}» del sistema no està disponible en aquesta copia de seguretat", "backup_archive_writing_error": "No es poden afegir els arxius «{source:s}» (anomenats en l'arxiu «{dest:s}») a l'arxiu comprimit de la còpia de seguretat «{archive:s}»", - "backup_ask_for_copying_if_needed": "Voleu fer la còpia de seguretat utilitzant {size:s} MB temporalment? (S'utilitza aquest mètode ja que alguns dels fitxers no s'han pogut preparar utilitzar un mètode més eficient.)", + "backup_ask_for_copying_if_needed": "Voleu fer la còpia de seguretat utilitzant {size:s}MB temporalment? (S'utilitza aquest mètode ja que alguns dels fitxers no s'han pogut preparar utilitzar un mètode més eficient.)", "backup_borg_not_implemented": "El mètode de còpia de seguretat Borg encara no està implementat", "backup_cant_mount_uncompress_archive": "No es pot carregar l'arxiu descomprimit com a protegit contra escriptura", "backup_cleaning_failed": "No s'ha pogut netejar el directori temporal de la còpia de seguretat", @@ -60,14 +60,14 @@ "backup_created": "S'ha creat la còpia de seguretat", "aborting": "Avortant.", "app_not_upgraded": "L'aplicació «{failed_app}» no s'ha pogut actualitzar, i com a conseqüència s'ha cancel·lat l'actualització de les següents aplicacions: {apps}", - "app_start_install": "instal·lant l'aplicació «{app}»…", - "app_start_remove": "Eliminant l'aplicació «{app}»…", - "app_start_backup": "Recuperant els fitxers pels que s'ha de fer una còpia de seguretat per «{app}»…", - "app_start_restore": "Recuperant l'aplicació «{app}»…", + "app_start_install": "instal·lant {app}...", + "app_start_remove": "Eliminant {app}...", + "app_start_backup": "Recuperant els fitxers pels que s'ha de fer una còpia de seguretat per «{app}»...", + "app_start_restore": "Recuperant {app}...", "app_upgrade_several_apps": "S'actualitzaran les següents aplicacions: {apps}", "ask_new_domain": "Nou domini", "ask_new_path": "Nou camí", - "backup_actually_backuping": "Creant un arxiu de còpia de seguretat a partir dels fitxers recuperats…", + "backup_actually_backuping": "Creant un arxiu de còpia de seguretat a partir dels fitxers recuperats...", "backup_creation_failed": "No s'ha pogut crear l'arxiu de la còpia de seguretat", "backup_csv_addition_failed": "No s'han pogut afegir fitxers per a fer-ne la còpia de seguretat al fitxer CSV", "backup_csv_creation_failed": "No s'ha pogut crear el fitxer CSV necessari per a la restauració", @@ -81,7 +81,7 @@ "backup_method_copy_finished": "La còpia de la còpia de seguretat ha acabat", "backup_method_custom_finished": "El mètode de còpia de seguretat personalitzat \"{method:s}\" ha acabat", "backup_method_tar_finished": "S'ha creat l'arxiu de còpia de seguretat TAR", - "backup_mount_archive_for_restore": "Preparant l'arxiu per la restauració…", + "backup_mount_archive_for_restore": "Preparant l'arxiu per la restauració...", "good_practices_about_user_password": "Esteu a punt de definir una nova contrasenya d'usuari. 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).", "password_listed": "Aquesta contrasenya és una de les més utilitzades en el món. Si us plau utilitzeu-ne una més única.", "password_too_simple_1": "La contrasenya ha de tenir un mínim de 8 caràcters", @@ -95,11 +95,11 @@ "backup_output_directory_required": "Heu d'especificar un directori de sortida per la còpia de seguretat", "backup_output_symlink_dir_broken": "El directori del arxiu «{path:s}» es un enllaç simbòlic trencat. Pot ser heu oblidat muntar, tornar a muntar o connectar el mitja d'emmagatzematge al que apunta.", "backup_php5_to_php7_migration_may_fail": "No s'ha pogut convertir l'arxiu per suportar PHP 7, pot ser que no es puguin restaurar les vostres aplicacions PHP (raó: {error:s})", - "backup_running_hooks": "Executant els scripts de la còpia de seguretat…", + "backup_running_hooks": "Executant els scripts de la còpia de seguretat...", "backup_system_part_failed": "No s'ha pogut fer la còpia de seguretat de la part \"{part:s}\" del sistema", "backup_unable_to_organize_files": "No s'ha pogut utilitzar el mètode ràpid per organitzar els fitxers dins de l'arxiu", "backup_with_no_backup_script_for_app": "L'aplicació «{app:s}» no té un script de còpia de seguretat. Serà ignorat.", - "backup_with_no_restore_script_for_app": "L'aplicació «{app:s}» no té un script de restauració, no podreu restaurar automàticament la còpia de seguretat d'aquesta aplicació.", + "backup_with_no_restore_script_for_app": "{app:s} no té un script de restauració, no podreu restaurar automàticament la còpia de seguretat d'aquesta aplicació.", "certmanager_acme_not_configured_for_domain": "No s'ha pogut executar el ACME challenge pel domini {domain} en aquests moments ja que a la seva configuració de nginx li manca el codi corresponent… Assegureu-vos que la configuració nginx està actualitzada utilitzant «yunohost tools regen-conf nginx --dry-run --with-diff».", "certmanager_attempt_to_renew_nonLE_cert": "El certificat pel domini «{domain:s}» no ha estat emès per Let's Encrypt. No es pot renovar automàticament!", "certmanager_attempt_to_renew_valid_cert": "El certificat pel domini «{domain:s}» està a punt de caducar! (Utilitzeu --force si sabeu el que esteu fent)", @@ -109,7 +109,7 @@ "certmanager_cert_install_success_selfsigned": "S'ha instal·lat correctament un certificat auto-signat pel domini «{domain:s}»", "certmanager_cert_renew_success": "S'ha renovat correctament el certificat Let's Encrypt pel domini «{domain:s}»", "certmanager_cert_signing_failed": "No s'ha pogut firmar el nou certificat", - "certmanager_certificate_fetching_or_enabling_failed": "Sembla que utilitzar el nou certificat per {domain:s} ha fallat…", + "certmanager_certificate_fetching_or_enabling_failed": "Sembla que utilitzar el nou certificat per {domain:s} ha fallat...", "certmanager_conflicting_nginx_file": "No s'ha pogut preparar el domini per al desafiament ACME: l'arxiu de configuració NGINX {filepath:s} entra en conflicte i s'ha d'eliminar primer", "certmanager_couldnt_fetch_intermediate_cert": "S'ha exhaurit el temps d'esperar al intentar recollir el certificat intermedi des de Let's Encrypt. La instal·lació/renovació del certificat s'ha cancel·lat - torneu a intentar-ho més tard.", "certmanager_domain_cert_not_selfsigned": "El certificat pel domini {domain:s} no és auto-signat Esteu segur de voler canviar-lo? (Utilitzeu «--force» per fer-ho)", @@ -149,16 +149,16 @@ "dyndns_could_not_check_available": "No s'ha pogut verificar la disponibilitat de {domain:s} a {provider:s}.", "dyndns_ip_update_failed": "No s'ha pogut actualitzar l'adreça IP al DynDNS", "dyndns_ip_updated": "S'ha actualitzat l'adreça IP al DynDNS", - "dyndns_key_generating": "S'està generant la clau DNS… això pot trigar una estona.", + "dyndns_key_generating": "S'està generant la clau DNS... això pot trigar una estona.", "dyndns_key_not_found": "No s'ha trobat la clau DNS pel domini", "dyndns_no_domain_registered": "No hi ha cap domini registrat amb DynDNS", "dyndns_registered": "S'ha registrat el domini DynDNS", "dyndns_registration_failed": "No s'ha pogut registrar el domini DynDNS: {error:s}", "dyndns_domain_not_provided": "El proveïdor de DynDNS {provider:s} no pot oferir el domini {domain:s}.", "dyndns_unavailable": "El domini {domain:s} no està disponible.", - "executing_command": "Execució de l'ordre « {command:s} »…", - "executing_script": "Execució de l'script « {script:s} »…", - "extracting": "Extracció en curs…", + "executing_command": "Execució de l'ordre « {command:s} »...", + "executing_script": "Execució de l'script « {script:s} »...", + "extracting": "Extracció en curs...", "dyndns_cron_installed": "S'ha creat la tasca cron pel DynDNS", "dyndns_cron_remove_failed": "No s'ha pogut eliminar la tasca cron per a DynDNS: {error}", "dyndns_cron_removed": "S'ha eliminat la tasca cron pel DynDNS", @@ -288,10 +288,10 @@ "migration_0009_not_needed": "Sembla que ja s'ha fet aquesta migració… (?) Ometent.", "migrations_cant_reach_migration_file": "No s'ha pogut accedir als fitxers de migració al camí «%s»", "migrations_list_conflict_pending_done": "No es pot utilitzar «--previous» i «--done» al mateix temps.", - "migrations_loading_migration": "Carregant la migració {id}…", + "migrations_loading_migration": "Carregant la migració {id}...", "migrations_migration_has_failed": "La migració {id} ha fallat, cancel·lant. Error: {exception}", "migrations_no_migrations_to_run": "No hi ha cap migració a fer", - "migrations_skip_migration": "Saltant migració {id}…", + "migrations_skip_migration": "Saltant migració {id}...", "migrations_to_be_ran_manually": "La migració {id} s'ha de fer manualment. Aneu a Eines → Migracions a la interfície admin, o executeu «yunohost tools migrations migrate».", "migrations_need_to_accept_disclaimer": "Per fer la migració {id}, heu d'acceptar aquesta clàusula de no responsabilitat:\n---\n{disclaimer}\n---\nSi accepteu fer la migració, torneu a executar l'ordre amb l'opció «--accept-disclaimer».", "no_internet_connection": "El servidor no està connectat a Internet", @@ -325,9 +325,9 @@ "regenconf_would_be_updated": "La configuració hagués estat actualitzada per la categoria «{category}»", "regenconf_dry_pending_applying": "Verificació de la configuració pendent que s'hauria d'haver aplicat per la categoria «{category}»…", "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}»…", + "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 l'aplicació «{app:s}»", + "restore_app_failed": "No s'ha pogut restaurar {app: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}]", @@ -390,7 +390,7 @@ "ssowat_conf_updated": "S'ha actualitzat la configuració SSOwat", "system_upgraded": "S'ha actualitzat el sistema", "system_username_exists": "El nom d'usuari ja existeix en la llista d'usuaris de sistema", - "this_action_broke_dpkg": "Aquesta acció a trencat dpkg/APT (els gestors de paquets del sistema)… Podeu intentar resoldre el problema connectant-vos amb SSH i executant «sudo apt install --fix-broken» i/o «sudo dpkg --configure -a».", + "this_action_broke_dpkg": "Aquesta acció a trencat dpkg/APT (els gestors de paquets del sistema)... Podeu intentar resoldre el problema connectant-vos amb SSH i executant «sudo apt install --fix-broken» i/o «sudo dpkg --configure -a».", "tools_upgrade_at_least_one": "Especifiqueu «--apps», o «--system»", "tools_upgrade_cant_both": "No es poden actualitzar tant el sistema com les aplicacions al mateix temps", "tools_upgrade_cant_hold_critical_packages": "No es poden mantenir els paquets crítics…", @@ -400,15 +400,15 @@ "tools_upgrade_special_packages": "Actualitzant els paquets «especials» (relacionats amb YunoHost)…", "tools_upgrade_special_packages_explanation": "Aquesta actualització especial continuarà en segon pla. No comenceu cap altra acció al servidor en els pròxims ~10 minuts (depèn de la velocitat del maquinari). Després d'això, pot ser que us hagueu de tornar a connectar a la interfície d'administració. Els registres de l'actualització estaran disponibles a Eines → Registres (a la interfície d'administració) o utilitzant «yunohost log list» (des de la línia d'ordres).", "tools_upgrade_special_packages_completed": "Actualització dels paquets YunoHost acabada.\nPremeu [Enter] per tornar a la línia d'ordres", - "unbackup_app": "L'aplicació «{app:s}» no serà guardada", + "unbackup_app": "{app:s} no es guardarà", "unexpected_error": "Hi ha hagut un error inesperat: {error}", "unlimit": "Sense quota", - "unrestore_app": "L'aplicació «{app:s} no serà restaurada", + "unrestore_app": "{app:s} no es restaurarà", "update_apt_cache_failed": "No s'ha pogut actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list, que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}", "update_apt_cache_warning": "Hi ha hagut errors al actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}", - "updating_apt_cache": "Obtenció de les actualitzacions disponibles per als paquets del sistema…", + "updating_apt_cache": "Obtenció de les actualitzacions disponibles per als paquets del sistema...", "upgrade_complete": "Actualització acabada", - "upgrading_packages": "Actualitzant els paquets…", + "upgrading_packages": "Actualitzant els paquets...", "upnp_dev_not_found": "No s'ha trobat cap dispositiu UPnP", "upnp_disabled": "S'ha desactivat UPnP", "upnp_enabled": "S'ha activat UPnP", @@ -426,9 +426,9 @@ "yunohost_ca_creation_failed": "No s'ha pogut crear l'autoritat de certificació", "yunohost_ca_creation_success": "S'ha creat l'autoritat de certificació local.", "yunohost_configured": "YunoHost està configurat", - "yunohost_installing": "Instal·lació de YunoHost…", + "yunohost_installing": "Instal·lació de YunoHost...", "yunohost_not_installed": "YunoHost no està instal·lat correctament. Executeu «yunohost tools postinstall»", - "backup_permission": "Permís de còpia de seguretat per l'aplicació {app:s}", + "backup_permission": "Permís de còpia de seguretat per {app:s}", "group_created": "S'ha creat el grup «{group}»", "group_creation_failed": "No s'ha pogut crear el grup «{group}»: {error}", "group_deleted": "S'ha eliminat el grup «{group}»", @@ -445,11 +445,11 @@ "migration_0011_create_group": "Creant un grup per a cada usuari…", "migration_0011_done": "Migració completada. Ja podeu gestionar grups d'usuaris.", "migration_0011_LDAP_update_failed": "Ha fallat l'actualització de LDAP. Error: {error:s}", - "migration_0011_migrate_permission": "Fent la migració dels permisos de la configuració de les aplicacions a LDAP…", + "migration_0011_migrate_permission": "Fent la migració dels permisos de la configuració de les aplicacions a LDAP...", "migration_0011_migration_failed_trying_to_rollback": "No s'ha pogut fer la migració… s'intenta tornar el sistema a l'estat anterior.", "migration_0011_rollback_success": "S'ha tornat el sistema a l'estat anterior.", - "migration_0011_update_LDAP_database": "Actualitzant la base de dades LDAP…", - "migration_0011_update_LDAP_schema": "Actualitzant l'esquema LDAP…", + "migration_0011_update_LDAP_database": "Actualitzant la base de dades LDAP...", + "migration_0011_update_LDAP_schema": "Actualitzant l'esquema LDAP...", "permission_already_exist": "El permís «{permission:s}» ja existeix", "permission_created": "S'ha creat el permís «{permission:s}»", "permission_creation_failed": "No s'ha pogut crear el permís «{permission}»: {error}", @@ -474,7 +474,7 @@ "migrations_must_provide_explicit_targets": "Heu de proporcionar objectius explícits al utilitzar «--skip» o «--force-rerun»", "migrations_no_such_migration": "No hi ha cap migració anomenada «{id}»", "migrations_pending_cant_rerun": "Aquestes migracions encara estan pendents, així que no es poden tornar a executar: {ids}", - "migrations_running_forward": "Executant la migració {id}…", + "migrations_running_forward": "Executant la migració {id}...", "migrations_success_forward": "Migració {id} completada", "apps_already_up_to_date": "Ja estan actualitzades totes les aplicacions", "dyndns_provider_unreachable": "No s'ha pogut connectar amb el proveïdor DynDNS {provider}: o el vostre YunoHost no està ben connectat a Internet o el servidor dynette està caigut.", @@ -500,7 +500,7 @@ "permission_already_up_to_date": "No s'ha actualitzat el permís perquè la petició d'afegir/eliminar ja corresponent a l'estat actual.", "permission_currently_allowed_for_all_users": "El permís ha el té el grup de tots els usuaris (all_users) a més d'altres grups. Segurament s'hauria de revocar el permís a «all_users» o eliminar els altres grups als que s'ha atribuït.", "permission_require_account": "El permís {permission} només té sentit per als usuaris que tenen un compte, i per tant no es pot activar per als visitants.", - "app_remove_after_failed_install": "Eliminant l'aplicació després que hagi fallat la instal·lació…", + "app_remove_after_failed_install": "Eliminant l'aplicació després que hagi fallat la instal·lació...", "diagnosis_basesystem_ynh_main_version": "El servidor funciona amb YunoHost {main_version} ({repo})", "diagnosis_ram_low": "El sistema només té {available} ({available_percent}%) de memòria RAM disponibles d'un total de {total}. Aneu amb compte.", "diagnosis_swap_none": "El sistema no té swap. Hauríeu de considerar afegir un mínim de {recommended} de swap per evitar situacions en les que el sistema es queda sense memòria.", @@ -596,7 +596,7 @@ "diagnosis_description_web": "Web", "diagnosis_basesystem_hardware_board": "El model de la targeta del servidor és {model}", "diagnosis_basesystem_hardware": "L'arquitectura del maquinari del servidor és {virt} {arch}", - "group_already_exist_on_system_but_removing_it": "El grup {group} ja existeix en els grups del sistema, però YunoHost l'eliminarà…", + "group_already_exist_on_system_but_removing_it": "El grup {group} ja existeix en els grups del sistema, però YunoHost l'eliminarà...", "certmanager_warning_subdomain_dns_record": "El subdomini «{subdomain:s}» no resol a la mateixa adreça IP que «{domain:s}». Algunes funcions no estaran disponibles fins que no s'hagi arreglat i s'hagi regenerat el certificat.", "domain_cannot_add_xmpp_upload": "No podeu afegir dominis començant per «xmpp-upload.». Aquest tipus de nom està reservat per a la funció de pujada de XMPP integrada a YunoHost.", "diagnosis_display_tip": "Per veure els problemes que s'han trobat, podeu anar a la secció de Diagnòstic a la pàgina web d'administració, o utilitzar « yunohost diagnostic show --issues » a la línia de comandes.", @@ -637,9 +637,9 @@ "diagnosis_mail_fcrdns_nok_alternatives_4": "Alguns proveïdors no permeten configurar el DNS invers (o aquesta funció pot no funcionar…). Si teniu problemes a causa d'això, considereu les solucions següents:
- Alguns proveïdors d'accés a internet (ISP) donen l'alternativa de utilitzar un relay de servidor de correu electrònic tot i que implica que el relay podrà espiar el trànsit de correus electrònics.
- Una alternativa respectuosa amb la privacitat és utilitzar una VPN *amb una IP pública dedicada* per sobrepassar aquest tipus de limitacions. Mireu https://yunohost.org/#/vpn_advantage
- O es pot canviar a un proveïdor diferent", "diagnosis_mail_fcrdns_nok_alternatives_6": "Alguns proveïdors no permeten configurar el vostre DNS invers (o la funció no els hi funciona…). Si el vostre DNS invers està correctament configurat per IPv4, podeu intentar deshabilitar l'ús de IPv6 per a enviar correus electrònics utilitzant yunohost settings set smtp.allow_ipv6 -v off. Nota: aquesta última solució implica que no podreu enviar o rebre correus electrònics cap a els pocs servidors que hi ha que només tenen IPv-6.", "diagnosis_http_hairpinning_issue_details": "Això és probablement a causa del router del vostre proveïdor d'accés a internet. El que fa, que gent de fora de la xarxa local pugui accedir al servidor sense problemes, però no la gent de dins la xarxa local (com vostè probablement) quan s'utilitza el nom de domini o la IP global. Podreu segurament millorar la situació fent una ullada a https://yunohost.org/dns_local_network", - "backup_archive_cant_retrieve_info_json": "No s'ha pogut carregar la informació de l'arxiu «{archive}»… No s'ha pogut obtenir el fitxer info.json (o no és un fitxer json vàlid).", + "backup_archive_cant_retrieve_info_json": "No s'ha pogut carregar la informació de l'arxiu «{archive}»... No s'ha pogut obtenir el fitxer info.json (o no és un fitxer json vàlid).", "backup_archive_corrupted": "Sembla que l'arxiu de la còpia de seguretat «{archive}» està corromput : {error}", - "certmanager_domain_not_diagnosed_yet": "Encara no hi ha cap resultat de diagnòstic per al domini %s. Torneu a executar el diagnòstic per a les categories «Registres DNS» i «Web» en la secció de diagnòstic per comprovar que el domini està preparat per a Let's Encrypt. (O si sabeu el que esteu fent, utilitzant «--no-checks» per deshabilitar les comprovacions.)", + "certmanager_domain_not_diagnosed_yet": "Encara no hi ha cap resultat de diagnòstic per al domini {domain}. Torneu a executar el diagnòstic per a les categories «Registres DNS» i «Web» en la secció de diagnòstic per comprovar que el domini està preparat per a Let's Encrypt. (O si sabeu el que esteu fent, utilitzant «--no-checks» per deshabilitar les comprovacions.)", "diagnosis_ip_no_ipv6_tip": "Utilitzar una IPv6 no és obligatori per a que funcioni el servidor, però és millor per la salut d'Internet en conjunt. La IPv6 hauria d'estar configurada automàticament pel sistema o pel proveïdor si està disponible. Si no és el cas, pot ser necessari configurar alguns paràmetres més de forma manual tal i com s'explica en la documentació disponible aquí: https://yunohost.org/#/ipv6. Si no podeu habilitar IPv6 o us sembla massa tècnic, podeu ignorar aquest avís sense problemes.", "diagnosis_domain_expiration_not_found": "No s'ha pogut comprovar la data d'expiració d'alguns dominis", "diagnosis_domain_not_found_details": "El domini {domain} no existeix en la base de dades WHOIS o ha expirat!", @@ -652,20 +652,29 @@ "restore_already_installed_apps": "No s'han pogut restaurar les següents aplicacions perquè ja estan instal·lades: {apps}", "app_packaging_format_not_supported": "No es pot instal·lar aquesta aplicació ja que el format del paquet no és compatible amb la versió de YunoHost del sistema. Hauríeu de considerar actualitzar el sistema.", "diagnosis_dns_try_dyndns_update_force": "La configuració DNS d'aquest domini hauria de ser gestionada automàticament per YunoHost. Si aquest no és el cas, podeu intentar forçar-ne l'actualització utilitzant yunohost dyndns update --force.", - "migration_0015_cleaning_up": "Netejant la memòria cau i els paquets que ja no són necessaris…", - "migration_0015_specific_upgrade": "Començant l'actualització dels paquets del sistema que s'han d'actualitzar de forma independent…", + "migration_0015_cleaning_up": "Netejant la memòria cau i els paquets que ja no són necessaris...", + "migration_0015_specific_upgrade": "Començant l'actualització dels paquets del sistema que s'han d'actualitzar de forma independent...", "migration_0015_modified_files": "Tingueu en compte que s'han trobat els següents fitxers que es van modificar manualment i podria ser que es sobreescriguin durant l'actualització: {manually_modified_files}", "migration_0015_problematic_apps_warning": "Tingueu en compte que s'han trobat les següents aplicacions que podrien ser problemàtiques. Sembla que aquestes aplicacions no s'han instal·lat des del catàleg d'aplicacions de YunoHost, o no estan marcades com «funcionant». En conseqüència, no es pot garantir que segueixin funcionant després de l'actualització: {problematic_apps}", "migration_0015_general_warning": "Tingueu en compte que aquesta migració és una operació delicada. L'equip de YunoHost ha fet tots els possibles per revisar i testejar, però tot i això podria ser que la migració trenqui alguna part del sistema o algunes aplicacions.\n\nPer tant, està recomana:\n - Fer una còpia de seguretat de totes les dades o aplicacions crítiques. Més informació a https://yunohost.org/backup;\n - Ser pacient un cop comenci la migració: en funció de la connexió Internet i del maquinari, podria estar unes hores per actualitzar-ho tot.", "migration_0015_system_not_fully_up_to_date": "El sistema no està completament al dia. Heu de fer una actualització normal abans de fer la migració a Buster.", "migration_0015_not_enough_free_space": "Hi ha poc espai lliure a /var/! HI hauria d'haver un mínim de 1GB lliure per poder fer aquesta migració.", "migration_0015_not_stretch": "La distribució actual de Debian no és Stretch!", - "migration_0015_yunohost_upgrade": "Començant l'actualització del nucli de YunoHost…", + "migration_0015_yunohost_upgrade": "Començant l'actualització del nucli de YunoHost...", "migration_0015_still_on_stretch_after_main_upgrade": "Alguna cosa ha anat malament durant la actualització principal, sembla que el sistema encara està en Debian Stretch", - "migration_0015_main_upgrade": "Començant l'actualització principal…", - "migration_0015_patching_sources_list": "Apedaçament de source.lists…", + "migration_0015_main_upgrade": "Començant l'actualització principal...", + "migration_0015_patching_sources_list": "Apedaçament de source.lists...", "migration_0015_start": "Començant la migració a Buster", "migration_description_0015_migrate_to_buster": "Actualitza els sistema a Debian Buster i YunoHost 4.x", "regenconf_need_to_explicitly_specify_ssh": "La configuració ssh ha estat modificada manualment, però heu d'especificar explícitament la categoria «ssh» amb --force per fer realment els canvis.", - "migration_0015_weak_certs": "S'han trobat els següents certificats que encara utilitzen algoritmes de signatura febles i s'han d'actualitzar per a ser compatibles amb la propera versió de nginx: {certs}" + "migration_0015_weak_certs": "S'han trobat els següents certificats que encara utilitzen algoritmes de signatura febles i s'han d'actualitzar per a ser compatibles amb la propera versió de nginx: {certs}", + "service_description_php7.3-fpm": "Executa aplicacions escrites en PHP amb NGINX", + "migration_0018_failed_to_reset_legacy_rules": "No s'ha pogut restaurar les regles legacy iptables: {error}", + "migration_0018_failed_to_migrate_iptables_rules": "No s'ha pogut migrar les regles legacy iptables a nftables: {error}", + "migration_0017_not_enough_space": "Feu suficient espai disponible en {path} per a realitzar la migració.", + "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 està instal·lat, però postgreSQL 11 no? Potser que hagi passat alguna cosa rara en aquest sistema :(...", + "migration_0017_postgresql_96_not_installed": "PostgreSQL no està instal·lat en aquest sistema. No s'ha de realitzar cap operació.", + "migration_description_0018_xtable_to_nftable": "Migrar les regles del trànsit de xarxa al nou sistema nftable", + "migration_description_0017_postgresql_9p6_to_11": "Migrar les bases de dades de PosrgreSQL 9.6 a 11", + "migration_description_0016_php70_to_php73_pools": "Migrar els fitxers de configuració «pool» php7.0-fpm a php7.3" } From c29b816efcad64f73fbd08ccec3c71269711e857 Mon Sep 17 00:00:00 2001 From: Christian Wehrli Date: Mon, 24 Aug 2020 18:30:22 +0000 Subject: [PATCH 161/262] Translated using Weblate (German) Currently translated at 39.2% (239 of 609 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 79 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/locales/de.json b/locales/de.json index fab9eaf2c..b99516205 100644 --- a/locales/de.json +++ b/locales/de.json @@ -11,7 +11,7 @@ "app_id_invalid": "Falsche App-ID", "app_install_files_invalid": "Diese Dateien können nicht installiert werden", "app_manifest_invalid": "Mit dem App-Manifest stimmt etwas nicht: {error}", - "app_not_installed": "Die App {app:s} konnte nicht in der Liste installierter Apps gefunden werden: {all_apps}", + "app_not_installed": "{app:s} konnte nicht in der Liste installierter Apps gefunden werden: {all_apps}", "app_removed": "{app:s} wurde entfernt", "app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden, ist die URL korrekt?", "app_unknown": "Unbekannte App", @@ -23,8 +23,8 @@ "ask_main_domain": "Hauptdomain", "ask_new_admin_password": "Neues Verwaltungskennwort", "ask_password": "Passwort", - "backup_app_failed": "Konnte keine Sicherung für die App '{app:s}' erstellen", - "backup_archive_app_not_found": "App '{app:s}' konnte in keiner Datensicherung gefunden werden", + "backup_app_failed": "Konnte keine Sicherung für {app:s} erstellen", + "backup_archive_app_not_found": "{app:s} konnte in keiner Datensicherung gefunden werden", "backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits.", "backup_archive_name_unknown": "Unbekanntes lokale Datensicherung mit Namen '{name:s}' gefunden", "backup_archive_open_failed": "Kann Sicherungsarchiv nicht öfnen", @@ -38,7 +38,7 @@ "backup_output_directory_forbidden": "Wähle ein anderes Ausgabeverzeichnis. Datensicherungen können nicht in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var oder in Unterordnern von /home/yunohost.backup/archives erstellt werden", "backup_output_directory_not_empty": "Der gewählte Ausgabeordner sollte leer sein", "backup_output_directory_required": "Für die Datensicherung muss ein Zielverzeichnis angegeben werden", - "backup_running_hooks": "Datensicherunghook wird ausgeführt…", + "backup_running_hooks": "Datensicherunghook wird ausgeführt...", "custom_app_url_required": "Es muss eine URL angegeben werden, um deine benutzerdefinierte App {app:s} zu aktualisieren", "domain_cert_gen_failed": "Zertifikat konnte nicht erzeugt werden", "domain_created": "Die Domain wurde angelegt", @@ -78,7 +78,7 @@ "iptables_unavailable": "iptables kann nicht verwendet werden. Du befindest dich entweder in einem Container oder es wird nicht vom Kernel unterstützt", "ldap_initialized": "LDAP wurde initialisiert", "mail_alias_remove_failed": "E-Mail Alias '{mail:s}' konnte nicht entfernt werden", - "mail_domain_unknown": "Unbekannte Mail Domain '{domain:s}'", + "mail_domain_unknown": "Die Domäne '{domain:s}' dieser E-Mail-Adresse ist ungültig. Wähle bitte eine Domäne, welche durch diesen Server verwaltet wird.", "mail_forward_remove_failed": "Mailweiterleitung '{mail:s}' konnte nicht entfernt werden", "main_domain_change_failed": "Die Hauptdomain konnte nicht geändert werden", "main_domain_changed": "Die Hauptdomain wurde geändert", @@ -154,7 +154,7 @@ "backup_creation_failed": "Konnte Backup-Archiv nicht erstellen", "pattern_positive_number": "Muss eine positive Zahl sein", "app_not_correctly_installed": "{app:s} scheint nicht korrekt installiert zu sein", - "app_requirements_checking": "Überprüfe notwendige Pakete für {app}…", + "app_requirements_checking": "Überprüfe notwendige Pakete für {app}...", "app_requirements_unmeet": "Anforderungen für {app} werden nicht erfüllt, das Paket {pkgname} ({version}) muss {spec} sein", "app_unsupported_remote_type": "Für die App wurde ein nicht unterstützer Steuerungstyp verwendet", "backup_archive_broken_link": "Auf das Backup-Archiv konnte nicht zugegriffen werden (ungültiger Link zu {path:s})", @@ -166,8 +166,8 @@ "package_unknown": "Unbekanntes Paket '{pkgname}'", "certmanager_attempt_to_replace_valid_cert": "Du versuchst gerade eine richtiges und gültiges Zertifikat der Domain {domain:s} zu überschreiben! (Benutze --force , um diese Nachricht zu umgehen)", "certmanager_domain_unknown": "Unbekannte Domain '{domain:s}'", - "certmanager_domain_cert_not_selfsigned": "Das Zertifikat der Domain {domain:s} ist kein selbstsigniertes Zertifikat. Bist du dir sicher, dass du es ersetzen willst? (Benutze dafür '--force')", - "certmanager_certificate_fetching_or_enabling_failed": "Die Aktivierung des neuen Zertifikats für die Domain {domain:s} ist fehlgeschlagen…", + "certmanager_domain_cert_not_selfsigned": "Das Zertifikat der Domain {domain:s} ist kein selbstsigniertes Zertifikat. Sind Sie sich sicher, dass Sie es ersetzen wollen? (Benutzen Sie dafür '--force')", + "certmanager_certificate_fetching_or_enabling_failed": "Die Aktivierung des neuen Zertifikats für die {domain:s} ist fehlgeschlagen...", "certmanager_attempt_to_renew_nonLE_cert": "Das Zertifikat der Domain '{domain:s}' wurde nicht von Let's Encrypt ausgestellt. Es kann nicht automatisch erneuert werden!", "certmanager_attempt_to_renew_valid_cert": "Das Zertifikat der Domain {domain:s} läuft nicht in Kürze ab! (Benutze --force um diese Nachricht zu umgehen)", "certmanager_domain_http_not_working": "Es scheint so, dass die Domain {domain:s} nicht über HTTP erreicht werden kann. Bitte überprüfe, ob deine DNS und nginx Konfiguration in Ordnung ist", @@ -194,15 +194,15 @@ "app_change_url_identical_domains": "Die alte und neue domain/url_path sind identisch: ('{domain:s} {path:s}'). Es gibt nichts zu tun.", "app_already_up_to_date": "{app:s} ist bereits aktuell", "backup_abstract_method": "Diese Backup-Methode wird noch nicht unterstützt", - "backup_applying_method_tar": "Erstellen des Backup-tar Archives…", - "backup_applying_method_copy": "Kopiere alle Dateien ins Backup…", + "backup_applying_method_tar": "Erstellen des Backup-tar Archives...", + "backup_applying_method_copy": "Kopiere alle Dateien ins Backup...", "app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modifikation. Vielleicht sollte sie aktualisiert werden.", "app_location_unavailable": "Diese URL ist nicht verfügbar oder wird von einer installierten Anwendung genutzt:\n{apps:s}", - "backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf…", + "backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf...", "backup_archive_system_part_not_available": "Der System-Teil '{part:s}' ist in diesem Backup nicht enthalten", "backup_archive_writing_error": "Die Dateien '{source:s} (im Ordner '{dest:s}') konnten nicht in das komprimierte Archiv-Backup '{archive:s}' hinzugefügt werden", "app_change_url_success": "{app:s} URL ist nun {domain:s}{path:s}", - "backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository…", + "backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository...", "global_settings_bad_type_for_setting": "Falscher Typ für Einstellung {setting:s}. Empfangen: {received_type:s}, aber erwartet: {expected_type:s}", "global_settings_bad_choice_for_enum": "Falsche Wahl für die Einstellung {setting:s}. Habe '{choice:s}' erhalten, aber es stehen nur folgende Auswahlmöglichkeiten zur Verfügung: {available_choices:s}", "file_does_not_exist": "Die Datei {path:s} existiert nicht.", @@ -215,13 +215,13 @@ "confirm_app_install_thirdparty": "WARNUNG! Das Installieren von Anwendungen von Drittanbietern kann die Integrität und Sicherheit Deines Systems beeinträchtigen. Du solltest es wahrscheinlich NICHT installieren, es sei denn, Du weisst, was Du tust. Bist du bereit, dieses Risiko einzugehen? [{answers:s}]", "confirm_app_install_danger": "WARNUNG! Diese Anwendung ist noch experimentell (wenn nicht ausdrücklich \"not working\"/\"nicht funktionsfähig\")! Du solltest es wahrscheinlich NICHT installieren, es sei denn, du weißt, was du tust. Es wird keine Unterstützung geleistet, falls diese Anwendung nicht funktioniert oder dein System zerstört... Falls du bereit bist, dieses Risiko einzugehen, tippe '{answers:s}'", "confirm_app_install_warning": "Warnung: Diese Anwendung funktioniert möglicherweise, ist jedoch nicht gut in YunoHost integriert. Einige Funktionen wie Single Sign-On und Backup / Restore sind möglicherweise nicht verfügbar. Trotzdem installieren? [{answers:s}] ", - "backup_with_no_restore_script_for_app": "Die App {app:s} hat kein Wiederherstellungsskript. Das Backup dieser App kann nicht automatisch wiederhergestellt werden.", + "backup_with_no_restore_script_for_app": "{app:s} hat kein Wiederherstellungsskript. Das Backup dieser App kann nicht automatisch wiederhergestellt werden.", "backup_with_no_backup_script_for_app": "Die App {app:s} hat kein Sicherungsskript. Ignoriere es.", "backup_unable_to_organize_files": "Dateien im Archiv konnten nicht mit der schnellen Methode organisiert werden", "backup_system_part_failed": "Der Systemteil '{part:s}' konnte nicht gesichert werden", - "backup_permission": "Sicherungsberechtigung für App {app:s}", + "backup_permission": "Sicherungsberechtigung für {app:s}", "backup_output_symlink_dir_broken": "Ihr Archivverzeichnis '{path:s}' ist ein fehlerhafter Symlink. Vielleicht haben Sie vergessen, das Speichermedium, auf das er verweist, neu zu mounten oder einzustecken.", - "backup_mount_archive_for_restore": "Archiv für Wiederherstellung vorbereiten…", + "backup_mount_archive_for_restore": "Archiv für Wiederherstellung vorbereiten...", "backup_method_tar_finished": "Tar-Backup-Archiv erstellt", "backup_method_custom_finished": "Benutzerdefinierte Sicherungsmethode '{method:s}' beendet", "backup_method_copy_finished": "Sicherungskopie beendet", @@ -231,17 +231,17 @@ "backup_csv_creation_failed": "Die zur Wiederherstellung erforderliche CSV-Datei kann nicht erstellt werden", "backup_couldnt_bind": "{src:s} konnte nicht an {dest:s} angebunden werden.", "backup_borg_not_implemented": "Die Borg-Sicherungsmethode ist noch nicht implementiert", - "backup_ask_for_copying_if_needed": "Möchten Sie die Sicherung mit {size:s} MB temporär durchführen? (Dieser Weg wird verwendet, da einige Dateien nicht mit einer effizienteren Methode vorbereitet werden konnten.)", - "backup_actually_backuping": "Erstellt ein Backup-Archiv aus den gesammelten Dateien …", + "backup_ask_for_copying_if_needed": "Möchten Sie die Sicherung mit {size:s}MB temporär durchführen? (Dieser Weg wird verwendet, da einige Dateien nicht mit einer effizienteren Methode vorbereitet werden konnten.)", + "backup_actually_backuping": "Erstellt ein Backup-Archiv aus den gesammelten Dateien...", "ask_new_path": "Neuer Pfad", "ask_new_domain": "Neue Domain", "app_upgrade_some_app_failed": "Einige Anwendungen können nicht aktualisiert werden", - "app_upgrade_app_name": "{app} wird jetzt aktualisiert…", + "app_upgrade_app_name": "{app} wird jetzt aktualisiert...", "app_upgrade_several_apps": "Die folgenden Apps werden aktualisiert: {apps}", - "app_start_restore": "Anwendung {app} wird wiederhergestellt…", - "app_start_backup": "Sammeln von Dateien, die für {app} gesichert werden sollen…", - "app_start_remove": "Anwendung {app} wird entfernt…", - "app_start_install": "Anwendung {app} wird installiert…", + "app_start_restore": "{app} wird wiederhergestellt...", + "app_start_backup": "Sammeln von Dateien, die für {app} gesichert werden sollen...", + "app_start_remove": "{app} wird entfernt...", + "app_start_install": "{app} wird installiert...", "app_not_upgraded": "Die App '{failed_app}' konnte nicht aktualisiert werden. Infolgedessen wurden die folgenden App-Upgrades abgebrochen: {apps}", "app_make_default_location_already_used": "Die App \"{app}\" kann nicht als Standard für die Domain \"{domain}\" festgelegt werden. Sie wird bereits von der App \"{other_app}\" verwendet", "aborting": "Breche ab.", @@ -300,7 +300,7 @@ "app_full_domain_unavailable": "Es tut uns leid, aber diese Anwendung erfordert die Installation auf einer eigenen Domain, aber einige andere Anwendungen sind bereits auf der Domäne'{domain}' installiert. Eine mögliche Lösung ist das Hinzufügen und Verwenden einer Subdomain, die dieser Anwendung zugeordnet ist.", "app_install_failed": "Installation von {app} fehlgeschlagen: {error}", "app_install_script_failed": "Im Installationsscript ist ein Fehler aufgetreten", - "app_remove_after_failed_install": "Entfernen der App nach fehlgeschlagener Installation…", + "app_remove_after_failed_install": "Entfernen der App nach fehlgeschlagener Installation...", "app_upgrade_script_failed": "Es ist ein Fehler im App-Upgrade-Skript aufgetreten", "diagnosis_basesystem_host": "Server läuft unter Debian {debian_version}", "diagnosis_basesystem_kernel": "Server läuft unter Linux-Kernel {kernel_version}", @@ -330,16 +330,41 @@ "diagnosis_ip_broken_dnsresolution": "Domänen-Namens-Auflösung scheint aus einem bestimmten Grund nicht zu funktionieren... Blockiert eine Firewall die DNS Anfragen?", "diagnosis_ip_broken_resolvconf": "Domänen-Namens-Auflösung scheint nicht zu funktionieren, was daran liegen könnte, dass in /etc/resolv.conf kein Eintrag auf 127.0.0.1 zeigt.", "diagnosis_ip_weird_resolvconf_details": "Stattdessen sollte diese Datei ein Softlink auf /etc/resolvconf/run/resolv.conf sein, die auf sich selbst zu 127.0.0.1 zeigt (dnsmasq). Der eigentlich Auflösende sollte in /etc/resolv.dnsmasq.conf konfiguriert werden.", - "diagnosis_dns_good_conf": "Gute DNS Konfiguration für Domäne {domain} (Kategorie {category})", + "diagnosis_dns_good_conf": "Die DNS-Einträge für die Domäne {domain} (Kategorie {category}) sind korrekt konfiguriert", "diagnosis_ignored_issues": "(+ {nb_ignored} ignorierte(s) Problem(e))", "diagnosis_basesystem_hardware": "Server Hardware Architektur ist {virt} {arch}", "diagnosis_basesystem_hardware_board": "Server Platinen Modell ist {model}", "diagnosis_found_errors": "Habe {errors} erhebliche(s) Problem(e) in Verbindung mit {category} gefunden!", "diagnosis_found_warnings": "Habe {warnings} Ding(e) gefunden, die verbessert werden könnten für {category}.", "diagnosis_ip_dnsresolution_working": "Domänen-Namens-Auflösung funktioniert!", - "diagnosis_ip_weird_resolvconf": "DNS Auflösung scheint zu funktionieren, aber sei vorsichtig wenn du eine eigene /etc/resolv.conf verwendest.", - "diagnosis_display_tip": "Um die gefundenen Probleme zu sehen, kannst Du zum Diagnose-Bereich des webadmin gehen, oder 'yunohost diagnosis show --issues' in der Kommandozeile ausführen.", + "diagnosis_ip_weird_resolvconf": "DNS Auflösung scheint zu funktionieren, aber seien Sie vorsichtig wenn Sie eine eigene /etc/resolv.conf verwendest.", + "diagnosis_display_tip": "Um die gefundenen Probleme zu sehen, können Sie zum Diagnose-Bereich des webadmin gehen, oder 'yunohost diagnosis show --issues' in der Kommandozeile ausführen.", "backup_archive_corrupted": "Das Backup-Archiv '{archive}' scheint beschädigt: {error}", "backup_archive_cant_retrieve_info_json": "Die Informationen für das Archiv '{archive}' konnten nicht geladen werden... Die Datei info.json wurde nicht gefunden (oder ist kein gültiges json).", - "app_packaging_format_not_supported": "Diese App kann nicht installiert werden da das Paketformat nicht von der Yunohost-Version unterstützt wird. Denken Sie darüber nach das System zu aktualisieren." + "app_packaging_format_not_supported": "Diese App kann nicht installiert werden da das Paketformat nicht von der YunoHost-Version unterstützt wird. Denken Sie darüber nach das System zu aktualisieren.", + "certmanager_domain_not_diagnosed_yet": "Für {domain} gibt es noch keine Diagnose-Resultate. Bitte wiederholen Sie die Diagnose für die Kategorien 'DNS records' und 'Web' im Diagnose-Bereich um zu überprüfen ob die Domain für Let's Encrypt bereit ist. (Oder wenn Sie wissen was Sie tun, verwenden Sie '--no-checks' um diese Überprüfungen abzuschalten.", + "migration_0015_patching_sources_list": "sources.lists wird repariert...", + "migration_0015_start": "Start der Migration auf Buster", + "migration_0011_failed_to_remove_stale_object": "Abgelaufenes Objekt konne nicht entfernt werden. {dn}: {error}", + "migration_0011_update_LDAP_schema": "Das LDAP-Schema aktualisieren...", + "migration_0011_update_LDAP_database": "Die LDAP-Datenbank aktualisieren...", + "migration_0011_migrate_permission": "Berechtigungen der Applikationen von den Einstellungen zu LDAP migrieren...", + "migration_0011_LDAP_update_failed": "LDAP konnte nicht aktualisiert werden. Fehler:n{error:s}", + "migration_0011_create_group": "Eine Gruppe für jeden Benutzer erstellen…", + "migration_description_0015_migrate_to_buster": "Auf Debian Buster und YunoHost 4.x upgraden", + "mail_unavailable": "Diese E-Mail Adresse ist reserviert und wird dem ersten Benutzer automatisch zugewiesen", + "diagnosis_services_conf_broken": "Die Konfiguration für den Dienst {service} ist fehlerhaft!", + "diagnosis_services_running": "Dienst {service} läuft!", + "diagnosis_domain_expires_in": "{domain} läuft in {days} Tagen ab.", + "diagnosis_domain_expiration_error": "Einige Domänen werden SEHR BALD ablaufen!", + "diagnosis_domain_expiration_success": "Deine Domänen sind registriert und werden in nächster Zeit nicht ablaufen.", + "diagnosis_domain_not_found_details": "Die Domäne {domain} existiert nicht in der WHOIS-Datenbank oder sie ist abgelaufen!", + "diagnosis_domain_expiration_not_found": "Konnte die Ablaufdaten für einige Domänen nicht überprüfen.", + "diagnosis_dns_try_dyndns_update_force": "Die DNS-Konfiguration dieser Domäne sollte automatisch von Yunohost verwaltet werden. Andernfalls können Sie mittels yunohost dyndns update --force ein Update erzwingen.", + "diagnosis_dns_point_to_doc": "Bitte schauen Sie in die Dokumentation unter https://yunohost.org/dns_config wenn Sie Hilfe bei der Konfiguration der DNS-Einträge brauchen.", + "diagnosis_dns_discrepancy": "Der folgende DNS-Eintrag scheint nicht den empfohlenen Einstellungen zu entsprechen:
Typ: {type}
Name: {name}
Aktueller Wert: {current}
Erwarteter Wert: {value}", + "diagnosis_dns_missing_record": "Gemäß der empfohlenen DNS-Konfiguration sollten Sie einen DNS-Eintrag mit den folgenden Informationen hinzufügen.
Typ: {type}
Name: {name}
Wert: {value}", + "diagnosis_dns_bad_conf": "Einige DNS-Einträge für die Domäne {domain} fehlen oder sind nicht korrekt (Kategorie {category})", + "diagnosis_ip_local": "Lokale IP: {local}", + "diagnosis_ip_global": "Globale IP: {global}" } From a23233ab0fb53326325185f04f7ffb78d1e29719 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 28 Aug 2020 22:30:20 +0200 Subject: [PATCH 162/262] Add apt regenconf hook to manage sury pinning policy on all --- data/hooks/conf_regen/10-apt | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 data/hooks/conf_regen/10-apt diff --git a/data/hooks/conf_regen/10-apt b/data/hooks/conf_regen/10-apt new file mode 100755 index 000000000..4ce838e4c --- /dev/null +++ b/data/hooks/conf_regen/10-apt @@ -0,0 +1,39 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + mkdir --parents "${pending_dir}/etc/apt/preferences.d" + + for package in "php" "php-fpm" "php-mysql" "php-xml" "php-zip" "php-mbstring" "php-ldap" "php-gd" "php-curl" "php-bz2" "php-json" "php-sqlite3" "php-intl" "openssl" "libssl1.1" "libssl-dev" + do + echo " +Package: $package +Pin: origin \"packages.sury.org\" +Pin-Priority: -1" >> "/etc/apt/preferences.d/extra_php_version" + done +} + +do_post_regen() { + regen_conf_files=$1 +} + +FORCE=${2:-0} +DRY_RUN=${3:-0} + +case "$1" in + pre) + do_pre_regen $4 + ;; + post) + do_post_regen $4 + ;; + *) + echo "hook called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 From 346728e55dbed915f9dbee68929a92cf70f1ffcf Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 28 Aug 2020 22:31:10 +0200 Subject: [PATCH 163/262] Remove the pinning stuff of sury from the helpers, since it's now managed by the regenconf core --- data/helpers.d/apt | 6 ------ data/helpers.d/php | 6 ------ 2 files changed, 12 deletions(-) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index 377464786..1a4a9f74a 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -265,12 +265,6 @@ ynh_install_app_dependencies () { then # Re-add sury ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600 - - # Pin this sury repository to prevent sury of doing shit - for package_to_not_upgrade in "php" "php-fpm" "php-mysql" "php-xml" "php-zip" "php-mbstring" "php-ldap" "php-gd" "php-curl" "php-bz2" "php-json" "php-sqlite3" "php-intl" "openssl" "libssl1.1" "libssl-dev" - do - ynh_pin_repo --package="$package_to_not_upgrade" --pin="origin \"packages.sury.org\"" --priority="-1" --name=extra_php_version --append - done fi fi fi diff --git a/data/helpers.d/php b/data/helpers.d/php index 0fe118fde..489c448a8 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -364,12 +364,6 @@ ynh_install_php () { # Set the default PHP version back as the default version for php-cli. update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION - # Pin this extra repository after packages are installed to prevent sury of doing shit - for package_to_not_upgrade in "php" "php-fpm" "php-mysql" "php-xml" "php-zip" "php-mbstring" "php-ldap" "php-gd" "php-curl" "php-bz2" "php-json" "php-sqlite3" "php-intl" "openssl" "libssl1.1" "libssl-dev" - do - ynh_pin_repo --package="$package_to_not_upgrade" --pin="origin \"packages.sury.org\"" --priority="-1" --name=extra_php_version --append - done - # Advertise service in admin panel yunohost service add php${phpversion}-fpm --log "/var/log/php${phpversion}-fpm.log" } From bc7344b63614cec2468460c2b8569e0fb81c9d8d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 16:41:31 +0200 Subject: [PATCH 164/262] Use proper templating for dnsmasq conf --- data/hooks/conf_regen/43-dnsmasq | 11 +++++------ data/templates/dnsmasq/domain.tpl | 5 ++++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq index 75d74b09c..e7b0531e8 100755 --- a/data/hooks/conf_regen/43-dnsmasq +++ b/data/hooks/conf_regen/43-dnsmasq @@ -27,14 +27,13 @@ do_pre_regen() { ipv6=$(curl -s -6 https://ip6.yunohost.org 2>/dev/null || true) ynh_validate_ip6 "$ipv6" || ipv6='' + export ipv4 + export ipv6 + # add domain conf files for domain in $YNH_DOMAINS; do - cat domain.tpl \ - | sed "s/{{ domain }}/${domain}/g" \ - | sed "s/{{ ip }}/${ipv4}/g" \ - > "${dnsmasq_dir}/${domain}" - [[ -n $ipv6 ]] \ - && echo "address=/${domain}/${ipv6}" >> "${dnsmasq_dir}/${domain}" + export domain + ynh_render_template "domain.tpl" "${dnsmasq_dir}/${domain}" done # remove old domain conf files diff --git a/data/templates/dnsmasq/domain.tpl b/data/templates/dnsmasq/domain.tpl index bbfc2864c..edc65eaea 100644 --- a/data/templates/dnsmasq/domain.tpl +++ b/data/templates/dnsmasq/domain.tpl @@ -1,4 +1,7 @@ -address=/{{ domain }}/{{ ip }} +address=/{{ domain }}/{{ ipv4 }} +{% if ipv6 %} +address=/{{ domain }}/{{ ipv6 }} +{% endif %} txt-record={{ domain }},"v=spf1 mx a -all" mx-host={{ domain }},{{ domain }},5 srv-host=_xmpp-client._tcp.{{ domain }},{{ domain }},5222,0,5 From 503e08b58bd9c09742621b8e532670f398370cc2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 16:41:54 +0200 Subject: [PATCH 165/262] Add xmpp-upload stuff in dnsmasq conf --- data/templates/dnsmasq/domain.tpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/templates/dnsmasq/domain.tpl b/data/templates/dnsmasq/domain.tpl index edc65eaea..f4c8d6c4c 100644 --- a/data/templates/dnsmasq/domain.tpl +++ b/data/templates/dnsmasq/domain.tpl @@ -1,6 +1,8 @@ address=/{{ domain }}/{{ ipv4 }} +address=/xmpp-upload.{{ domain }}/{{ ipv4 }} {% if ipv6 %} address=/{{ domain }}/{{ ipv6 }} +address=/xmpp-upload.{{ domain }}/{{ ipv6 }} {% endif %} txt-record={{ domain }},"v=spf1 mx a -all" mx-host={{ domain }},{{ domain }},5 From 98e972a49a405621fb5839d218a35ec3b7d89136 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 17:32:03 +0200 Subject: [PATCH 166/262] Get rid of unimplemented Borg stuff because that code is complicated enough already -_- --- locales/en.json | 3 --- src/yunohost/backup.py | 27 +++------------------------ 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/locales/en.json b/locales/en.json index abc1c1092..23cf92f0d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -66,7 +66,6 @@ "backup_abstract_method": "This backup method has yet to be implemented", "backup_actually_backuping": "Creating a backup archive from the collected files…", "backup_app_failed": "Could not back up the app '{app:s}'", - "backup_applying_method_borg": "Sending all files to backup into borg-backup repository…", "backup_applying_method_copy": "Copying all files to backup…", "backup_applying_method_custom": "Calling the custom backup method '{method:s}'…", "backup_applying_method_tar": "Creating the backup TAR archive…", @@ -80,7 +79,6 @@ "backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup", "backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'", "backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s} MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)", - "backup_borg_not_implemented": "The Borg backup method is not yet implemented", "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected", "backup_cleaning_failed": "Could not clean up the temporary backup folder", "backup_copying_to_organize_the_archive": "Copying {size:s}MB to organize the archive", @@ -94,7 +92,6 @@ "backup_delete_error": "Could not delete '{path:s}'", "backup_deleted": "Backup deleted", "backup_hook_unknown": "The backup hook '{hook:s}' is unknown", - "backup_method_borg_finished": "Backup into Borg finished", "backup_method_copy_finished": "Backup copy finalized", "backup_method_custom_finished": "Custom backup method '{method:s}' finished", "backup_method_tar_finished": "TAR backup archive created", diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 0d97617c9..76e18ad87 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1772,9 +1772,8 @@ class BackupMethod(object): bm_class = { 'copy': CopyBackupMethod, 'tar': TarBackupMethod, - 'borg': BorgBackupMethod } - if method in ["copy", "tar", "borg"]: + if method in ["copy", "tar"]: return bm_class[method](manager, *args) else: return CustomBackupMethod(manager, method=method, *args) @@ -2008,26 +2007,6 @@ class TarBackupMethod(BackupMethod): tar.close() -class BorgBackupMethod(BackupMethod): - - @property - def method_name(self): - return 'borg' - - def backup(self): - """ Backup prepared files with borg """ - super(CopyBackupMethod, self).backup() - - # TODO run borg create command - raise YunohostError('backup_borg_not_implemented') - - def mount(self, mnt_path): - raise YunohostError('backup_borg_not_implemented') - - def copy(self, file, target): - raise YunohostError('backup_borg_not_implemented') - - class CustomBackupMethod(BackupMethod): """ @@ -2044,7 +2023,7 @@ class CustomBackupMethod(BackupMethod): @property def method_name(self): - return 'borg' + return 'custom' def need_mount(self): """Call the backup_method hook to know if we need to organize files @@ -2153,7 +2132,7 @@ def backup_create(name=None, description=None, methods=[], if no_compress: methods = ['copy'] else: - methods = ['tar'] # In future, borg will be the default actions + methods = ['tar'] # If no --system or --apps given, backup everything if system is None and apps is None: From efc2e7ef1d7b06f8a086c178801812999dce9bac Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 17:51:41 +0200 Subject: [PATCH 167/262] Fuck it let's keep that code simple, we don't need to maintain the list of exception that a command is likely to return, this is supposed to be simple to read if code is well written --- src/yunohost/backup.py | 94 ++++-------------------------------------- 1 file changed, 9 insertions(+), 85 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 76e18ad87..aed9c7eb3 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -309,17 +309,6 @@ class BackupManager(): """Initialize preparation directory Ensure the working directory exists and is empty - - exception: - backup_output_directory_not_empty -- (YunohostError) Raised if the - directory was given by the user and isn't empty - - (TODO) backup_cant_clean_tmp_working_directory -- (YunohostError) - Raised if the working directory isn't empty, is temporary and can't - be automaticcaly cleaned - - (TODO) backup_cant_create_working_directory -- (YunohostError) Raised - if iyunohost can't create the working directory """ # FIXME replace isdir by exists ? manage better the case where the path @@ -503,10 +492,6 @@ class BackupManager(): files to backup hooks/ -- restore scripts associated to system backup scripts are copied here - - Exceptions: - "backup_nothings_done" -- (YunohostError) This exception is raised if - nothing has been listed. """ self._collect_system_files() @@ -673,10 +658,6 @@ class BackupManager(): Args: app -- (string) an app instance name (already installed) to backup - - Exceptions: - backup_app_failed -- Raised at the end if the app backup script - execution failed """ from yunohost.permission import user_permission_list @@ -809,7 +790,7 @@ class RestoreManager(): """ RestoreManager allow to restore a past backup archive - Currently it's a tar.gz file, but it could be another kind of archive + Currently it's a tar file, but it could be another kind of archive Public properties: info (getter)i # FIXME @@ -870,9 +851,6 @@ class RestoreManager(): def _read_info_files(self): """ Read the info file from inside an archive - - Exceptions: - backup_archive_cant_retrieve_info_json -- Raised if we can't read the info """ # Retrieve backup info info_file = os.path.join(self.work_dir, "info.json") @@ -1030,10 +1008,6 @@ class RestoreManager(): Use the mount method from the BackupMethod instance and read info about this archive - - Exceptions: - restore_removing_tmp_dir_failed -- Raised if it's not possible to remove - the working directory """ self.work_dir = os.path.join(BACKUP_PATH, "tmp", self.name) @@ -1107,11 +1081,6 @@ class RestoreManager(): def assert_enough_free_space(self): """ Check available disk space - - Exceptions: - restore_may_be_not_enough_disk_space -- Raised if there isn't enough - space to cover the security margin space - restore_not_enough_disk_space -- Raised if there isn't enough space """ free_space = free_space_in_directory(BACKUP_PATH) @@ -1293,11 +1262,6 @@ class RestoreManager(): Args: app_instance_name -- (string) The app name to restore (no app with this name should be already install) - - Exceptions: - restore_already_installed_app -- Raised if an app with this app instance - name already exists - restore_app_failed -- Raised if the restore bash script failed """ from yunohost.user import user_group_list from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update, permission_sync_to_user @@ -1500,7 +1464,7 @@ class BackupMethod(object): TarBackupMethod --------------- - This method compresses all files to backup in a .tar.gz archive. When + This method compresses all files to backup in a .tar archive. When restoring, it untars the required parts. CustomBackupMethod @@ -1610,10 +1574,6 @@ class BackupMethod(object): def clean(self): """ Umount sub directories of working dirextories and delete it if temporary - - Exceptions: - backup_cleaning_failed -- Raise if we were not able to unmount sub - directories of the working directories """ if self.need_mount(): if not _recursive_umount(self.work_dir): @@ -1625,9 +1585,6 @@ class BackupMethod(object): def _check_is_enough_free_space(self): """ Check free space in repository or output directory before to backup - - Exceptions: - not_enough_disk_space -- Raise if there isn't enough space. """ # TODO How to do with distant repo or with deduplicated backup ? backup_size = self.manager.size @@ -1649,9 +1606,6 @@ class BackupMethod(object): The usage of binding could be strange for a user because the du -sb command will return that the working directory is big. - - Exceptions: - backup_unable_to_organize_files """ paths_needed_to_be_copied = [] for path in self.manager.paths_to_backup: @@ -1786,13 +1740,11 @@ class CopyBackupMethod(BackupMethod): could be the inverse for restoring """ + method_name = "copy" + def __init__(self, manager, repo=None): super(CopyBackupMethod, self).__init__(manager, repo) - @property - def method_name(self): - return 'copy' - def backup(self): """ Copy prepared files into a the repo """ # Check free space in output @@ -1817,10 +1769,6 @@ class CopyBackupMethod(BackupMethod): def mount(self): """ Mount the uncompress backup in readonly mode to the working directory - - Exceptions: - backup_no_uncompress_archive_dir -- Raised if the repo doesn't exists - backup_cant_mount_uncompress_archive -- Raised if the binding failed """ # FIXME: This code is untested because there is no way to run it from # the ynh cli @@ -1851,13 +1799,11 @@ class TarBackupMethod(BackupMethod): This class compress all files to backup in archive. """ + method_name = "tar" + def __init__(self, manager, repo=None): super(TarBackupMethod, self).__init__(manager, repo) - @property - def method_name(self): - return 'tar' - @property def _archive_file(self): """Return the compress archive path""" @@ -1872,11 +1818,6 @@ class TarBackupMethod(BackupMethod): It adds the info.json in /home/yunohost.backup/archives and if the compress archive isn't located here, add a symlink to the archive to. - - Exceptions: - backup_archive_open_failed -- Raised if we can't open the archive - backup_creation_failed -- Raised if we can't write in the - compress archive """ if not os.path.exists(self.repo): @@ -1917,13 +1858,7 @@ class TarBackupMethod(BackupMethod): def mount(self): """ - Mount the archive. We avoid copy to be able to restore on system without - too many space. - - Exceptions: - backup_archive_open_failed -- Raised if the archive can't be open - backup_archive_corrupted -- Raised if the archive appears corrupted - backup_archive_cant_retrieve_info_json -- If the info.json file can't be retrieved + Mount the archive. We avoid intermediate copy to be able to restore on system with low free space. """ super(TarBackupMethod, self).mount() @@ -2015,21 +1950,16 @@ class CustomBackupMethod(BackupMethod): /etc/yunohost/hooks.d/backup_method/ """ + method_name = "custom" + def __init__(self, manager, repo=None, method=None, **kwargs): super(CustomBackupMethod, self).__init__(manager, repo) self.args = kwargs self.method = method self._need_mount = None - @property - def method_name(self): - return 'custom' - def need_mount(self): """Call the backup_method hook to know if we need to organize files - - Exceptions: - backup_custom_need_mount_error -- Raised if the hook failed """ if self._need_mount is not None: return self._need_mount @@ -2044,9 +1974,6 @@ class CustomBackupMethod(BackupMethod): def backup(self): """ Launch a custom script to backup - - Exceptions: - backup_custom_backup_error -- Raised if the custom script failed """ ret = hook_callback('backup_method', [self.method], @@ -2060,9 +1987,6 @@ class CustomBackupMethod(BackupMethod): def mount(self): """ Launch a custom script to mount the custom archive - - Exceptions: - backup_custom_mount_error -- Raised if the custom script failed """ super(CustomBackupMethod, self).mount() ret = hook_callback('backup_method', [self.method], From 659d23ceb37863c3d907f7dbfb170f737a15bfa1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 18:36:57 +0200 Subject: [PATCH 168/262] Simplify the damn spaggethi --- src/yunohost/backup.py | 82 +++++++++++++----------------------------- 1 file changed, 25 insertions(+), 57 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index aed9c7eb3..3ce3b211d 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -219,8 +219,8 @@ class BackupManager(): backup_manager = BackupManager(name="mybackup", description="bkp things") # Add backup method to apply - backup_manager.add(BackupMethod.create('copy', backup_manager, '/mnt/local_fs')) - backup_manager.add(BackupMethod.create('tar', backup_manager, '/mnt/remote_fs')) + backup_manager.add('copy', output_directory='/mnt/local_fs') + backup_manager.add('tar', output_directory='/mnt/remote_fs') # Define targets to be backuped backup_manager.set_system_targets(["data"]) @@ -716,17 +716,11 @@ class BackupManager(): # Actual backup archive creation / method management # # - def add(self, method): + def add(self, method, output_directory=None): """ Add a backup method that will be applied after the files collection step - - Args: - method -- (BackupMethod) A backup method. Currently, you can use those: - TarBackupMethod - CopyBackupMethod - CustomBackupMethod """ - self.methods.append(method) + self.methods.append(BackupMethod.create(method, self, output_directory=output_directory)) def backup(self): """Apply backup methods""" @@ -816,14 +810,12 @@ class RestoreManager(): return restore_manager.result """ - def __init__(self, name, repo=None, method='tar'): + def __init__(self, name, method='tar'): """ RestoreManager constructor Args: name -- (string) Archive name - repo -- (string|None) Repository where is this archive, it could be a - path (default: /home/yunohost.backup/archives) method -- (string) Method name to use to mount the archive """ # Retrieve and open the archive @@ -1489,7 +1481,24 @@ class BackupMethod(object): method.mount() """ - def __init__(self, manager, repo=None): + @classmethod + def create(cls, method, manager, *args, **kwargs): + """ + Factory method to create instance of BackupMethod + + Args: + method -- (string) The method name of an existing BackupMethod. If the + name is unknown the CustomBackupMethod will be tried + *args -- Specific args for the method, could be the repo target by the + method + + Return a BackupMethod instance + """ + known_methods = {c.method_name:c for c in BackupMethod.__subclasses__()} + backup_method = known_methods.get(method, CustomBackupMethod) + return backup_method(manager, method=method, *args, **kwargs) + + def __init__(self, manager, repo=None, **kwargs): """ BackupMethod constructors @@ -1703,35 +1712,6 @@ class BackupMethod(object): else: shutil.copy(path['source'], dest) - @classmethod - def create(cls, method, manager, *args): - """ - Factory method to create instance of BackupMethod - - Args: - method -- (string) The method name of an existing BackupMethod. If the - name is unknown the CustomBackupMethod will be tried - - ... -- Specific args for the method, could be the repo target by the - method - - Return a BackupMethod instance - """ - if not isinstance(method, basestring): - methods = [] - for m in method: - methods.append(BackupMethod.create(m, manager, *args)) - return methods - - bm_class = { - 'copy': CopyBackupMethod, - 'tar': TarBackupMethod, - } - if method in ["copy", "tar"]: - return bm_class[method](manager, *args) - else: - return CustomBackupMethod(manager, method=method, *args) - class CopyBackupMethod(BackupMethod): @@ -1742,9 +1722,6 @@ class CopyBackupMethod(BackupMethod): method_name = "copy" - def __init__(self, manager, repo=None): - super(CopyBackupMethod, self).__init__(manager, repo) - def backup(self): """ Copy prepared files into a the repo """ # Check free space in output @@ -1801,9 +1778,6 @@ class TarBackupMethod(BackupMethod): method_name = "tar" - def __init__(self, manager, repo=None): - super(TarBackupMethod, self).__init__(manager, repo) - @property def _archive_file(self): """Return the compress archive path""" @@ -1858,7 +1832,7 @@ class TarBackupMethod(BackupMethod): def mount(self): """ - Mount the archive. We avoid intermediate copy to be able to restore on system with low free space. + Mount the archive. We avoid intermediate copies to be able to restore on system with low free space. """ super(TarBackupMethod, self).mount() @@ -2077,14 +2051,8 @@ def backup_create(name=None, description=None, methods=[], else: backup_manager = BackupManager(name, description) - # Add backup methods - if output_directory: - methods = BackupMethod.create(methods, backup_manager, output_directory) - else: - methods = BackupMethod.create(methods, backup_manager) - for method in methods: - backup_manager.add(method) + backup_manager.add(method, output_directory=output_directory) # Add backup targets (system and apps) backup_manager.set_system_targets(system) From 7ccc7fa13eb0920d8e32429bad35d3b55195f4f8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 18:51:23 +0200 Subject: [PATCH 169/262] No moar borg i18n keys --- tests/test_i18n_keys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_i18n_keys.py b/tests/test_i18n_keys.py index 874794e11..2323f55be 100644 --- a/tests/test_i18n_keys.py +++ b/tests/test_i18n_keys.py @@ -112,7 +112,7 @@ def find_expected_string_keys(): # Hardcoded expected keys ... yield "admin_password" # Not sure that's actually used nowadays... - for method in ["tar", "copy", "borg", "custom"]: + for method in ["tar", "copy", "custom"]: yield "backup_applying_method_%s" % method yield "backup_method_%s_finished" % method From 839a7b9a47623998509f77256b9a0042130013d8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 19:08:43 +0200 Subject: [PATCH 170/262] Add a global setting to still compress tar archives --- src/yunohost/backup.py | 10 +++++----- src/yunohost/settings.py | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 3ce3b211d..57f86ab10 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -53,6 +53,7 @@ from yunohost.regenconf import regen_conf from yunohost.log import OperationLogger from yunohost.utils.error import YunohostError from yunohost.utils.packages import ynh_packages_version +from yunohost.settings import settings_get BACKUP_PATH = '/home/yunohost.backup' ARCHIVES_PATH = '%s/archives' % BACKUP_PATH @@ -1772,15 +1773,14 @@ class CopyBackupMethod(BackupMethod): class TarBackupMethod(BackupMethod): - """ - This class compress all files to backup in archive. - """ - method_name = "tar" @property def _archive_file(self): - """Return the compress archive path""" + + if isinstance(self.manager, BackupManager) and settings_get("backup.compress_tar_archives"): + return os.path.join(self.repo, self.name + '.tar.gz') + f = os.path.join(self.repo, self.name + '.tar') if os.path.exists(f + ".gz"): f += ".gz" diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index c1edadb93..71a63becd 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -71,6 +71,7 @@ DEFAULTS = OrderedDict([ "choices": ["intermediate", "modern"]}), ("pop3.enabled", {"type": "bool", "default": False}), ("smtp.allow_ipv6", {"type": "bool", "default": True}), + ("backup.compress_tar_archives", {"type": "bool", "default": False}), ]) From acaa05c2caebd572db80114b96c16577c1237462 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 19:09:28 +0200 Subject: [PATCH 171/262] Fix a small issue where there would be duplicated entries in backup_list --- src/yunohost/backup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 57f86ab10..3102022d2 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -2158,7 +2158,10 @@ def backup_list(with_info=False, human_readable=False): """ # Get local archives sorted according to last modification time - archives = sorted(glob("%s/*.tar.gz" % ARCHIVES_PATH) + glob("%s/*.tar" % ARCHIVES_PATH), key=lambda x: os.path.getctime(x)) + # (we do a realpath() to resolve symlinks) + archives = glob("%s/*.tar.gz" % ARCHIVES_PATH) + glob("%s/*.tar" % ARCHIVES_PATH) + archives = set([os.path.realpath(archive) for archive in archives]) + archives = sorted(archives, key=lambda x: os.path.getctime(x)) # Extract only filename without the extension def remove_extension(f): if f.endswith(".tar.gz"): From 41813744a83c94309c0a0c66d9caeb9100618c64 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 19:15:27 +0200 Subject: [PATCH 172/262] Explicitly require php-fpm >= 7.3 ... --- debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 1f2c24678..2076a8210 100644 --- a/debian/control +++ b/debian/control @@ -11,12 +11,12 @@ Package: yunohost Essential: yes Architecture: all Depends: ${python:Depends}, ${misc:Depends} - , moulinette (>= 4.0.0~alpha), ssowat (>= 4.0.0~alpha) + , moulinette (>= 4.0), ssowat (>= 4.0) , python-psutil, python-requests, python-dnspython, python-openssl , python-miniupnpc, python-dbus, python-jinja2 , python-toml, python-packaging, python-publicsuffix , apt, apt-transport-https, dirmngr - , php-fpm, php-ldap, php-intl + , php-fpm (>= 7.3), php-ldap, php-intl , mariadb-server, php-mysql | php-mysqlnd , openssh-server, iptables, fail2ban, dnsutils, bind9utils , openssl, ca-certificates, netcat-openbsd, iproute2 From 78a2d012bb6926185d047f452712bb073c34fd4c Mon Sep 17 00:00:00 2001 From: ekhae Date: Fri, 16 Aug 2019 17:59:22 +0000 Subject: [PATCH 173/262] UX improved about mail and domains --- data/actionsmap/yunohost.yml | 31 +++++++++++++------------------ locales/en.json | 1 + src/yunohost/user.py | 22 +++++++++++++++++----- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index d61538c5c..13a3cfaaf 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -97,15 +97,6 @@ user: pattern: &pattern_lastname - !!str ^([^\W\d_]{2,30}[ ,.'-]{0,3})+$ - "pattern_lastname" - -m: - full: --mail - help: Main unique email address - extra: - ask: ask_email - required: True - pattern: &pattern_email - - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ - - "pattern_email" -p: full: --password help: User password @@ -116,6 +107,13 @@ user: - !!str ^.{3,}$ - "pattern_password" comment: good_practices_about_user_password + -d: + full: --domain + help: Domain for email and xmpp + extra: + pattern: &pattern_domain + - !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ + - "pattern_domain" -q: full: --mailbox-quota help: Mailbox size quota @@ -157,7 +155,9 @@ user: -m: full: --mail extra: - pattern: *pattern_email + pattern: &pattern_email + - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ + - "pattern_email" -p: full: --change-password help: New password to set @@ -419,9 +419,7 @@ domain: domain: help: Domain name to add extra: - pattern: &pattern_domain - - !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ - - "pattern_domain" + pattern: *pattern_domain -d: full: --dyndns help: Subscribe to the DynDNS service @@ -1340,7 +1338,7 @@ dyndns: tools: category_help: Specific tools actions: - + ### tools_adminpw() adminpw: action_help: Change password of admin and root users @@ -1368,9 +1366,6 @@ tools: postinstall: action_help: YunoHost post-install api: POST /postinstall - configuration: - # We need to be able to run the postinstall without being authenticated, otherwise we can't run the postinstall - authenticate: false arguments: -d: full: --domain @@ -1378,7 +1373,7 @@ tools: extra: ask: ask_main_domain pattern: *pattern_domain - required: True + required: False -p: full: --password help: YunoHost admin password diff --git a/locales/en.json b/locales/en.json index 83cc84442..afc9db13a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -60,6 +60,7 @@ "apps_catalog_failed_to_download": "Unable to download the {apps_catalog} app catalog: {error}", "apps_catalog_obsolete_cache": "The app catalog cache is empty or obsolete.", "apps_catalog_update_success": "The application catalog has been updated!", + "ask_domain": "Choose a domain", "ask_email": "E-mail address", "ask_firstname": "First name", "ask_lastname": "Last name", diff --git a/src/yunohost/user.py b/src/yunohost/user.py index d19da177c..bd8252d0e 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -105,7 +105,7 @@ def user_list(fields=None): @is_unit_operation([('username', 'user')]) -def user_create(operation_logger, username, firstname, lastname, mail, password, +def user_create(operation_logger, username, firstname, lastname, domain, password, mailbox_quota="0"): """ Create user @@ -126,7 +126,22 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, # Ensure sufficiently complex password assert_password_is_strong_enough("user", password) + from moulinette import msignals, msettings, m18n + from yunohost.domain import domain_list + if domain is None: + if msettings.get('interface') == 'api': + raise YunohostError('Invalide usage, specify domain argument') + else: + # On affiche les differents domaines possibles + for domain_checked in domain_list()['domains'] : + msignals.display("- {}".format(domain_checked)) + domain = msignals.prompt(m18n.n('ask_domain')) + # Check that the domain exists + if domain not in domain_list()['domains']: + raise YunohostError('domain_unknown', domain) + + mail=username+'@'+ domain ldap = _get_ldap_interface() if username in user_list()["users"]: @@ -158,10 +173,6 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, if mail in aliases: raise YunohostError('mail_unavailable') - # Check that the mail domain exists - if mail.split("@")[1] not in domain_list()['domains']: - raise YunohostError('mail_domain_unknown', domain=mail.split("@")[1]) - operation_logger.start() # Get random UID/GID @@ -176,6 +187,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, # Adapt values for LDAP fullname = '%s %s' % (firstname, lastname) + attr_dict = { 'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh'], 'givenName': [firstname], From 4a15358ed9618df18701c57c15666167eaafa17f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 27 Aug 2020 17:31:17 +0200 Subject: [PATCH 174/262] Revert a few unecessary changes / polish code --- data/actionsmap/yunohost.yml | 7 +++++-- src/yunohost/user.py | 7 +++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 13a3cfaaf..803c5bfda 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1338,7 +1338,7 @@ dyndns: tools: category_help: Specific tools actions: - + ### tools_adminpw() adminpw: action_help: Change password of admin and root users @@ -1366,6 +1366,9 @@ tools: postinstall: action_help: YunoHost post-install api: POST /postinstall + configuration: + # We need to be able to run the postinstall without being authenticated, otherwise we can't run the postinstall + authenticate: false arguments: -d: full: --domain @@ -1373,7 +1376,7 @@ tools: extra: ask: ask_main_domain pattern: *pattern_domain - required: False + required: True -p: full: --password help: YunoHost admin password diff --git a/src/yunohost/user.py b/src/yunohost/user.py index bd8252d0e..98dafc24d 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -34,7 +34,7 @@ import string import subprocess import copy -from moulinette import m18n +from moulinette import msignals, msettings, m18n from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_json, write_to_json, read_yaml, write_to_yaml @@ -126,8 +126,7 @@ def user_create(operation_logger, username, firstname, lastname, domain, passwor # Ensure sufficiently complex password assert_password_is_strong_enough("user", password) - from moulinette import msignals, msettings, m18n - from yunohost.domain import domain_list + if domain is None: if msettings.get('interface') == 'api': raise YunohostError('Invalide usage, specify domain argument') @@ -141,7 +140,7 @@ def user_create(operation_logger, username, firstname, lastname, domain, passwor if domain not in domain_list()['domains']: raise YunohostError('domain_unknown', domain) - mail=username+'@'+ domain + mail = username + '@' + domain ldap = _get_ldap_interface() if username in user_list()["users"]: From dea6a187125c55dff9d64921c355fd1414072cd7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 27 Aug 2020 17:31:33 +0200 Subject: [PATCH 175/262] Polish UX --- data/actionsmap/yunohost.yml | 2 +- locales/en.json | 2 +- src/yunohost/user.py | 12 +++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 803c5bfda..e2439b2dc 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -109,7 +109,7 @@ user: comment: good_practices_about_user_password -d: full: --domain - help: Domain for email and xmpp + help: Domain for the email address and xmpp account extra: pattern: &pattern_domain - !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ diff --git a/locales/en.json b/locales/en.json index afc9db13a..20bad0a8f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -60,7 +60,7 @@ "apps_catalog_failed_to_download": "Unable to download the {apps_catalog} app catalog: {error}", "apps_catalog_obsolete_cache": "The app catalog cache is empty or obsolete.", "apps_catalog_update_success": "The application catalog has been updated!", - "ask_domain": "Choose a domain", + "ask_user_domain": "Domain to use for the user's email address and XMPP account", "ask_email": "E-mail address", "ask_firstname": "First name", "ask_lastname": "Last name", diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 98dafc24d..2f5c13d97 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -127,14 +127,20 @@ def user_create(operation_logger, username, firstname, lastname, domain, passwor # Ensure sufficiently complex password assert_password_is_strong_enough("user", password) + # Validate domain used for email address/xmpp account if domain is None: if msettings.get('interface') == 'api': raise YunohostError('Invalide usage, specify domain argument') else: # On affiche les differents domaines possibles - for domain_checked in domain_list()['domains'] : - msignals.display("- {}".format(domain_checked)) - domain = msignals.prompt(m18n.n('ask_domain')) + msignals.display(m18n.n('domains_available')) + for domain in domain_list()['domains']: + msignals.display("- {}".format(domain)) + + maindomain = _get_maindomain() + domain = msignals.prompt(m18n.n('ask_user_domain') + ' (default: %s)' % maindomain) + if not domain: + domain = maindomain # Check that the domain exists if domain not in domain_list()['domains']: From 3612ac434d0d01411d64c9a92b4335f6fff4ad62 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 19:45:41 +0200 Subject: [PATCH 176/262] Enable this behavior by default (no need to enable the option in the app manifest) --- src/yunohost/app.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index cac7b45c3..bffe1e7a1 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -189,15 +189,12 @@ def _app_upgradable(app_infos): # Firstly use the version to know if an upgrade is available app_is_in_catalog = bool(app_infos.get("from_catalog")) - upgrade_only_if_version_changes = app_infos["manifest"].get('integration', {}).get("upgrade_only_if_version_changes", None) is True installed_version = version.parse(app_infos.get("version", "0~ynh0")) version_in_catalog = version.parse(app_infos.get("from_catalog", {}).get("manifest", {}).get("version", "0~ynh0")) if app_is_in_catalog and '~ynh' in str(installed_version) and '~ynh' in str(version_in_catalog): - if upgrade_only_if_version_changes and installed_version < version_in_catalog: + if installed_version < version_in_catalog: return "yes" - else: - return "no" if not app_is_in_catalog: return "url_required" @@ -512,14 +509,10 @@ def app_upgrade(app=[], url=None, file=None, force=False): # Manage upgrade type and avoid any upgrade if there is nothing to do upgrade_type = "UNKNOWN" - upgrade_only_if_version_changes = manifest.get('integration', {}).get("upgrade_only_if_version_changes", None) is True # Get current_version and new version app_new_version = version.parse(manifest.get("version", "?")) app_current_version = version.parse(app_dict.get("version", "?")) - if "~ynh" not in str(app_current_version) or "~ynh" not in str(app_new_version): - logger.warning("/!\\ Packagers ! You have enabled the setting 'upgrade_only_if_version_changes' but you haven't used the official way to define the package version") - upgrade_only_if_version_changes = False - if upgrade_only_if_version_changes: + if "~ynh" in str(app_current_version) and "~ynh" in str(app_new_version): if app_current_version >= app_new_version and not force: # In case of upgrade from file or custom repository # No new version available @@ -543,7 +536,6 @@ def app_upgrade(app=[], url=None, file=None, force=False): else: upgrade_type = "UPGRADE_FULL" - # Check requirements _check_manifest_requirements(manifest, app_instance_name=app_instance_name) _assert_system_is_sane_for_app(manifest, "pre") From 43417bd00bc9bfd64a12d36349a143879b80c2ed Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 31 Aug 2020 20:00:23 +0200 Subject: [PATCH 177/262] Update changelog for 4.0.6 --- debian/changelog | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/debian/changelog b/debian/changelog index 92d5b6410..9fd08c0ae 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +yunohost (4.0.6) stable; urgency=low + + - [mod] Add apt conf regen hook to manage sury pinning policy (#1041) + - [fix] Use proper templating + handle xmpp-upload.domain.tld in dnsmasq conf (bc7344b6, 503e08b5) + - [fix] Explicitly require php-fpm >= 7.3 ... (41813744) + - [i18n] Translations updated for Catalan, French, German + + Thanks to all contributors <3 ! (Christian W., Titus PiJean, xaloc33) + + -- Alexandre Aubin Mon, 31 Aug 2020 19:57:24 +0200 + yunohost (4.0.5) testing; urgency=low - [enh] Update postfix, dovecot, nginx configuration according to Mozilla guidelines (Buster + DH params) (f3a4334a, 89bcf1ba, 2d661737) From f46e318374b2c5fdb4629829a1946ee4ba14f3ee Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 1 Sep 2020 01:59:49 +0200 Subject: [PATCH 178/262] [fix] Stupid syntax issue in dovecot conf --- data/templates/dovecot/dovecot.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/dovecot/dovecot.conf b/data/templates/dovecot/dovecot.conf index 2e17ff163..ee8511f83 100644 --- a/data/templates/dovecot/dovecot.conf +++ b/data/templates/dovecot/dovecot.conf @@ -23,7 +23,7 @@ ssl_cert = /path/to/dhparam -ssl_dh = /usr/share/yunohost/other/ffdhe2048.pem; +ssl_dh = Date: Tue, 1 Sep 2020 02:00:50 +0200 Subject: [PATCH 179/262] Update changelog for 4.0.6.1 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 9fd08c0ae..92af480ac 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +yunohost (4.0.6.1) stable; urgency=low + + - [fix] Stupid syntax issue in dovecot conf + + -- Alexandre Aubin Tue, 01 Sep 2020 02:00:19 +0200 + yunohost (4.0.6) stable; urgency=low - [mod] Add apt conf regen hook to manage sury pinning policy (#1041) From 080fe88b2d4685f54fce48589bb71dea491c157b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 1 Sep 2020 17:07:24 +0200 Subject: [PATCH 180/262] Duplicate Depends entry --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 2076a8210..d312e63bf 100644 --- a/debian/control +++ b/debian/control @@ -28,7 +28,7 @@ Depends: ${python:Depends}, ${misc:Depends} , redis-server , metronome (>=3.14.0) , git, curl, wget, cron, unzip, jq, bc - , lsb-release, haveged, fake-hwclock, equivs, lsof, whois, python-publicsuffix + , lsb-release, haveged, fake-hwclock, equivs, lsof, whois Recommends: yunohost-admin , ntp, inetutils-ping | iputils-ping , bash-completion, rsyslog From 6c3a74c5acb0d9b001ff1f1814c7ba7d8fc1f691 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 1 Sep 2020 17:19:50 +0200 Subject: [PATCH 181/262] Handle case where the lock doesn't exist : just assume we're the root logger --- src/yunohost/log.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 20305b2c6..6605d97f6 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -384,6 +384,12 @@ class OperationLogger(object): # We are a child of the first one we found return instance.name + # If no lock exists, we are probably in tests or yunohost is used as a + # lib ... let's not really care about that case and assume we're the + # root logger then. + if not os.path.exists("/var/run/moulinette_yunohost.lock"): + return None + locks = read_file("/var/run/moulinette_yunohost.lock").strip().split("\n") # If we're the process with the lock, we're the root logger if locks == [] or str(os.getpid()) in locks: From 624c7eaf776013871166b7a505f131dad9ec1b83 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 1 Sep 2020 17:21:12 +0200 Subject: [PATCH 182/262] Move import to top of file --- src/yunohost/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 6605d97f6..3b0ad8c08 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -29,6 +29,7 @@ import re import yaml import collections import glob +import psutil from datetime import datetime from logging import FileHandler, getLogger, Formatter @@ -408,7 +409,6 @@ class OperationLogger(object): recent_operation_logs = sorted(glob.iglob("/var/log/yunohost/categories/operation/*.log"), key=os.path.getctime, reverse=True)[:20] - import psutil proc = psutil.Process().parent() while proc is not None: # We use proc.open_files() to list files opened / actively used by this proc From bd064e317e825a08bd2bd81c80f12720cd955107 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 1 Sep 2020 17:22:07 +0200 Subject: [PATCH 183/262] Use global var --- src/yunohost/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 3b0ad8c08..73a5821c8 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -407,7 +407,7 @@ class OperationLogger(object): # 4. if among those file, there's an operation log file, we use the id # of the most recent file - recent_operation_logs = sorted(glob.iglob("/var/log/yunohost/categories/operation/*.log"), key=os.path.getctime, reverse=True)[:20] + recent_operation_logs = sorted(glob.iglob(OPERATIONS_PATH + "*.log"), key=os.path.getctime, reverse=True)[:20] proc = psutil.Process().parent() while proc is not None: From 0f2e9ab1d672361270072962e6ef4285b7dfe5eb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 1 Sep 2020 17:29:24 +0200 Subject: [PATCH 184/262] Lazy loading of smtplib to reduce memory footprint a bit --- src/yunohost/certificate.py | 2 +- src/yunohost/diagnosis.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 882e37863..64582c222 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -27,7 +27,6 @@ import sys import shutil import pwd import grp -import smtplib import subprocess import glob @@ -467,6 +466,7 @@ Subject: %s %s """ % (from_, to_, subject_, text) + import smtplib smtp = smtplib.SMTP("localhost") smtp.sendmail(from_, [to_], message) smtp.quit() diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index 76c4d1243..23884a53d 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -27,7 +27,6 @@ import re import os import time -import smtplib from moulinette import m18n, msettings from moulinette.utils import log @@ -583,6 +582,7 @@ Subject: %s %s """ % (from_, to_, subject_, disclaimer, content) + import smtplib smtp = smtplib.SMTP("localhost") smtp.sendmail(from_, [to_], message) smtp.quit() From 9a7bd77269cad1ed788c3050e42a702ab857fb57 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 1 Sep 2020 18:09:15 +0200 Subject: [PATCH 185/262] Get rid of unused complexity for other log categories ... it's been since 3.2 and we only have operations --- data/actionsmap/yunohost.yml | 3 -- src/yunohost/log.py | 94 +++++++++++++++--------------------- 2 files changed, 39 insertions(+), 58 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index d61538c5c..d5f60d73e 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1631,9 +1631,6 @@ log: action_help: List logs api: GET /logs arguments: - category: - help: Log category to display (default operations), could be operation, history, package, system, access, service or app - nargs: "*" -l: full: --limit help: Maximum number of logs diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 353e508fd..c6a3576aa 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -27,7 +27,6 @@ import os import re import yaml -import collections import glob import psutil @@ -43,8 +42,6 @@ from moulinette.utils.filesystem import read_file, read_yaml CATEGORIES_PATH = '/var/log/yunohost/categories/' OPERATIONS_PATH = '/var/log/yunohost/categories/operation/' -#CATEGORIES = ['operation', 'history', 'package', 'system', 'access', 'service', 'app'] -CATEGORIES = ['operation'] METADATA_FILE_EXT = '.yml' LOG_FILE_EXT = '.log' RELATED_CATEGORIES = ['app', 'domain', 'group', 'service', 'user'] @@ -52,77 +49,67 @@ RELATED_CATEGORIES = ['app', 'domain', 'group', 'service', 'user'] logger = getActionLogger('yunohost.log') -def log_list(category=[], limit=None, with_details=False): +def log_list(limit=None, with_details=False, with_suboperations=False): """ List available logs Keyword argument: limit -- Maximum number of logs - with_details -- Include details (e.g. if the operation was a success). Likely to increase the command time as it needs to open and parse the metadata file for each log... So try to use this in combination with --limit. + with_details -- Include details (e.g. if the operation was a success). + Likely to increase the command time as it needs to open and parse the + metadata file for each log... So try to use this in combination with + --limit. """ - categories = category is_api = msettings.get('interface') == 'api' - # In cli we just display `operation` logs by default - if not categories: - categories = CATEGORIES + operations = [] - result = collections.OrderedDict() - for category in categories: - result[category] = [] + logs = filter(lambda x: x.endswith(METADATA_FILE_EXT), + os.listdir(OPERATIONS_PATH)) + logs = list(reversed(sorted(logs))) - category_path = os.path.join(CATEGORIES_PATH, category) - if not os.path.exists(category_path): - logger.debug(m18n.n('log_category_404', category=category)) - continue + if limit is not None: + logs = logs[:limit] - logs = filter(lambda x: x.endswith(METADATA_FILE_EXT), - os.listdir(category_path)) - logs = list(reversed(sorted(logs))) + for log in logs: - if limit is not None: - logs = logs[:limit] + base_filename = log[:-len(METADATA_FILE_EXT)] + md_filename = log + md_path = os.path.join(OPERATIONS_PATH, md_filename) - for log in logs: + log = base_filename.split("-") - base_filename = log[:-len(METADATA_FILE_EXT)] - md_filename = log - md_path = os.path.join(category_path, md_filename) + entry = { + "name": base_filename, + "path": md_path, + } + entry["description"] = _get_description_from_name(base_filename) + try: + log_datetime = datetime.strptime(" ".join(log[:2]), + "%Y%m%d %H%M%S") + except ValueError: + pass + else: + entry["started_at"] = log_datetime - log = base_filename.split("-") - - entry = { - "name": base_filename, - "path": md_path, - } - entry["description"] = _get_description_from_name(base_filename) + if with_details: try: - log_datetime = datetime.strptime(" ".join(log[:2]), - "%Y%m%d %H%M%S") - except ValueError: - pass - else: - entry["started_at"] = log_datetime + metadata = read_yaml(md_path) + except Exception as e: + # If we can't read the yaml for some reason, report an error and ignore this entry... + logger.error(m18n.n('log_corrupted_md_file', md_file=md_path, error=e)) + continue + entry["success"] = metadata.get("success", "?") if metadata else "?" - if with_details: - try: - metadata = read_yaml(md_path) - except Exception as e: - # If we can't read the yaml for some reason, report an error and ignore this entry... - logger.error(m18n.n('log_corrupted_md_file', md_file=md_path, error=e)) - continue - entry["success"] = metadata.get("success", "?") if metadata else "?" - - result[category].append(entry) + operations.append(entry) # Reverse the order of log when in cli, more comfortable to read (avoid # unecessary scrolling) if not is_api: - for category in result: - result[category] = list(reversed(result[category])) + operations = list(reversed(operations)) - return result + return {"operation": operations} def log_display(path, number=None, share=False, filter_irrelevant=False): @@ -165,10 +152,7 @@ def log_display(path, number=None, share=False, filter_irrelevant=False): abs_path = path log_path = None if not path.startswith('/'): - for category in CATEGORIES: - abs_path = os.path.join(CATEGORIES_PATH, category, path) - if os.path.exists(abs_path) or os.path.exists(abs_path + METADATA_FILE_EXT): - break + abs_path = os.path.join(OPERATIONS_PATH, path) if os.path.exists(abs_path) and not path.endswith(METADATA_FILE_EXT): log_path = abs_path From ef4c3066ece6291dac7b3c0ae14b17420b56aed3 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Wed, 2 Sep 2020 12:55:37 +0200 Subject: [PATCH 186/262] [fix] Accept chinese/arabic domain --- data/actionsmap/yunohost.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index e2439b2dc..5f7f3bc66 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -112,7 +112,7 @@ user: help: Domain for the email address and xmpp account extra: pattern: &pattern_domain - - !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ + - !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W\d_]{2,})$ - "pattern_domain" -q: full: --mailbox-quota @@ -156,7 +156,7 @@ user: full: --mail extra: pattern: &pattern_email - - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ + - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W\d_]{2,})$ - "pattern_email" -p: full: --change-password From b94187466463169e7e9e85960e75401e24a10923 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Wed, 2 Sep 2020 12:56:41 +0200 Subject: [PATCH 187/262] [fix] Accept all domain --- data/actionsmap/yunohost.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 5f7f3bc66..e0f33e554 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -112,7 +112,7 @@ user: help: Domain for the email address and xmpp account extra: pattern: &pattern_domain - - !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W\d_]{2,})$ + - !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$ - "pattern_domain" -q: full: --mailbox-quota @@ -156,7 +156,7 @@ user: full: --mail extra: pattern: &pattern_email - - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W\d_]{2,})$ + - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$ - "pattern_email" -p: full: --change-password From 1da9666da7e632a9b04cc6a0144bf06e84633531 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 2 Sep 2020 16:07:25 +0200 Subject: [PATCH 188/262] Add description for new setting about backup compression --- locales/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/en.json b/locales/en.json index 8d76e2936..febcb51a9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -324,6 +324,7 @@ "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discard 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_setting_smtp_allow_ipv6": "Allow the use of IPv6 to receive and send mail", + "global_settings_setting_backup_compress_tar_archives": "When creating new backups, compress the archives (.tar.gz) instead of uncompressed archives (.tar). N.B. : enabling this option means create lighter backup archives, but the initial backup procedure will be significantly longer and heavy on CPU.", "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it is not a type supported by the system.", "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to use a variation of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).", From 083776bc4accc273884b7ce70729a0d67340998b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 2 Sep 2020 16:30:29 +0200 Subject: [PATCH 189/262] Get rid of example settings, only keep them for tests --- locales/en.json | 4 ---- src/yunohost/settings.py | 18 +++++++++++------- src/yunohost/tests/test_settings.py | 16 ++++++++++------ 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/locales/en.json b/locales/en.json index febcb51a9..7d95cac1a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -312,10 +312,6 @@ "global_settings_key_doesnt_exists": "The key '{settings_key:s}' does not exist in the global settings, you can see all the available keys by running 'yunohost settings list'", "global_settings_reset_success": "Previous settings now backed up to {path:s}", "global_settings_setting_pop3_enabled": "Enable the POP3 protocol for the mail server", - "global_settings_setting_example_bool": "Example boolean option", - "global_settings_setting_example_enum": "Example enum option", - "global_settings_setting_example_int": "Example int option", - "global_settings_setting_example_string": "Example string option", "global_settings_setting_security_nginx_compatibility": "Compatibility vs. security tradeoff for the web server NGINX. Affects the ciphers (and other security-related aspects)", "global_settings_setting_security_password_admin_strength": "Admin password strength", "global_settings_setting_security_password_user_strength": "User password strength", diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 71a63becd..9b8589d35 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -53,15 +53,11 @@ def is_boolean(value): # * enum (in the form of a python list) DEFAULTS = OrderedDict([ - ("example.bool", {"type": "bool", "default": True}), - ("example.int", {"type": "int", "default": 42}), - ("example.string", {"type": "string", "default": "yolo swag"}), - ("example.enum", {"type": "enum", "default": "a", "choices": ["a", "b", "c"]}), - # Password Validation # -1 disabled, 0 alert if listed, 1 8-letter, 2 normal, 3 strong, 4 strongest ("security.password.admin.strength", {"type": "int", "default": 1}), ("security.password.user.strength", {"type": "int", "default": 1}), + ("service.ssh.allow_deprecated_dsa_hostkey", {"type": "bool", "default": False}), ("security.ssh.compatibility", {"type": "enum", "default": "modern", "choices": ["intermediate", "modern"]}), @@ -69,6 +65,7 @@ DEFAULTS = OrderedDict([ "choices": ["intermediate", "modern"]}), ("security.postfix.compatibility", {"type": "enum", "default": "intermediate", "choices": ["intermediate", "modern"]}), + ("pop3.enabled", {"type": "bool", "default": False}), ("smtp.allow_ipv6", {"type": "bool", "default": True}), ("backup.compress_tar_archives", {"type": "bool", "default": False}), @@ -209,12 +206,19 @@ def settings_reset_all(): def _get_settings(): + + def get_setting_description(key): + if key.startswith("example"): + # (This is for dummy stuff used during unit tests) + return "Dummy %s setting" % key.split(".")[-1] + return m18n.n("global_settings_setting_%s" % key.replace(".", "_")) + settings = {} for key, value in DEFAULTS.copy().items(): settings[key] = value settings[key]["value"] = value["default"] - settings[key]["description"] = m18n.n("global_settings_setting_%s" % key.replace(".", "_")) + settings[key]["description"] = get_setting_description(key) if not os.path.exists(SETTINGS_PATH): return settings @@ -243,7 +247,7 @@ def _get_settings(): for key, value in local_settings.items(): if key in settings: settings[key] = value - settings[key]["description"] = m18n.n("global_settings_setting_%s" % key.replace(".", "_")) + settings[key]["description"] = get_setting_description(key) else: logger.warning(m18n.n('global_settings_unknown_setting_from_settings_file', setting_key=key)) diff --git a/src/yunohost/tests/test_settings.py b/src/yunohost/tests/test_settings.py index ea6c6922c..3fdfce67c 100644 --- a/src/yunohost/tests/test_settings.py +++ b/src/yunohost/tests/test_settings.py @@ -6,8 +6,12 @@ from yunohost.utils.error import YunohostError from yunohost.settings import settings_get, settings_list, _get_settings, \ settings_set, settings_reset, settings_reset_all, \ - SETTINGS_PATH_OTHER_LOCATION, SETTINGS_PATH + SETTINGS_PATH_OTHER_LOCATION, SETTINGS_PATH, DEFAULTS +DEFAULTS["example.bool"] = {"type": "bool", "default": True} +DEFAULTS["example.int"] = {"type": "int", "default": 42} +DEFAULTS["example.string"] = {"type": "string", "default": "yolo swag"} +DEFAULTS["example.enum"] = {"type": "enum", "default": "a", "choices": ["a", "b", "c"]} def setup_function(function): os.system("mv /etc/yunohost/settings.json /etc/yunohost/settings.json.saved") @@ -22,7 +26,7 @@ def test_settings_get_bool(): def test_settings_get_full_bool(): - assert settings_get("example.bool", True) == {"type": "bool", "value": True, "default": True, "description": "Example boolean option"} + assert settings_get("example.bool", True) == {"type": "bool", "value": True, "default": True, "description": "Dummy bool setting"} def test_settings_get_int(): @@ -30,7 +34,7 @@ def test_settings_get_int(): def test_settings_get_full_int(): - assert settings_get("example.int", True) == {"type": "int", "value": 42, "default": 42, "description": "Example int option"} + assert settings_get("example.int", True) == {"type": "int", "value": 42, "default": 42, "description": "Dummy int setting"} def test_settings_get_string(): @@ -38,7 +42,7 @@ def test_settings_get_string(): def test_settings_get_full_string(): - assert settings_get("example.string", True) == {"type": "string", "value": "yolo swag", "default": "yolo swag", "description": "Example string option"} + assert settings_get("example.string", True) == {"type": "string", "value": "yolo swag", "default": "yolo swag", "description": "Dummy string setting"} def test_settings_get_enum(): @@ -46,7 +50,7 @@ def test_settings_get_enum(): def test_settings_get_full_enum(): - assert settings_get("example.enum", True) == {"type": "enum", "value": "a", "default": "a", "description": "Example enum option", "choices": ["a", "b", "c"]} + assert settings_get("example.enum", True) == {"type": "enum", "value": "a", "default": "a", "description": "Dummy enum setting", "choices": ["a", "b", "c"]} def test_settings_get_doesnt_exists(): @@ -114,7 +118,7 @@ def test_settings_set_bad_value_enum(): def test_settings_list_modified(): settings_set("example.int", 21) - assert settings_list()["example.int"] == {'default': 42, 'description': 'Example int option', 'type': 'int', 'value': 21} + assert settings_list()["example.int"] == {'default': 42, 'description': 'Dummy int setting', 'type': 'int', 'value': 21} def test_reset(): From 955508c33ae9ba7cc2f9260cb0c45acb00f6ed49 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 2 Sep 2020 17:03:14 +0200 Subject: [PATCH 190/262] Wording --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 7d95cac1a..5b2d03897 100644 --- a/locales/en.json +++ b/locales/en.json @@ -386,7 +386,7 @@ "log_user_group_create": "Create '{}' group", "log_user_group_delete": "Delete '{}' group", "log_user_group_update": "Update '{}' group", - "log_user_update": "Update user info of '{}'", + "log_user_update": "Update info for user '{}'", "log_user_permission_update": "Update accesses for permission '{}'", "log_user_permission_reset": "Reset permission '{}'", "log_domain_main_domain": "Make '{}' the main domain", From 8831c638e64949a89f445f37d494f30c50d503c3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 2 Sep 2020 19:33:48 +0200 Subject: [PATCH 191/262] Add options to include metadata about suboperations --- data/actionsmap/yunohost.yml | 11 +++- src/yunohost/log.py | 110 +++++++++++++++++++++++++++-------- 2 files changed, 96 insertions(+), 25 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index d5f60d73e..129dbe0a9 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1633,12 +1633,17 @@ log: arguments: -l: full: --limit - help: Maximum number of logs + help: Maximum number of operations to list (default to 50) type: int + default: 50 -d: full: --with-details help: Show additional infos (e.g. operation success) but may significantly increase command time. Consider using --limit in combination with this. action: store_true + -s: + full: --with-suboperations + help: Include metadata about operations that are not the main operation but are sub-operations triggered by another ongoing operation... (e.g. initializing groups/permissions when installing an app) + action: store_true ### log_display() display: @@ -1659,6 +1664,10 @@ log: full: --filter-irrelevant help: Do not show some lines deemed not relevant (like set +x or helper argument parsing) action: store_true + -s: + full: --with-suboperations + help: Include metadata about sub-operations of this operation... (e.g. initializing groups/permissions when installing an app) + action: store_true ############################# diff --git a/src/yunohost/log.py b/src/yunohost/log.py index c6a3576aa..df2b0c195 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -30,7 +30,7 @@ import yaml import glob import psutil -from datetime import datetime +from datetime import datetime, timedelta from logging import FileHandler, getLogger, Formatter from moulinette import m18n, msettings @@ -57,13 +57,13 @@ def log_list(limit=None, with_details=False, with_suboperations=False): limit -- Maximum number of logs with_details -- Include details (e.g. if the operation was a success). Likely to increase the command time as it needs to open and parse the - metadata file for each log... So try to use this in combination with - --limit. + metadata file for each log... + with_suboperations -- Include operations that are not the "main" + operation but are sub-operations triggered by another ongoing operation + ... (e.g. initializing groups/permissions when installing an app) """ - is_api = msettings.get('interface') == 'api' - - operations = [] + operations = {} logs = filter(lambda x: x.endswith(METADATA_FILE_EXT), os.listdir(OPERATIONS_PATH)) @@ -75,44 +75,62 @@ def log_list(limit=None, with_details=False, with_suboperations=False): for log in logs: base_filename = log[:-len(METADATA_FILE_EXT)] - md_filename = log - md_path = os.path.join(OPERATIONS_PATH, md_filename) - - log = base_filename.split("-") + md_path = os.path.join(OPERATIONS_PATH, log) entry = { "name": base_filename, "path": md_path, + "description": _get_description_from_name(base_filename), } - entry["description"] = _get_description_from_name(base_filename) + try: - log_datetime = datetime.strptime(" ".join(log[:2]), - "%Y%m%d %H%M%S") + entry["started_at"] = _get_datetime_from_name(base_filename) except ValueError: pass - else: - entry["started_at"] = log_datetime + + try: + metadata = read_yaml(md_path) + except Exception as e: + # If we can't read the yaml for some reason, report an error and ignore this entry... + logger.error(m18n.n('log_corrupted_md_file', md_file=md_path, error=e)) + continue if with_details: - try: - metadata = read_yaml(md_path) - except Exception as e: - # If we can't read the yaml for some reason, report an error and ignore this entry... - logger.error(m18n.n('log_corrupted_md_file', md_file=md_path, error=e)) - continue entry["success"] = metadata.get("success", "?") if metadata else "?" + entry["parent"] = metadata.get("parent") - operations.append(entry) + if with_suboperations: + entry["parent"] = metadata.get("parent") + entry["suboperations"] = [] + elif metadata.get("parent") is not None: + continue + + operations[base_filename] = entry + + # When displaying suboperations, we build a tree-like structure where + # "suboperations" is a list of suboperations (each of them may also have a list of + # "suboperations" suboperations etc... + if with_suboperations: + suboperations = [o for o in operations.values() if o["parent"] is not None] + for suboperation in suboperations: + parent = operations.get(suboperation["parent"]) + if not parent: + continue + parent["suboperations"].append(suboperation) + operations = list(reversed(sorted([o for o in operations.values() if o["parent"] is None], key=lambda o: o["name"]))) + else: + operations = [o for o in operations.values()] # Reverse the order of log when in cli, more comfortable to read (avoid # unecessary scrolling) + is_api = msettings.get('interface') == 'api' if not is_api: operations = list(reversed(operations)) return {"operation": operations} -def log_display(path, number=None, share=False, filter_irrelevant=False): +def log_display(path, number=None, share=False, filter_irrelevant=False, with_suboperations=False): """ Display a log file enriched with metadata if any. @@ -211,6 +229,42 @@ def log_display(path, number=None, share=False, filter_irrelevant=False): if 'log_path' in metadata: log_path = metadata['log_path'] + if with_suboperations: + + def suboperations(): + try: + log_start = _get_datetime_from_name(base_filename) + except ValueError: + return + + for filename in os.listdir(OPERATIONS_PATH): + + if not filename.endswith(METADATA_FILE_EXT): + continue + + # We first retrict search to a ~48h time window to limit the number + # of .yml we look into + try: + date = _get_datetime_from_name(base_filename) + except ValueError: + continue + if (date < log_start) or (date > log_start + timedelta(hours=48)): + continue + + try: + submetadata = read_yaml(os.path.join(OPERATIONS_PATH, filename)) + except Exception: + continue + + if submetadata.get("parent") == base_filename: + yield { + "name": filename[:-len(METADATA_FILE_EXT)], + "description": _get_description_from_name(filename), + "success": submetadata.get("success", "?") + } + + metadata["suboperations"] = list(suboperations()) + # Display logs if exist if os.path.exists(log_path): from yunohost.service import _tail @@ -418,7 +472,6 @@ class OperationLogger(object): # If nothing found, assume we're the root operation logger return None - def start(self): """ Start to record logs that change the system @@ -578,6 +631,15 @@ class OperationLogger(object): self.error(m18n.n('log_operation_unit_unclosed_properly')) +def _get_datetime_from_name(name): + + # Filenames are expected to follow the format: + # 20200831-170740-short_description-and-stuff + + raw_datetime = " ".join(name.split("-")[:2]) + return datetime.strptime(raw_datetime, "%Y%m%d %H%M%S") + + def _get_description_from_name(name): """ Return the translated description from the filename From 7edd3ff84037220afa61a8c00fe1662ad011aa91 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 2 Sep 2020 20:30:50 +0200 Subject: [PATCH 192/262] Minor fixes after tests.. --- src/yunohost/log.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index df2b0c195..58205d49c 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -117,10 +117,11 @@ def log_list(limit=None, with_details=False, with_suboperations=False): if not parent: continue parent["suboperations"].append(suboperation) - operations = list(reversed(sorted([o for o in operations.values() if o["parent"] is None], key=lambda o: o["name"]))) + operations = [o for o in operations.values() if o["parent"] is None] else: operations = [o for o in operations.values()] + operations = list(reversed(sorted(operations, key=lambda o: o["name"]))) # Reverse the order of log when in cli, more comfortable to read (avoid # unecessary scrolling) is_api = msettings.get('interface') == 'api' @@ -259,7 +260,7 @@ def log_display(path, number=None, share=False, filter_irrelevant=False, with_su if submetadata.get("parent") == base_filename: yield { "name": filename[:-len(METADATA_FILE_EXT)], - "description": _get_description_from_name(filename), + "description": _get_description_from_name(filename[:-len(METADATA_FILE_EXT)]), "success": submetadata.get("success", "?") } From 88997f5728981358cfc1802a65dcd9b2ec654621 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 2 Sep 2020 20:57:51 +0200 Subject: [PATCH 193/262] Small old issue where the number of line filtered doesnt match the requested number, and then the admin doesn't display the 'show more line' button.. --- src/yunohost/log.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 58205d49c..892105c6b 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -269,11 +269,15 @@ def log_display(path, number=None, share=False, filter_irrelevant=False, with_su # Display logs if exist if os.path.exists(log_path): from yunohost.service import _tail - if number: + if number and filters: + logs = _tail(log_path, int(number*4)) + elif number: logs = _tail(log_path, int(number)) else: logs = read_file(log_path) logs = _filter_lines(logs, filters) + if number: + logs = logs[-number:] infos['log_path'] = log_path infos['logs'] = logs From 1288159a39f844c0f3c3338f2deb0d38b6a2711f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 3 Sep 2020 16:13:40 +0200 Subject: [PATCH 194/262] Require explicitly php7.3-foo packages because in some cases Sury's php7.4- packages are installed and php7.3-fpm doesn't get installed ... --- debian/control | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debian/control b/debian/control index d312e63bf..66c85d3f5 100644 --- a/debian/control +++ b/debian/control @@ -16,8 +16,8 @@ Depends: ${python:Depends}, ${misc:Depends} , python-miniupnpc, python-dbus, python-jinja2 , python-toml, python-packaging, python-publicsuffix , apt, apt-transport-https, dirmngr - , php-fpm (>= 7.3), php-ldap, php-intl - , mariadb-server, php-mysql | php-mysqlnd + , php7.3-common, php7.3-fpm, php7.3-ldap, php7.3-intl + , mariadb-server, php7.3-mysql , openssh-server, iptables, fail2ban, dnsutils, bind9utils , openssl, ca-certificates, netcat-openbsd, iproute2 , slapd, ldap-utils, sudo-ldap, libnss-ldapd, unscd, libpam-ldapd @@ -32,7 +32,7 @@ Depends: ${python:Depends}, ${misc:Depends} Recommends: yunohost-admin , ntp, inetutils-ping | iputils-ping , bash-completion, rsyslog - , php-gd, php-curl, php-gettext + , php7.3-gd, php7.3-curl, php-gettext , python-pip , unattended-upgrades , libdbd-ldap-perl, libnet-dns-perl From fa19c9480c817ee263e60f1592ee197e085ea8e7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 3 Sep 2020 17:01:24 +0200 Subject: [PATCH 195/262] Cleanup unused imports / misc 'fatal' flake8 errors --- data/hooks/diagnosis/14-ports.py | 1 - data/hooks/diagnosis/21-web.py | 1 - data/hooks/diagnosis/70-regenconf.py | 1 - src/yunohost/app.py | 2 +- src/yunohost/backup.py | 6 ++---- .../data_migrations/0016_php70_to_php73_pools.py | 2 +- .../data_migrations/0017_postgresql_9p6_to_11.py | 2 +- src/yunohost/firewall.py | 1 - src/yunohost/permission.py | 1 - src/yunohost/regenconf.py | 2 -- src/yunohost/tests/test_appscatalog.py | 2 +- src/yunohost/tests/test_permission.py | 12 +----------- src/yunohost/tests/test_settings.py | 4 ++-- src/yunohost/tools.py | 8 +++----- src/yunohost/user.py | 3 --- src/yunohost/utils/password.py | 6 +----- tests/remove_stale_translated_strings.py | 1 - 17 files changed, 13 insertions(+), 42 deletions(-) diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index 38f6e0089..eec2e5f03 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -3,7 +3,6 @@ import os from yunohost.diagnosis import Diagnoser -from yunohost.utils.error import YunohostError from yunohost.service import _get_services class PortsDiagnoser(Diagnoser): diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index c1f6d912a..94665a6e6 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -8,7 +8,6 @@ from moulinette.utils.filesystem import read_file from yunohost.diagnosis import Diagnoser from yunohost.domain import domain_list -from yunohost.utils.error import YunohostError DIAGNOSIS_SERVER = "diagnosis.yunohost.org" diff --git a/data/hooks/diagnosis/70-regenconf.py b/data/hooks/diagnosis/70-regenconf.py index 0dff71b5b..396f64a0f 100644 --- a/data/hooks/diagnosis/70-regenconf.py +++ b/data/hooks/diagnosis/70-regenconf.py @@ -2,7 +2,6 @@ import os -import subprocess from yunohost.diagnosis import Diagnoser from yunohost.regenconf import _get_regenconf_infos, _calculate_hash diff --git a/src/yunohost/app.py b/src/yunohost/app.py index f9f31c64a..0caa9d5e1 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -681,7 +681,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.log import OperationLogger - from yunohost.permission import user_permission_list, permission_create, permission_url, permission_delete, permission_sync_to_user, user_permission_update + from yunohost.permission import user_permission_list, permission_create, permission_url, permission_delete, permission_sync_to_user from yunohost.regenconf import manually_modified_files # Fetch or extract sources diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index bfe958a89..14d96f45c 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -894,8 +894,6 @@ class RestoreManager(): """ from permission import permission_sync_to_user - successfull_apps = self.targets.list("apps", include=["Success", "Warning"]) - permission_sync_to_user() if os.path.ismount(self.work_dir): @@ -1160,7 +1158,7 @@ class RestoreManager(): return from yunohost.user import user_group_list - from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list, permission_sync_to_user + from yunohost.permission import permission_create, permission_delete, user_permission_list, permission_sync_to_user # Backup old permission for apps # We need to do that because in case of an app is installed we can't remove the permission for this app @@ -1257,7 +1255,7 @@ class RestoreManager(): name should be already install) """ from yunohost.user import user_group_list - from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update, permission_sync_to_user + from yunohost.permission import permission_create, permission_delete, user_permission_list, permission_sync_to_user def copytree(src, dst, symlinks=False, ignore=None): for item in os.listdir(src): diff --git a/src/yunohost/data_migrations/0016_php70_to_php73_pools.py b/src/yunohost/data_migrations/0016_php70_to_php73_pools.py index 1cdb2bc4d..73875d359 100644 --- a/src/yunohost/data_migrations/0016_php70_to_php73_pools.py +++ b/src/yunohost/data_migrations/0016_php70_to_php73_pools.py @@ -4,7 +4,7 @@ from shutil import copy2 from moulinette.utils.log import getActionLogger -from yunohost.app import _is_installed, _get_app_settings, _set_app_settings, _patch_legacy_php_versions_in_settings +from yunohost.app import _is_installed, _patch_legacy_php_versions_in_settings from yunohost.tools import Migration from yunohost.service import _run_service_command diff --git a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py index 955393c5b..3cb904613 100644 --- a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py +++ b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py @@ -28,7 +28,7 @@ class MyMigration(Migration): # Make sure there's a 9.6 cluster try: self.runcmd("pg_lsclusters | grep -q '^9.6 '") - except Exception as e: + except Exception: logger.warning("It looks like there's not active 9.6 cluster, so probably don't need to run this migration") return diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index 4c3cfb0f7..c17e958e7 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -24,7 +24,6 @@ Manage firewall rules """ import os -import sys import yaml import miniupnpc diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 67a0f57b0..cd81489a2 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -31,7 +31,6 @@ import random from moulinette import m18n from moulinette.utils.log import getActionLogger from yunohost.utils.error import YunohostError -from yunohost.user import user_list from yunohost.log import is_unit_operation logger = getActionLogger('yunohost.user') diff --git a/src/yunohost/regenconf.py b/src/yunohost/regenconf.py index 6c9b10aac..7f4f73247 100644 --- a/src/yunohost/regenconf.py +++ b/src/yunohost/regenconf.py @@ -21,7 +21,6 @@ import os import yaml -import json import subprocess import shutil import hashlib @@ -31,7 +30,6 @@ from datetime import datetime from moulinette import m18n from moulinette.utils import log, filesystem -from moulinette.utils.filesystem import read_file from yunohost.utils.error import YunohostError from yunohost.log import is_unit_operation diff --git a/src/yunohost/tests/test_appscatalog.py b/src/yunohost/tests/test_appscatalog.py index 40cf1489f..d9f0c14e5 100644 --- a/src/yunohost/tests/test_appscatalog.py +++ b/src/yunohost/tests/test_appscatalog.py @@ -6,7 +6,7 @@ import glob import shutil from moulinette import m18n -from moulinette.utils.filesystem import read_json, write_to_json, write_to_yaml, mkdir +from moulinette.utils.filesystem import read_json, write_to_json, write_to_yaml from yunohost.utils.error import YunohostError from yunohost.app import (_initialize_apps_catalog_system, diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 82ec151f3..4dfc1ce6b 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -4,7 +4,7 @@ import os from conftest import message, raiseYunohostError, get_test_apps_dir -from yunohost.app import app_install, app_remove, app_change_url, app_list, app_map, _installed_apps +from yunohost.app import app_install, app_remove, app_change_url, app_map, _installed_apps from yunohost.user import user_list, user_create, user_delete, \ user_group_list, user_group_delete from yunohost.permission import user_permission_update, user_permission_list, user_permission_reset, \ @@ -370,16 +370,6 @@ def test_permission_reset_idempotency(): assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"]) -def test_permission_reset_idempotency(): - # Reset permission - user_permission_reset("blog.main") - user_permission_reset("blog.main") - - res = user_permission_list(full=True)['permissions'] - assert res['blog.main']['allowed'] == ["all_users"] - assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"]) - - # # Error on update function # diff --git a/src/yunohost/tests/test_settings.py b/src/yunohost/tests/test_settings.py index 3fdfce67c..c7105fd1b 100644 --- a/src/yunohost/tests/test_settings.py +++ b/src/yunohost/tests/test_settings.py @@ -64,10 +64,10 @@ def test_settings_list(): def test_settings_set(): settings_set("example.bool", False) - assert settings_get("example.bool") == False + assert settings_get("example.bool") is False settings_set("example.bool", "on") - assert settings_get("example.bool") == True + assert settings_get("example.bool") is True def test_settings_set_int(): settings_set("example.int", 21) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index b6d45b03c..2e90b38ee 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -26,10 +26,8 @@ import re import os import yaml -import json import subprocess import pwd -import socket from importlib import import_module from moulinette import msignals, m18n @@ -37,8 +35,8 @@ 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, read_yaml, write_to_yaml -from yunohost.app import _update_apps_catalog, app_info, app_upgrade, app_ssowatconf, app_list, _initialize_apps_catalog_system -from yunohost.domain import domain_add, domain_list +from yunohost.app import _update_apps_catalog, app_info, app_upgrade, _initialize_apps_catalog_system +from yunohost.domain import domain_add from yunohost.dyndns import _dyndns_available, _dyndns_provides from yunohost.firewall import firewall_upnp from yunohost.service import service_start, service_enable @@ -118,7 +116,7 @@ def tools_ldapinit(): if not os.path.isdir('/home/{0}'.format("admin")): logger.warning(m18n.n('user_home_creation_failed'), exc_info=1) - + logger.success(m18n.n('ldap_initialized')) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index d19da177c..35b316859 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -27,7 +27,6 @@ import os import re import pwd import grp -import json import crypt import random import string @@ -36,7 +35,6 @@ import copy from moulinette import m18n from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_json, write_to_json, read_yaml, write_to_yaml from yunohost.utils.error import YunohostError from yunohost.service import service_status @@ -239,7 +237,6 @@ def user_delete(operation_logger, username, purge=False): """ from yunohost.hook import hook_callback from yunohost.utils.ldap import _get_ldap_interface - from yunohost.permission import permission_sync_to_user if username not in user_list()["users"]: raise YunohostError('user_unknown', user=username) diff --git a/src/yunohost/utils/password.py b/src/yunohost/utils/password.py index b4f7025f7..e7ff6c275 100644 --- a/src/yunohost/utils/password.py +++ b/src/yunohost/utils/password.py @@ -63,7 +63,7 @@ class PasswordValidator(object): settings = json.load(open('/etc/yunohost/settings.json', "r")) setting_key = "security.password." + profile + ".strength" self.validation_strength = int(settings[setting_key]["value"]) - except Exception as e: + except Exception: # Fallback to default value if we can't fetch settings for some reason self.validation_strength = 1 @@ -83,11 +83,7 @@ class PasswordValidator(object): # on top (at least not the moulinette ones) # because the moulinette needs to be correctly initialized # as well as modules available in python's path. - import logging from yunohost.utils.error import YunohostError - from moulinette.utils.log import getActionLogger - - logger = logging.getLogger('yunohost.utils.password') status, msg = self.validation_summary(password) if status == "error": diff --git a/tests/remove_stale_translated_strings.py b/tests/remove_stale_translated_strings.py index 0213bc2be..4e39f131e 100644 --- a/tests/remove_stale_translated_strings.py +++ b/tests/remove_stale_translated_strings.py @@ -1,4 +1,3 @@ -import re import json import glob from collections import OrderedDict From ecbd63636b572750144258428b715d092317a4eb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 3 Sep 2020 17:07:39 +0200 Subject: [PATCH 196/262] autopep8 --in-place -a -a -a -a -a -r data/ doc/ tests/ src/yunohost/tests/ src/yunohost/utils/ src/yunohost/data_migrations/ --- data/actionsmap/yunohost_completion.py | 10 +- data/hooks/diagnosis/00-basesystem.py | 5 +- data/hooks/diagnosis/14-ports.py | 1 + data/hooks/diagnosis/24-mail.py | 6 +- data/hooks/diagnosis/30-services.py | 2 + data/hooks/diagnosis/50-systemresources.py | 10 +- doc/generate_helper_doc.py | 37 ++-- .../0017_postgresql_9p6_to_11.py | 3 +- .../data_migrations/0018_xtable_to_nftable.py | 1 - src/yunohost/tests/conftest.py | 6 +- .../tests/test_apps_arguments_parsing.py | 176 +++++++++--------- src/yunohost/tests/test_appscatalog.py | 4 +- src/yunohost/tests/test_backuprestore.py | 37 ++-- src/yunohost/tests/test_changeurl.py | 1 + src/yunohost/tests/test_permission.py | 17 +- src/yunohost/tests/test_regenconf.py | 3 +- src/yunohost/tests/test_service.py | 2 + src/yunohost/tests/test_settings.py | 2 + src/yunohost/tests/test_user-group.py | 4 +- src/yunohost/utils/error.py | 4 +- src/yunohost/utils/ldap.py | 16 +- src/yunohost/utils/network.py | 1 + src/yunohost/utils/yunopaste.py | 2 +- tests/remove_stale_translated_strings.py | 2 +- tests/test_actionmap.py | 1 + 25 files changed, 189 insertions(+), 164 deletions(-) diff --git a/data/actionsmap/yunohost_completion.py b/data/actionsmap/yunohost_completion.py index 45d15f16c..f4fee30ca 100644 --- a/data/actionsmap/yunohost_completion.py +++ b/data/actionsmap/yunohost_completion.py @@ -15,15 +15,17 @@ THIS_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) ACTIONSMAP_FILE = THIS_SCRIPT_DIR + '/yunohost.yml' BASH_COMPLETION_FILE = THIS_SCRIPT_DIR + '/../bash-completion.d/yunohost' + def get_dict_actions(OPTION_SUBTREE, category): ACTIONS = [action for action in OPTION_SUBTREE[category]["actions"].keys() - if not action.startswith('_')] + if not action.startswith('_')] ACTIONS_STR = '{}'.format(' '.join(ACTIONS)) - DICT = { "actions_str": ACTIONS_STR } + DICT = {"actions_str": ACTIONS_STR} return DICT + with open(ACTIONSMAP_FILE, 'r') as stream: # Getting the dictionary containning what actions are possible per category @@ -40,10 +42,10 @@ with open(ACTIONSMAP_FILE, 'r') as stream: ACTIONS_DICT[category]["subcategories_str"] = "" if "subcategories" in OPTION_TREE[category].keys(): - SUBCATEGORIES = [ subcategory for subcategory in OPTION_TREE[category]["subcategories"].keys() ] + SUBCATEGORIES = [subcategory for subcategory in OPTION_TREE[category]["subcategories"].keys()] SUBCATEGORIES_STR = '{}'.format(' '.join(SUBCATEGORIES)) - + ACTIONS_DICT[category]["subcategories_str"] = SUBCATEGORIES_STR for subcategory in SUBCATEGORIES: diff --git a/data/hooks/diagnosis/00-basesystem.py b/data/hooks/diagnosis/00-basesystem.py index ec802c870..8773f8b53 100644 --- a/data/hooks/diagnosis/00-basesystem.py +++ b/data/hooks/diagnosis/00-basesystem.py @@ -63,10 +63,10 @@ class BaseSystemDiagnoser(Diagnoser): ynh_core_version = ynh_packages["yunohost"]["version"] consistent_versions = all(infos["version"][:3] == ynh_core_version[:3] for infos in ynh_packages.values()) ynh_version_details = [("diagnosis_basesystem_ynh_single_version", - {"package":package, + {"package": package, "version": infos["version"], "repo": infos["repo"]} - ) + ) for package, infos in ynh_packages.items()] yield dict(meta={"test": "ynh_versions"}, @@ -75,7 +75,6 @@ class BaseSystemDiagnoser(Diagnoser): summary="diagnosis_basesystem_ynh_main_version" if consistent_versions else "diagnosis_basesystem_ynh_inconsistent_versions", details=ynh_version_details) - if self.is_vulnerable_to_meltdown(): yield dict(meta={"test": "meltdown"}, status="ERROR", diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index eec2e5f03..b74b3907e 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -5,6 +5,7 @@ import os from yunohost.diagnosis import Diagnoser from yunohost.service import _get_services + class PortsDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index a483e676d..fca9345ab 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -47,7 +47,6 @@ class MailDiagnoser(Diagnoser): status="SUCCESS", summary="diagnosis_mail_" + name + "_ok") - def check_outgoing_port_25(self): """ Check outgoing port 25 is open and not blocked by router @@ -64,7 +63,6 @@ class MailDiagnoser(Diagnoser): details=["diagnosis_mail_outgoing_port_25_blocked_details", "diagnosis_mail_outgoing_port_25_blocked_relay_vpn"]) - def check_ehlo(self): """ Check the server is reachable from outside and it's the good one @@ -99,7 +97,6 @@ class MailDiagnoser(Diagnoser): summary="diagnosis_mail_ehlo_wrong", details=["diagnosis_mail_ehlo_wrong_details"]) - def check_fcrdns(self): """ Check the reverse DNS is well defined by doing a Forward-confirmed @@ -148,7 +145,6 @@ class MailDiagnoser(Diagnoser): summary="diagnosis_mail_fcrdns_different_from_ehlo_domain", details=details) - def check_blacklist(self): """ Check with dig onto blacklist DNS server @@ -225,7 +221,6 @@ class MailDiagnoser(Diagnoser): status="SUCCESS", summary="diagnosis_mail_queue_ok") - def get_ips_checked(self): outgoing_ipversions = [] outgoing_ips = [] @@ -245,5 +240,6 @@ class MailDiagnoser(Diagnoser): outgoing_ips.append(global_ipv6) return (outgoing_ipversions, outgoing_ips) + def main(args, env, loggers): return MailDiagnoser(args, env, loggers).diagnose() diff --git a/data/hooks/diagnosis/30-services.py b/data/hooks/diagnosis/30-services.py index d0fe50ae9..562d48d6d 100644 --- a/data/hooks/diagnosis/30-services.py +++ b/data/hooks/diagnosis/30-services.py @@ -5,6 +5,7 @@ import os from yunohost.diagnosis import Diagnoser from yunohost.service import service_status + class ServicesDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] @@ -36,5 +37,6 @@ class ServicesDiagnoser(Diagnoser): yield item + def main(args, env, loggers): return ServicesDiagnoser(args, env, loggers).diagnose() diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index 6960b9bf0..00e5ee84a 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -7,6 +7,7 @@ import re from yunohost.diagnosis import Diagnoser + class SystemResourcesDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] @@ -16,7 +17,7 @@ class SystemResourcesDiagnoser(Diagnoser): def run(self): MB = 1024**2 - GB = MB*1024 + GB = MB * 1024 # # RAM @@ -79,7 +80,7 @@ class SystemResourcesDiagnoser(Diagnoser): # N.B.: we do not use usage.total because we want # to take into account the 5% security margin # correctly (c.f. the doc of psutil ...) - "total": human_size(usage.used+usage.free), + "total": human_size(usage.used + usage.free), "free": human_size(usage.free), "free_percent": free_percent}) @@ -96,7 +97,6 @@ class SystemResourcesDiagnoser(Diagnoser): item["status"] = "SUCCESS" item["summary"] = "diagnosis_diskusage_ok" - yield item # @@ -112,7 +112,6 @@ class SystemResourcesDiagnoser(Diagnoser): summary="diagnosis_processes_killed_by_oom_reaper", data={"kills_summary": kills_summary}) - def recent_kills_by_oom_reaper(self): if not os.path.exists("/var/log/kern.log"): return [] @@ -145,7 +144,7 @@ class SystemResourcesDiagnoser(Diagnoser): def human_size(bytes_): # Adapted from https://stackoverflow.com/a/1094933 - for unit in ['','ki','Mi','Gi','Ti','Pi','Ei','Zi']: + for unit in ['', 'ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: if abs(bytes_) < 1024.0: return "%s %sB" % (round_(bytes_), unit) bytes_ /= 1024.0 @@ -160,5 +159,6 @@ def round_(n): n = int(round(n)) return n + def main(args, env, loggers): return SystemResourcesDiagnoser(args, env, loggers).diagnose() diff --git a/doc/generate_helper_doc.py b/doc/generate_helper_doc.py index a7866b85b..a97d424eb 100644 --- a/doc/generate_helper_doc.py +++ b/doc/generate_helper_doc.py @@ -4,12 +4,13 @@ import os import glob import datetime + def render(helpers): - data = { "helpers": helpers, - "date": datetime.datetime.now().strftime("%m/%d/%Y"), - "version": open("../debian/changelog").readlines()[0].split()[1].strip("()") - } + data = {"helpers": helpers, + "date": datetime.datetime.now().strftime("%m/%d/%Y"), + "version": open("../debian/changelog").readlines()[0].split()[1].strip("()") + } from jinja2 import Template from ansi2html import Ansi2HTMLConverter @@ -22,13 +23,14 @@ def render(helpers): return conv.convert(shell, False) template = open("helper_doc_template.html", "r").read() - t = Template(template) + t = Template(template) t.globals['now'] = datetime.datetime.utcnow result = t.render(data=data, convert=shell_to_html, shell_css=shell_css) open("helpers.html", "w").write(result) ############################################################################## + class Parser(): def __init__(self, filename): @@ -42,15 +44,15 @@ class Parser(): self.blocks = [] current_reading = "void" - current_block = { "name": None, - "line": -1, - "comments": [], - "code": [] } + current_block = {"name": None, + "line": -1, + "comments": [], + "code": []} for i, line in enumerate(self.file): if line.startswith("#!/bin/bash"): - continue + continue line = line.rstrip().replace("\t", " ") @@ -73,7 +75,7 @@ class Parser(): elif line.strip() == "": # Well eh that was not an actual helper definition ... start over ? current_reading = "void" - current_block = { "name": None, + current_block = {"name": None, "line": -1, "comments": [], "code": [] @@ -101,10 +103,10 @@ class Parser(): # (we ignore helpers containing [internal] ...) if not "[internal]" in current_block["comments"]: self.blocks.append(current_block) - current_block = { "name": None, - "line": -1, - "comments": [], - "code": [] } + current_block = {"name": None, + "line": -1, + "comments": [], + "code": []} else: current_block["code"].append(line) @@ -180,13 +182,14 @@ class Parser(): b["usage"] = b["usage"].strip() - def is_global_comment(line): return line.startswith('#') + def malformed_error(line_number): return "Malformed file line {} ?".format(line_number) + def main(): helper_files = sorted(glob.glob("../data/helpers.d/*")) @@ -204,5 +207,5 @@ def main(): render(helpers) -main() +main() diff --git a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py index 3cb904613..b81cbd8be 100644 --- a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py +++ b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py @@ -36,7 +36,7 @@ class MyMigration(Migration): raise YunohostError("migration_0017_not_enough_space", path="/var/lib/postgresql/") self.runcmd("systemctl stop postgresql") - self.runcmd("pg_dropcluster --stop 11 main || true") # We do not trigger an exception if the command fails because that probably means cluster 11 doesn't exists, which is fine because it's created during the pg_upgradecluster) + self.runcmd("pg_dropcluster --stop 11 main || true") # We do not trigger an exception if the command fails because that probably means cluster 11 doesn't exists, which is fine because it's created during the pg_upgradecluster) self.runcmd("pg_upgradecluster -m upgrade 9.6 main") self.runcmd("pg_dropcluster --stop 9.6 main") self.runcmd("systemctl start postgresql") @@ -63,4 +63,3 @@ class MyMigration(Migration): out = out.strip().split("\n") return (returncode, out, err) - diff --git a/src/yunohost/data_migrations/0018_xtable_to_nftable.py b/src/yunohost/data_migrations/0018_xtable_to_nftable.py index 2f931a6ef..8a7e11412 100644 --- a/src/yunohost/data_migrations/0018_xtable_to_nftable.py +++ b/src/yunohost/data_migrations/0018_xtable_to_nftable.py @@ -106,4 +106,3 @@ class MyMigration(Migration): out = out.strip().split("\n") return (returncode, out, err) - diff --git a/src/yunohost/tests/conftest.py b/src/yunohost/tests/conftest.py index 1bfbd0cec..2bda72852 100644 --- a/src/yunohost/tests/conftest.py +++ b/src/yunohost/tests/conftest.py @@ -18,10 +18,12 @@ def clone_test_app(request): else: os.system("cd %s/apps && git pull > /dev/null 2>&1" % cwd) + def get_test_apps_dir(): cwd = os.path.split(os.path.realpath(__file__))[0] return os.path.join(cwd, "apps") + @contextmanager def message(mocker, key, **kwargs): mocker.spy(m18n, "n") @@ -38,7 +40,6 @@ def raiseYunohostError(mocker, key, **kwargs): assert e_info._excinfo[1].kwargs == kwargs - def pytest_addoption(parser): parser.addoption("--yunodebug", action="store_true", default=False) @@ -56,12 +57,15 @@ def new_translate(self, key, *args, **kwargs): raise KeyError("Unable to retrieve key %s for default locale !" % key) return old_translate(self, key, *args, **kwargs) + + moulinette.core.Translator.translate = new_translate def new_m18nn(self, key, *args, **kwargs): return self._namespaces[self._current_namespace].translate(key, *args, **kwargs) + moulinette.core.Moulinette18n.n = new_m18nn # diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index a3d5b7f09..03ed9e93a 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -40,21 +40,21 @@ def test_parse_args_in_yunohost_format_empty(): def test_parse_args_in_yunohost_format_string(): - questions = [{"name": "some_string", "type": "string",}] + questions = [{"name": "some_string", "type": "string", }] answers = {"some_string": "some_value"} expected_result = OrderedDict({"some_string": ("some_value", "string")}) assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_string_default_type(): - questions = [{"name": "some_string",}] + questions = [{"name": "some_string", }] answers = {"some_string": "some_value"} expected_result = OrderedDict({"some_string": ("some_value", "string")}) assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_string_no_input(): - questions = [{"name": "some_string",}] + questions = [{"name": "some_string", }] answers = {} with pytest.raises(YunohostError): @@ -62,7 +62,7 @@ def test_parse_args_in_yunohost_format_string_no_input(): def test_parse_args_in_yunohost_format_string_input(): - questions = [{"name": "some_string", "ask": "some question",}] + questions = [{"name": "some_string", "ask": "some question", }] answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) @@ -72,7 +72,7 @@ def test_parse_args_in_yunohost_format_string_input(): @pytest.mark.skip # that shit should work x( def test_parse_args_in_yunohost_format_string_input_no_ask(): - questions = [{"name": "some_string",}] + questions = [{"name": "some_string", }] answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) @@ -81,14 +81,14 @@ def test_parse_args_in_yunohost_format_string_input_no_ask(): def test_parse_args_in_yunohost_format_string_no_input_optional(): - questions = [{"name": "some_string", "optional": True,}] + questions = [{"name": "some_string", "optional": True, }] answers = {} expected_result = OrderedDict({"some_string": ("", "string")}) assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_string_optional_with_input(): - questions = [{"name": "some_string", "ask": "some question", "optional": True,}] + questions = [{"name": "some_string", "ask": "some question", "optional": True, }] answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) @@ -98,7 +98,7 @@ def test_parse_args_in_yunohost_format_string_optional_with_input(): @pytest.mark.skip # this should work without ask def test_parse_args_in_yunohost_format_string_optional_with_input_without_ask(): - questions = [{"name": "some_string", "optional": True,}] + questions = [{"name": "some_string", "optional": True, }] answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) @@ -108,7 +108,7 @@ def test_parse_args_in_yunohost_format_string_optional_with_input_without_ask(): def test_parse_args_in_yunohost_format_string_no_input_default(): questions = [ - {"name": "some_string", "ask": "some question", "default": "some_value",} + {"name": "some_string", "ask": "some question", "default": "some_value", } ] answers = {} expected_result = OrderedDict({"some_string": ("some_value", "string")}) @@ -117,7 +117,7 @@ def test_parse_args_in_yunohost_format_string_no_input_default(): def test_parse_args_in_yunohost_format_string_input_test_ask(): ask_text = "some question" - questions = [{"name": "some_string", "ask": ask_text,}] + questions = [{"name": "some_string", "ask": ask_text, }] answers = {} with patch.object(msignals, "prompt", return_value="some_value") as prompt: @@ -128,7 +128,7 @@ def test_parse_args_in_yunohost_format_string_input_test_ask(): def test_parse_args_in_yunohost_format_string_input_test_ask_with_default(): ask_text = "some question" default_text = "some example" - questions = [{"name": "some_string", "ask": ask_text, "default": default_text,}] + questions = [{"name": "some_string", "ask": ask_text, "default": default_text, }] answers = {} with patch.object(msignals, "prompt", return_value="some_value") as prompt: @@ -140,7 +140,7 @@ def test_parse_args_in_yunohost_format_string_input_test_ask_with_default(): def test_parse_args_in_yunohost_format_string_input_test_ask_with_example(): ask_text = "some question" example_text = "some example" - questions = [{"name": "some_string", "ask": ask_text, "example": example_text,}] + questions = [{"name": "some_string", "ask": ask_text, "example": example_text, }] answers = {} with patch.object(msignals, "prompt", return_value="some_value") as prompt: @@ -153,7 +153,7 @@ def test_parse_args_in_yunohost_format_string_input_test_ask_with_example(): def test_parse_args_in_yunohost_format_string_input_test_ask_with_help(): ask_text = "some question" help_text = "some_help" - questions = [{"name": "some_string", "ask": ask_text, "help": help_text,}] + questions = [{"name": "some_string", "ask": ask_text, "help": help_text, }] answers = {} with patch.object(msignals, "prompt", return_value="some_value") as prompt: @@ -188,7 +188,7 @@ def test_parse_args_in_yunohost_format_string_with_choice_bad(): def test_parse_args_in_yunohost_format_string_with_choice_ask(): ask_text = "some question" choices = ["fr", "en", "es", "it", "ru"] - questions = [{"name": "some_string", "ask": ask_text, "choices": choices,}] + questions = [{"name": "some_string", "ask": ask_text, "choices": choices, }] answers = {} with patch.object(msignals, "prompt", return_value="ru") as prompt: @@ -214,14 +214,14 @@ def test_parse_args_in_yunohost_format_string_with_choice_default(): def test_parse_args_in_yunohost_format_password(): - questions = [{"name": "some_password", "type": "password",}] + questions = [{"name": "some_password", "type": "password", }] answers = {"some_password": "some_value"} expected_result = OrderedDict({"some_password": ("some_value", "password")}) assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_password_no_input(): - questions = [{"name": "some_password", "type": "password",}] + questions = [{"name": "some_password", "type": "password", }] answers = {} with pytest.raises(YunohostError): @@ -229,7 +229,7 @@ def test_parse_args_in_yunohost_format_password_no_input(): def test_parse_args_in_yunohost_format_password_input(): - questions = [{"name": "some_password", "type": "password", "ask": "some question",}] + questions = [{"name": "some_password", "type": "password", "ask": "some question", }] answers = {} expected_result = OrderedDict({"some_password": ("some_value", "password")}) @@ -239,7 +239,7 @@ def test_parse_args_in_yunohost_format_password_input(): @pytest.mark.skip # that shit should work x( def test_parse_args_in_yunohost_format_password_input_no_ask(): - questions = [{"name": "some_password", "type": "password",}] + questions = [{"name": "some_password", "type": "password", }] answers = {} expected_result = OrderedDict({"some_password": ("some_value", "password")}) @@ -248,7 +248,7 @@ def test_parse_args_in_yunohost_format_password_input_no_ask(): def test_parse_args_in_yunohost_format_password_no_input_optional(): - questions = [{"name": "some_password", "type": "password", "optional": True,}] + questions = [{"name": "some_password", "type": "password", "optional": True, }] answers = {} expected_result = OrderedDict({"some_password": ("", "password")}) assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -272,7 +272,7 @@ def test_parse_args_in_yunohost_format_password_optional_with_input(): @pytest.mark.skip # this should work without ask def test_parse_args_in_yunohost_format_password_optional_with_input_without_ask(): - questions = [{"name": "some_password", "type": "password", "optional": True,}] + questions = [{"name": "some_password", "type": "password", "optional": True, }] answers = {} expected_result = OrderedDict({"some_password": ("some_value", "password")}) @@ -316,7 +316,7 @@ def test_parse_args_in_yunohost_format_password_no_input_example(): def test_parse_args_in_yunohost_format_password_input_test_ask(): ask_text = "some question" - questions = [{"name": "some_password", "type": "password", "ask": ask_text,}] + questions = [{"name": "some_password", "type": "password", "ask": ask_text, }] answers = {} with patch.object(msignals, "prompt", return_value="some_value") as prompt: @@ -365,14 +365,14 @@ def test_parse_args_in_yunohost_format_password_input_test_ask_with_help(): def test_parse_args_in_yunohost_format_path(): - questions = [{"name": "some_path", "type": "path",}] + questions = [{"name": "some_path", "type": "path", }] answers = {"some_path": "some_value"} expected_result = OrderedDict({"some_path": ("some_value", "path")}) assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_path_no_input(): - questions = [{"name": "some_path", "type": "path",}] + questions = [{"name": "some_path", "type": "path", }] answers = {} with pytest.raises(YunohostError): @@ -380,7 +380,7 @@ def test_parse_args_in_yunohost_format_path_no_input(): def test_parse_args_in_yunohost_format_path_input(): - questions = [{"name": "some_path", "type": "path", "ask": "some question",}] + questions = [{"name": "some_path", "type": "path", "ask": "some question", }] answers = {} expected_result = OrderedDict({"some_path": ("some_value", "path")}) @@ -390,7 +390,7 @@ def test_parse_args_in_yunohost_format_path_input(): @pytest.mark.skip # that shit should work x( def test_parse_args_in_yunohost_format_path_input_no_ask(): - questions = [{"name": "some_path", "type": "path",}] + questions = [{"name": "some_path", "type": "path", }] answers = {} expected_result = OrderedDict({"some_path": ("some_value", "path")}) @@ -399,7 +399,7 @@ def test_parse_args_in_yunohost_format_path_input_no_ask(): def test_parse_args_in_yunohost_format_path_no_input_optional(): - questions = [{"name": "some_path", "type": "path", "optional": True,}] + questions = [{"name": "some_path", "type": "path", "optional": True, }] answers = {} expected_result = OrderedDict({"some_path": ("", "path")}) assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -407,7 +407,7 @@ def test_parse_args_in_yunohost_format_path_no_input_optional(): def test_parse_args_in_yunohost_format_path_optional_with_input(): questions = [ - {"name": "some_path", "ask": "some question", "type": "path", "optional": True,} + {"name": "some_path", "ask": "some question", "type": "path", "optional": True, } ] answers = {} expected_result = OrderedDict({"some_path": ("some_value", "path")}) @@ -418,7 +418,7 @@ def test_parse_args_in_yunohost_format_path_optional_with_input(): @pytest.mark.skip # this should work without ask def test_parse_args_in_yunohost_format_path_optional_with_input_without_ask(): - questions = [{"name": "some_path", "type": "path", "optional": True,}] + questions = [{"name": "some_path", "type": "path", "optional": True, }] answers = {} expected_result = OrderedDict({"some_path": ("some_value", "path")}) @@ -442,7 +442,7 @@ def test_parse_args_in_yunohost_format_path_no_input_default(): def test_parse_args_in_yunohost_format_path_input_test_ask(): ask_text = "some question" - questions = [{"name": "some_path", "type": "path", "ask": ask_text,}] + questions = [{"name": "some_path", "type": "path", "ask": ask_text, }] answers = {} with patch.object(msignals, "prompt", return_value="some_value") as prompt: @@ -454,7 +454,7 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_default(): ask_text = "some question" default_text = "some example" questions = [ - {"name": "some_path", "type": "path", "ask": ask_text, "default": default_text,} + {"name": "some_path", "type": "path", "ask": ask_text, "default": default_text, } ] answers = {} @@ -468,7 +468,7 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_example(): ask_text = "some question" example_text = "some example" questions = [ - {"name": "some_path", "type": "path", "ask": ask_text, "example": example_text,} + {"name": "some_path", "type": "path", "ask": ask_text, "example": example_text, } ] answers = {} @@ -483,7 +483,7 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_help(): ask_text = "some question" help_text = "some_help" questions = [ - {"name": "some_path", "type": "path", "ask": ask_text, "help": help_text,} + {"name": "some_path", "type": "path", "ask": ask_text, "help": help_text, } ] answers = {} @@ -494,89 +494,89 @@ def test_parse_args_in_yunohost_format_path_input_test_ask_with_help(): def test_parse_args_in_yunohost_format_boolean(): - questions = [{"name": "some_boolean", "type": "boolean",}] + questions = [{"name": "some_boolean", "type": "boolean", }] answers = {"some_boolean": "y"} expected_result = OrderedDict({"some_boolean": (1, "boolean")}) assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_boolean_all_yes(): - questions = [{"name": "some_boolean", "type": "boolean",}] + questions = [{"name": "some_boolean", "type": "boolean", }] expected_result = OrderedDict({"some_boolean": (1, "boolean")}) assert ( - _parse_args_in_yunohost_format({"some_boolean": "y"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "y"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "Y"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "Y"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "yes"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "yes"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "Yes"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "Yes"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "YES"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "YES"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "1"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "1"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": 1}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": 1}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": True}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": True}, questions) == + expected_result ) def test_parse_args_in_yunohost_format_boolean_all_no(): - questions = [{"name": "some_boolean", "type": "boolean",}] + questions = [{"name": "some_boolean", "type": "boolean", }] expected_result = OrderedDict({"some_boolean": (0, "boolean")}) assert ( - _parse_args_in_yunohost_format({"some_boolean": "n"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "n"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "N"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "N"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "no"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "no"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "No"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "No"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "No"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "No"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": "0"}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": "0"}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": 0}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": 0}, questions) == + expected_result ) assert ( - _parse_args_in_yunohost_format({"some_boolean": False}, questions) - == expected_result + _parse_args_in_yunohost_format({"some_boolean": False}, questions) == + expected_result ) # XXX apparently boolean are always False (0) by default, I'm not sure what to think about that def test_parse_args_in_yunohost_format_boolean_no_input(): - questions = [{"name": "some_boolean", "type": "boolean",}] + questions = [{"name": "some_boolean", "type": "boolean", }] answers = {} expected_result = OrderedDict({"some_boolean": (0, "boolean")}) @@ -584,7 +584,7 @@ def test_parse_args_in_yunohost_format_boolean_no_input(): def test_parse_args_in_yunohost_format_boolean_bad_input(): - questions = [{"name": "some_boolean", "type": "boolean",}] + questions = [{"name": "some_boolean", "type": "boolean", }] answers = {"some_boolean": "stuff"} with pytest.raises(YunohostError): @@ -592,7 +592,7 @@ def test_parse_args_in_yunohost_format_boolean_bad_input(): def test_parse_args_in_yunohost_format_boolean_input(): - questions = [{"name": "some_boolean", "type": "boolean", "ask": "some question",}] + questions = [{"name": "some_boolean", "type": "boolean", "ask": "some question", }] answers = {} expected_result = OrderedDict({"some_boolean": (1, "boolean")}) @@ -606,7 +606,7 @@ def test_parse_args_in_yunohost_format_boolean_input(): @pytest.mark.skip # we should work def test_parse_args_in_yunohost_format_boolean_input_no_ask(): - questions = [{"name": "some_boolean", "type": "boolean",}] + questions = [{"name": "some_boolean", "type": "boolean", }] answers = {} expected_result = OrderedDict({"some_boolean": ("some_value", "boolean")}) @@ -615,7 +615,7 @@ def test_parse_args_in_yunohost_format_boolean_input_no_ask(): def test_parse_args_in_yunohost_format_boolean_no_input_optional(): - questions = [{"name": "some_boolean", "type": "boolean", "optional": True,}] + questions = [{"name": "some_boolean", "type": "boolean", "optional": True, }] answers = {} expected_result = OrderedDict({"some_boolean": (0, "boolean")}) # default to false assert _parse_args_in_yunohost_format(answers, questions) == expected_result @@ -638,7 +638,7 @@ def test_parse_args_in_yunohost_format_boolean_optional_with_input(): def test_parse_args_in_yunohost_format_boolean_optional_with_input_without_ask(): - questions = [{"name": "some_boolean", "type": "boolean", "optional": True,}] + questions = [{"name": "some_boolean", "type": "boolean", "optional": True, }] answers = {} expected_result = OrderedDict({"some_boolean": (0, "boolean")}) @@ -677,7 +677,7 @@ def test_parse_args_in_yunohost_format_boolean_bad_default(): def test_parse_args_in_yunohost_format_boolean_input_test_ask(): ask_text = "some question" - questions = [{"name": "some_boolean", "type": "boolean", "ask": ask_text,}] + questions = [{"name": "some_boolean", "type": "boolean", "ask": ask_text, }] answers = {} with patch.object(msignals, "prompt", return_value=0) as prompt: @@ -704,7 +704,7 @@ def test_parse_args_in_yunohost_format_boolean_input_test_ask_with_default(): def test_parse_args_in_yunohost_format_domain_empty(): - questions = [{"name": "some_domain", "type": "domain",}] + questions = [{"name": "some_domain", "type": "domain", }] answers = {} with patch.object( @@ -719,7 +719,7 @@ def test_parse_args_in_yunohost_format_domain_empty(): def test_parse_args_in_yunohost_format_domain(): main_domain = "my_main_domain.com" domains = [main_domain] - questions = [{"name": "some_domain", "type": "domain",}] + questions = [{"name": "some_domain", "type": "domain", }] answers = {"some_domain": main_domain} expected_result = OrderedDict({"some_domain": (main_domain, "domain")}) @@ -735,7 +735,7 @@ def test_parse_args_in_yunohost_format_domain_two_domains(): other_domain = "some_other_domain.tld" domains = [main_domain, other_domain] - questions = [{"name": "some_domain", "type": "domain",}] + questions = [{"name": "some_domain", "type": "domain", }] answers = {"some_domain": other_domain} expected_result = OrderedDict({"some_domain": (other_domain, "domain")}) @@ -758,7 +758,7 @@ def test_parse_args_in_yunohost_format_domain_two_domains_wrong_answer(): other_domain = "some_other_domain.tld" domains = [main_domain, other_domain] - questions = [{"name": "some_domain", "type": "domain",}] + questions = [{"name": "some_domain", "type": "domain", }] answers = {"some_domain": "doesnt_exist.pouet"} with patch.object( @@ -774,7 +774,7 @@ def test_parse_args_in_yunohost_format_domain_two_domains_default_no_ask(): other_domain = "some_other_domain.tld" domains = [main_domain, other_domain] - questions = [{"name": "some_domain", "type": "domain",}] + questions = [{"name": "some_domain", "type": "domain", }] answers = {} expected_result = OrderedDict({"some_domain": (main_domain, "domain")}) @@ -831,7 +831,7 @@ def test_parse_args_in_yunohost_format_user_empty(): } } - questions = [{"name": "some_user", "type": "user",}] + questions = [{"name": "some_user", "type": "user", }] answers = {} with patch.object(user, "user_list", return_value={"users": users}): @@ -852,7 +852,7 @@ def test_parse_args_in_yunohost_format_user(): } } - questions = [{"name": "some_user", "type": "user",}] + questions = [{"name": "some_user", "type": "user", }] answers = {"some_user": username} expected_result = OrderedDict({"some_user": (username, "user")}) @@ -883,7 +883,7 @@ def test_parse_args_in_yunohost_format_user_two_users(): }, } - questions = [{"name": "some_user", "type": "user",}] + questions = [{"name": "some_user", "type": "user", }] answers = {"some_user": other_user} expected_result = OrderedDict({"some_user": (other_user, "user")}) @@ -919,7 +919,7 @@ def test_parse_args_in_yunohost_format_user_two_users_wrong_answer(): }, } - questions = [{"name": "some_user", "type": "user",}] + questions = [{"name": "some_user", "type": "user", }] answers = {"some_user": "doesnt_exist.pouet"} with patch.object(user, "user_list", return_value={"users": users}): @@ -1002,7 +1002,7 @@ def test_parse_args_in_yunohost_format_app_empty(): } ] - questions = [{"name": "some_app", "type": "app",}] + questions = [{"name": "some_app", "type": "app", }] answers = {} with patch.object(app, "app_list", return_value={"apps": apps}): @@ -1012,7 +1012,7 @@ def test_parse_args_in_yunohost_format_app_empty(): def test_parse_args_in_yunohost_format_app_no_apps(): apps = [] - questions = [{"name": "some_app", "type": "app",}] + questions = [{"name": "some_app", "type": "app", }] answers = {} with patch.object(app, "app_list", return_value={"apps": apps}): @@ -1041,7 +1041,7 @@ def test_parse_args_in_yunohost_format_app(): } ] - questions = [{"name": "some_app", "type": "app",}] + questions = [{"name": "some_app", "type": "app", }] answers = {"some_app": app_name} expected_result = OrderedDict({"some_app": (app_name, "app")}) @@ -1072,7 +1072,7 @@ def test_parse_args_in_yunohost_format_app_two_apps(): }, ] - questions = [{"name": "some_app", "type": "app",}] + questions = [{"name": "some_app", "type": "app", }] answers = {"some_app": other_app} expected_result = OrderedDict({"some_app": (other_app, "app")}) @@ -1112,7 +1112,7 @@ def test_parse_args_in_yunohost_format_app_two_apps_wrong_answer(): }, ] - questions = [{"name": "some_app", "type": "app",}] + questions = [{"name": "some_app", "type": "app", }] answers = {"some_app": "doesnt_exist"} with pytest.raises(YunohostError): diff --git a/src/yunohost/tests/test_appscatalog.py b/src/yunohost/tests/test_appscatalog.py index d9f0c14e5..a173501d3 100644 --- a/src/yunohost/tests/test_appscatalog.py +++ b/src/yunohost/tests/test_appscatalog.py @@ -37,10 +37,12 @@ DUMMY_APP_CATALOG = """{ } """ + class AnyStringWith(str): def __eq__(self, other): return self in other + def setup_function(function): # Clear apps catalog cache @@ -165,6 +167,7 @@ def test_apps_catalog_update_404(mocker): _update_apps_catalog() m18n.n.assert_any_call("apps_catalog_failed_to_download") + def test_apps_catalog_update_timeout(mocker): # Initialize ... @@ -237,7 +240,6 @@ def test_apps_catalog_load_with_empty_cache(mocker): m18n.n.assert_any_call("apps_catalog_obsolete_cache") m18n.n.assert_any_call("apps_catalog_update_success") - # Cache shouldn't be empty anymore empty assert glob.glob(APPS_CATALOG_CACHE + "/*") diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 35a3e23f1..d07ebff01 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -16,6 +16,7 @@ from yunohost.hook import CUSTOM_HOOK_FOLDER # Get main domain maindomain = "" + def setup_function(function): global maindomain @@ -32,7 +33,7 @@ def setup_function(function): assert len(backup_list()["archives"]) == 0 - markers = [m.name for m in function.__dict__.get("pytestmark",[])] + markers = [m.name for m in function.__dict__.get("pytestmark", [])] if "with_wordpress_archive_from_2p4" in markers: add_archive_wordpress_from_2p4() @@ -75,7 +76,7 @@ def teardown_function(function): delete_all_backups() uninstall_test_apps_if_needed() - markers = [m.name for m in function.__dict__.get("pytestmark",[])] + markers = [m.name for m in function.__dict__.get("pytestmark", [])] if "clean_opt_dir" in markers: shutil.rmtree("/opt/test_backup_output_directory") @@ -90,6 +91,7 @@ def check_LDAP_db_integrity_call(): yield check_LDAP_db_integrity() + @pytest.fixture(autouse=True) def check_permission_for_apps_call(): check_permission_for_apps() @@ -100,6 +102,7 @@ def check_permission_for_apps_call(): # Helpers # # + def app_is_installed(app): if app == "permissions_app": @@ -184,22 +187,22 @@ def add_archive_wordpress_from_2p4(): os.system("mkdir -p /home/yunohost.backup/archives") - os.system("cp " + os.path.join(get_test_apps_dir(), "backup_wordpress_from_2p4/backup.info.json") + \ - " /home/yunohost.backup/archives/backup_wordpress_from_2p4.info.json") + os.system("cp " + os.path.join(get_test_apps_dir(), "backup_wordpress_from_2p4/backup.info.json") + + " /home/yunohost.backup/archives/backup_wordpress_from_2p4.info.json") - os.system("cp " + os.path.join(get_test_apps_dir(), "backup_wordpress_from_2p4/backup.tar.gz") + \ - " /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz") + os.system("cp " + os.path.join(get_test_apps_dir(), "backup_wordpress_from_2p4/backup.tar.gz") + + " /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz") def add_archive_system_from_2p4(): os.system("mkdir -p /home/yunohost.backup/archives") - os.system("cp " + os.path.join(get_test_apps_dir(), "backup_system_from_2p4/backup.info.json") + \ - " /home/yunohost.backup/archives/backup_system_from_2p4.info.json") + os.system("cp " + os.path.join(get_test_apps_dir(), "backup_system_from_2p4/backup.info.json") + + " /home/yunohost.backup/archives/backup_system_from_2p4.info.json") - os.system("cp " + os.path.join(get_test_apps_dir(), "backup_system_from_2p4/backup.tar.gz") + \ - " /home/yunohost.backup/archives/backup_system_from_2p4.tar.gz") + os.system("cp " + os.path.join(get_test_apps_dir(), "backup_system_from_2p4/backup.tar.gz") + + " /home/yunohost.backup/archives/backup_system_from_2p4.tar.gz") # # System backup # @@ -309,7 +312,7 @@ def test_backup_script_failure_handling(monkeypatch, mocker): # with the expected error message key monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec) - with message(mocker, 'backup_app_failed', app='backup_recommended_app'): + with message(mocker, 'backup_app_failed', app='backup_recommended_app'): with raiseYunohostError(mocker, 'backup_nothings_done'): backup_create(system=None, apps=["backup_recommended_app"]) @@ -499,6 +502,7 @@ def test_backup_and_restore_with_ynh_restore(mocker): _test_backup_and_restore_app(mocker, "backup_recommended_app") + @pytest.mark.with_permission_app_installed def test_backup_and_restore_permission_app(mocker): @@ -548,7 +552,7 @@ def _test_backup_and_restore_app(mocker, app): # Uninstall the app app_remove(app) assert not app_is_installed(app) - assert app+".main" not in user_permission_list()['permissions'] + assert app + ".main" not in user_permission_list()['permissions'] # Restore the app with message(mocker, "restore_complete"): @@ -559,7 +563,7 @@ def _test_backup_and_restore_app(mocker, app): # Check permission per_list = user_permission_list()['permissions'] - assert app+".main" in per_list + assert app + ".main" in per_list # # Some edge cases # @@ -577,6 +581,7 @@ def test_restore_archive_with_no_json(mocker): with raiseYunohostError(mocker, 'backup_archive_cant_retrieve_info_json'): backup_restore(name="badbackup", force=True) + @pytest.mark.with_wordpress_archive_from_2p4 def test_restore_archive_with_bad_archive(mocker): @@ -605,9 +610,9 @@ def test_restore_archive_with_custom_hook(mocker): # Restore system with custom hook with message(mocker, "restore_complete"): backup_restore(name=backup_list()["archives"][0], - system=[], - apps=None, - force=True) + system=[], + apps=None, + force=True) os.system("rm %s/99-yolo" % custom_restore_hook_folder) diff --git a/src/yunohost/tests/test_changeurl.py b/src/yunohost/tests/test_changeurl.py index b1bedc7eb..ecd926a2e 100644 --- a/src/yunohost/tests/test_changeurl.py +++ b/src/yunohost/tests/test_changeurl.py @@ -13,6 +13,7 @@ from yunohost.utils.error import YunohostError # Get main domain maindomain = "" + def setup_function(function): global maindomain maindomain = _get_maindomain() diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 4dfc1ce6b..abb289fad 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -1,3 +1,4 @@ +import socket import requests import pytest import os @@ -6,9 +7,9 @@ from conftest import message, raiseYunohostError, get_test_apps_dir from yunohost.app import app_install, app_remove, app_change_url, app_map, _installed_apps from yunohost.user import user_list, user_create, user_delete, \ - user_group_list, user_group_delete + user_group_list, user_group_delete from yunohost.permission import user_permission_update, user_permission_list, user_permission_reset, \ - permission_create, permission_delete, permission_url + permission_create, permission_delete, permission_url from yunohost.domain import _get_maindomain # Get main domain @@ -17,10 +18,10 @@ dummy_password = "test123Ynh" # Dirty patch of DNS resolution. Force the DNS to 127.0.0.1 address even if dnsmasq have the public address. # Mainly used for 'can_access_webpage' function -import socket prv_getaddrinfo = socket.getaddrinfo + def clean_user_groups_permission(): for u in user_list()['users']: user_delete(u) @@ -44,6 +45,7 @@ def setup_function(function): # Dirty patch of DNS resolution. Force the DNS to 127.0.0.1 address even if dnsmasq have the public address. # Mainly used for 'can_access_webpage' function dns_cache = {(maindomain, 443, 0, 1): [(2, 1, 6, '', ('127.0.0.1', 443))]} + def new_getaddrinfo(*args): try: return dns_cache[args] @@ -55,7 +57,7 @@ def setup_function(function): user_create("alice", "Alice", "White", "alice@" + maindomain, dummy_password) user_create("bob", "Bob", "Snow", "bob@" + maindomain, dummy_password) - permission_create("wiki.main", url="/", allowed=["all_users"] , sync_perm=False) + permission_create("wiki.main", url="/", allowed=["all_users"], sync_perm=False) permission_create("blog.main", allowed=["all_users"], sync_perm=False) user_permission_update("blog.main", remove="all_users", add="alice") @@ -397,6 +399,7 @@ def test_permission_redefine_url(): res = user_permission_list(full=True)['permissions'] assert res["blog.main"]["url"] == "/pwet" + def test_permission_remove_url(): permission_url("blog.main", url=None) @@ -489,9 +492,9 @@ def test_permission_app_propagation_on_ssowat(): # alice gotta be allowed on the main permission to access the admin tho user_permission_update("permissions_app.main", remove="bob", add="all_users") - assert not can_access_webpage(app_webroot+"/admin", logged_as=None) - assert can_access_webpage(app_webroot+"/admin", logged_as="alice") - assert not can_access_webpage(app_webroot+"/admin", logged_as="bob") + assert not can_access_webpage(app_webroot + "/admin", logged_as=None) + assert can_access_webpage(app_webroot + "/admin", logged_as="alice") + assert not can_access_webpage(app_webroot + "/admin", logged_as="bob") def test_permission_legacy_app_propagation_on_ssowat(): diff --git a/src/yunohost/tests/test_regenconf.py b/src/yunohost/tests/test_regenconf.py index 426f62da4..4e1ae679b 100644 --- a/src/yunohost/tests/test_regenconf.py +++ b/src/yunohost/tests/test_regenconf.py @@ -9,6 +9,7 @@ TEST_DOMAIN_NGINX_CONFIG = "/etc/nginx/conf.d/%s.conf" % TEST_DOMAIN TEST_DOMAIN_DNSMASQ_CONFIG = "/etc/dnsmasq.d/%s" % TEST_DOMAIN SSHD_CONFIG = "/etc/ssh/sshd_config" + def setup_function(function): _force_clear_hashes([TEST_DOMAIN_NGINX_CONFIG]) @@ -169,7 +170,7 @@ def test_stale_hashes_if_file_manually_deleted(): # ... Anyway, the proper way to write these tests would be to use a dummy # regen-conf hook just for tests but meh I'm lazy # -#def test_stale_hashes_if_file_manually_modified(): +# def test_stale_hashes_if_file_manually_modified(): # """ # Same as other test, but manually delete the file in between and check # behavior diff --git a/src/yunohost/tests/test_service.py b/src/yunohost/tests/test_service.py index f91a601c4..d0a3d4fc2 100644 --- a/src/yunohost/tests/test_service.py +++ b/src/yunohost/tests/test_service.py @@ -64,11 +64,13 @@ def test_service_add(): service_add("dummyservice", description="A dummy service to run tests") assert "dummyservice" in service_status().keys() + def test_service_add_real_service(): service_add("networking") assert "networking" in service_status().keys() + def test_service_remove(): service_add("dummyservice", description="A dummy service to run tests") diff --git a/src/yunohost/tests/test_settings.py b/src/yunohost/tests/test_settings.py index c7105fd1b..0a717192c 100644 --- a/src/yunohost/tests/test_settings.py +++ b/src/yunohost/tests/test_settings.py @@ -13,6 +13,7 @@ DEFAULTS["example.int"] = {"type": "int", "default": 42} DEFAULTS["example.string"] = {"type": "string", "default": "yolo swag"} DEFAULTS["example.enum"] = {"type": "enum", "default": "a", "choices": ["a", "b", "c"]} + def setup_function(function): os.system("mv /etc/yunohost/settings.json /etc/yunohost/settings.json.saved") @@ -69,6 +70,7 @@ def test_settings_set(): settings_set("example.bool", "on") assert settings_get("example.bool") is True + def test_settings_set_int(): settings_set("example.int", 21) assert settings_get("example.int") == 21 diff --git a/src/yunohost/tests/test_user-group.py b/src/yunohost/tests/test_user-group.py index f1eae9c4e..21ece3ed5 100644 --- a/src/yunohost/tests/test_user-group.py +++ b/src/yunohost/tests/test_user-group.py @@ -3,7 +3,7 @@ import pytest from conftest import message, raiseYunohostError from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ - user_group_list, user_group_create, user_group_delete, user_group_update + user_group_list, user_group_create, user_group_delete, user_group_update from yunohost.domain import _get_maindomain from yunohost.tests.test_permission import check_LDAP_db_integrity @@ -25,7 +25,7 @@ def setup_function(function): global maindomain maindomain = _get_maindomain() - + user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh") user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh") user_create("jack", "Jack", "Black", "jack@" + maindomain, "test123Ynh") diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py index 75cf35093..f2486473b 100644 --- a/src/yunohost/utils/error.py +++ b/src/yunohost/utils/error.py @@ -33,8 +33,8 @@ class YunohostError(MoulinetteError): """ def __init__(self, key, raw_msg=False, *args, **kwargs): - self.key = key # Saving the key is useful for unit testing - self.kwargs = kwargs # Saving the key is useful for unit testing + self.key = key # Saving the key is useful for unit testing + self.kwargs = kwargs # Saving the key is useful for unit testing if raw_msg: msg = key else: diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py index b1f49e287..f16472e28 100644 --- a/src/yunohost/utils/ldap.py +++ b/src/yunohost/utils/ldap.py @@ -29,19 +29,20 @@ from yunohost.utils.error import YunohostError # 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: - conf = { "vendor": "ldap", - "name": "as-root", - "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' }, - "extra": {} - } + conf = {"vendor": "ldap", + "name": "as-root", + "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'}, + "extra": {} + } try: _ldap_interface = ldap.Authenticator(**conf) @@ -82,4 +83,5 @@ def _destroy_ldap_interface(): if _ldap_interface is not None: del _ldap_interface + atexit.register(_destroy_ldap_interface) diff --git a/src/yunohost/utils/network.py b/src/yunohost/utils/network.py index 4bb0b288a..909e14784 100644 --- a/src/yunohost/utils/network.py +++ b/src/yunohost/utils/network.py @@ -58,6 +58,7 @@ def get_public_ip_from_remote_server(protocol=4): # If we are indeed connected in ipv4 or ipv6, we should find a default route routes = check_output("ip -%s route show table all" % protocol).split("\n") + def is_default_route(r): # Typically the default route starts with "default" # But of course IPv6 is more complex ... e.g. on internet cube there's diff --git a/src/yunohost/utils/yunopaste.py b/src/yunohost/utils/yunopaste.py index dc8b6fb8d..9a406a580 100644 --- a/src/yunohost/utils/yunopaste.py +++ b/src/yunohost/utils/yunopaste.py @@ -10,6 +10,7 @@ from yunohost.utils.error import YunohostError logger = logging.getLogger('yunohost.utils.yunopaste') + def yunopaste(data): paste_server = "https://paste.yunohost.org" @@ -44,7 +45,6 @@ def anonymize(data): data = data.replace(domain.replace(".", "%2e"), redact.replace(".", "%2e")) return data - # First, let's replace every occurence of the main domain by "domain.tld" # This should cover a good fraction of the info leaked main_domain = _get_maindomain() diff --git a/tests/remove_stale_translated_strings.py b/tests/remove_stale_translated_strings.py index 4e39f131e..cfecea348 100644 --- a/tests/remove_stale_translated_strings.py +++ b/tests/remove_stale_translated_strings.py @@ -13,6 +13,6 @@ for locale_file in locale_files: print(locale_file) this_locale = json.loads(open(locale_folder + locale_file).read(), object_pairs_hook=OrderedDict) - this_locale_fixed = {k:v for k, v in this_locale.items() if k in reference} + this_locale_fixed = {k: v for k, v in this_locale.items() if k in reference} json.dump(this_locale_fixed, open(locale_folder + locale_file, "w"), indent=4, ensure_ascii=False) diff --git a/tests/test_actionmap.py b/tests/test_actionmap.py index 08b868839..bf6755979 100644 --- a/tests/test_actionmap.py +++ b/tests/test_actionmap.py @@ -1,4 +1,5 @@ import yaml + def test_yaml_syntax(): yaml.load(open("data/actionsmap/yunohost.yml")) From 49f63942337a5953b6b6345d553b697820045225 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 3 Sep 2020 17:31:52 +0200 Subject: [PATCH 197/262] autopep8 --in-place -a -a -a --ignore E402,E501,E722 -r src/yunohost/{__init__.py,certificate.py,diagnosis.py,domain.py,dyndns.py,firewall.py,hook.py,log.py,regenconf.py,service.py,settings.py,ssh.py,tools.py} --- src/yunohost/__init__.py | 2 +- src/yunohost/diagnosis.py | 9 +++++---- src/yunohost/domain.py | 16 ++++++++-------- src/yunohost/hook.py | 20 ++++++++++---------- src/yunohost/log.py | 2 +- src/yunohost/regenconf.py | 2 +- src/yunohost/service.py | 1 - src/yunohost/settings.py | 10 +++++++--- src/yunohost/tools.py | 13 +++++++------ 9 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/yunohost/__init__.py b/src/yunohost/__init__.py index ce505ffd9..d909ad9e2 100644 --- a/src/yunohost/__init__.py +++ b/src/yunohost/__init__.py @@ -91,7 +91,7 @@ def init_logging(interface="cli", logfile = os.path.join(logdir, "yunohost-%s.log" % interface) if not os.path.isdir(logdir): - os.makedirs(logdir, 0750) + os.makedirs(logdir, 0o750) # ####################################################################### # # Logging configuration for CLI (or any other interface than api...) # diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index 23884a53d..e4dd84f6d 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -176,7 +176,7 @@ def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False, ema code, report = hook_exec(path, args={"force": force}, env=None) except Exception: import traceback - logger.error(m18n.n("diagnosis_failed_for_category", category=category, error='\n'+traceback.format_exc())) + logger.error(m18n.n("diagnosis_failed_for_category", category=category, error='\n' + traceback.format_exc())) else: diagnosed_categories.append(category) if report != {}: @@ -403,11 +403,11 @@ class Diagnoser(): Diagnoser.i18n(new_report) add_ignore_flag_to_issues(new_report) - errors = [item for item in new_report["items"] if item["status"] == "ERROR" and not item["ignored"]] + errors = [item for item in new_report["items"] if item["status"] == "ERROR" and not item["ignored"]] warnings = [item for item in new_report["items"] if item["status"] == "WARNING" and not item["ignored"]] errors_ignored = [item for item in new_report["items"] if item["status"] == "ERROR" and item["ignored"]] warning_ignored = [item for item in new_report["items"] if item["status"] == "WARNING" and item["ignored"]] - ignored_msg = " " + m18n.n("diagnosis_ignored_issues", nb_ignored=len(errors_ignored+warning_ignored)) if errors_ignored or warning_ignored else "" + ignored_msg = " " + m18n.n("diagnosis_ignored_issues", nb_ignored=len(errors_ignored + warning_ignored)) if errors_ignored or warning_ignored else "" if errors and warnings: logger.error(m18n.n("diagnosis_found_errors_and_warnings", errors=len(errors), warnings=len(warnings), category=new_report["description"]) + ignored_msg) @@ -477,6 +477,7 @@ class Diagnoser(): meta_data.update(item.get("data", {})) html_tags = re.compile(r'<[^>]+>') + def m18n_(info): if not isinstance(info, tuple) and not isinstance(info, list): info = (info, {}) @@ -485,7 +486,7 @@ class Diagnoser(): # In cli, we remove the html tags if msettings.get("interface") != "api" or force_remove_html_tags: s = s.replace("", "'").replace("", "'") - s = html_tags.sub('', s.replace("
","\n")) + s = html_tags.sub('', s.replace("
", "\n")) else: s = s.replace("", "").replace("", "") # Make it so that links open in new tabs diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index e1d2b1649..160c8f622 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -89,7 +89,7 @@ def domain_add(operation_logger, domain, dyndns=False): raise YunohostError('domain_exists') operation_logger.start() - + # Lower domain to avoid some edge cases issues # See: https://forum.yunohost.org/t/invalid-domain-causes-diagnosis-web-to-fail-fr-on-demand/11765 domain = domain.lower() @@ -614,17 +614,17 @@ def _get_DKIM(domain): if is_legacy_format: dkim = re.match(( r'^(?P[a-z_\-\.]+)[\s]+([0-9]+[\s]+)?IN[\s]+TXT[\s]+' - '[^"]*"v=(?P[^";]+);' - '[\s"]*k=(?P[^";]+);' - '[\s"]*p=(?P

[^";]+)'), dkim_content, re.M | re.S + '[^"]*"v=(?P[^";]+);' + r'[\s"]*k=(?P[^";]+);' + '[\s"]*p=(?P

[^";]+)'), dkim_content, re.M | re.S ) else: dkim = re.match(( r'^(?P[a-z_\-\.]+)[\s]+([0-9]+[\s]+)?IN[\s]+TXT[\s]+' - '[^"]*"v=(?P[^";]+);' - '[\s"]*h=(?P[^";]+);' - '[\s"]*k=(?P[^";]+);' - '[\s"]*p=(?P

[^";]+)'), dkim_content, re.M | re.S + '[^"]*"v=(?P[^";]+);' + r'[\s"]*h=(?P[^";]+);' + r'[\s"]*k=(?P[^";]+);' + '[\s"]*p=(?P

[^";]+)'), dkim_content, re.M | re.S ) if not dkim: diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index b57300f54..f939f2c99 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -270,9 +270,9 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, # Validate callbacks if not callable(pre_callback): - pre_callback = lambda name, priority, path, args: args + def pre_callback(name, priority, path, args): return args if not callable(post_callback): - post_callback = lambda name, priority, path, succeed: None + def post_callback(name, priority, path, succeed): return None # Iterate over hooks and execute them for priority in sorted(hooks_dict): @@ -283,7 +283,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, hook_args = pre_callback(name=name, priority=priority, path=path, args=args) hook_return = hook_exec(path, args=hook_args, chdir=chdir, env=env, - no_trace=no_trace, raise_on_error=True)[1] + no_trace=no_trace, raise_on_error=True)[1] except YunohostError as e: state = 'failed' hook_return = {} @@ -293,9 +293,9 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, else: post_callback(name=name, priority=priority, path=path, succeed=True) - if not name in result: + if name not in result: result[name] = {} - result[name][path] = {'state' : state, 'stdreturn' : hook_return } + result[name][path] = {'state': state, 'stdreturn': hook_return} return result @@ -446,17 +446,17 @@ def _hook_exec_python(path, args, env, loggers): dir_ = os.path.dirname(path) name = os.path.splitext(os.path.basename(path))[0] - if not dir_ in sys.path: + if dir_ not in sys.path: sys.path = [dir_] + sys.path module = import_module(name) ret = module.main(args, env, loggers) # # Assert that the return is a (int, dict) tuple assert isinstance(ret, tuple) \ - and len(ret) == 2 \ - and isinstance(ret[0],int) \ - and isinstance(ret[1],dict), \ - "Module %s did not return a (int, dict) tuple !" % module + and len(ret) == 2 \ + and isinstance(ret[0], int) \ + and isinstance(ret[1], dict), \ + "Module %s did not return a (int, dict) tuple !" % module return ret diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 892105c6b..88da1ebf0 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -270,7 +270,7 @@ def log_display(path, number=None, share=False, filter_irrelevant=False, with_su if os.path.exists(log_path): from yunohost.service import _tail if number and filters: - logs = _tail(log_path, int(number*4)) + logs = _tail(log_path, int(number * 4)) elif number: logs = _tail(log_path, int(number)) else: diff --git a/src/yunohost/regenconf.py b/src/yunohost/regenconf.py index 7f4f73247..6b369fc8c 100644 --- a/src/yunohost/regenconf.py +++ b/src/yunohost/regenconf.py @@ -47,7 +47,7 @@ logger = log.getActionLogger('yunohost.regenconf') # FIXME : check for all reference of 'service' close to operation_logger stuff @is_unit_operation([('names', 'configuration')]) def regen_conf(operation_logger, names=[], with_diff=False, force=False, dry_run=False, - list_pending=False): + list_pending=False): """ Regenerate the configuration file(s) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 522395718..084df471d 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -654,7 +654,6 @@ def _tail(file, n): avg_line_length = 74 to_read = n - try: if file.endswith(".gz"): import gzip diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 9b8589d35..0f8860529 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -15,6 +15,7 @@ logger = getActionLogger('yunohost.settings') SETTINGS_PATH = "/etc/yunohost/settings.json" SETTINGS_PATH_OTHER_LOCATION = "/etc/yunohost/settings-%s.json" + def is_boolean(value): """ Ensure a string value is intended as a boolean @@ -60,11 +61,11 @@ DEFAULTS = OrderedDict([ ("service.ssh.allow_deprecated_dsa_hostkey", {"type": "bool", "default": False}), ("security.ssh.compatibility", {"type": "enum", "default": "modern", - "choices": ["intermediate", "modern"]}), + "choices": ["intermediate", "modern"]}), ("security.nginx.compatibility", {"type": "enum", "default": "intermediate", - "choices": ["intermediate", "modern"]}), + "choices": ["intermediate", "modern"]}), ("security.postfix.compatibility", {"type": "enum", "default": "intermediate", - "choices": ["intermediate", "modern"]}), + "choices": ["intermediate", "modern"]}), ("pop3.enabled", {"type": "bool", "default": False}), ("smtp.allow_ipv6", {"type": "bool", "default": True}), @@ -321,17 +322,20 @@ def reconfigure_nginx(setting_name, old_value, new_value): if old_value != new_value: service_regen_conf(names=['nginx']) + @post_change_hook("security.ssh.compatibility") def reconfigure_ssh(setting_name, old_value, new_value): if old_value != new_value: service_regen_conf(names=['ssh']) + @post_change_hook("smtp.allow_ipv6") @post_change_hook("security.postfix.compatibility") def reconfigure_postfix(setting_name, old_value, new_value): if old_value != new_value: service_regen_conf(names=['postfix']) + @post_change_hook("pop3.enabled") def reconfigure_dovecot(setting_name, old_value, new_value): dovecot_package = 'dovecot-pop3d' diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 2e90b38ee..fcc2810d6 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -51,9 +51,11 @@ MIGRATIONS_STATE_PATH = "/etc/yunohost/migrations.yaml" logger = getActionLogger('yunohost.tools') + def tools_versions(): return ynh_packages_version() + def tools_ldapinit(): """ YunoHost LDAP initialization @@ -146,7 +148,7 @@ def tools_adminpw(new_password, check_strength=True): ldap = _get_ldap_interface() try: - ldap.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') @@ -599,7 +601,6 @@ def tools_upgrade(operation_logger, apps=None, system=False, allow_yunohost_upgr logger.debug("Running apt command :\n{}".format(dist_upgrade)) - def is_relevant(l): irrelevants = [ "service sudo-ldap already provided", @@ -936,7 +937,7 @@ def _migrate_legacy_migration_json(): # Extract the list of migration ids from . import data_migrations migrations_path = data_migrations.__path__[0] - migration_files = filter(lambda x: re.match("^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)) + migration_files = filter(lambda x: re.match(r"^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)) # (here we remove the .py extension and make sure the ids are sorted) migration_ids = sorted([f.rsplit(".", 1)[0] for f in migration_files]) @@ -985,7 +986,7 @@ def _get_migrations_list(): # (in particular, pending migrations / not already ran are not listed states = tools_migrations_state()["migrations"] - for migration_file in filter(lambda x: re.match("^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)): + for migration_file in filter(lambda x: re.match(r"^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)): m = _load_migration(migration_file) m.state = states.get(m.id, "pending") migrations.append(m) @@ -1004,7 +1005,7 @@ def _get_migration_by_name(migration_name): raise AssertionError("Unable to find migration with name %s" % migration_name) migrations_path = data_migrations.__path__[0] - migrations_found = filter(lambda x: re.match("^\d+_%s\.py$" % migration_name, x), os.listdir(migrations_path)) + migrations_found = filter(lambda x: re.match(r"^\d+_%s\.py$" % migration_name, x), os.listdir(migrations_path)) assert len(migrations_found) == 1, "Unable to find migration with name %s" % migration_name @@ -1048,7 +1049,7 @@ class Migration(object): # Those are to be implemented by daughter classes mode = "auto" - dependencies = [] # List of migration ids required before running this migration + dependencies = [] # List of migration ids required before running this migration @property def disclaimer(self): From a1807d1f88868f46a22dce627001ef8f9fdc6cfb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 3 Sep 2020 17:41:33 +0200 Subject: [PATCH 198/262] Update flake8 lint test policy --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 0c0b01a5e..6c1139d52 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,6 @@ deps = py{27,37}-{lint,invalidcode}: flake8 py37-black: black commands = - py{27,37}-lint: flake8 src doc data tests - py{27,37}-invalidcode: flake8 src data --exclude src/yunohost/tests --select F --ignore F401,F841 - py37-black: black --check --diff src doc data tests \ No newline at end of file + py{27,37}-lint: flake8 src doc data tests --ignore E402,E501 --exclude src/yunohost/vendor + py{27,37}-invalidcode: flake8 src data --exclude src/yunohost/tests,src/yunohost/vendor --select F + py37-black: black --check --diff src doc data tests From ca97118116d167c36b3fd135211aa8ca0f6f181a Mon Sep 17 00:00:00 2001 From: Kayou Date: Thu, 3 Sep 2020 18:51:18 +0200 Subject: [PATCH 199/262] Try to clone moulinette and ssowat with the version in the debian/control --- .gitlab/ci/build.gitlab-ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab/ci/build.gitlab-ci.yml b/.gitlab/ci/build.gitlab-ci.yml index 5e25bcd1f..74ae8c6ee 100644 --- a/.gitlab/ci/build.gitlab-ci.yml +++ b/.gitlab/ci/build.gitlab-ci.yml @@ -38,7 +38,8 @@ build-ssowat: variables: PACKAGE: "ssowat" script: - - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 + - DEBIAN_DEPENDS=$(cat debian/control | grep -Po "ssowat \([>,=,<]+ .*\)," | grep -Po "[0-9]+([.][0-9]+)?") + - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script @@ -47,6 +48,7 @@ build-moulinette: variables: PACKAGE: "moulinette" script: - - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 + - DEBIAN_DEPENDS=$(cat debian/control | grep -Po "moulinette \([>,=,<]+ .*\)," | grep -Po "[0-9]+([.][0-9]+)?") + - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 | git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script From e9e7fba41db7b05f68e8b7a26c6aa912f38a3e78 Mon Sep 17 00:00:00 2001 From: Kayou Date: Thu, 3 Sep 2020 18:59:01 +0200 Subject: [PATCH 200/262] Update build.gitlab-ci.yml --- .gitlab/ci/build.gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab/ci/build.gitlab-ci.yml b/.gitlab/ci/build.gitlab-ci.yml index 74ae8c6ee..9a4035ae6 100644 --- a/.gitlab/ci/build.gitlab-ci.yml +++ b/.gitlab/ci/build.gitlab-ci.yml @@ -38,7 +38,7 @@ build-ssowat: variables: PACKAGE: "ssowat" script: - - DEBIAN_DEPENDS=$(cat debian/control | grep -Po "ssowat \([>,=,<]+ .*\)," | grep -Po "[0-9]+([.][0-9]+)?") + - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "ssowat \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)?") - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script @@ -48,7 +48,7 @@ build-moulinette: variables: PACKAGE: "moulinette" script: - - DEBIAN_DEPENDS=$(cat debian/control | grep -Po "moulinette \([>,=,<]+ .*\)," | grep -Po "[0-9]+([.][0-9]+)?") - - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 | git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 + - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "moulinette \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)?") + - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script From 37e8ba57acbefe096401449df4c95bc9704f2c66 Mon Sep 17 00:00:00 2001 From: Kayou Date: Thu, 3 Sep 2020 19:09:29 +0200 Subject: [PATCH 201/262] Update build.gitlab-ci.yml --- .gitlab/ci/build.gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/ci/build.gitlab-ci.yml b/.gitlab/ci/build.gitlab-ci.yml index 9a4035ae6..f575589aa 100644 --- a/.gitlab/ci/build.gitlab-ci.yml +++ b/.gitlab/ci/build.gitlab-ci.yml @@ -38,7 +38,7 @@ build-ssowat: variables: PACKAGE: "ssowat" script: - - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "ssowat \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)?") + - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "ssowat \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)? | head -n 1") - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script @@ -48,7 +48,7 @@ build-moulinette: variables: PACKAGE: "moulinette" script: - - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "moulinette \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)?") + - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "moulinette \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)? | head -n 1") - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script From cb3ff1ed83a3f5fb43a81edf63495c991f2299ee Mon Sep 17 00:00:00 2001 From: Kayou Date: Thu, 3 Sep 2020 19:14:14 +0200 Subject: [PATCH 202/262] Woups, my grep in ci/build --- .gitlab/ci/build.gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/ci/build.gitlab-ci.yml b/.gitlab/ci/build.gitlab-ci.yml index f575589aa..d2dbbaa80 100644 --- a/.gitlab/ci/build.gitlab-ci.yml +++ b/.gitlab/ci/build.gitlab-ci.yml @@ -38,7 +38,7 @@ build-ssowat: variables: PACKAGE: "ssowat" script: - - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "ssowat \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)? | head -n 1") + - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "ssowat \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)?" | head -n 1) - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script @@ -48,7 +48,7 @@ build-moulinette: variables: PACKAGE: "moulinette" script: - - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "moulinette \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)? | head -n 1") + - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "moulinette \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)?" | head -n 1) - git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1 - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - *build_script From f92b84bd94f44bd32b4c6b799262a6e5d21f60fb Mon Sep 17 00:00:00 2001 From: ljf Date: Thu, 3 Sep 2020 22:27:46 +0200 Subject: [PATCH 203/262] [fix] SSO unavailable --- data/templates/nginx/plain/yunohost_sso.conf.inc | 5 +++++ data/templates/nginx/server.tpl.conf | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 data/templates/nginx/plain/yunohost_sso.conf.inc diff --git a/data/templates/nginx/plain/yunohost_sso.conf.inc b/data/templates/nginx/plain/yunohost_sso.conf.inc new file mode 100644 index 000000000..cb3c5453d --- /dev/null +++ b/data/templates/nginx/plain/yunohost_sso.conf.inc @@ -0,0 +1,5 @@ +# Avoid the nginx path/alias traversal weakness ( #1037 ) +rewrite ^/yunohost/sso$ /yunohost/sso/ permanent; + +location /yunohost/sso/ { +} diff --git a/data/templates/nginx/server.tpl.conf b/data/templates/nginx/server.tpl.conf index 29af9f532..8bd689a92 100644 --- a/data/templates/nginx/server.tpl.conf +++ b/data/templates/nginx/server.tpl.conf @@ -14,7 +14,7 @@ server { include /etc/nginx/conf.d/{{ domain }}.d/*.conf; - location /yunohost/admin { + location /yunohost { return 301 https://$http_host$request_uri; } @@ -60,6 +60,7 @@ server { include /etc/nginx/conf.d/{{ domain }}.d/*.conf; + include /etc/nginx/conf.d/yunohost_sso.conf.inc; include /etc/nginx/conf.d/yunohost_admin.conf.inc; include /etc/nginx/conf.d/yunohost_api.conf.inc; From cc4db7a6f621aaf918c7729c20899f8ca91c4663 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 3 Sep 2020 23:47:24 +0200 Subject: [PATCH 204/262] Add a comment explaining why the location is empty --- data/templates/nginx/plain/yunohost_sso.conf.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/templates/nginx/plain/yunohost_sso.conf.inc b/data/templates/nginx/plain/yunohost_sso.conf.inc index cb3c5453d..308e5a9a4 100644 --- a/data/templates/nginx/plain/yunohost_sso.conf.inc +++ b/data/templates/nginx/plain/yunohost_sso.conf.inc @@ -2,4 +2,6 @@ rewrite ^/yunohost/sso$ /yunohost/sso/ permanent; location /yunohost/sso/ { + # This is an empty location, only meant to avoid other locations + # from matching /yunohost/sso, such that it's correctly handled by ssowat } From d49f1ce73358f2b68f28f8ef0f46a994fca638d3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 4 Sep 2020 14:35:16 +0200 Subject: [PATCH 205/262] Update changelog for 4.0.7 --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index 92af480ac..34d0da4dd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +yunohost (4.0.7) stable; urgency=low + + - [fix] Require explicitly php7.3-foo packages because in some cases Sury's php7.4- packages are installed and php7.3-fpm doesn't get installed ... (1288159a) + - [fix] Make sure app nginx confs do not prevent the loading of /yunohost/sso (#1044) + + Thanks to all contributors <3 ! (Kayou, ljf) + + -- Alexandre Aubin Fri, 04 Sep 2020 14:32:07 +0200 + yunohost (4.0.6.1) stable; urgency=low - [fix] Stupid syntax issue in dovecot conf From 3fc4baa08fc9eba73004b90048736c792fcb0fc8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 5 Sep 2020 17:21:45 +0200 Subject: [PATCH 206/262] Propagate change to unit tests --- src/yunohost/tests/test_user-group.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/yunohost/tests/test_user-group.py b/src/yunohost/tests/test_user-group.py index f1eae9c4e..361b411f3 100644 --- a/src/yunohost/tests/test_user-group.py +++ b/src/yunohost/tests/test_user-group.py @@ -25,10 +25,10 @@ def setup_function(function): global maindomain maindomain = _get_maindomain() - - user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh") - user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh") - user_create("jack", "Jack", "Black", "jack@" + maindomain, "test123Ynh") + + user_create("alice", "Alice", "White", maindomain, "test123Ynh") + user_create("bob", "Bob", "Snow", maindomain, "test123Ynh") + user_create("jack", "Jack", "Black", maindomain, "test123Ynh") user_group_create("dev") user_group_create("apps") @@ -79,7 +79,7 @@ def test_list_groups(): def test_create_user(mocker): with message(mocker, "user_created"): - user_create("albert", "Albert", "Good", "alber@" + maindomain, "test123Ynh") + user_create("albert", "Albert", "Good", maindomain, "test123Ynh") group_res = user_group_list()['groups'] assert "albert" in user_list()['users'] @@ -123,25 +123,26 @@ def test_del_group(mocker): # -def test_create_user_with_mail_address_already_taken(mocker): - with raiseYunohostError(mocker, "user_creation_failed"): - user_create("alice2", "Alice", "White", "alice@" + maindomain, "test123Ynh") - - def test_create_user_with_password_too_simple(mocker): with raiseYunohostError(mocker, "password_listed"): - user_create("other", "Alice", "White", "other@" + maindomain, "12") + user_create("other", "Alice", "White", maindomain, "12") def test_create_user_already_exists(mocker): with raiseYunohostError(mocker, "user_already_exists"): - user_create("alice", "Alice", "White", "other@" + maindomain, "test123Ynh") + user_create("alice", "Alice", "White", maindomain, "test123Ynh") +def test_create_user_with_domain_that_doesnt_exists(mocker): + with raiseYunohostError(mocker, "domain_unknown"): + user_create("alice", "Alice", "White", "doesnt.exists", "test123Ynh") def test_update_user_with_mail_address_already_taken(mocker): with raiseYunohostError(mocker, "user_update_failed"): user_update("bob", add_mailalias="alice@" + maindomain) +def test_update_user_with_mail_address_with_unknown_domain(mocker): + with raiseYunohostError(mocker, "mail_domain_unknown"): + user_update("alice", add_mailalias="alice@doesnt.exists") def test_del_user_that_does_not_exist(mocker): with raiseYunohostError(mocker, "user_unknown"): From 5805acbf6a124cb548d34077803dfcbfa1d1cca4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 5 Sep 2020 18:35:35 +0200 Subject: [PATCH 207/262] Reimplement --mail as a deprecated option --- data/actionsmap/yunohost.yml | 3 +++ src/yunohost/user.py | 26 +++++--------------------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index e0f33e554..054418ebd 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -97,6 +97,9 @@ user: pattern: &pattern_lastname - !!str ^([^\W\d_]{2,30}[ ,.'-]{0,3})+$ - "pattern_lastname" + -m: + full: --mail + help: (Deprecated, see --domain) Main unique email address -p: full: --password help: User password diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 2f5c13d97..77062b6da 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -46,16 +46,7 @@ logger = getActionLogger('yunohost.user') def user_list(fields=None): - """ - List users - Keyword argument: - filter -- LDAP filter used to search - offset -- Starting number for user fetching - limit -- Maximum number of user fetched - fields -- fields to fetch - - """ from yunohost.utils.ldap import _get_ldap_interface user_attrs = { @@ -106,19 +97,8 @@ def user_list(fields=None): @is_unit_operation([('username', 'user')]) def user_create(operation_logger, username, firstname, lastname, domain, password, - mailbox_quota="0"): - """ - Create user + mailbox_quota="0", mail=None): - Keyword argument: - firstname - lastname - username -- Must be unique - mail -- Main mail address must be unique - password - mailbox_quota -- Mailbox size quota - - """ from yunohost.domain import domain_list, _get_maindomain from yunohost.hook import hook_callback from yunohost.utils.password import assert_password_is_strong_enough @@ -127,6 +107,10 @@ def user_create(operation_logger, username, firstname, lastname, domain, passwor # Ensure sufficiently complex password assert_password_is_strong_enough("user", password) + if mail is not None: + logger.warning("Packagers ! Using --mail in 'yunohost user create' is deprecated ... please use --domain instead.") + domain = mail.split("@")[-1] + # Validate domain used for email address/xmpp account if domain is None: if msettings.get('interface') == 'api': From d83a7180387a208feaa83453603c6d60900af0a1 Mon Sep 17 00:00:00 2001 From: Christian Wehrli Date: Tue, 1 Sep 2020 12:06:52 +0000 Subject: [PATCH 208/262] Translated using Weblate (German) Currently translated at 49.3% (300 of 609 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 70 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/locales/de.json b/locales/de.json index b99516205..59c48ef0c 100644 --- a/locales/de.json +++ b/locales/de.json @@ -105,26 +105,26 @@ "restore_nothings_done": "Es wurde nicht wiederhergestellt", "restore_running_app_script": "Wiederherstellung wird ausfeührt für App '{app:s}'...", "restore_running_hooks": "Wiederherstellung wird gestartet…", - "service_add_failed": "Der Dienst '{service:s}' kann nicht hinzugefügt werden", - "service_added": "Der Service '{service:s}' wurde erfolgreich hinzugefügt", + "service_add_failed": "Der Dienst '{service:s}' konnte nicht hinzugefügt werden", + "service_added": "Der Dienst '{service:s}' wurde erfolgreich hinzugefügt", "service_already_started": "Der Dienst '{service:s}' läuft bereits", - "service_already_stopped": "Dienst '{service:s}' wurde bereits gestoppt", + "service_already_stopped": "Der Dienst '{service:s}' wurde bereits gestoppt", "service_cmd_exec_failed": "Der Befehl '{command:s}' konnte nicht ausgeführt werden", "service_disable_failed": "Der Dienst '{service:s}' konnte nicht deaktiviert werden", "service_disabled": "Der Dienst '{service:s}' wurde erfolgreich deaktiviert", - "service_enable_failed": "Der Dienst '{service:s}' konnte nicht aktiviert werden", - "service_enabled": "Der Dienst '{service:s}' wurde erfolgreich aktiviert", + "service_enable_failed": "Der Dienst '{service:s}' konnte beim Hochfahren nicht gestartet werden.\n\nKürzlich erstelle Logs des Dienstes: {logs:s}", + "service_enabled": "Der Dienst '{service:s}' wird nun beim Hochfahren des Systems automatisch gestartet.", "service_remove_failed": "Der Dienst '{service:s}' konnte nicht entfernt werden", "service_removed": "Der Dienst '{service:s}' wurde erfolgreich entfernt", - "service_start_failed": "Der Dienst '{service:s}' konnte nicht gestartet werden", + "service_start_failed": "Der Dienst '{service:s}' konnte nicht gestartet werden\n\nKürzlich erstellte Logs des Dienstes: {logs:s}", "service_started": "Der Dienst '{service:s}' wurde erfolgreich gestartet", "service_stop_failed": "Der Dienst '{service:s}' kann nicht gestoppt werden", "service_stopped": "Der Dienst '{service:s}' wurde erfolgreich beendet", "service_unknown": "Unbekannter Dienst '{service:s}'", - "ssowat_conf_generated": "Die Konfiguration von SSOwat war erfolgreich", - "ssowat_conf_updated": "Die persistente SSOwat Einstellung wurde aktualisiert", - "system_upgraded": "Das System wurde aktualisiert", - "system_username_exists": "Der Benutzername existiert bereits", + "ssowat_conf_generated": "Die Konfiguration von SSOwat erstellt", + "ssowat_conf_updated": "Die Konfiguration von SSOwat aktualisiert", + "system_upgraded": "System aktualisiert", + "system_username_exists": "Der Benutzername existiert bereits in der Liste der System-Benutzer", "unbackup_app": "App '{app:s}' konnte nicht gespeichert werden", "unexpected_error": "Ein unerwarteter Fehler ist aufgetreten", "unlimit": "Kein Kontingent", @@ -170,14 +170,14 @@ "certmanager_certificate_fetching_or_enabling_failed": "Die Aktivierung des neuen Zertifikats für die {domain:s} ist fehlgeschlagen...", "certmanager_attempt_to_renew_nonLE_cert": "Das Zertifikat der Domain '{domain:s}' wurde nicht von Let's Encrypt ausgestellt. Es kann nicht automatisch erneuert werden!", "certmanager_attempt_to_renew_valid_cert": "Das Zertifikat der Domain {domain:s} läuft nicht in Kürze ab! (Benutze --force um diese Nachricht zu umgehen)", - "certmanager_domain_http_not_working": "Es scheint so, dass die Domain {domain:s} nicht über HTTP erreicht werden kann. Bitte überprüfe, ob deine DNS und nginx Konfiguration in Ordnung ist", + "certmanager_domain_http_not_working": "Die Domäne {domain:s} scheint über HTTP nicht erreichbar zu sein. Für weitere Informationen überprüfen Sie bitte die Kategorie 'Web' im Diagnose-Bereich. (Wenn Sie wißen was Sie tun, nutzen Sie '--no-checks' um die Überprüfung zu überspringen.)", "certmanager_error_no_A_record": "Kein DNS 'A' Eintrag für die Domain {domain:s} gefunden. Dein Domainname muss auf diese Maschine weitergeleitet werden, um ein Let's Encrypt Zertifikat installieren zu können! (Wenn du weißt was du tust, kannst du --no-checks benutzen, um diese Überprüfung zu überspringen. )", - "certmanager_domain_dns_ip_differs_from_public_ip": "Der DNS 'A' Eintrag der Domain {domain:s} unterscheidet sich von dieser Server-IP. Wenn du gerade deinen A Eintrag verändert hast, warte bitte etwas, damit die Änderungen wirksam werden (du kannst die DNS Propagation mittels Website überprüfen) (Wenn du weißt was du tust, kannst du --no-checks benutzen, um diese Überprüfung zu überspringen. )", + "certmanager_domain_dns_ip_differs_from_public_ip": "Die DNS-Einträge der Domäne {domain:s} unterscheiden sich von der IP dieses Servers. Wenn Sie gerade Ihren A-Eintrag verändert haben, warten Sie bitte etwas, damit die Änderungen wirksam werden (Sie können die DNS Propagation mittels Website überprüfen) (Wenn Sie wißen was Sie tun, können Sie --no-checks benutzen, um diese Überprüfung zu überspringen. )", "certmanager_cannot_read_cert": "Es ist ein Fehler aufgetreten, als es versucht wurde das aktuelle Zertifikat für die Domain {domain:s} zu öffnen (Datei: {file:s}), Grund: {reason:s}", "certmanager_cert_install_success_selfsigned": "Ein selbstsigniertes Zertifikat für die Domain {domain:s} wurde erfolgreich installiert", "certmanager_cert_install_success": "Für die Domain {domain:s} wurde erfolgreich ein Let's Encrypt Zertifikat installiert.", "certmanager_cert_renew_success": "Das Let's Encrypt Zertifikat für die Domain {domain:s} wurde erfolgreich erneuert.", - "certmanager_hit_rate_limit": "Es wurden innerhalb kurzer Zeit schon zu viele Zertifikate für die exakt gleiche Domain {domain:s} ausgestellt. Bitte versuche es später nochmal. Besuche https://letsencrypt.org/docs/rate-limits/ für mehr Informationen", + "certmanager_hit_rate_limit": "Es wurden innerhalb kurzer Zeit zu viele Zertifikate für dieselbe Domain {domain:s} ausgestellt. Bitte versuchen Sie es später nochmal. Besuchen Sie https://letsencrypt.org/docs/rate-limits/ für mehr Informationen", "certmanager_cert_signing_failed": "Das neue Zertifikat konnte nicht signiert werden", "certmanager_no_cert_file": "Die Zertifikatsdatei für die Domain {domain:s} (Datei: {file:s}) konnte nicht gelesen werden", "certmanager_conflicting_nginx_file": "Die Domain konnte nicht für die ACME challenge vorbereitet werden: Die nginx Konfigurationsdatei {filepath:s} verursacht Probleme und sollte vorher entfernt werden", @@ -212,8 +212,8 @@ "dyndns_could_not_check_provide": "Konnte nicht überprüft, ob {provider:s} die Domain(s) {domain:s} bereitstellen kann.", "domain_dns_conf_is_just_a_recommendation": "Dieser Befehl zeigt Ihnen, was die * empfohlene * Konfiguration ist. Die DNS-Konfiguration wird NICHT für Sie eingerichtet. Es liegt in Ihrer Verantwortung, Ihre DNS-Zone in Ihrem Registrar gemäß dieser Empfehlung zu konfigurieren.", "dpkg_lock_not_available": "Dieser Befehl kann momentan nicht ausgeführt werden, da anscheinend ein anderes Programm die Sperre von dpkg (dem Systempaket-Manager) verwendet", - "confirm_app_install_thirdparty": "WARNUNG! Das Installieren von Anwendungen von Drittanbietern kann die Integrität und Sicherheit Deines Systems beeinträchtigen. Du solltest es wahrscheinlich NICHT installieren, es sei denn, Du weisst, was Du tust. Bist du bereit, dieses Risiko einzugehen? [{answers:s}]", - "confirm_app_install_danger": "WARNUNG! Diese Anwendung ist noch experimentell (wenn nicht ausdrücklich \"not working\"/\"nicht funktionsfähig\")! Du solltest es wahrscheinlich NICHT installieren, es sei denn, du weißt, was du tust. Es wird keine Unterstützung geleistet, falls diese Anwendung nicht funktioniert oder dein System zerstört... Falls du bereit bist, dieses Risiko einzugehen, tippe '{answers:s}'", + "confirm_app_install_thirdparty": "WARNUNG! Das Installieren von Anwendungen von Drittanbietern kann die Integrität und Sicherheit Ihres Systems beeinträchtigen. Sie sollten Sie wahrscheinlich NICHT installieren, es sei denn, Sie wiẞen, was Sie tun. Sind Sie bereit, dieses Risiko einzugehen? [{answers:s}]", + "confirm_app_install_danger": "WARNUNG! Diese Anwendung ist noch experimentell (wenn nicht ausdrücklich \"not working\"/\"nicht funktionsfähig\")! Sie sollten sie wahrscheinlich NICHT installieren, es sei denn, Sie wißen, was Sie tun. Es wird keine Unterstützung geleistet, falls diese Anwendung nicht funktioniert oder Ihr System zerstört... Falls Sie bereit bist, dieses Risiko einzugehen, tippe '{answers:s}'", "confirm_app_install_warning": "Warnung: Diese Anwendung funktioniert möglicherweise, ist jedoch nicht gut in YunoHost integriert. Einige Funktionen wie Single Sign-On und Backup / Restore sind möglicherweise nicht verfügbar. Trotzdem installieren? [{answers:s}] ", "backup_with_no_restore_script_for_app": "{app:s} hat kein Wiederherstellungsskript. Das Backup dieser App kann nicht automatisch wiederhergestellt werden.", "backup_with_no_backup_script_for_app": "Die App {app:s} hat kein Sicherungsskript. Ignoriere es.", @@ -329,7 +329,7 @@ "diagnosis_found_errors_and_warnings": "Habe {errors} erhebliche(s) Problem(e) (und {warnings} Warnung(en)) in Verbindung mit {category} gefunden!", "diagnosis_ip_broken_dnsresolution": "Domänen-Namens-Auflösung scheint aus einem bestimmten Grund nicht zu funktionieren... Blockiert eine Firewall die DNS Anfragen?", "diagnosis_ip_broken_resolvconf": "Domänen-Namens-Auflösung scheint nicht zu funktionieren, was daran liegen könnte, dass in /etc/resolv.conf kein Eintrag auf 127.0.0.1 zeigt.", - "diagnosis_ip_weird_resolvconf_details": "Stattdessen sollte diese Datei ein Softlink auf /etc/resolvconf/run/resolv.conf sein, die auf sich selbst zu 127.0.0.1 zeigt (dnsmasq). Der eigentlich Auflösende sollte in /etc/resolv.dnsmasq.conf konfiguriert werden.", + "diagnosis_ip_weird_resolvconf_details": "Die Datei /etc/resolv.conf muss ein Symlink auf /etc/resolvconf/run/resolv.conf sein, welcher auf 127.0.0.1 (dnsmasq) zeigt. Falls Sie die DNS-Resolver manuell konfigurieren möchten, bearbeiten Sie bitte /etc/resolv.dnsmasq.conf.", "diagnosis_dns_good_conf": "Die DNS-Einträge für die Domäne {domain} (Kategorie {category}) sind korrekt konfiguriert", "diagnosis_ignored_issues": "(+ {nb_ignored} ignorierte(s) Problem(e))", "diagnosis_basesystem_hardware": "Server Hardware Architektur ist {virt} {arch}", @@ -366,5 +366,41 @@ "diagnosis_dns_missing_record": "Gemäß der empfohlenen DNS-Konfiguration sollten Sie einen DNS-Eintrag mit den folgenden Informationen hinzufügen.
Typ: {type}
Name: {name}
Wert: {value}", "diagnosis_dns_bad_conf": "Einige DNS-Einträge für die Domäne {domain} fehlen oder sind nicht korrekt (Kategorie {category})", "diagnosis_ip_local": "Lokale IP: {local}", - "diagnosis_ip_global": "Globale IP: {global}" + "diagnosis_ip_global": "Globale IP: {global}", + "diagnosis_ip_no_ipv6_tip": "Die Verwendung von IPv6 ist nicht Voraussetzung für das Funktionieren Ihres Servers, trägt aber zur Gesundheit des Internet als Ganzes bei. IPv6 sollte normalerweise automatisch von Ihrem Server oder Ihrem Provider konfiguriert werden, sofern verfügbar. Andernfalls müßen Sie einige Dinge manuell konfigurieren. Weitere Informationen finden Sie hier: https://yunohost.org/#/ipv6. Wenn Sie IPv6 nicht aktivieren können oder Ihnen das zu technisch ist, können Sie diese Warnung gefahrlos ignorieren.", + "diagnosis_services_bad_status_tip": "Sie können versuchen, den Dienst neu zu starten, und wenn das nicht funktioniert, schauen Sie sich die (Dienst-)Logs in der Verwaltung an (In der Kommandozeile können Sie dies mit yunohost service restart {service} und yunohost service log {service} tun).", + "diagnosis_services_bad_status": "Der Dienst {service} ist {status} :(", + "diagnosis_diskusage_verylow": "Der Speicher {mountpoint} (auf Gerät {device}) hat nur noch {free} ({free_percent}%) freien Speicherplatz (von ingesamt {total}). Sie sollten sich ernsthaft überlegen, einigen Seicherplatz frei zu machen!", + "diagnosis_http_ok": "Die Domäne {domain} ist über HTTP von außerhalb des lokalen Netzwerks erreichbar.", + "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Einige Hosting-Anbieter werden es Ihnen nicht gestatten, den ausgehenden Port 25 zu öffnen, da diese sich nicht um die Netzneutralität kümmern.
- Einige davon bieten als Alternative an, ein Mailserver-Relay zu verwenden, was jedoch bedeutet, dass das Relay Ihren E-Mail-Verkehr ausspionieren kann.
- Eine die Privatsphäre berücksichtigende Alternative ist die Verwendung eines VPN *mit einer dedizierten öffentlichen IP* um solche Einschränkungen zu umgehen. Schauen Sie unter https://yunohost.org/#/vpn_advantage nach.
- Sie können auch in Betracht ziehen, zu einem netzneutralitätfreundlicheren Anbieter zu wechseln.", + "diagnosis_http_timeout": "Wartezeit wurde beim Versuch, von außen eine Verbindung zum Server aufzubauen, überschritten. Er scheint nicht erreichbar zu sein.
1. Die häufigste Ursache für dieses Problem ist daß der Port 80 (und 433) nicht richtig zu Ihrem Server weitergeleitet werden.
2. Sie sollten auch sicherstellen, daß der Dienst nginx läuft.
3. In komplexeren Umgebungen: Stellen Sie sicher, daß keine Firewall oder Reverse-Proxy stört.", + "service_reloaded_or_restarted": "Der Dienst '{service:s}' wurde erfolgreich neu geladen oder gestartet", + "service_restarted": "Der Dienst '{service:s}' wurde neu gestartet", + "service_regen_conf_is_deprecated": "'yunohost service regen-conf' ist veraltet! Bitte verwenden Sie stattdessen 'yunohost tools regen-conf'.", + "certmanager_warning_subdomain_dns_record": "Die Subdomain '{subdomain:s}' löst nicht dieselbe IP wie '{domain:s} auf. Einige Funktionen werden nicht verfügbar sein, solange Sie dies nicht beheben und das Zertifikat erneuern.", + "diagnosis_ports_ok": "Port {port} ist von außen erreichbar.", + "diagnosis_ram_verylow": "Das System hat nur {available} ({available_percent}%) RAM zur Verfügung! (von insgesamt {total})", + "diagnosis_mail_outgoing_port_25_blocked_details": "Sie sollten zuerst versuchen den ausgehenden Port 25 auf Ihrer Router-Konfigurationsoberfläche oder Ihrer Hosting-Anbieter-Konfigurationsoberfläche zu öffnen. (Bei einigen Hosting-Anbieter kann es sein, daß Sie verlangen, daß man dafür ein Support-Ticket sendet).", + "diagnosis_mail_ehlo_ok": "Der SMTP-Server ist von von außen erreichbar und darum auch in der Lage E-Mails zu empfangen!", + "diagnosis_mail_ehlo_bad_answer": "Ein nicht-SMTP-Dienst antwortete auf Port 25 per IPv{ipversion}", + "diagnosis_swap_notsomuch": "Das System hat nur {total} Swap. Sie sollten sich überlegen mindestens {recommended} an Swap einzurichten, um Situationen zu verhindern, in welchen der RAM des Systems knapp wird.", + "diagnosis_swap_ok": "Das System hat {total} Swap!", + "diagnosis_swap_tip": "Wir sind Ihnen sehr dankbar dafür, daß Sie behutsam und sich bewußt sind, dass das Betreiben einer Swap-Partition auf einer SD-Karte oder einem SSD-Speicher das Risiko einer drastischen Verkürzung der Lebenserwartung dieser Platte nach sich zieht.", + "diagnosis_mail_outgoing_port_25_ok": "Der SMTP-Server ist in der Lage E-Mails zu versenden (der ausgehende Port 25 ist nicht blockiert).", + "diagnosis_mail_outgoing_port_25_blocked": "Der SMTP-Server kann keine E-Mails an andere Server senden, weil der ausgehende Port 25 per IPv{ipversion} blockiert ist. Sie können versuchen diesen in der Konfigurations-Oberfläche Ihres Internet-Anbieters (oder Hosters) zu öffnen.", + "diagnosis_mail_ehlo_unreachable": "Der SMTP-Server ist von außen nicht erreichbar per IPv{ipversion}. Er wird nicht in der Lage sein E-Mails zu empfangen.", + "diagnosis_diskusage_low": "Der Speicher {mountpoint} (auf Gerät {device}) hat nur noch {free} ({free_percent}%) freien Speicherplatz (von insgesamt {total}). Seien Sie vorsichtig.", + "diagnosis_ram_low": "Das System hat nur {available} ({available_percent}%) RAM zur Verfügung! (von insgesamt {total}). Seien Sie vorsichtig.", + "service_reload_or_restart_failed": "Der Dienst '{service:s}' konnte nicht erneut geladen oder gestartet werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}", + "diagnosis_domain_expiration_not_found_details": "Die WHOIS-Informationen für die Domäne {domain} scheint keine Informationen über das Ablaufdatum zu enthalten.", + "diagnosis_domain_expiration_warning": "Einige Domänen werden bald ablaufen!", + "diagnosis_diskusage_ok": "Der Speicher {mountpoint} (auf Gerät {device}) hat immer noch {free} ({free_percent}%) freien Speicherplatz übrig(von insgesamt {total})!", + "diagnosis_ram_ok": "Das System hat immer noch {available} ({available_percent}%) RAM zu Verfügung von {total}.", + "diagnosis_swap_none": "Das System hat gar keinen Swap. Sie sollten sich überlegen mindestens {recommended} an Swap einzurichten, um Situationen zu verhindern, in welchen der RAM des Systems knapp wird.", + "diagnosis_mail_ehlo_unreachable_details": "Konnte keine Verbindung zu Ihrem Server auf dem Port 25 herzustellen per IPv{ipversion}. Er scheint nicht erreichbar zu sein.
1. Das häufigste Problem ist, dass der Port 25 nicht richtig zu Ihrem Server weitergeleitet ist.
2. Sie sollten auch sicherstellen, dass der Postfix-Dienst läuft.
3. In komplexeren Umgebungen: Stellen Sie sicher, daß keine Firewall oder Reverse-Proxy stört.", + "diagnosis_mail_ehlo_wrong": "Ein anderer SMTP-Server antwortet auf IPv{ipversion}. Ihr Server wird wahrscheinlich nicht in der Lage sein, E-Mails zu empfangen.", + "migration_description_0018_xtable_to_nftable": "Alte Netzwerkverkehrsregeln zum neuen nftable-System migrieren", + "service_reload_failed": "Der Dienst '{service:s}' konnte nicht erneut geladen werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}", + "service_reloaded": "Der Dienst '{service:s}' wurde erneut geladen", + "service_restart_failed": "Der Dienst '{service:s}' konnte nicht erneut gestartet werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}" } From 51e3e0ea0f2d1dc6f42b452556d5c4d046664dcb Mon Sep 17 00:00:00 2001 From: Christian Wehrli Date: Tue, 1 Sep 2020 12:12:15 +0000 Subject: [PATCH 209/262] Translated using Weblate (French) Currently translated at 95.2% (580 of 609 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 70 ++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index ee9ba7b16..022ec23a1 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -12,10 +12,10 @@ "app_install_files_invalid": "Fichiers d’installation incorrects", "app_manifest_invalid": "Manifeste d’application incorrect : {error}", "app_not_correctly_installed": "{app:s} semble être mal installé", - "app_not_installed": "Nous n’avons pas trouvé l’application « {app:s} » dans la liste des applications installées : {all_apps}", + "app_not_installed": "Nous n’avons pas trouvé {app:s} dans la liste des applications installées : {all_apps}", "app_not_properly_removed": "{app:s} n’a pas été supprimé correctement", "app_removed": "{app:s} supprimé", - "app_requirements_checking": "Vérification des paquets requis pour {app} …", + "app_requirements_checking": "Vérification des paquets requis pour {app}...", "app_requirements_unmeet": "Les pré-requis de {app} ne sont pas satisfaits, le paquet {pkgname} ({version}) doit être {spec}", "app_sources_fetch_failed": "Impossible de récupérer les fichiers sources, l’URL est-elle correcte ?", "app_unknown": "Application inconnue", @@ -28,8 +28,8 @@ "ask_main_domain": "Domaine principal", "ask_new_admin_password": "Nouveau mot de passe d’administration", "ask_password": "Mot de passe", - "backup_app_failed": "Impossible de sauvegarder l’application '{app:s}'", - "backup_archive_app_not_found": "L’application '{app:s}' n’a pas été trouvée dans l’archive de la sauvegarde", + "backup_app_failed": "Impossible de sauvegarder {app:s}", + "backup_archive_app_not_found": "{app:s} n’a pas été trouvée dans l’archive de la sauvegarde", "backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà.", "backup_archive_name_unknown": "L’archive locale de sauvegarde nommée '{name:s}' est inconnue", "backup_archive_open_failed": "Impossible d’ouvrir l’archive de la sauvegarde", @@ -44,7 +44,7 @@ "backup_output_directory_forbidden": "Choisissez un répertoire de destination différent. Les sauvegardes ne peuvent pas être créées dans les sous-dossiers /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives", "backup_output_directory_not_empty": "Le répertoire de destination n’est pas vide", "backup_output_directory_required": "Vous devez spécifier un dossier de destination pour la sauvegarde", - "backup_running_hooks": "Exécution des scripts de sauvegarde …", + "backup_running_hooks": "Exécution des scripts de sauvegarde...", "custom_app_url_required": "Vous devez spécifier une URL pour mettre à jour votre application personnalisée {app:s}", "domain_cert_gen_failed": "Impossible de générer le certificat", "domain_created": "Le domaine a été créé", @@ -63,15 +63,15 @@ "dyndns_cron_removed": "La tâche cron pour le domaine DynDNS enlevée", "dyndns_ip_update_failed": "Impossible de mettre à jour l’adresse IP sur le domaine DynDNS", "dyndns_ip_updated": "Mise à jour de votre IP pour le domaine DynDNS", - "dyndns_key_generating": "Génération de la clé DNS …, cela peut prendre un certain temps.", + "dyndns_key_generating": "Génération de la clé DNS..., cela peut prendre un certain temps.", "dyndns_key_not_found": "Clé DNS introuvable pour le domaine", "dyndns_no_domain_registered": "Aucun domaine enregistré avec DynDNS", "dyndns_registered": "Domaine DynDNS enregistré", "dyndns_registration_failed": "Impossible d’enregistrer le domaine DynDNS : {error:s}", "dyndns_unavailable": "Le domaine {domain:s} est indisponible.", - "executing_command": "Exécution de la commande '{command:s}' …", - "executing_script": "Exécution du script '{script:s}' …", - "extracting": "Extraction en cours …", + "executing_command": "Exécution de la commande '{command:s}'...", + "executing_script": "Exécution du script '{script:s}'...", + "extracting": "Extraction en cours...", "field_invalid": "Champ incorrect : '{:s}'", "firewall_reload_failed": "Impossible de recharger le pare-feu", "firewall_reloaded": "Pare-feu rechargé", @@ -163,7 +163,7 @@ "certmanager_attempt_to_replace_valid_cert": "Vous êtes en train de vouloir remplacer un certificat correct et valide pour le domaine {domain:s} ! (Utilisez --force pour contourner cela)", "certmanager_domain_unknown": "Domaine {domain:s} inconnu", "certmanager_domain_cert_not_selfsigned": "Le certificat du domaine {domain:s} n’est pas auto-signé. Voulez-vous vraiment le remplacer ? (Utilisez --force pour cela)", - "certmanager_certificate_fetching_or_enabling_failed": "Il semble que l’activation du nouveau certificat pour {domain:s} a échoué …", + "certmanager_certificate_fetching_or_enabling_failed": "Il semble que l’activation du nouveau certificat pour {domain:s} a échoué...", "certmanager_attempt_to_renew_nonLE_cert": "Le certificat pour le domaine {domain:s} n’est pas émis par Let’s Encrypt. Impossible de le renouveler automatiquement !", "certmanager_attempt_to_renew_valid_cert": "Le certificat pour le domaine {domain:s} n’est pas sur le point d’expirer ! (Vous pouvez utiliser --force si vous savez ce que vous faites)", "certmanager_domain_http_not_working": "Le domaine {domain:s} ne semble pas être accessible via HTTP. Merci de vérifier la catégorie 'Web' dans le diagnostic pour plus d'informations. (Ou si vous savez ce que vous faites, utilisez '--no-checks' pour désactiver la vérification.)", @@ -209,13 +209,13 @@ "global_settings_unknown_type": "Situation inattendue : la configuration {setting:s} semble avoir le type {unknown_type:s} mais celui-ci n’est pas pris en charge par le système.", "global_settings_unknown_setting_from_settings_file": "Clé inconnue dans les paramètres : '{setting_key:s}', rejet de cette clé et sauvegarde de celle-ci dans /etc/yunohost/unkown_settings.json", "backup_abstract_method": "Cette méthode de sauvegarde reste à implémenter", - "backup_applying_method_tar": "Création de l’archive TAR de la sauvegarde …", - "backup_applying_method_copy": "Copie de tous les fichiers à sauvegarder …", - "backup_applying_method_borg": "Envoi de tous les fichiers à sauvegarder dans le répertoire borg-backup …", - "backup_applying_method_custom": "Appel de la méthode de sauvegarde personnalisée '{method:s}' …", + "backup_applying_method_tar": "Création de l’archive TAR de la sauvegarde...", + "backup_applying_method_copy": "Copie de tous les fichiers à sauvegarder...", + "backup_applying_method_borg": "Envoi de tous les fichiers à sauvegarder dans le répertoire borg-backup...", + "backup_applying_method_custom": "Appel de la méthode de sauvegarde personnalisée '{method:s}'...", "backup_archive_system_part_not_available": "La partie '{part:s}' du système n’est pas disponible dans cette sauvegarde", "backup_archive_writing_error": "Impossible d’ajouter des fichiers '{source:s}' (nommés dans l’archive : '{dest:s}') à sauvegarder dans l’archive compressée '{archive:s}'", - "backup_ask_for_copying_if_needed": "Voulez-vous effectuer la sauvegarde en utilisant {size:s} temporairement ? (Cette méthode est utilisée car certains fichiers n’ont pas pu être préparés avec une méthode plus efficace.)", + "backup_ask_for_copying_if_needed": "Voulez-vous effectuer la sauvegarde en utilisant {size:s}Mo temporairement ? (Cette méthode est utilisée car certains fichiers n’ont pas pu être préparés avec une méthode plus efficace.)", "backup_borg_not_implemented": "La méthode de sauvegarde Borg n’est pas encore implémentée", "backup_cant_mount_uncompress_archive": "Impossible de monter en lecture seule le dossier de l’archive décompressée", "backup_copying_to_organize_the_archive": "Copie de {size:s} Mo pour organiser l’archive", @@ -231,7 +231,7 @@ "backup_system_part_failed": "Impossible de sauvegarder la partie '{part:s}' du système", "backup_unable_to_organize_files": "Impossible d’utiliser la méthode rapide pour organiser les fichiers dans l’archive", "backup_with_no_backup_script_for_app": "L’application {app:s} n’a pas de script de sauvegarde. Ignorer.", - "backup_with_no_restore_script_for_app": "L’application « {app:s} » n’a pas de script de restauration, vous ne pourrez pas restaurer automatiquement la sauvegarde de cette application.", + "backup_with_no_restore_script_for_app": "{app:s} n’a pas de script de restauration, vous ne pourrez pas restaurer automatiquement la sauvegarde de cette application.", "global_settings_cant_serialize_settings": "Échec de la sérialisation des données de paramétrage car : {reason:s}", "restore_removing_tmp_dir_failed": "Impossible de sauvegarder un ancien dossier temporaire", "restore_extracting": "Extraction des fichiers nécessaires depuis l’archive…", @@ -253,7 +253,7 @@ "dyndns_could_not_check_provide": "Impossible de vérifier si {provider:s} peut fournir {domain:s}.", "dyndns_domain_not_provided": "Le fournisseur DynDNS {provider:s} ne peut pas fournir le domaine {domain:s}.", "app_make_default_location_already_used": "Impossible de configurer l’application '{app}' par défaut pour le domaine '{domain}' car il est déjà utilisé par l’application '{other_app}'", - "app_upgrade_app_name": "Mise à jour de l’application {app} …", + "app_upgrade_app_name": "Mise à jour de {app}...", "backup_output_symlink_dir_broken": "Votre répertoire d’archivage '{path:s}' est un lien symbolique brisé. Peut-être avez-vous oublié de re/monter ou de brancher le support de stockage sur lequel il pointe.", "migrate_tsig_end": "La migration à HMAC-SHA-512 est terminée", "migrate_tsig_failed": "La migration du domaine DynDNS {domain} à HMAC-SHA-512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}", @@ -352,15 +352,15 @@ "root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost n’a pas pu le propager au mot de passe root !", "aborting": "Annulation.", "app_not_upgraded": "L’application {failed_app} n’a pas été mise à jour et par conséquence les applications suivantes n’ont pas été mises à jour : {apps}", - "app_start_install": "Installation de l’application {app} …", - "app_start_remove": "Suppression de l’application {app} …", - "app_start_backup": "Collecte des fichiers devant être sauvegardés pour l’application {app} …", - "app_start_restore": "Restauration de l’application {app} …", + "app_start_install": "Installation de {app}...", + "app_start_remove": "Suppression de {app}...", + "app_start_backup": "Collecte des fichiers devant être sauvegardés pour {app}...", + "app_start_restore": "Restauration de {app}...", "app_upgrade_several_apps": "Les applications suivantes seront mises à jour : {apps}", "ask_new_domain": "Nouveau domaine", "ask_new_path": "Nouveau chemin", - "backup_actually_backuping": "Création d’une archive de sauvegarde à partir des fichiers collectés …", - "backup_mount_archive_for_restore": "Préparation de l’archive pour restauration …", + "backup_actually_backuping": "Création d’une archive de sauvegarde à partir des fichiers collectés...", + "backup_mount_archive_for_restore": "Préparation de l’archive pour restauration...", "confirm_app_install_warning": "Avertissement : cette application peut fonctionner mais n’est pas bien intégrée dans YunoHost. Certaines fonctionnalités telles que l’authentification unique et la sauvegarde/restauration peuvent ne pas être disponibles. L’installer quand même ? [{answers:s}] ", "confirm_app_install_danger": "DANGER ! Cette application est connue pour être encore expérimentale (si elle ne fonctionne pas explicitement) ! Vous ne devriez probablement PAS l’installer à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système … Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers:s}'", "confirm_app_install_thirdparty": "DANGER! Cette application ne fait pas partie du catalogue d'applications de Yunohost. L'installation d'applications tierces peut compromettre l'intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l'installer à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système ... Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers:s}'", @@ -429,7 +429,7 @@ "tools_upgrade_special_packages_explanation": "La mise à niveau spécifique à YunoHost se poursuivra en arrière-plan. Veuillez ne pas lancer d'autres actions sur votre serveur pendant les 10 prochaines minutes (selon la vitesse du matériel). Après cela, vous devrez peut-être vous reconnecter à l'administrateur Web. Le journal de mise à niveau sera disponible dans Outils → Journal (dans le webadmin) ou en utilisant la « liste des journaux yunohost » (à partir de la ligne de commande).", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", - "backup_permission": "Permission de sauvegarde pour l’application {app:s}", + "backup_permission": "Permission de sauvegarde pour {app:s}", "group_created": "Le groupe '{group}' a été créé", "group_deleted": "Suppression du groupe '{group}'", "group_unknown": "Le groupe {group:s} est inconnu", @@ -501,7 +501,7 @@ "app_install_failed": "Impossible d’installer {app} : {error}", "app_install_script_failed": "Une erreur est survenue dans le script d’installation de l’application", "permission_require_account": "Permission {permission} n’a de sens que pour les utilisateurs ayant un compte et ne peut donc pas être activé pour les visiteurs.", - "app_remove_after_failed_install": "Supprimer l’application après l’échec de l’installation …", + "app_remove_after_failed_install": "Supprimer l’application après l’échec de l’installation...", "diagnosis_display_tip_web": "Vous pouvez aller à la section Diagnostic (dans l’écran d’accueil) pour voir les problèmes rencontrés.", "diagnosis_cant_run_because_of_dep": "Impossible d’exécuter le diagnostic pour {category} alors qu’il existe des problèmes importants liés à {dep}.", "diagnosis_found_errors": "Trouvé {errors} problème(s) significatif(s) lié(s) à {category} !", @@ -526,9 +526,9 @@ "diagnosis_found_warnings": "Trouvé {warnings} objet(s) pouvant être amélioré(s) pour {category}.", "diagnosis_everything_ok": "Tout semble bien pour {category} !", "diagnosis_failed": "Échec de la récupération du résultat du diagnostic pour la catégorie '{category}' : {error}", - "diagnosis_ip_connected_ipv4": "Le serveur est connecté à Internet en IPv4 !", + "diagnosis_ip_connected_ipv4": "Le serveur est connecté à Internet en IPv4!", "diagnosis_ip_no_ipv4": "Le serveur ne dispose pas d’une adresse IPv4.", - "diagnosis_ip_connected_ipv6": "Le serveur est connecté à Internet en IPv6 !", + "diagnosis_ip_connected_ipv6": "Le serveur est connecté à Internet en IPv6!", "diagnosis_ip_no_ipv6": "Le serveur ne dispose pas d’une adresse IPv6.", "diagnosis_ip_dnsresolution_working": "La résolution de nom de domaine fonctionne !", "diagnosis_ip_broken_dnsresolution": "La résolution du nom de domaine semble interrompue pour une raison quelconque … Un pare-feu bloque-t-il les requêtes DNS ?", @@ -550,7 +550,7 @@ "diagnosis_regenconf_manually_modified_debian_details": "Cela peut probablement être OK, mais il faut garder un œil dessus …", "apps_catalog_init_success": "Système de catalogue d’applications initialisé !", "apps_catalog_failed_to_download": "Impossible de télécharger le catalogue des applications {apps_catalog} : {error}", - "diagnosis_mail_outgoing_port_25_blocked": "Le port sortant 25 semble être bloqué. Vous devriez essayer de le débloquer dans le panneau de configuration de votre fournisseur de services Internet (ou hébergeur). En attendant, le serveur ne pourra pas envoyer de courrier électronique à d’autres serveurs.", + "diagnosis_mail_outgoing_port_25_blocked": "Le port sortant 25 semble être bloqué. Vous devriez essayer de le débloquer dans le panneau de configuration de votre fournisseur de services Internet (ou hébergeur). En attendant, le serveur ne pourra pas envoyer des courriels à d’autres serveurs.", "domain_cannot_remove_main_add_new_one": "Vous ne pouvez pas supprimer '{domain:s}' car il s’agit du domaine principal et de votre seul domaine. Vous devez d’abord ajouter un autre domaine à l’aide de 'yunohost domain add ', puis définir comme domaine principal à l’aide de 'yunohost domain main-domain -n ' et vous pouvez ensuite supprimer le domaine '{domain:s}' à l’aide de 'yunohost domain remove {domain:s}'.'", "diagnosis_security_vulnerable_to_meltdown_details": "Pour résoudre ce problème, vous devez mettre à niveau votre système et redémarrer pour charger le nouveau noyau Linux (ou contacter votre fournisseur de serveur si cela ne fonctionne pas). Voir https://meltdownattack.com/ pour plus d’informations.", "diagnosis_description_basesystem": "Système de base", @@ -596,14 +596,14 @@ "diagnosis_description_web": "Web", "diagnosis_basesystem_hardware_board": "Le modèle de carte du serveur est {model}", "diagnosis_basesystem_hardware": "L’architecture du serveur est {virt} {arch}", - "group_already_exist_on_system_but_removing_it": "Le groupe {group} est déjà présent dans les groupes du système, mais YunoHost va le supprimer…", + "group_already_exist_on_system_but_removing_it": "Le groupe {group} est déjà présent dans les groupes du système, mais YunoHost va le supprimer...", "certmanager_warning_subdomain_dns_record": "Le sous-domaine '{subdomain:s}' ne résout pas vers la même adresse IP que '{domain:s}'. Certaines fonctionnalités seront indisponibles tant que vous n’aurez pas corrigé cela et regénéré le certificat.", "domain_cannot_add_xmpp_upload": "Vous ne pouvez pas ajouter de domaine commençant par 'xmpp-upload.'. Ce type de nom est réservé à la fonctionnalité d’upload XMPP intégrée dans YunoHost.", - "diagnosis_mail_outgoing_port_25_ok": "Le serveur de messagerie SMTP peut envoyer des e-mails (le port sortant 25 n'est pas bloqué).", + "diagnosis_mail_outgoing_port_25_ok": "Le serveur de messagerie SMTP peut envoyer des courriels (le port sortant 25 n'est pas bloqué).", "diagnosis_mail_outgoing_port_25_blocked_details": "Vous devez d’abord essayer de débloquer le port sortant 25 dans votre interface de routeur Internet ou votre interface d’hébergement. (Certains hébergeurs peuvent vous demander de leur envoyer un ticket de support pour cela).", "diagnosis_mail_ehlo_bad_answer": "Un service non SMTP a répondu sur le port 25 en IPv{ipversion}", "diagnosis_mail_ehlo_bad_answer_details": "Cela peut être dû à une autre machine qui répond au lieu de votre serveur.", - "diagnosis_mail_ehlo_wrong": "Un autre serveur de messagerie SMTP répond sur IPv{ipversion}. Votre serveur ne sera probablement pas en mesure de recevoir des e-mails.", + "diagnosis_mail_ehlo_wrong": "Un autre serveur de messagerie SMTP répond sur IPv{ipversion}. Votre serveur ne sera probablement pas en mesure de recevoir des courriel.", "diagnosis_mail_ehlo_could_not_diagnose": "Impossible de diagnostiquer si le serveur de messagerie postfix est accessible de l’extérieur en IPv{ipversion}.", "diagnosis_mail_ehlo_could_not_diagnose_details": "Erreur : {error}", "diagnosis_mail_fcrdns_dns_missing": "Aucun DNS inverse n’est défini pour IPv{ipversion}. Certains e-mails seront peut-être refusés ou considérés comme des spam.", @@ -623,8 +623,8 @@ "diagnosis_ip_local": "IP locale : {local}", "diagnosis_dns_point_to_doc": "Veuillez consulter la documentation sur https://yunohost.org/dns_config si vous avez besoin d’aide pour configurer les enregistrements DNS.", "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Certains fournisseurs ne vous laisseront pas débloquer le port sortant 25 parce qu’ils ne se soucient pas de la neutralité du Net.
- Certains d’entre eux offrent l’alternative d'utiliser un serveur de messagerie relai bien que cela implique que le relai sera en mesure d’espionner votre trafic de messagerie.
- Une alternative respectueuse de la vie privée consiste à utiliser un VPN *avec une IP publique dédiée* pour contourner ce type de limites. Voir https://yunohost.org/#/vpn_advantage
- Vous pouvez également envisager de passer à un fournisseur plus respectueux de la neutralité du net", - "diagnosis_mail_ehlo_ok": "Le serveur de messagerie SMTP est accessible de l'extérieur et peut donc recevoir des e-mails !", - "diagnosis_mail_ehlo_unreachable": "Le serveur de messagerie SMTP est inaccessible de l’extérieur en IPv{ipversion}. Il ne pourra pas recevoir d’e-mails.", + "diagnosis_mail_ehlo_ok": "Le serveur de messagerie SMTP est accessible de l'extérieur et peut donc recevoir des courriels!", + "diagnosis_mail_ehlo_unreachable": "Le serveur de messagerie SMTP est inaccessible de l’extérieur en IPv{ipversion}. Il ne pourra pas recevoir des courriels.", "diagnosis_mail_ehlo_unreachable_details": "Impossible d'ouvrir une connexion sur le port 25 à votre serveur en IPv{ipversion}. Il semble inaccessible.
1. La cause la plus courante de ce problème est que le port 25 n'est pas correctement redirigé vers votre serveur.
2. Vous devez également vous assurer que le service postfix est en cours d'exécution.
3. Sur les configurations plus complexes: assurez-vous qu'aucun pare-feu ou proxy inversé n'interfère.", "diagnosis_mail_ehlo_wrong_details": "Le EHLO reçu par le serveur de diagnostique distant en IPv{ipversion} est différent du domaine de votre serveur.
EHLO reçu: {wrong_ehlo}
Attendu : {right_ehlo}
La cause la plus courante ce problème est que le port 25 n’est pas correctement redirigé vers votre serveur . Vous pouvez également vous assurer qu’aucun pare-feu ou proxy inversé n’interfère.", "diagnosis_mail_fcrdns_nok_alternatives_4": "Certains fournisseurs ne vous laisseront pas configurer votre DNS inversé (ou leur fonctionnalité pourrait être cassée …). Si vous rencontrez des problèmes à cause de cela, envisagez les solutions suivantes :
- Certains FAI fournissent l’alternative de à l’aide d’un relais de serveur de messagerie bien que cela implique que le relais pourra espionner votre trafic de messagerie.
- Une alternative respectueuse de la vie privée consiste à utiliser un VPN *avec une IP publique dédiée* pour contourner ce type de limites. Voir https://yunohost.org/#/vpn_advantage
- Enfin, il est également possible de changer de fournisseur", @@ -648,7 +648,7 @@ "diagnosis_domain_expiration_warning": "Certains domaines vont expirer prochainement !", "diagnosis_domain_expiration_error": "Certains domaines vont expirer TRÈS PROCHAINEMENT !", "diagnosis_domain_expires_in": "{domain} expire dans {days} jours.", - "certmanager_domain_not_diagnosed_yet": "Il n'y a pas encore de résultat de diagnostic pour le domaine %s. Merci de relancer un diagnostic pour les catégories 'Enregistrements DNS' et 'Web' dans la section Diagnostique pour vérifier si le domaine est prêt pour Let's Encrypt. (Ou si vous savez ce que vous faites, utilisez '--no-checks' pour désactiver la vérification.)", + "certmanager_domain_not_diagnosed_yet": "Il n'y a pas encore de résultat de diagnostic pour le domaine {domain}. Merci de relancer un diagnostic pour les catégories 'Enregistrements DNS' et 'Web' dans la section Diagnostique pour vérifier si le domaine est prêt pour Let's Encrypt. (Ou si vous savez ce que vous faites, utilisez '--no-checks' pour désactiver la vérification.)", "diagnosis_swap_tip": "Merci d'être prudent et conscient que si vous hébergez une partition SWAP sur une carte SD ou un disque SSD, cela risque de réduire drastiquement l’espérance de vie du périphérique.", "restore_already_installed_apps": "Les applications suivantes ne peuvent pas être restaurées car elles sont déjà installées : {apps}", "regenconf_need_to_explicitly_specify_ssh": "La configuration de ssh a été modifiée manuellement. Vous devez explicitement indiquer la mention --force à \"ssh\" pour appliquer les changements.", @@ -667,6 +667,6 @@ "migration_0015_start": "Démarrage de la migration vers Buster", "migration_description_0015_migrate_to_buster": "Mise à niveau du système vers Debian Buster et YunoHost 4.x", "diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par Yunohost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant yunohost dyndns update --force.", - "app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de Yunohost. Vous devriez probablement envisager de mettre à jour votre système.", + "app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de YunoHost. Vous devriez probablement envisager de mettre à jour votre système.", "migration_0015_weak_certs": "Il a été constaté que les certificats suivants utilisent encore des algorithmes de signature peu robustes et doivent être mis à jour pour être compatibles avec la prochaine version de nginx : {certs}" } From 91c301dc7d408b34e07504d5f9b136cb19c2519f Mon Sep 17 00:00:00 2001 From: Leandro Noferini Date: Thu, 3 Sep 2020 17:22:02 +0000 Subject: [PATCH 210/262] Translated using Weblate (Italian) Currently translated at 23.0% (140 of 609 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/it/ --- locales/it.json | 53 +++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/locales/it.json b/locales/it.json index 88968863c..2ca2c3087 100644 --- a/locales/it.json +++ b/locales/it.json @@ -1,11 +1,11 @@ { "app_already_installed": "{app:s} è già installata", "app_extraction_failed": "Impossibile estrarre i file di installazione", - "app_not_installed": "Impossibile trovare l'applicazione '{app:s}' nell'elenco delle applicazioni installate: {all_apps}", + "app_not_installed": "Impossibile trovare l'applicazione {app:s} nell'elenco delle applicazioni installate: {all_apps}", "app_unknown": "Applicazione sconosciuta", "ask_email": "Indirizzo email", "ask_password": "Password", - "backup_archive_name_exists": "Il nome dell'archivio del backup è già esistente", + "backup_archive_name_exists": "Il nome dell'archivio del backup è già esistente.", "backup_created": "Backup completo", "backup_invalid_archive": "Archivio di backup non valido", "backup_output_directory_not_empty": "La directory di output non è vuota", @@ -37,22 +37,22 @@ "app_sources_fetch_failed": "Impossibile riportare i file sorgenti, l'URL è corretto?", "app_upgrade_failed": "Impossibile aggiornare {app:s}: {error}", "app_upgraded": "{app:s} aggiornata", - "app_requirements_checking": "Controllo i pacchetti richiesti per {app}…", + "app_requirements_checking": "Controllo i pacchetti richiesti per {app}...", "app_requirements_unmeet": "Requisiti non soddisfatti per {app}, il pacchetto {pkgname} ({version}) deve essere {spec}", "ask_firstname": "Nome", "ask_lastname": "Cognome", "ask_main_domain": "Dominio principale", "ask_new_admin_password": "Nuova password dell'amministrazione", - "backup_app_failed": "Non è possibile fare il backup dell'applicazione '{app:s}'", - "backup_archive_app_not_found": "L'applicazione '{app:s}' non è stata trovata nel archivio di backup", + "backup_app_failed": "Non è possibile fare il backup {app:s}", + "backup_archive_app_not_found": "{app:s} non è stata trovata nel archivio di backup", "app_argument_choice_invalid": "Usa una delle seguenti scelte '{choices:s}' per il parametro '{name:s}'", "app_argument_invalid": "Scegli un valore valido per il parametro '{name:s}': {error:s}", "app_argument_required": "L'argomento '{name:s}' è requisito", "app_id_invalid": "Identificativo dell'applicazione non valido", "app_unsupported_remote_type": "Il tipo remoto usato per l'applicazione non è supportato", - "backup_archive_broken_link": "Non è possibile accedere al archivio di backup (link rotto verso {path:s})", + "backup_archive_broken_link": "Non è possibile accedere all'archivio di backup (link rotto verso {path:s})", "backup_archive_name_unknown": "Archivio di backup locale chiamato '{name:s}' sconosciuto", - "backup_archive_open_failed": "Non è possibile aprire l'archivio di backup", + "backup_archive_open_failed": "Impossibile aprire l'archivio di backup", "backup_cleaning_failed": "Non è possibile pulire la directory temporanea di backup", "backup_creation_failed": "La creazione del backup è fallita", "backup_delete_error": "Impossibile cancellare '{path:s}'", @@ -179,15 +179,15 @@ "app_change_url_success": "L'URL dell'applicazione {app:s} è stato cambiato in {domain:s}{path:s}", "app_make_default_location_already_used": "Impostazione dell'applicazione '{app}' come predefinita del dominio non riuscita perché il dominio {domain} è già in uso per l'altra applicazione '{other_app}'", "app_location_unavailable": "Questo URL non è più disponibile o va in conflitto con la/le applicazione/i già installata/e:\n{apps:s}", - "app_upgrade_app_name": "Aggiornamento dell'applicazione {app}…", + "app_upgrade_app_name": "Aggiornamento di {app}...", "app_upgrade_some_app_failed": "Impossibile aggiornare alcune applicazioni", - "backup_abstract_method": "Questo metodo di backup non è ancora stato implementato", - "backup_applying_method_borg": "Inviando tutti i file da salvare nel backup nel deposito borg-backup…", - "backup_applying_method_copy": "Copiando tutti i files nel backup…", - "backup_applying_method_custom": "Chiamando il metodo di backup personalizzato '{method:s}'…", - "backup_applying_method_tar": "Creando l'archivio tar del backup…", + "backup_abstract_method": "Questo metodo di backup deve essere ancora implementato", + "backup_applying_method_borg": "Invio di tutti i file del backup nel deposito borg-backup...", + "backup_applying_method_copy": "Copiando tutti i files nel backup...", + "backup_applying_method_custom": "Chiamando il metodo di backup personalizzato '{method:s}'...", + "backup_applying_method_tar": "Creando l'archivio TAR del backup...", "backup_archive_system_part_not_available": "La parte di sistema '{part:s}' non è disponibile in questo backup", - "backup_archive_writing_error": "Impossibile aggiungere i file al backup nell'archivio compresso", + "backup_archive_writing_error": "Impossibile aggiungere i file '{source:s}' (indicati nell'archivio '{dest:s}') al backup nell'archivio compresso '{archive:s}'", "backup_ask_for_copying_if_needed": "Alcuni files non possono essere preparati al backup utilizzando il metodo che consente di evitare il consumo temporaneo di spazio nel sistema. Per eseguire il backup, {size:s}MB dovranno essere utilizzati temporaneamente. Sei d'accordo?", "backup_borg_not_implemented": "Il metodo di backup Borg non è ancora stato implementato", "backup_cant_mount_uncompress_archive": "Impossibile montare in modalità sola lettura la cartella di archivio non compressa", @@ -213,14 +213,14 @@ "aborting": "Annullamento.", "admin_password_too_long": "Per favore scegli una password più corta di 127 caratteri", "app_not_upgraded": "Impossibile aggiornare le applicazioni '{failed_app}' e di conseguenza l'aggiornamento delle seguenti applicazione è stato cancellato: {apps}", - "app_start_install": "Installando l'applicazione '{app}'…", - "app_start_remove": "Rimozione dell'applicazione {app}…", - "app_start_backup": "Raccogliendo file da salvare nel backup per '{app}'…", - "app_start_restore": "Ripristino dell'applicazione '{app}'…", - "app_upgrade_several_apps": "Le seguenti app saranno aggiornate : {apps}", + "app_start_install": "Installando '{app}'...", + "app_start_remove": "Rimozione di {app}...", + "app_start_backup": "Raccogliendo file da salvare nel backup per '{app}'...", + "app_start_restore": "Ripristino di '{app}'...", + "app_upgrade_several_apps": "Le seguenti applicazioni saranno aggiornate : {apps}", "ask_new_domain": "Nuovo dominio", "ask_new_path": "Nuovo percorso", - "backup_actually_backuping": "Creando un archivio di backup con i file raccolti…", + "backup_actually_backuping": "Creazione di un archivio di backup con i file raccolti...", "backup_mount_archive_for_restore": "Preparando l'archivio per il ripristino…", "certmanager_cert_install_success_selfsigned": "Certificato autofirmato installato con successo per il dominio {domain:s}!", "certmanager_cert_renew_success": "Certificato di Let's Encrypt rinnovato con successo per il dominio {domain:s}!", @@ -336,9 +336,18 @@ "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`.", "app_action_broke_system": "Questa azione sembra avere rotto questi servizi importanti: {services}", - "app_remove_after_failed_install": "Rimozione dell'applicazione a causa del fallimento dell'installazione…", + "app_remove_after_failed_install": "Rimozione dell'applicazione a causa del fallimento dell'installazione...", "app_install_script_failed": "Si è verificato un errore nello script di installazione dell'applicazione", "app_install_failed": "Impossibile installare {app}:{error}", "app_full_domain_unavailable": "Spiacente, questa app deve essere installata su un proprio dominio, ma altre applicazioni sono state installate sul dominio '{domain}'. Dovresti invece usare un sotto-dominio dedicato per questa app.", - "app_upgrade_script_failed": "È stato trovato un errore nello script di aggiornamento dell'applicazione" + "app_upgrade_script_failed": "È stato trovato un errore nello script di aggiornamento dell'applicazione", + "apps_already_up_to_date": "Tutte le applicazioni sono aggiornate", + "apps_catalog_init_success": "Catalogo delle applicazioni inizializzato!", + "apps_catalog_updating": "Aggiornamento del catalogo delle applicazioni…", + "apps_catalog_failed_to_download": "Impossibile scaricare il catalogo delle applicazioni {apps_catalog} : {error}", + "apps_catalog_obsolete_cache": "La cache del catalogo della applicazioni è vuoto o obsoleto.", + "apps_catalog_update_success": "Il catalogo delle applicazioni è stato aggiornato!", + "backup_archive_corrupted": "Sembra che l'archivio di backup '{archive}' sia corrotto: {error}", + "backup_archive_cant_retrieve_info_json": "Impossibile caricare informazione per l'archivio '{archive}'... Impossibile scaricare info.json (oppure non è un json valido).", + "app_packaging_format_not_supported": "Quest'applicazione non può essere installata perché il formato non è supportato dalla vostra versione di YunoHost. Dovreste considerare di aggiornare il vostro sistema." } From 1307e3b2869fd84ae854e0d19dfa5ea8edd4e679 Mon Sep 17 00:00:00 2001 From: Yifei Ding Date: Tue, 1 Sep 2020 20:57:45 +0000 Subject: [PATCH 211/262] Translated using Weblate (Chinese (Simplified)) Currently translated at 2.3% (14 of 609 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/zh_Hans/ --- locales/zh_Hans.json | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/locales/zh_Hans.json b/locales/zh_Hans.json index dee71a1d4..d11e570d0 100644 --- a/locales/zh_Hans.json +++ b/locales/zh_Hans.json @@ -1,3 +1,16 @@ { - "password_too_simple_1": "密码长度至少为8个字符" -} \ No newline at end of file + "password_too_simple_1": "密码长度至少为8个字符", + "backup_created": "备份已创建", + "app_start_remove": "正在删除{app}……", + "admin_password_change_failed": "不能修改密码", + "admin_password_too_long": "请选择一个小于127个字符的密码", + "app_upgrade_failed": "不能升级{app:s}:{error}", + "app_id_invalid": "无效 app ID", + "app_unknown": "未知应用", + "admin_password_changed": "管理密码已更改", + "aborting": "正在放弃。", + "admin_password": "管理密码", + "app_start_restore": "正在恢复{app}……", + "action_invalid": "无效操作 '{action:s}'", + "ask_lastname": "姓" +} From 34227b6172f1a6f60ee77870e95a185644e2ef62 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 5 Sep 2020 18:57:11 +0200 Subject: [PATCH 212/262] Remove unused i18n strings --- locales/en.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index 3942b5812..039e9a084 100644 --- a/locales/en.json +++ b/locales/en.json @@ -61,7 +61,6 @@ "apps_catalog_obsolete_cache": "The app catalog cache is empty or obsolete.", "apps_catalog_update_success": "The application catalog has been updated!", "ask_user_domain": "Domain to use for the user's email address and XMPP account", - "ask_email": "E-mail address", "ask_firstname": "First name", "ask_lastname": "Last name", "ask_main_domain": "Main domain", @@ -351,7 +350,6 @@ "ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it", "iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it", "log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'", - "log_category_404": "The log category '{category}' does not exist", "log_link_to_log": "Full log of this operation: '{desc}'", "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log display {name}'", "log_link_to_failed_log": "Could not complete the operation '{desc}'. Please provide the full log of this operation by clicking here to get help", From fad373c016987ed9eeacdee047ae4fcbca58179d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 5 Sep 2020 19:05:45 +0200 Subject: [PATCH 213/262] Forgot to propagate change in user_create here as well --- src/yunohost/tests/test_backuprestore.py | 2 +- src/yunohost/tests/test_permission.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 35a3e23f1..0a67a7d00 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -61,7 +61,7 @@ def setup_function(function): if "with_permission_app_installed" in markers: assert not app_is_installed("permissions_app") - user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh") + user_create("alice", "Alice", "White", maindomain, "test123Ynh") install_app("permissions_app_ynh", "/urlpermissionapp" "&admin=alice") assert app_is_installed("permissions_app") diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 82ec151f3..fdfeb141c 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -53,8 +53,8 @@ def setup_function(function): return res socket.getaddrinfo = new_getaddrinfo - user_create("alice", "Alice", "White", "alice@" + maindomain, dummy_password) - user_create("bob", "Bob", "Snow", "bob@" + maindomain, dummy_password) + user_create("alice", "Alice", "White", maindomain, dummy_password) + user_create("bob", "Bob", "Snow", maindomain, dummy_password) permission_create("wiki.main", url="/", allowed=["all_users"] , sync_perm=False) permission_create("blog.main", allowed=["all_users"], sync_perm=False) user_permission_update("blog.main", remove="all_users", add="alice") From 9640b22a23bfa023d43e4a88d7d6b22c048c196b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 5 Sep 2020 19:15:34 +0200 Subject: [PATCH 214/262] Do not miserably fail if no oom_reaper line found... --- data/hooks/diagnosis/50-systemresources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index 6960b9bf0..d33f17dec 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -119,7 +119,7 @@ class SystemResourcesDiagnoser(Diagnoser): def analyzed_kern_log(): - cmd = 'tail -n 10000 /var/log/kern.log | grep "oom_reaper: reaped process"' + cmd = 'tail -n 10000 /var/log/kern.log | grep "oom_reaper: reaped process" || true' out = subprocess.check_output(cmd, shell=True) lines = out.strip().split("\n") From 5807d256679a07d1da96e16fee3751c5bffc1cb3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 6 Sep 2020 21:24:06 +0200 Subject: [PATCH 215/262] Add possibility to download backups --- data/actionsmap/yunohost.yml | 8 ++++++++ src/yunohost/backup.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 4acd4edd3..0c19a5e48 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -913,6 +913,14 @@ backup: help: Print sizes in human readable format action: store_true + ### backup_download() + download: + action_help: (API only) Request to download the file + api: GET /backup/download/ + arguments: + name: + help: Name of the local backup archive + ### backup_delete() delete: action_help: Delete a backup archive diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index bfe958a89..0780d9044 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -2186,6 +2186,36 @@ def backup_list(with_info=False, human_readable=False): return {'archives': archives} +def backup_download(name): + + if msettings.get('interface') != 'api': + logger.error("This option is only meant for the API/webadmin and doesn't make sense for the command line.") + return + + archive_file = '%s/%s.tar' % (ARCHIVES_PATH, name) + + # Check file exist (even if it's a broken symlink) + if not os.path.lexists(archive_file): + archive_file += ".gz" + if not os.path.lexists(archive_file): + raise YunohostError('backup_archive_name_unknown', name=name) + + # If symlink, retrieve the real path + if os.path.islink(archive_file): + archive_file = os.path.realpath(archive_file) + + # Raise exception if link is broken (e.g. on unmounted external storage) + if not os.path.exists(archive_file): + raise YunohostError('backup_archive_broken_link', + path=archive_file) + + # We return a raw bottle HTTPresponse (instead of serializable data like + # list/dict, ...), which is gonna be picked and used directly by moulinette + from bottle import static_file + archive_folder, archive_file_name = archive_file.rsplit("/", 1) + return static_file(archive_file_name, archive_folder, download=archive_file_name) + + def backup_info(name, with_details=False, human_readable=False): """ Get info about a local backup archive From a6bbe41705aa381f752a4b34d3cb0feb2f8d6f18 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Sep 2020 01:18:43 +0200 Subject: [PATCH 216/262] Fuck it these messages are confusing people and we don't need them --- src/yunohost/hook.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index b57300f54..2c0340ce3 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -196,8 +196,7 @@ def hook_list(action, list_by='name', show_info=False): else: _append_folder(result, HOOK_FOLDER) except OSError: - logger.debug("No default hook for action '%s' in %s", - action, HOOK_FOLDER) + pass try: # Append custom hooks @@ -207,8 +206,7 @@ def hook_list(action, list_by='name', show_info=False): else: _append_folder(result, CUSTOM_HOOK_FOLDER) except OSError: - logger.debug("No custom hook for action '%s' in %s", - action, CUSTOM_HOOK_FOLDER) + pass return {'hooks': result} From 835701d590c15ae484815c41533cc0c0b475ed72 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Sep 2020 15:49:54 +0200 Subject: [PATCH 217/262] Clean more madness / fix tests --- src/yunohost/backup.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index bfe958a89..4de1a9537 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -723,12 +723,6 @@ class BackupManager(): # Actual backup archive creation / method management # # - def add(self, method, output_directory=None): - """ - Add a backup method that will be applied after the files collection step - """ - self.methods.append(BackupMethod.create(method, self, output_directory=output_directory)) - def backup(self): """Apply backup methods""" @@ -1483,7 +1477,7 @@ class BackupMethod(object): """ @classmethod - def create(cls, method, manager, *args, **kwargs): + def create(cls, method, manager, **kwargs): """ Factory method to create instance of BackupMethod @@ -1497,7 +1491,7 @@ class BackupMethod(object): """ known_methods = {c.method_name:c for c in BackupMethod.__subclasses__()} backup_method = known_methods.get(method, CustomBackupMethod) - return backup_method(manager, method=method, *args, **kwargs) + return backup_method(manager, method=method, **kwargs) def __init__(self, manager, repo=None, **kwargs): """ @@ -2052,7 +2046,8 @@ def backup_create(name=None, description=None, methods=[], backup_manager = BackupManager(name, description) for method in methods: - backup_manager.add(method, output_directory=output_directory) + backup_manager.methods += BackupMethod.create(method, backup_manager, repo=output_directory)) + # Add backup targets (system and apps) backup_manager.set_system_targets(system) From 42f430d23424494f6a6ae7b21882b6e65a4e77dc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Sep 2020 16:02:47 +0200 Subject: [PATCH 218/262] Get rid of misleading/old 'no_compress' option --- data/actionsmap/yunohost.yml | 4 --- src/yunohost/backup.py | 33 +++++++++--------------- src/yunohost/tests/test_backuprestore.py | 4 +-- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 4acd4edd3..195220938 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -852,10 +852,6 @@ backup: -o: full: --output-directory help: Output directory for the backup - -r: - full: --no-compress - help: Do not create an archive file - action: store_true --methods: help: List of backup methods to apply (copy or tar by default) nargs: "*" diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 4de1a9537..12f5288d8 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1976,7 +1976,7 @@ class CustomBackupMethod(BackupMethod): # def backup_create(name=None, description=None, methods=[], - output_directory=None, no_compress=False, + output_directory=None, system=[], apps=[]): """ Create a backup local archive @@ -1986,7 +1986,6 @@ def backup_create(name=None, description=None, methods=[], description -- Short description of the backup method -- Method of backup to use output_directory -- Output directory for the backup - no_compress -- Do not create an archive file system -- List of system elements to backup apps -- List of application names to backup """ @@ -2001,6 +2000,10 @@ def backup_create(name=None, description=None, methods=[], if name and name in backup_list()['archives']: raise YunohostError('backup_archive_name_exists') + # By default we backup using the tar method + if not methods: + methods = ['tar'] + # Validate output_directory option if output_directory: output_directory = os.path.abspath(output_directory) @@ -2011,20 +2014,12 @@ def backup_create(name=None, description=None, methods=[], output_directory): raise YunohostError('backup_output_directory_forbidden') + if "copy" in methods: + if not output_directory: + raise YunohostError('backup_output_directory_required') # Check that output directory is empty - if os.path.isdir(output_directory) and no_compress and \ - os.listdir(output_directory): - + elif os.path.isdir(output_directory) and os.listdir(output_directory): raise YunohostError('backup_output_directory_not_empty') - elif no_compress: - raise YunohostError('backup_output_directory_required') - - # Define methods (retro-compat) - if not methods: - if no_compress: - methods = ['copy'] - else: - methods = ['tar'] # If no --system or --apps given, backup everything if system is None and apps is None: @@ -2038,18 +2033,14 @@ def backup_create(name=None, description=None, methods=[], # Create yunohost archives directory if it does not exists _create_archive_dir() - # Prepare files to backup - if no_compress: - backup_manager = BackupManager(name, description, - work_dir=output_directory) - else: - backup_manager = BackupManager(name, description) + # Initialize backup manager + backup_manager = BackupManager(name, description, work_dir=output_directory) for method in methods: backup_manager.methods += BackupMethod.create(method, backup_manager, repo=output_directory)) - # Add backup targets (system and apps) + backup_manager.set_system_targets(system) backup_manager.set_apps_targets(apps) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 0a67a7d00..8de06cb7b 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -387,13 +387,13 @@ def test_backup_with_different_output_directory(mocker): @pytest.mark.clean_opt_dir -def test_backup_with_no_compress(mocker): +def test_backup_using_copy_method(mocker): # Create the backup with message(mocker, "backup_created"): backup_create(system=["conf_nginx"], apps=None, output_directory="/opt/test_backup_output_directory", - no_compress=True, + methods=["copy"], name="backup") assert os.path.exists("/opt/test_backup_output_directory/info.json") From 1be93094b5470238d9cfb9b33f980b60b95d33c7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Sep 2020 16:16:35 +0200 Subject: [PATCH 219/262] Fix syntax error + more minor refactoring/cleanup --- src/yunohost/backup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 12f5288d8..a7b703296 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -240,7 +240,7 @@ class BackupManager(): backup_manager.backup() """ - def __init__(self, name=None, description='', work_dir=None): + def __init__(self, name=None, description='', methods=[], work_dir=None): """ BackupManager constructor @@ -258,7 +258,6 @@ class BackupManager(): self.created_at = int(time.time()) self.apps_return = {} self.system_return = {} - self.methods = [] self.paths_to_backup = [] self.size_details = { 'system': {}, @@ -277,6 +276,9 @@ class BackupManager(): self.work_dir = os.path.join(BACKUP_PATH, 'tmp', name) self._init_work_dir() + # Initialize backup methods + self.methods = [BackupMethod.create(method, self, repo=work_dir) for method in methods] + # # Misc helpers # # @@ -2035,9 +2037,7 @@ def backup_create(name=None, description=None, methods=[], # Initialize backup manager - backup_manager = BackupManager(name, description, work_dir=output_directory) - for method in methods: - backup_manager.methods += BackupMethod.create(method, backup_manager, repo=output_directory)) + backup_manager = BackupManager(name, description, methods=methods, work_dir=output_directory) # Add backup targets (system and apps) From 50f68e2886c24452b7f1aafe101b56e021df5280 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Sep 2020 17:20:59 +0200 Subject: [PATCH 220/262] Properly handle case where no match from grep --- data/hooks/diagnosis/50-systemresources.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index a9ed5b424..f0fac4974 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -119,8 +119,8 @@ class SystemResourcesDiagnoser(Diagnoser): def analyzed_kern_log(): cmd = 'tail -n 10000 /var/log/kern.log | grep "oom_reaper: reaped process" || true' - out = subprocess.check_output(cmd, shell=True) - lines = out.strip().split("\n") + out = subprocess.check_output(cmd, shell=True).strip() + lines = out.split("\n") if out else [] now = datetime.datetime.now() From 9a2a6385bb225bdcf56c14a3bc2e77e560ef0e07 Mon Sep 17 00:00:00 2001 From: SiM Date: Tue, 8 Sep 2020 18:10:34 +0200 Subject: [PATCH 221/262] move BACKUP_CORE_ONLY msg to standard output --- data/helpers.d/backup | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/backup b/data/helpers.d/backup index a62f6c104..27f6ddf26 100644 --- a/data/helpers.d/backup +++ b/data/helpers.d/backup @@ -67,9 +67,9 @@ ynh_backup() { then if [ $BACKUP_CORE_ONLY -eq 1 ] then - ynh_print_warn --message="$src_path will not be saved, because 'BACKUP_CORE_ONLY' is set." + ynh_print_info --message="$src_path will not be saved, because 'BACKUP_CORE_ONLY' is set." else - ynh_print_warn --message="$src_path will not be saved, because 'do_not_backup_data' is set." + ynh_print_info --message="$src_path will not be saved, because 'do_not_backup_data' is set." fi return 0 fi From db93307feaedc8f9aebf4d0021c87edef9773a85 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Sep 2020 19:48:05 +0200 Subject: [PATCH 222/262] Update src/yunohost/app.py Co-authored-by: Kayou --- src/yunohost/app.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index a36bd1d85..3c9b9d352 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2683,6 +2683,12 @@ class UserArgumentParser(YunoHostArgumentFormatParser): question = super(UserArgumentParser, self).parse_question(question, user_answers) question.choices = user_list()["users"] + if question.default is None: + root_mail = "root@%s" % _get_maindomain() + for user in question.choices.keys(): + if root_mail in user_info(user)["mail-aliases"]: + question.default = user + break return question From 07fb33597239bc8214fece2032acab4c6f79e4e2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Sep 2020 21:20:40 +0200 Subject: [PATCH 223/262] Missing imports --- src/yunohost/app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 3c9b9d352..bd6987efd 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2679,7 +2679,8 @@ class UserArgumentParser(YunoHostArgumentFormatParser): argument_type = "user" def parse_question(self, question, user_answers): - from yunohost.user import user_list + from yunohost.user import user_list, user_info + from yunohost.domain import _get_maindomain question = super(UserArgumentParser, self).parse_question(question, user_answers) question.choices = user_list()["users"] From bfa0f304aa4f566aab684b3ea022e0d46622ba41 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Sep 2020 22:23:03 +0200 Subject: [PATCH 224/262] mail-aliases key only exists if it's not empty? --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 0caa9d5e1..8cf83e594 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2567,7 +2567,7 @@ def _parse_args_in_yunohost_format(user_answers, argument_questions): root_mail = "root@%s" % _get_maindomain() for user in users.keys(): - if root_mail in user_info(user)["mail-aliases"]: + if root_mail in user_info(user).get("mail-aliases", []): question_default = user break From 7483d04f54b1423a1191aca9523d8839b0c404c8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Sep 2020 00:06:23 +0200 Subject: [PATCH 225/262] Fix a weird issue that may arise during test, strangely unspotted so far --- src/yunohost/app.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 8cf83e594..dfea9dc52 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -91,7 +91,7 @@ def app_catalog(full=False, with_categories=False): "level": infos["level"], } else: - infos["manifest"]["arguments"] = _set_default_ask_questions(infos["manifest"]["arguments"]) + infos["manifest"]["arguments"] = _set_default_ask_questions(infos["manifest"].get("arguments", {})) # Trim info for categories if not using --full for category in catalog["categories"]: @@ -170,7 +170,7 @@ def app_info(app, full=False): return ret ret["manifest"] = local_manifest - ret["manifest"]["arguments"] = _set_default_ask_questions(ret["manifest"]["arguments"]) + ret["manifest"]["arguments"] = _set_default_ask_questions(ret["manifest"].get("arguments", {})) ret['settings'] = settings absolute_app_name, _ = _parse_app_instance_name(app) @@ -2130,12 +2130,6 @@ def _get_manifest_of_app(path): manifest = manifest_toml.copy() - if "arguments" not in manifest: - return manifest - - if "install" not in manifest["arguments"]: - return manifest - install_arguments = [] for name, values in manifest_toml.get("arguments", {}).get("install", {}).items(): args = values.copy() @@ -2150,7 +2144,7 @@ def _get_manifest_of_app(path): else: raise YunohostError("There doesn't seem to be any manifest file in %s ... It looks like an app was not correctly installed/removed." % path, raw_msg=True) - manifest["arguments"] = _set_default_ask_questions(manifest["arguments"]) + manifest["arguments"] = _set_default_ask_questions(manifest.get("arguments", {})) return manifest From b9189b1979db2a640faa742ca19a545498e73941 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Sep 2020 23:58:30 +0200 Subject: [PATCH 226/262] Fix tests --- locales/en.json | 1 - src/yunohost/app.py | 1 + .../tests/test_apps_arguments_parsing.py | 22 +++++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/locales/en.json b/locales/en.json index 67ef69922..9fb31c663 100644 --- a/locales/en.json +++ b/locales/en.json @@ -600,7 +600,6 @@ "user_unknown": "Unknown user: {user:s}", "user_update_failed": "Could not update user {user}: {error}", "user_updated": "User info changed", - "users_available": "Available users:", "yunohost_already_installed": "YunoHost is already installed", "yunohost_ca_creation_failed": "Could not create certificate authority", "yunohost_ca_creation_success": "Local certification authority created.", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index aab4f2a41..336e2a71b 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2715,6 +2715,7 @@ class AppArgumentParser(YunoHostArgumentFormatParser): class DisplayTextArgumentParser(YunoHostArgumentFormatParser): + argument_type = "display_text" def parse(self, question, user_answers): print(question["ask"]) diff --git a/src/yunohost/tests/test_apps_arguments_parsing.py b/src/yunohost/tests/test_apps_arguments_parsing.py index 79e87b7bc..dede7a0f9 100644 --- a/src/yunohost/tests/test_apps_arguments_parsing.py +++ b/src/yunohost/tests/test_apps_arguments_parsing.py @@ -883,7 +883,8 @@ def test_parse_args_in_yunohost_format_user(): expected_result = OrderedDict({"some_user": (username, "user")}) with patch.object(user, "user_list", return_value={"users": users}): - assert _parse_args_in_yunohost_format(answers, questions) == expected_result + with patch.object(user, "user_info", return_value={}): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_user_two_users(): @@ -913,13 +914,15 @@ def test_parse_args_in_yunohost_format_user_two_users(): expected_result = OrderedDict({"some_user": (other_user, "user")}) with patch.object(user, "user_list", return_value={"users": users}): - assert _parse_args_in_yunohost_format(answers, questions) == expected_result + with patch.object(user, "user_info", return_value={}): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result answers = {"some_user": username} expected_result = OrderedDict({"some_user": (username, "user")}) with patch.object(user, "user_list", return_value={"users": users}): - assert _parse_args_in_yunohost_format(answers, questions) == expected_result + with patch.object(user, "user_info", return_value={}): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_user_two_users_wrong_answer(): @@ -1008,13 +1011,14 @@ def test_parse_args_in_yunohost_format_user_two_users_default_input(): answers = {} with patch.object(user, "user_list", return_value={"users": users}): - expected_result = OrderedDict({"some_user": (username, "user")}) - with patch.object(msignals, "prompt", return_value=username): - assert _parse_args_in_yunohost_format(answers, questions) == expected_result + with patch.object(user, "user_info", return_value={}): + expected_result = OrderedDict({"some_user": (username, "user")}) + with patch.object(msignals, "prompt", return_value=username): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result - expected_result = OrderedDict({"some_user": (other_user, "user")}) - with patch.object(msignals, "prompt", return_value=other_user): - assert _parse_args_in_yunohost_format(answers, questions) == expected_result + expected_result = OrderedDict({"some_user": (other_user, "user")}) + with patch.object(msignals, "prompt", return_value=other_user): + assert _parse_args_in_yunohost_format(answers, questions) == expected_result def test_parse_args_in_yunohost_format_app_empty(): From ce9689e0ef79182204314646bdeb7c52ac791903 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Thu, 10 Sep 2020 03:27:13 +0200 Subject: [PATCH 227/262] [enh] Force encrypt if we are using an smtp relay Thanks to @khimaros for this suggestion --- data/templates/postfix/main.cf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/templates/postfix/main.cf b/data/templates/postfix/main.cf index b15964241..43151e672 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -52,8 +52,12 @@ smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtpd_tls_loglevel=1 # -- TLS for outgoing connections +{% if smtp_relayhost %} +smtp_tls_security_level = encrypt +{% else %} # Use TLS if this is supported by the remote SMTP server, otherwise use plaintext. -smtp_tls_security_level=may +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 From bc2de62c7bf97f4bde4e3be6e9eb0c8a2307a696 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Thu, 10 Sep 2020 03:29:09 +0200 Subject: [PATCH 228/262] [fix] Typo in setting name --- 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 43151e672..4d27498c4 100644 --- a/data/templates/postfix/main.cf +++ b/data/templates/postfix/main.cf @@ -52,7 +52,7 @@ smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtpd_tls_loglevel=1 # -- TLS for outgoing connections -{% if smtp_relayhost %} +{% if relay_host != "" %} smtp_tls_security_level = encrypt {% else %} # Use TLS if this is supported by the remote SMTP server, otherwise use plaintext. From 78c5ea9098bf196705079846287a84786dc34212 Mon Sep 17 00:00:00 2001 From: ljf Date: Thu, 10 Sep 2020 04:02:25 +0200 Subject: [PATCH 229/262] [enh] Refactoring of package download process --- src/yunohost/app.py | 127 +++++++++++++------------------------------- 1 file changed, 37 insertions(+), 90 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index dfea9dc52..d2b93ee34 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -39,6 +39,7 @@ from collections import OrderedDict from moulinette import msignals, m18n, msettings from moulinette.utils.log import getActionLogger from moulinette.utils.network import download_json +from moulinette.utils.process import run_commands from moulinette.utils.filesystem import read_file, read_json, read_toml, read_yaml, write_to_file, write_to_json, write_to_yaml, chmod, chown, mkdir from yunohost.service import service_status, _run_service_command @@ -2237,61 +2238,15 @@ def _fetch_app_from_git(app): logger.debug(m18n.n('downloading')) + # Extract URL, branch and revision to download if ('@' in app) or ('http://' in app) or ('https://' in app): url = app branch = 'master' - github_repo = re_github_repo.match(app) - if github_repo: - if github_repo.group('tree'): - branch = github_repo.group('tree') - url = "https://github.com/{owner}/{repo}".format( - owner=github_repo.group('owner'), - repo=github_repo.group('repo'), - ) - tarball_url = "{url}/archive/{tree}.zip".format( - url=url, tree=branch - ) - try: - subprocess.check_call([ - 'wget', '-qO', app_tmp_archive, tarball_url]) - except subprocess.CalledProcessError: - logger.exception('unable to download %s', tarball_url) - raise YunohostError('app_sources_fetch_failed') - else: - manifest, extracted_app_folder = _extract_app_from_file( - app_tmp_archive, remove=True) - else: - tree_index = url.rfind('/tree/') - if tree_index > 0: - url = url[:tree_index] - branch = app[tree_index + 6:] - try: - # We use currently git 2.1 so we can't use --shallow-submodules - # option. When git will be in 2.9 (with the new debian version) - # we will be able to use it. Without this option all the history - # of the submodules repo is downloaded. - subprocess.check_call([ - 'git', 'clone', '-b', branch, '--single-branch', '--recursive', '--depth=1', url, - extracted_app_folder]) - subprocess.check_call([ - 'git', 'reset', '--hard', branch - ], cwd=extracted_app_folder) - manifest = _get_manifest_of_app(extracted_app_folder) - except subprocess.CalledProcessError: - raise YunohostError('app_sources_fetch_failed') - except ValueError as e: - raise YunohostError('app_manifest_invalid', error=e) - else: - logger.debug(m18n.n('done')) - - # Store remote repository info into the returned manifest - manifest['remote'] = {'type': 'git', 'url': url, 'branch': branch} - try: - revision = _get_git_last_commit_hash(url, branch) - except Exception as e: - logger.debug("cannot get last commit hash because: %s ", e) - else: - manifest['remote']['revision'] = revision + tree_index = url.rfind('/tree/') + if tree_index > 0: + url = url[:tree_index] + branch = app[tree_index + 6:] + revision = 'HEAD' else: app_dict = _load_apps_catalog()["apps"] @@ -2303,47 +2258,39 @@ def _fetch_app_from_git(app): raise YunohostError('app_unsupported_remote_type') app_info = app_dict[app_id] - app_info['manifest']['lastUpdate'] = app_info['lastUpdate'] - manifest = app_info['manifest'] url = app_info['git']['url'] + branch = app_info['git']['branch'] + revision = str(app_info['git']['revision']) - if 'github.com' in url: - tarball_url = "{url}/archive/{tree}.zip".format( - url=url, tree=app_info['git']['revision'] - ) - try: - subprocess.check_call([ - 'wget', '-qO', app_tmp_archive, tarball_url]) - except subprocess.CalledProcessError: - logger.exception('unable to download %s', tarball_url) - raise YunohostError('app_sources_fetch_failed') - else: - manifest, extracted_app_folder = _extract_app_from_file( - app_tmp_archive, remove=True) - else: - try: - subprocess.check_call([ - 'git', 'clone', app_info['git']['url'], - '-b', app_info['git']['branch'], extracted_app_folder]) - subprocess.check_call([ - 'git', 'reset', '--hard', - str(app_info['git']['revision']) - ], cwd=extracted_app_folder) - manifest = _get_manifest_of_app(extracted_app_folder) - except subprocess.CalledProcessError: - raise YunohostError('app_sources_fetch_failed') - except ValueError as e: - raise YunohostError('app_manifest_invalid', error=e) - else: - logger.debug(m18n.n('done')) + # Download only this commit + try: + # We don't use git clone because, git clone can't download + # a specific revision only + run_commands([['git', 'init', extracted_app_folder]], shell=False) + run_commands([ + ['git', 'remote', 'add', 'origin', url], + ['git', 'fetch', '--depth=1', 'origin', + branch if revision == 'HEAD' else revision], + ['git', 'reset', '--hard', 'FETCH_HEAD'] + ], cwd=extracted_app_folder, shell=False) + manifest = _get_manifest_of_app(extracted_app_folder) + except subprocess.CalledProcessError: + raise YunohostError('app_sources_fetch_failed') + except ValueError as e: + raise YunohostError('app_manifest_invalid', error=e) + else: + logger.debug(m18n.n('done')) - # Store remote repository info into the returned manifest - manifest['remote'] = { - 'type': 'git', - 'url': url, - 'branch': app_info['git']['branch'], - 'revision': app_info['git']['revision'], - } + # Store remote repository info into the returned manifest + manifest['remote'] = {'type': 'git', 'url': url, 'branch': branch} + if revision == 'HEAD': + try: + manifest['remote']['revision'] = _get_git_last_commit_hash(url, branch) + except Exception as e: + logger.debug("cannot get last commit hash because: %s ", e) + else: + manifest['remote']['revision'] = revision + manifest['lastUpdate'] = app_info['lastUpdate'] return manifest, extracted_app_folder From 5923114b20abddc886b6d34e24517b10324522a6 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Fri, 11 Sep 2020 20:27:40 +0200 Subject: [PATCH 230/262] [fix] Reduce right given to ynh users with ssh (#1050) * [fix] Avoid ynh user to be able to use X11 forwarding * [fix] Avoid some bad situations * [fix] Remove chroot restrictions and x11 authorization * Update comments Co-authored-by: Alexandre Aubin --- data/templates/ssh/sshd_config | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index bd3efdef3..84f06d4e5 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -66,12 +66,19 @@ AcceptEnv LANG LC_* # SFTP stuff Subsystem sftp internal-sftp -Match User sftpusers - ForceCommand internal-sftp - ChrootDirectory /home/%u - AllowTcpForwarding no - GatewayPorts no - X11Forwarding no + +# Forbid users from using their account SSH as a VPN (even if SSH login is disabled) +AllowTcpForwarding no +AllowStreamLocalForwarding no + +# Disable .ssh/rc, which could be edited (e.g. from Nextcloud or whatever) by users to execute arbitrary commands even if SSH login is disabled +PermitUserRC no + +Match User admin,root + AllowTcpForwarding yes + AllowStreamLocalForwarding yes + PermitUserRC yes + # root login is allowed on local networks # It's meant to be a backup solution in case LDAP is down and From d2bd6b6c12618d11ee6dae5d9edcf971f4115ef1 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Sun, 3 May 2020 21:09:45 +0200 Subject: [PATCH 231/262] Add doc about is_big --- data/helpers.d/backup | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/data/helpers.d/backup b/data/helpers.d/backup index a62f6c104..b5dfd7b03 100644 --- a/data/helpers.d/backup +++ b/data/helpers.d/backup @@ -40,6 +40,24 @@ CAN_BIND=${CAN_BIND:-1} # ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "/conf/" # # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf" # +# +# How to use --is_big: +# --is_big is used to specify that this part of the backup can be quite huge. +# So, you don't want that your package does backup that part during ynh_backup_before_upgrade. +# In the same way, an user may doesn't want to backup this big part of the app for +# each of his backup. And so handle that part differently. +# +# As this part of your backup may not be done, your restore script has to handle it. +# In your restore script, use --not_mandatory with ynh_restore_file +# As well in your remove script, you should not remove those data ! Or an user may end up with +# a failed upgrade restoring an app without data anymore ! +# +# To have the benefit of --is_big while doing a backup, you can whether set the environement +# variable BACKUP_CORE_ONLY to 1 (BACKUP_CORE_ONLY=1) before the backup command. It will affect +# only that backup command. +# Or set the config do_not_backup_data to 1 into the settings.yml of the app. This will affect +# all backups for this app until the setting is removed. +# # Requires YunoHost version 2.4.0 or higher. # Requires YunoHost version 3.5.0 or higher for the argument --not_mandatory ynh_backup() { From ca5f264baf2f2ab681b3692cdbccf5fe443f9371 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 14 Sep 2020 17:06:40 +0200 Subject: [PATCH 232/262] Remove pipe char when trying to re-run apt with dry-run ... Because apt becomes absolutely crazy and make every package installed conflict with weird stuff --- data/helpers.d/apt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index 1a4a9f74a..1e880af76 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -198,7 +198,7 @@ ynh_package_install_from_equivs () { # (the following is ran inside { } to not start a subshell otherwise ynh_die wouldnt exit the original process) # Get the list of dependencies from the deb local dependencies="$(dpkg --info "$TMPDIR/${pkgname}_${pkgversion}_all.deb" | grep Depends | \ - sed 's/^ Depends: //' | sed 's/,//g')" + sed 's/^ Depends: //' | sed 's/,//g' | tr -d '|')" # Fake an install of those dependencies to see the errors # The sed command here is, Print only from '--fix-broken' to the end. ynh_package_install $dependencies --dry-run | sed --quiet '/--fix-broken/,$p' >&2 From 9156b1e56af2dba336305557c4f2146a7c798bc8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 14 Sep 2020 17:54:20 +0200 Subject: [PATCH 233/262] Fix warnings and weird stuff >_> --- data/hooks/conf_regen/19-postfix | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 67ca22991..29787576e 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -27,22 +27,21 @@ do_pre_regen() { # Add possibility to specify a relay # Could be useful with some isp with no 25 port open or more complex setup export relay_host="$(yunohost settings get 'smtp.relay.host')" - if [ ! -z "${relay_host}" ]; then + if [ -n "${relay_host}" ] + then export relay_port="$(yunohost settings get 'smtp.relay.port')" export relay_user="$(yunohost settings get 'smtp.relay.user')" relay_password="$(yunohost settings get 'smtp.relay.password')" # Avoid to display "Relay account paswword" to other users touch ${postfix_dir}/sasl_passwd - chmod o=--- ${postfix_dir}/sasl_passwd + chmod 750 ${postfix_dir}/sasl_passwd # Avoid "postmap: warning: removing zero-length database file" chown postfix ${pending_dir}/etc/postfix chown postfix ${pending_dir}/etc/postfix/sasl_passwd cat <<< "[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" > ${postfix_dir}/sasl_passwd postmap ${postfix_dir}/sasl_passwd - - fi export main_domain export domain_list="$YNH_DOMAINS" @@ -67,8 +66,12 @@ do_pre_regen() { do_post_regen() { regen_conf_files=$1 - chmod o=--- /etc/postfix/sasl_passwd* - chown postfix /etc/postfix/sasl_passwd* + + if [ -e /etc/postfix/sasl_passwd ] + then + chmod 750 /etc/postfix/sasl_passwd* + chown postfix:root /etc/postfix/sasl_passwd* + fi [[ -z "$regen_conf_files" ]] \ || { service postfix restart && service postsrsd restart; } From 15a7967f474be96d27381ecb739ac4ae09d243e9 Mon Sep 17 00:00:00 2001 From: Kayou Date: Tue, 15 Sep 2020 20:53:26 +0200 Subject: [PATCH 234/262] Epic bugfix --- data/helpers.d/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 489c448a8..c538d8688 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -219,7 +219,7 @@ ynh_add_fpm_config () { if [ -e "../conf/php-fpm.ini" ] then - ynh_print_warn -message="Packagers ! Please do not use a separate php ini file, merge your directives in the pool file instead." + ynh_print_warn --message="Packagers ! Please do not use a separate php ini file, merge your directives in the pool file instead." finalphpini="$fpm_config_dir/conf.d/20-$app.ini" ynh_backup_if_checksum_is_different "$finalphpini" cp ../conf/php-fpm.ini "$finalphpini" From 6945867f860f5a42390f36dd88dfabd6b7e92a65 Mon Sep 17 00:00:00 2001 From: Kayou Date: Wed, 16 Sep 2020 13:23:10 +0200 Subject: [PATCH 235/262] detect wrong arguments --- data/helpers.d/getopts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/helpers.d/getopts b/data/helpers.d/getopts index a4bbe20e6..8d9e55826 100644 --- a/data/helpers.d/getopts +++ b/data/helpers.d/getopts @@ -130,6 +130,12 @@ ynh_handle_getopts_args () { then # Remove the option and the space, so keep only the value itself. all_args[0]="${all_args[0]#-${parameter} }" + + # At this point, if all_args[0] start with "-", then the argument is not well formed + if [ "${all_args[0]:0:1}" == "-" ] + then + ynh_die --message="Argument \"${all_args[0]}\" not valid! Did you use a single \"-\" instead of two?" + fi # Reduce the value of shift, because the option has been removed manually shift_value=$(( shift_value - 1 )) fi From 6a618b0f332606fd2a75f8398da2c69ef352e47d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Sep 2020 15:24:09 +0200 Subject: [PATCH 236/262] Use php7.3 by default in CLI --- data/hooks/conf_regen/10-apt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/hooks/conf_regen/10-apt b/data/hooks/conf_regen/10-apt index 4ce838e4c..5446c262c 100755 --- a/data/hooks/conf_regen/10-apt +++ b/data/hooks/conf_regen/10-apt @@ -18,6 +18,9 @@ Pin-Priority: -1" >> "/etc/apt/preferences.d/extra_php_version" do_post_regen() { regen_conf_files=$1 + + # Make sure php7.3 is the default version when using php in cli + update-alternatives --set php /usr/bin/php7.3 } FORCE=${2:-0} From 4805d43b96c24a8be2afdd61b4d8925bb0fb9efd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Sep 2020 15:27:32 +0200 Subject: [PATCH 237/262] Force locale to C during postgresql migration to avoid some stupid issue related to locale --- src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py index b81cbd8be..2f277443e 100644 --- a/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py +++ b/src/yunohost/data_migrations/0017_postgresql_9p6_to_11.py @@ -36,9 +36,9 @@ class MyMigration(Migration): raise YunohostError("migration_0017_not_enough_space", path="/var/lib/postgresql/") self.runcmd("systemctl stop postgresql") - self.runcmd("pg_dropcluster --stop 11 main || true") # We do not trigger an exception if the command fails because that probably means cluster 11 doesn't exists, which is fine because it's created during the pg_upgradecluster) - self.runcmd("pg_upgradecluster -m upgrade 9.6 main") - self.runcmd("pg_dropcluster --stop 9.6 main") + self.runcmd("LC_ALL=C pg_dropcluster --stop 11 main || true") # We do not trigger an exception if the command fails because that probably means cluster 11 doesn't exists, which is fine because it's created during the pg_upgradecluster) + self.runcmd("LC_ALL=C pg_upgradecluster -m upgrade 9.6 main") + self.runcmd("LC_ALL=C pg_dropcluster --stop 9.6 main") self.runcmd("systemctl start postgresql") def package_is_installed(self, package_name): From d243fe76ce1fe8cb8f31ec89404c8359d4974c3f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Sep 2020 16:14:03 +0200 Subject: [PATCH 238/262] [enh] Better problematic apt dependencies auto-investigation mechanism (#1051) * [enh] Better problematic apt dependencies auto-investigation mechanism * Misc tweak / fixes following tests --- data/helpers.d/apt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index 1e880af76..59f233c60 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -191,17 +191,17 @@ ynh_package_install_from_equivs () { cp "$controlfile" "${TMPDIR}/control" (cd "$TMPDIR" LC_ALL=C equivs-build ./control 1> /dev/null - dpkg --force-depends --install "./${pkgname}_${pkgversion}_all.deb" 2>&1) + LC_ALL=C dpkg --force-depends --install "./${pkgname}_${pkgversion}_all.deb" 2>&1 | tee ./dpkg_log) ynh_package_install --fix-broken || \ { # If the installation failed # (the following is ran inside { } to not start a subshell otherwise ynh_die wouldnt exit the original process) - # Get the list of dependencies from the deb - local dependencies="$(dpkg --info "$TMPDIR/${pkgname}_${pkgversion}_all.deb" | grep Depends | \ - sed 's/^ Depends: //' | sed 's/,//g' | tr -d '|')" + # Parse the list of problematic dependencies from dpkg's log ... + # (relevant lines look like: "foo-ynh-deps depends on bar; however:") + local problematic_dependencies="$(cat $TMPDIR/dpkg_log | grep -oP '(?<=-ynh-deps depends on ).*(?=; however)' | tr '\n' ' ')" # Fake an install of those dependencies to see the errors - # The sed command here is, Print only from '--fix-broken' to the end. - ynh_package_install $dependencies --dry-run | sed --quiet '/--fix-broken/,$p' >&2 + # The sed command here is, Print only from 'Reading state info' to the end. + [[ -n "$problematic_dependencies" ]] && ynh_package_install $problematic_dependencies --dry-run 2>&1 | sed --quiet '/Reading state info/,$p' | grep -v "fix-broken\|Reading state info" >&2 ynh_die --message="Unable to install dependencies"; } [[ -n "$TMPDIR" ]] && rm --recursive --force $TMPDIR # Remove the temp dir. From a60cd4f559288e5364add334670a50fdc43ffb6a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Sep 2020 16:18:10 +0200 Subject: [PATCH 239/262] Diagnose ssl libs installed from sury (#1053) * Small fix / syntax improvement in apt conf regen hook * Diagnose, report and add a tip if some ssl libs are installed from Sury (shouldnt happen with the new pinning strategy, but some user still encounter issues because of this because of legacy installs) --- data/hooks/conf_regen/10-apt | 5 +++-- data/hooks/diagnosis/00-basesystem.py | 23 +++++++++++++++++++++++ locales/en.json | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/data/hooks/conf_regen/10-apt b/data/hooks/conf_regen/10-apt index 5446c262c..09789470b 100755 --- a/data/hooks/conf_regen/10-apt +++ b/data/hooks/conf_regen/10-apt @@ -7,12 +7,13 @@ do_pre_regen() { mkdir --parents "${pending_dir}/etc/apt/preferences.d" - for package in "php" "php-fpm" "php-mysql" "php-xml" "php-zip" "php-mbstring" "php-ldap" "php-gd" "php-curl" "php-bz2" "php-json" "php-sqlite3" "php-intl" "openssl" "libssl1.1" "libssl-dev" + packages_to_refuse_from_sury="php php-fpm php-mysql php-xml php-zip php-mbstring php-ldap php-gd php-curl php-bz2 php-json php-sqlite3 php-intl openssl libssl1.1 libssl-dev" + for package in $packages_to_refuse_from_sury do echo " Package: $package Pin: origin \"packages.sury.org\" -Pin-Priority: -1" >> "/etc/apt/preferences.d/extra_php_version" +Pin-Priority: -1" >> "${pending_dir}/etc/apt/preferences.d/extra_php_version" done } diff --git a/data/hooks/diagnosis/00-basesystem.py b/data/hooks/diagnosis/00-basesystem.py index 8773f8b53..d58ca4aff 100644 --- a/data/hooks/diagnosis/00-basesystem.py +++ b/data/hooks/diagnosis/00-basesystem.py @@ -82,6 +82,29 @@ class BaseSystemDiagnoser(Diagnoser): details=["diagnosis_security_vulnerable_to_meltdown_details"] ) + bad_sury_packages = list(self.bad_sury_packages()) + if bad_sury_packages: + cmd_to_fix = "apt install --allow-downgrades " \ + + " ".join(["%s=%s" % (package, version) for package, version in bad_sury_packages]) + yield dict(meta={"test": "packages_from_sury"}, + data={"cmd_to_fix": cmd_to_fix}, + status="WARNING", + summary="diagnosis_package_installed_from_sury", + details=["diagnosis_package_installed_from_sury_details"]) + + def bad_sury_packages(self): + + packages_to_check = ["openssl", "libssl1.1", "libssl-dev"] + for package in packages_to_check: + cmd = "dpkg --list | grep '^ii' | grep gbp | grep -q -w %s" % package + # If version currently installed is not from sury, nothing to report + if os.system(cmd) != 0: + continue + + cmd = "LC_ALL=C apt policy %s 2>&1 | grep http -B1 | tr -d '*' | grep '+deb' | grep -v 'gbp' | head -n 1 | awk '{print $1}'" % package + version_to_downgrade_to = check_output(cmd).strip() + yield (package, version_to_downgrade_to) + def is_vulnerable_to_meltdown(self): # meltdown CVE: https://security-tracker.debian.org/tracker/CVE-2017-5754 diff --git a/locales/en.json b/locales/en.json index 340696054..1a620124b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -146,6 +146,8 @@ "diagnosis_basesystem_ynh_single_version": "{package} version: {version} ({repo})", "diagnosis_basesystem_ynh_main_version": "Server is running YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "You are running inconsistent versions of the YunoHost packages... most probably because of a failed or partial upgrade.", + "diagnosis_package_installed_from_sury": "Some system packages should be downgraded", + "diagnosis_package_installed_from_sury_details": "Some packages were inadvertendly installed from a third-party repository called Sury. The Yunohost team improved the strategy that handle these packages, but it's expected that some setups that installed PHP7.3 apps while still on Stretch have some remaining inconsistencies. To fix this situation, you should try running the following command: {cmd_to_fix}", "diagnosis_display_tip": "To see the issues found, you can go to the Diagnosis section of the webadmin, or run 'yunohost diagnosis show --issues' from the command-line.", "diagnosis_failed_for_category": "Diagnosis failed for category '{category}': {error}", "diagnosis_cache_still_valid": "(Cache still valid for {category} diagnosis. Won't re-diagnose it yet!)", From 674d8e7cb970d982e8cf41961c7306bb420dbfae Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Sep 2020 17:01:21 +0200 Subject: [PATCH 240/262] [fix] Minor issues in app questions parsing --- src/yunohost/app.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 97dc09621..300cc07b5 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2542,7 +2542,7 @@ class YunoHostArgumentFormatParser(object): question.value = None # we don't have an answer, check optional and default_value - if question.value is None: + if question.value is None or question.value == '': if not question.optional and question.default is None: raise YunohostError('app_argument_required', name=question.name) else: @@ -2566,12 +2566,12 @@ class YunoHostArgumentFormatParser(object): def _format_text_for_user_input_in_cli(self, question): text_for_user_input_in_cli = _value_for_locale(question.ask) - if question.default is not None: - text_for_user_input_in_cli += ' (default: {0})'.format(question.default) - if question.choices: text_for_user_input_in_cli += ' [{0}]'.format(' | '.join(question.choices)) + if question.default is not None: + text_for_user_input_in_cli += ' (default: {0})'.format(question.default) + return text_for_user_input_in_cli def _post_parse_value(self, question): From 56ebb06d2b468f873d7b8ed5818f7bc3cf7f052f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Sep 2020 18:49:17 +0200 Subject: [PATCH 241/262] Typo --- src/yunohost/app.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index d34a18d80..5bd9d7f08 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2498,7 +2498,7 @@ class YunoHostArgumentFormatParser(object): # we have an answer, do some post checks if question.value is not None: if question.choices and question.value not in question.choices: - self._raise_invalide_answer(question) + self._raise_invalid_answer(question) # this is done to enforce a certain formating like for boolean # by default it doesn't do anything @@ -2506,7 +2506,7 @@ class YunoHostArgumentFormatParser(object): return (question.value, self.argument_type) - def _raise_invalide_answer(self, question): + def _raise_invalid_answer(self, question): raise YunohostError('app_argument_choice_invalid', name=question.name, choices=', '.join(question.choices)) @@ -2611,7 +2611,7 @@ class DomainArgumentParser(YunoHostArgumentFormatParser): return question - def _raise_invalide_answer(self, question): + def _raise_invalid_answer(self, question): raise YunohostError('app_argument_invalid', name=question.name, error=m18n.n('domain_unknown')) @@ -2634,7 +2634,7 @@ class UserArgumentParser(YunoHostArgumentFormatParser): return question - def _raise_invalide_answer(self, question): + def _raise_invalid_answer(self, question): raise YunohostError('app_argument_invalid', name=question.name, error=m18n.n('user_unknown', user=question.value)) @@ -2650,7 +2650,7 @@ class AppArgumentParser(YunoHostArgumentFormatParser): return question - def _raise_invalide_answer(self, question): + def _raise_invalid_answer(self, question): raise YunohostError('app_argument_invalid', name=question.name, error=m18n.n('app_unknown')) From d5f8eb06c8996d66ae8de79a863241a1192b2742 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Sep 2020 19:28:45 +0200 Subject: [PATCH 242/262] Simplify code (suggestion from Bram) --- src/yunohost/app.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5bd9d7f08..7d5d36c4d 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -60,12 +60,6 @@ APPS_CATALOG_CRON_PATH = "/etc/cron.daily/yunohost-fetch-apps-catalog" APPS_CATALOG_API_VERSION = 2 APPS_CATALOG_DEFAULT_URL = "https://app.yunohost.org/default" -re_github_repo = re.compile( - r'^(http[s]?://|git@)github.com[/:]' - '(?P[\w\-_]+)/(?P[\w\-_]+)(.git)?' - '(/tree/(?P.+))?' -) - re_app_instance_name = re.compile( r'^(?P[\w-]+?)(__(?P[1-9][0-9]*))?$' ) @@ -2242,10 +2236,8 @@ def _fetch_app_from_git(app): if ('@' in app) or ('http://' in app) or ('https://' in app): url = app branch = 'master' - tree_index = url.rfind('/tree/') - if tree_index > 0: - url = url[:tree_index] - branch = app[tree_index + 6:] + if "/tree/" in url: + url, branch = url.split("/tree/", 1) revision = 'HEAD' else: app_dict = _load_apps_catalog()["apps"] From dd92828bf680d6179593d067e6340fc3444e1495 Mon Sep 17 00:00:00 2001 From: Baptiste Wojtkowski Date: Mon, 7 Sep 2020 11:56:06 +0000 Subject: [PATCH 243/262] Translated using Weblate (French) Currently translated at 96.4% (586 of 608 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 022ec23a1..dd78eacd7 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -668,5 +668,22 @@ "migration_description_0015_migrate_to_buster": "Mise à niveau du système vers Debian Buster et YunoHost 4.x", "diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par Yunohost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant yunohost dyndns update --force.", "app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de YunoHost. Vous devriez probablement envisager de mettre à jour votre système.", - "migration_0015_weak_certs": "Il a été constaté que les certificats suivants utilisent encore des algorithmes de signature peu robustes et doivent être mis à jour pour être compatibles avec la prochaine version de nginx : {certs}" + "migration_0015_weak_certs": "Il a été constaté que les certificats suivants utilisent encore des algorithmes de signature peu robustes et doivent être mis à jour pour être compatibles avec la prochaine version de nginx : {certs}", + "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de des archives non-compressées lors de la création des backups. N.B. Activer cette option implique de créer des archives plus légères, mais aussi d'avoir des procédures de backup significativement plus longues et plus gourmandes en CPU.", + "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers les nouvelles nftables système", + "service_description_php7.3-fpm": "Lancer les applications écrites en PHP avec NGINX", + "migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable legacy a échoué :", + "migration_0018_failed_to_migrate_iptables_rules": "La migration des iptables legacy vers nftables a échoué: {error}", + "migration_0017_not_enough_space": "Laissez suffisamment d'espace disponible dans {path} avant de lancer la migration.", + "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 est installé mais pas posgreSQL 11 ? Il s'est sans doute passé quelque chose d'étrange sur votre système :(...", + "migration_0017_postgresql_96_not_installed": "PostgreSQL n'a pas été installé sur votre système. Aucune opération à effectuer.", + "migration_description_0017_postgresql_9p6_to_11": "Migrer les bases de données de PostgreSQL 9.6 vers 11", + "migration_description_0016_php70_to_php73_pools": "Migrer php7.0-fpm 'pool' conf files vers php7.3", + "diagnosis_processes_killed_by_oom_reaper": "Certains processus ont été arrêtés récemment par le système car il manquait de mémoire. Cela apparaît généralement quand le système manque de mémoire ou qu'un processus consomme trop de mémoire. Liste des processus tués :", + "ask_user_domain": "Domaine à utiliser pour l'adresse mail des utilisateurs et le compte XMPP", + "app_manifest_install_ask_is_public": "Cette application devrait-elle apparaître aux visiteurs anonymes ?", + "app_manifest_install_ask_admin": "Choisissez un administrateur pour cette application", + "app_manifest_install_ask_password": "Choisissez un mot de passez administrateur pour cette application", + "app_manifest_install_ask_path": "Choisissez le chemin où cette application devrait être installée", + "app_manifest_install_ask_domain": "Choisissez le domaine où cette application devrait être insttallée" } From adc7a4f156bbd294507bc2b42460071040b31ddf Mon Sep 17 00:00:00 2001 From: Christian Wehrli Date: Tue, 8 Sep 2020 13:18:04 +0000 Subject: [PATCH 244/262] Translated using Weblate (French) Currently translated at 96.4% (586 of 608 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index dd78eacd7..88fbf3468 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -451,10 +451,10 @@ "migrations_exclusive_options": "'auto', '--skip' et '--force-rerun' sont des options mutuellement exclusives.", "migrations_not_pending_cant_skip": "Ces migrations ne sont pas en attente et ne peuvent donc pas être ignorées : {ids}", "migration_0011_can_not_backup_before_migration": "La sauvegarde du système n’a pas pu être terminée avant l’échec de la migration. Erreur : {error:s}", - "migration_0011_migrate_permission": "Migration des autorisations des paramètres des applications vers LDAP…", + "migration_0011_migrate_permission": "Migration des autorisations des paramètres des applications vers LDAP...", "migration_0011_migration_failed_trying_to_rollback": "La migration a échoué… Tentative de restauration du système.", "migration_0011_rollback_success": "Système restauré.", - "migration_0011_update_LDAP_database": "Mise à jour de la base de données LDAP…", + "migration_0011_update_LDAP_database": "Mise à jour de la base de données LDAP...", "migration_0011_backup_before_migration": "Création d’une sauvegarde des paramètres de la base de données LDAP et des applications avant la migration.", "permission_not_found": "Autorisation '{permission:s}' introuvable", "permission_update_failed": "Impossible de mettre à jour l’autorisation '{permission}' : {error}", @@ -638,7 +638,7 @@ "diagnosis_http_partially_unreachable": "Le domaine {domain} semble inaccessible en HTTP depuis l’extérieur du réseau local en IPv{failed}, bien qu’il fonctionne en IPv{passed}.", "diagnosis_http_nginx_conf_not_up_to_date": "La configuration Nginx de ce domaine semble avoir été modifiée manuellement et empêche YunoHost de diagnostiquer si elle est accessible en HTTP.", "diagnosis_http_nginx_conf_not_up_to_date_details": "Pour corriger la situation, inspectez la différence avec la ligne de commande en utilisant les outils yunohost tools regen-conf nginx --dry-run --with-diff et si vous êtes d’accord, appliquez les modifications avec yunohost tools regen-conf nginx --force.", - "backup_archive_cant_retrieve_info_json": "Impossible d'avoir des informations sur l'archive '{archive}' ... Le fichier info.json ne peut pas être trouvé (ou n'est pas un fichier json valide).", + "backup_archive_cant_retrieve_info_json": "Impossible d'avoir des informations sur l'archive '{archive}'... Le fichier info.json ne peut pas être trouvé (ou n'est pas un fichier json valide).", "backup_archive_corrupted": "Il semble que l'archive de la sauvegarde '{archive}' est corrompue : {error}", "diagnosis_ip_no_ipv6_tip": "L'utilisation de IPv6 n'est pas obligatoire pour le fonctionnement de votre serveur, mais cela contribue à la santé d'Internet dans son ensemble. IPv6 généralement configuré automatiquement par votre système ou votre FAI s'il est disponible. Autrement, vous devrez prendre quelque minutes pour le configurer manuellement à l'aide de cette documentation: https://yunohost.org/#/ipv6. Si vous ne pouvez pas activer IPv6 ou si c'est trop technique pour vous, vous pouvez aussi ignorer cet avertissement sans que cela pose problème.", "diagnosis_domain_expiration_not_found": "Impossible de vérifier la date d'expiration de certains domaines", From 16b5257ad3e6d6188d5c5aa4a552ef2ee4a47eb2 Mon Sep 17 00:00:00 2001 From: Christian Wehrli Date: Tue, 8 Sep 2020 12:41:17 +0000 Subject: [PATCH 245/262] Translated using Weblate (German) Currently translated at 48.7% (296 of 608 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/locales/de.json b/locales/de.json index 59c48ef0c..1f4bc4850 100644 --- a/locales/de.json +++ b/locales/de.json @@ -402,5 +402,10 @@ "migration_description_0018_xtable_to_nftable": "Alte Netzwerkverkehrsregeln zum neuen nftable-System migrieren", "service_reload_failed": "Der Dienst '{service:s}' konnte nicht erneut geladen werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}", "service_reloaded": "Der Dienst '{service:s}' wurde erneut geladen", - "service_restart_failed": "Der Dienst '{service:s}' konnte nicht erneut gestartet werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}" + "service_restart_failed": "Der Dienst '{service:s}' konnte nicht erneut gestartet werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}", + "app_manifest_install_ask_password": "Wählen Sie ein Verwaltungspasswort für diese Applikation", + "app_manifest_install_ask_domain": "Wählen Sie die Domäne, auf welcher die Applikation installiert werden soll", + "log_letsencrypt_cert_renew": "Erneuern des Let's Encrypt-Zeritifikates von '{}'", + "log_selfsigned_cert_install": "Das selbstsignierte Zertifikat auf der Domäne '{}' installieren", + "log_letsencrypt_cert_install": "Das Let’s Encrypt auf der Domäne '{}' installieren" } From dff93e950bba1d506026e6a72ff716b4dfb1a061 Mon Sep 17 00:00:00 2001 From: Christian Wehrli Date: Wed, 9 Sep 2020 17:06:43 +0000 Subject: [PATCH 246/262] Translated using Weblate (German) Currently translated at 50.7% (308 of 608 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/locales/de.json b/locales/de.json index 1f4bc4850..b43457cef 100644 --- a/locales/de.json +++ b/locales/de.json @@ -184,7 +184,7 @@ "domain_cannot_remove_main": "Die primäre Domain konnten nicht entfernt werden. Lege zuerst einen neue primäre Domain fest", "certmanager_self_ca_conf_file_not_found": "Die Konfigurationsdatei der Zertifizierungsstelle für selbstsignierte Zertifikate wurde nicht gefunden (Datei {file:s})", "certmanager_acme_not_configured_for_domain": "Die ACME Challenge kann im Moment nicht für {domain} ausgeführt werden, weil in ihrer nginx conf das entsprechende Code-Snippet fehlt... Bitte stellen Sie sicher, dass Ihre nginx-Konfiguration mit 'yunohost tools regen-conf nginx --dry-run --with-diff' auf dem neuesten Stand ist.", - "certmanager_unable_to_parse_self_CA_name": "Der Name der Zertifizierungsstelle für selbstsignierte Zertifikate konnte nicht analysiert werden (Datei: {file:s})", + "certmanager_unable_to_parse_self_CA_name": "Der Name der Zertifizierungsstelle für selbstsignierte Zertifikate konnte nicht aufgelöst werden (Datei: {file:s})", "certmanager_http_check_timeout": "Eine Zeitüberschreitung ist aufgetreten, als der Server versuchte sich selbst über HTTP mit der öffentlichen IP (Domain '{domain:s}' mit der IP '{ip:s}') zu erreichen. Möglicherweise ist dafür hairpinning oder eine falsch konfigurierte Firewall/Router deines Servers dafür verantwortlich.", "certmanager_couldnt_fetch_intermediate_cert": "Eine Zeitüberschreitung ist aufgetreten als der Server versuchte die Teilzertifikate von Let's Encrypt zusammenzusetzen. Die Installation/Erneuerung des Zertifikats wurde abgebrochen — bitte versuche es später erneut.", "domain_hostname_failed": "Erstellen des neuen Hostnamens fehlgeschlagen", @@ -407,5 +407,16 @@ "app_manifest_install_ask_domain": "Wählen Sie die Domäne, auf welcher die Applikation installiert werden soll", "log_letsencrypt_cert_renew": "Erneuern des Let's Encrypt-Zeritifikates von '{}'", "log_selfsigned_cert_install": "Das selbstsignierte Zertifikat auf der Domäne '{}' installieren", - "log_letsencrypt_cert_install": "Das Let’s Encrypt auf der Domäne '{}' installieren" + "log_letsencrypt_cert_install": "Das Let’s Encrypt auf der Domäne '{}' installieren", + "diagnosis_mail_fcrdns_nok_details": "Sie sollten zuerst versuchen, in Ihrer Internet-Router-Oberfläche oder in Ihrer Hosting-Anbieter-Oberfläche den Reverse-DNS-Eintrag mit {ehlo_domain}zu konfigurieren. (Gewisse Hosting-Anbieter können dafür möglicherweise verlangen, dass Sie dafür ein Support-Ticket erstellen).", + "diagnosis_mail_fcrdns_dns_missing": "Es wurde kein Reverse-DNS-Eintrag definiert für IPv{ipversion}. Einige E-Mails könnten möglicherweise zurückgewiesen oder als Spam markiert werden.", + "diagnosis_mail_fcrdns_ok": "Ihr Reverse-DNS-Eintrag ist korrekt konfiguriert!", + "diagnosis_mail_ehlo_could_not_diagnose_details": "Fehler: {error}", + "diagnosis_mail_ehlo_could_not_diagnose": "Konnte nicht überprüfen, ob der Postfix-Mail-Server von aussen per IPv{ipversion} erreichbar ist.", + "diagnosis_mail_ehlo_wrong_details": "Die vom Remote-Diagnose-Server per IPv{ipversion} empfangene EHLO weicht von der Domäne Ihres Servers ab.
Empfangene EHLO: {wrong_ehlo}
Erwartet: {right_ehlo}
Die geläufigste Ursache für dieses Problem ist, dass der Port 25 nicht korrekt auf Ihren Server weitergeleitet wird. Sie können sich zusätzlich auch versichern, dass keine Firewall oder Reverse-Proxy interferiert.", + "diagnosis_mail_ehlo_bad_answer_details": "Das könnte daran liegen, dass anstelle Ihres Servers eine andere Maschine antwortet.", + "ask_user_domain": "Domäne, welche für die E-Mail-Adresse und den XMPP-Account des Benutzers verwendet werden soll", + "app_manifest_install_ask_is_public": "Soll diese Applikation für anonyme Benutzer sichtbar sein?", + "app_manifest_install_ask_admin": "Wählen Sie einen Administrator für diese Applikation", + "app_manifest_install_ask_path": "Wählen Sie den Pfad, in welchem die Applikation installiert werden soll" } From e9fc4bc7f3efdbfdf38bda3273a3d8dec8ec8d25 Mon Sep 17 00:00:00 2001 From: Christian Wehrli Date: Fri, 11 Sep 2020 11:21:46 +0000 Subject: [PATCH 247/262] Translated using Weblate (German) Currently translated at 51.3% (314 of 612 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/locales/de.json b/locales/de.json index b43457cef..e27b05506 100644 --- a/locales/de.json +++ b/locales/de.json @@ -418,5 +418,11 @@ "ask_user_domain": "Domäne, welche für die E-Mail-Adresse und den XMPP-Account des Benutzers verwendet werden soll", "app_manifest_install_ask_is_public": "Soll diese Applikation für anonyme Benutzer sichtbar sein?", "app_manifest_install_ask_admin": "Wählen Sie einen Administrator für diese Applikation", - "app_manifest_install_ask_path": "Wählen Sie den Pfad, in welchem die Applikation installiert werden soll" + "app_manifest_install_ask_path": "Wählen Sie den Pfad, in welchem die Applikation installiert werden soll", + "diagnosis_mail_blacklist_listed_by": "Ihre IP-Adresse oder Domäne {item} ist auf der Blacklist auf {blacklist_name}", + "diagnosis_mail_blacklist_ok": "Die IP-Adressen und die Domänen, welche von diesem Server verwendet werden, scheinen nicht auf einer Blacklist zu sein", + "diagnosis_mail_fcrdns_different_from_ehlo_domain_details": "Aktueller Reverse-DNS-Eintrag: {rdns_domain}
Erwarteter Wert: {ehlo_domain}", + "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Der Reverse-DNS-Eintrag für IPv{ipversion} ist nicht korrekt konfiguriert. Einige E-Mails könnten abgewiesen oder als Spam markiert werden.", + "diagnosis_mail_fcrdns_nok_alternatives_6": "Einige Provider werden es Ihnen nicht erlauben, Ihren Reverse-DNS-Eintrag zu konfigurieren (oder ihre Funktionalität könnte defekt sein ...). Falls Ihr Reverse-DNS-Eintrag für IPv4 korrekt konfiguiert ist, können Sie versuchen, die Verwendung von IPv6 für das Versenden von E-Mails auszuschalten, indem Sie den Befehl yunohost settings set smtp.allow_ipv6 -v off ausführen. Bemerkung: Die Folge dieser letzten Lösung ist, dass Sie mit Servern, welche ausschliesslich über IPv6 verfügen, keine E-Mails mehr versenden oder empfangen können.", + "diagnosis_mail_fcrdns_nok_alternatives_4": "Einige Anbieter werden es Ihnen nicht erlauben, dass Sie Ihren Reverse-DNS (oder deren Funktionalität ist defekt...) konfigurieren. Falls Sie deswegen auf Probleme stossen sollten, ziehen Sie folgende Lösungen in Betracht:
- Manche ISPs stellen als Alternative die Benutzung eines Mail-Server-Relays zur Verfügung, was jedoch mit sich zieht, dass das Relay Ihren E-Mail-Verkehr ausspionieren kann.
- Eine privatsphärenfreundlichere Alternative ist die Benutzung eines VPN *mit einer dedizierten öffentlichen IP* um Einschränkungen dieser Art zu umgehen. Schauen Sie hier nach https://yunohost.org/#/vpn_advantage
- Schliesslich ist es auch möglich zu einem anderen Anbieter zu wechseln" } From 10abc848bb3e9056dc31191302ef8da93c8bd4b6 Mon Sep 17 00:00:00 2001 From: Christian Wehrli Date: Mon, 14 Sep 2020 06:10:36 +0000 Subject: [PATCH 248/262] Translated using Weblate (German) Currently translated at 52.1% (319 of 612 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/locales/de.json b/locales/de.json index e27b05506..4e0bd7130 100644 --- a/locales/de.json +++ b/locales/de.json @@ -424,5 +424,10 @@ "diagnosis_mail_fcrdns_different_from_ehlo_domain_details": "Aktueller Reverse-DNS-Eintrag: {rdns_domain}
Erwarteter Wert: {ehlo_domain}", "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Der Reverse-DNS-Eintrag für IPv{ipversion} ist nicht korrekt konfiguriert. Einige E-Mails könnten abgewiesen oder als Spam markiert werden.", "diagnosis_mail_fcrdns_nok_alternatives_6": "Einige Provider werden es Ihnen nicht erlauben, Ihren Reverse-DNS-Eintrag zu konfigurieren (oder ihre Funktionalität könnte defekt sein ...). Falls Ihr Reverse-DNS-Eintrag für IPv4 korrekt konfiguiert ist, können Sie versuchen, die Verwendung von IPv6 für das Versenden von E-Mails auszuschalten, indem Sie den Befehl yunohost settings set smtp.allow_ipv6 -v off ausführen. Bemerkung: Die Folge dieser letzten Lösung ist, dass Sie mit Servern, welche ausschliesslich über IPv6 verfügen, keine E-Mails mehr versenden oder empfangen können.", - "diagnosis_mail_fcrdns_nok_alternatives_4": "Einige Anbieter werden es Ihnen nicht erlauben, dass Sie Ihren Reverse-DNS (oder deren Funktionalität ist defekt...) konfigurieren. Falls Sie deswegen auf Probleme stossen sollten, ziehen Sie folgende Lösungen in Betracht:
- Manche ISPs stellen als Alternative die Benutzung eines Mail-Server-Relays zur Verfügung, was jedoch mit sich zieht, dass das Relay Ihren E-Mail-Verkehr ausspionieren kann.
- Eine privatsphärenfreundlichere Alternative ist die Benutzung eines VPN *mit einer dedizierten öffentlichen IP* um Einschränkungen dieser Art zu umgehen. Schauen Sie hier nach https://yunohost.org/#/vpn_advantage
- Schliesslich ist es auch möglich zu einem anderen Anbieter zu wechseln" + "diagnosis_mail_fcrdns_nok_alternatives_4": "Einige Anbieter werden es Ihnen nicht erlauben, dass Sie Ihren Reverse-DNS (oder deren Funktionalität ist defekt...) konfigurieren. Falls Sie deswegen auf Probleme stossen sollten, ziehen Sie folgende Lösungen in Betracht:
- Manche ISPs stellen als Alternative die Benutzung eines Mail-Server-Relays zur Verfügung, was jedoch mit sich zieht, dass das Relay Ihren E-Mail-Verkehr ausspionieren kann.
- Eine privatsphärenfreundlichere Alternative ist die Benutzung eines VPN *mit einer dedizierten öffentlichen IP* um Einschränkungen dieser Art zu umgehen. Schauen Sie hier nach https://yunohost.org/#/vpn_advantage
- Schliesslich ist es auch möglich zu einem anderen Anbieter zu wechseln", + "diagnosis_mail_queue_unavailable_details": "Fehler: {error}", + "diagnosis_mail_queue_unavailable": "Die Anzahl der anstehenden Nachrichten in der Warteschlange kann nicht abgefragt werden", + "diagnosis_mail_queue_ok": "{nb_pending} anstehende E-Mails in der Warteschlange", + "diagnosis_mail_blacklist_reason": "Der Grund für die Blacklist ist: {reason}", + "app_argument_password_no_default": "Fehler beim Verarbeiten des Passwort-Arguments '{name}': Passwort-Argument kann aus Sicherheitsgründen keinen Standardwert haben" } From 5c47a87cc089b8a01493fd70c8c8c78726a7321c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Sep 2020 19:43:18 +0200 Subject: [PATCH 249/262] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Éric Gaspar <46165813+ericgaspar@users.noreply.github.com> Co-authored-by: Kayou --- locales/fr.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 88fbf3468..b947fcb59 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -668,22 +668,22 @@ "migration_description_0015_migrate_to_buster": "Mise à niveau du système vers Debian Buster et YunoHost 4.x", "diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par Yunohost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant yunohost dyndns update --force.", "app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de YunoHost. Vous devriez probablement envisager de mettre à jour votre système.", - "migration_0015_weak_certs": "Il a été constaté que les certificats suivants utilisent encore des algorithmes de signature peu robustes et doivent être mis à jour pour être compatibles avec la prochaine version de nginx : {certs}", - "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu de des archives non-compressées lors de la création des backups. N.B. Activer cette option implique de créer des archives plus légères, mais aussi d'avoir des procédures de backup significativement plus longues et plus gourmandes en CPU.", - "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers les nouvelles nftables système", - "service_description_php7.3-fpm": "Lancer les applications écrites en PHP avec NGINX", + "migration_0015_weak_certs": "Il a été constaté que les certificats suivants utilisent encore des algorithmes de signature peu robustes et doivent être mis à jour pour être compatibles avec la prochaine version de NGINX : {certs}", + "global_settings_setting_backup_compress_tar_archives": "Compresser les archives (.tar.gz) au lieu des archives non-compressées lors de la création des backups. N.B. : activer cette option permet d'obtenir des backups plus légers, mais leur création sera significativement plus longue et plus gourmande en CPU.", + "migration_description_0018_xtable_to_nftable": "Migrer les anciennes règles de trafic réseau vers le nouveau système basé sur nftables", + "service_description_php7.3-fpm": "Exécute les applications écrites en PHP avec NGINX", "migration_0018_failed_to_reset_legacy_rules": "La réinitialisation des règles iptable legacy a échoué :", - "migration_0018_failed_to_migrate_iptables_rules": "La migration des iptables legacy vers nftables a échoué: {error}", + "migration_0018_failed_to_migrate_iptables_rules": "La migration des anciennes règles iptables vers nftables a échoué : {error}", "migration_0017_not_enough_space": "Laissez suffisamment d'espace disponible dans {path} avant de lancer la migration.", "migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 est installé mais pas posgreSQL 11 ? Il s'est sans doute passé quelque chose d'étrange sur votre système :(...", "migration_0017_postgresql_96_not_installed": "PostgreSQL n'a pas été installé sur votre système. Aucune opération à effectuer.", "migration_description_0017_postgresql_9p6_to_11": "Migrer les bases de données de PostgreSQL 9.6 vers 11", - "migration_description_0016_php70_to_php73_pools": "Migrer php7.0-fpm 'pool' conf files vers php7.3", - "diagnosis_processes_killed_by_oom_reaper": "Certains processus ont été arrêtés récemment par le système car il manquait de mémoire. Cela apparaît généralement quand le système manque de mémoire ou qu'un processus consomme trop de mémoire. Liste des processus tués :", - "ask_user_domain": "Domaine à utiliser pour l'adresse mail des utilisateurs et le compte XMPP", - "app_manifest_install_ask_is_public": "Cette application devrait-elle apparaître aux visiteurs anonymes ?", + "migration_description_0016_php70_to_php73_pools": "Migrer les configurations php7.0 vers php7.3", + "diagnosis_processes_killed_by_oom_reaper": "Certains processus ont été arrêtés récemment par le système car il manquait de mémoire. Cela apparaît généralement quand le système manque de mémoire ou qu'un processus consomme trop de mémoire. Liste des processus tués :\n{kills_summary}", + "ask_user_domain": "Domaine à utiliser pour l'adresse mail de l'utilisateur et le compte XMPP", + "app_manifest_install_ask_is_public": "Cette application devrait-elle être visible par les visiteurs anonymes ?", "app_manifest_install_ask_admin": "Choisissez un administrateur pour cette application", - "app_manifest_install_ask_password": "Choisissez un mot de passez administrateur pour cette application", - "app_manifest_install_ask_path": "Choisissez le chemin où cette application devrait être installée", - "app_manifest_install_ask_domain": "Choisissez le domaine où cette application devrait être insttallée" + "app_manifest_install_ask_password": "Choisissez un mot de passe administrateur pour cette application", + "app_manifest_install_ask_path": "Choisissez le chemin sur lequel vous souhaitez installer cette application", + "app_manifest_install_ask_domain": "Choisissez le domaine sur lequel vous souhaitez installer cette application" } From ffcd0e33ac2323551fc587883be606ef6fce6a30 Mon Sep 17 00:00:00 2001 From: Augustin Trancart Date: Sat, 19 Sep 2020 15:29:38 +0200 Subject: [PATCH 250/262] Fix ynh_app_upstream_version : restore ability to read manifest The documentation was saying A, the code was doing B, and calling functions were expecting both A and B (see ynh_check_app_version_changed). So this commit aims at making everyone agree, by matching usage. --- data/helpers.d/utils | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 95f8ddc52..8f79472eb 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -417,7 +417,7 @@ ynh_read_manifest () { jq ".$manifest_key" "$manifest" --raw-output } -# Read the upstream version from the manifest +# Read the upstream version from the manifest, or from the env variable $YNH_APP_MANIFEST_VERSION if not given # # usage: ynh_app_upstream_version [--manifest="manifest.json"] # | arg: -m, --manifest= - Path of the manifest to read @@ -437,7 +437,13 @@ ynh_app_upstream_version () { # Manage arguments with getopts ynh_handle_getopts_args "$@" - version_key=$YNH_APP_MANIFEST_VERSION + if [[ "$manifest" != "" ]] && [[ -e "$manifest" ]]; + then + version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") + else + version_key=$YNH_APP_MANIFEST_VERSION + fi + echo "${version_key/~ynh*/}" } From bca4cd0c51ad6452ca66af7afac815f56b7651e9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 19 Sep 2020 17:27:21 +0200 Subject: [PATCH 251/262] Admin/API wasn't logging or displaying messages anymore --- src/yunohost/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/__init__.py b/src/yunohost/__init__.py index d909ad9e2..76449a7e4 100644 --- a/src/yunohost/__init__.py +++ b/src/yunohost/__init__.py @@ -190,7 +190,7 @@ def init_logging(interface="cli", 'loggers': { 'yunohost': { 'level': 'DEBUG', - 'handlers': ['file', 'api'] + ['console'] if debug else [], + 'handlers': ['file', 'api'] + (['console'] if debug else []), 'propagate': False, }, 'moulinette': { @@ -201,6 +201,6 @@ def init_logging(interface="cli", }, 'root': { 'level': 'DEBUG', - 'handlers': ['file'] + ['console'] if debug else [], + 'handlers': ['file'] + (['console'] if debug else []), }, }) From 4186f757e1f6c32d072fdfe201ff2013a0ad5daf Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 22 Sep 2020 17:48:00 +0200 Subject: [PATCH 252/262] Cleanup some old stuff in postinstall code --- src/yunohost/tools.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index fcc2810d6..946c876e4 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -303,17 +303,10 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, # Change folders permissions os.system('chmod 755 /home/yunohost.app') - # Set hostname to avoid amavis bug - if os.system('hostname -d >/dev/null') != 0: - os.system('hostname yunohost.yunohost.org') - - # Add a temporary SSOwat rule to redirect SSO to admin page + # Init ssowat's conf.json.persistent if not os.path.exists('/etc/ssowat/conf.json.persistent'): - ssowat_conf = {} - else: - ssowat_conf = read_json('/etc/ssowat/conf.json.persistent') + write_to_json('/etc/ssowat/conf.json.persistent', {}) - write_to_json('/etc/ssowat/conf.json.persistent', ssowat_conf) os.system('chmod 644 /etc/ssowat/conf.json.persistent') # Create SSL CA From 3cecc7cb30ec90738a2b683187c5b769f5c94288 Mon Sep 17 00:00:00 2001 From: cyxae Date: Fri, 6 Dec 2019 18:38:19 +0100 Subject: [PATCH 253/262] Sort alphabetically domain list --- src/yunohost/domain.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 160c8f622..195d1e290 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -59,7 +59,10 @@ def domain_list(exclude_subdomains=False): parent_domain = domain.split(".", 1)[1] if parent_domain in result: continue - result_list.append(domain) + result_list.append(".".join(reversed(domain.split(".")))) + result_list = sorted(result_list) + for i in range(len(result_list)): + result_list[i] = ".".join(reversed(result_list[i].split("."))) return {'domains': result_list} From c8e24d898ae076ecb835e8409c742d46d4931dcb Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 23 Sep 2020 00:57:41 +0200 Subject: [PATCH 254/262] [enh] Group same domains and subdomains together --- src/yunohost/domain.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 195d1e290..179d5728f 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -59,10 +59,22 @@ def domain_list(exclude_subdomains=False): parent_domain = domain.split(".", 1)[1] if parent_domain in result: continue - result_list.append(".".join(reversed(domain.split(".")))) - result_list = sorted(result_list) - for i in range(len(result_list)): - result_list[i] = ".".join(reversed(result_list[i].split("."))) + + result_list.append(domain) + + + def cmp_domain(domain1, domain2): + # Keep the main part of the domain and the extension together + # eg: this.is.an.example.com -> ['example.com', 'an', 'is', 'this'] + domain1 = domain1.split('.') + domain2 = domain2.split('.') + domain1[-1] = domain1[-2] + domain1.pop() + domain2[-1] = domain2[-2] + domain2.pop() + domain1 = list(reversed(domain1)) + domain2 = list(reversed(domain2)) + return cmp(domain1, domain2) + + result_list = sorted(result_list, cmp_domain) return {'domains': result_list} From 69e20fa114cbb509ae911d930c50b751c5f255aa Mon Sep 17 00:00:00 2001 From: Julien Jershon Date: Sat, 5 Oct 2019 10:29:01 +0200 Subject: [PATCH 255/262] Update the email regex so it accept the '+' sign. Fix https://github.com/YunoHost/issues/issues/1333. --- data/actionsmap/yunohost.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 08416b69f..72b30328c 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -100,6 +100,12 @@ user: -m: full: --mail help: (Deprecated, see --domain) Main unique email address + extra: + ask: ask_email + required: True + pattern: &pattern_email_without_plus + - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ + - "pattern_email_without_plus" -p: full: --password help: User password @@ -158,9 +164,7 @@ user: -m: full: --mail extra: - pattern: &pattern_email - - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$ - - "pattern_email" + pattern: *pattern_email_without_plus -p: full: --change-password help: New password to set @@ -172,7 +176,9 @@ user: nargs: "*" metavar: MAIL extra: - pattern: *pattern_email + pattern: &pattern_email + - !!str ^[\w\+.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ + - "pattern_email" --remove-mailforward: help: Mailforward addresses to remove nargs: "*" @@ -182,7 +188,7 @@ user: nargs: "*" metavar: MAIL extra: - pattern: *pattern_email + pattern: *pattern_email_without_plus --remove-mailalias: help: Mail aliases to remove nargs: "*" From 4510b2b913427f56a5b566fe14805085455eb2b3 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 23 Sep 2020 02:03:46 +0200 Subject: [PATCH 256/262] [fix] Add pattern email desc --- locales/en.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 1a620124b..e832b5ec8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -468,7 +468,8 @@ "password_too_simple_4": "The password needs to be at least 12 characters long and contain a digit, upper, lower and special characters", "pattern_backup_archive_name": "Must be a valid filename with max 30 characters, alphanumeric and -_. characters only", "pattern_domain": "Must be a valid domain name (e.g. my-domain.org)", - "pattern_email": "Must be a valid e-mail address (e.g. someone@example.com)", + "pattern_email": "Must be a valid e-mail address, '+' symbol accepted(e.g. someone+tag@example.com)", + "pattern_email_without_plus": "Must be a valid e-mail address without '+' symbol (e.g. someone@example.com)", "pattern_firstname": "Must be a valid first name", "pattern_lastname": "Must be a valid last name", "pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to not have a quota", From 95f6772cba53b6c2f8f865f5b3d5c66d4b653c56 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Wed, 23 Sep 2020 03:25:26 +0200 Subject: [PATCH 257/262] [fix] Typo and ssh as system perm --- src/yunohost/permission.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index cd81489a2..010913556 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -35,7 +35,7 @@ from yunohost.log import is_unit_operation logger = getActionLogger('yunohost.user') -SYSTEM_PERMS = ["mail", "xmpp", "stfp"] +SYSTEM_PERMS = ["mail", "xmpp", "sftp", "ssh"] # # From ae897994cacfd764870ac9f8569d3e3cdc9633ec Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 23 Sep 2020 21:33:10 +0200 Subject: [PATCH 258/262] Remove legacy stuff, every instances that got migrated to Buster already have this applied --- src/yunohost/tools.py | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 946c876e4..8799102db 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -908,44 +908,12 @@ def tools_migrations_state(): """ Show current migration state """ - if os.path.exists("/etc/yunohost/migrations_state.json"): - _migrate_legacy_migration_json() - if not os.path.exists(MIGRATIONS_STATE_PATH): return {"migrations": {}} return read_yaml(MIGRATIONS_STATE_PATH) -def _migrate_legacy_migration_json(): - - from moulinette.utils.filesystem import read_json - - logger.debug("Migrating legacy migration state json to yaml...") - - # We fetch the old state containing the last run migration - old_state = read_json("/etc/yunohost/migrations_state.json")["last_run_migration"] - last_run_migration_id = str(old_state["number"]) + "_" + old_state["name"] - - # Extract the list of migration ids - from . import data_migrations - migrations_path = data_migrations.__path__[0] - migration_files = filter(lambda x: re.match(r"^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)) - # (here we remove the .py extension and make sure the ids are sorted) - migration_ids = sorted([f.rsplit(".", 1)[0] for f in migration_files]) - - # So now build the new dict for every id up to the last run migration - migrations = {} - for migration_id in migration_ids: - migrations[migration_id] = "done" - if last_run_migration_id in migration_id: - break - - # Write the new file and rename the old one - write_to_yaml(MIGRATIONS_STATE_PATH, {"migrations": migrations}) - os.rename("/etc/yunohost/migrations_state.json", "/etc/yunohost/migrations_state.json.old") - - def _write_migration_state(migration_id, state): current_states = tools_migrations_state() From f2b6a883a8200b58daa2210df401a278c84932ec Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 23 Sep 2020 21:37:08 +0200 Subject: [PATCH 259/262] Unused import --- 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 8799102db..1c45ef770 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -33,7 +33,7 @@ from importlib import import_module from moulinette import msignals, m18n 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, read_yaml, write_to_yaml +from moulinette.utils.filesystem import write_to_json, read_yaml, write_to_yaml from yunohost.app import _update_apps_catalog, app_info, app_upgrade, _initialize_apps_catalog_system from yunohost.domain import domain_add From c0234a75f52149d2b6650d6cecf566a2703579cc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 23 Sep 2020 21:46:31 +0200 Subject: [PATCH 260/262] Better naming ? :s --- data/actionsmap/yunohost.yml | 12 ++++++------ locales/en.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 72b30328c..7be0e0899 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -103,9 +103,9 @@ user: extra: ask: ask_email required: True - pattern: &pattern_email_without_plus + pattern: &pattern_email - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ - - "pattern_email_without_plus" + - "pattern_email" -p: full: --password help: User password @@ -164,7 +164,7 @@ user: -m: full: --mail extra: - pattern: *pattern_email_without_plus + pattern: *pattern_email -p: full: --change-password help: New password to set @@ -176,9 +176,9 @@ user: nargs: "*" metavar: MAIL extra: - pattern: &pattern_email + pattern: &pattern_email_forward - !!str ^[\w\+.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ - - "pattern_email" + - "pattern_email_forward" --remove-mailforward: help: Mailforward addresses to remove nargs: "*" @@ -188,7 +188,7 @@ user: nargs: "*" metavar: MAIL extra: - pattern: *pattern_email_without_plus + pattern: *pattern_email --remove-mailalias: help: Mail aliases to remove nargs: "*" diff --git a/locales/en.json b/locales/en.json index e832b5ec8..115928001 100644 --- a/locales/en.json +++ b/locales/en.json @@ -468,8 +468,8 @@ "password_too_simple_4": "The password needs to be at least 12 characters long and contain a digit, upper, lower and special characters", "pattern_backup_archive_name": "Must be a valid filename with max 30 characters, alphanumeric and -_. characters only", "pattern_domain": "Must be a valid domain name (e.g. my-domain.org)", - "pattern_email": "Must be a valid e-mail address, '+' symbol accepted(e.g. someone+tag@example.com)", - "pattern_email_without_plus": "Must be a valid e-mail address without '+' symbol (e.g. someone@example.com)", + "pattern_email_forward": "Must be a valid e-mail address, '+' symbol accepted (e.g. someone+tag@example.com)", + "pattern_email": "Must be a valid e-mail address, without '+' symbol (e.g. someone@example.com)", "pattern_firstname": "Must be a valid first name", "pattern_lastname": "Must be a valid last name", "pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to not have a quota", From b48f6521e2cf532a1da0ab76cc51c9834998a10a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 23 Sep 2020 21:53:06 +0200 Subject: [PATCH 261/262] Removing the pattern/required flag from --mail in user_create was intentional because it's now deprecated ... + restablish support xn-- stuff... --- data/actionsmap/yunohost.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 7be0e0899..5371d576d 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -100,12 +100,6 @@ user: -m: full: --mail help: (Deprecated, see --domain) Main unique email address - extra: - ask: ask_email - required: True - pattern: &pattern_email - - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ - - "pattern_email" -p: full: --password help: User password @@ -164,7 +158,9 @@ user: -m: full: --mail extra: - pattern: *pattern_email + pattern: &pattern_email + - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$ + - "pattern_email" -p: full: --change-password help: New password to set @@ -177,7 +173,7 @@ user: metavar: MAIL extra: pattern: &pattern_email_forward - - !!str ^[\w\+.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$ + - !!str ^[\w\+.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$ - "pattern_email_forward" --remove-mailforward: help: Mailforward addresses to remove From 14e8888078c760f2cc51c954a90321d0e25c3e51 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 23 Sep 2020 22:45:26 +0200 Subject: [PATCH 262/262] My god, please no, there's no damn jessie anymore --- data/helpers.d/php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index c538d8688..5df31f32b 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -111,12 +111,6 @@ ynh_add_fpm_config () { local fpm_service="php${phpversion}-fpm" local fpm_config_dir="/etc/php/$phpversion/fpm" fi - # Configure PHP-FPM 5 on Debian Jessie - if [ "$(ynh_get_debian_release)" == "jessie" ] - then - fpm_config_dir="/etc/php5/fpm" - fpm_service="php5-fpm" - fi # Create the directory for FPM pools mkdir --parents "$fpm_config_dir/pool.d"