From b7c8ab541f42eeaff0f15e46f872d79e5fcc3b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrea=20PIERR=C3=89?= Date: Fri, 5 Oct 2018 12:54:33 +0200 Subject: [PATCH 001/104] [#901] Human readable date --- data/actionsmap/yunohost.yml | 5 +++++ src/yunohost/app.py | 14 +++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index ec4a6bf08..fd1d98946 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -469,6 +469,11 @@ app: listlists: action_help: List registered application lists api: GET /appslists + arguments: + -H: + full: --human-readable + help: Return the lastUpdate with a human readable date + action: store_true ### app_removelist() removelist: diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 8c35d5e39..b2116d966 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -36,6 +36,7 @@ import glob import pwd import grp from collections import OrderedDict +import datetime from moulinette import msignals, m18n, msettings from moulinette.core import MoulinetteError @@ -66,10 +67,13 @@ re_app_instance_name = re.compile( ) -def app_listlists(): +def app_listlists(human_readable=False): """ List fetched lists + Keyword argument: + human_readable -- Show human readable dates + """ # Migrate appslist system if needed @@ -80,6 +84,14 @@ def app_listlists(): # Get the list appslist_list = _read_appslist_list() + # Human readable date + if human_readable: + for app in appslist_list: + now_for_humans = datetime.datetime.fromtimestamp( + appslist_list[app].get("lastUpdate")) + appslist_list[app]["lastUpdate"] = now_for_humans.strftime( + '%Y-%m-%d %H:%M:%S') + return appslist_list From 8598d81bb1a18589127b25ba7156986f719c14d1 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 26 Aug 2018 14:15:55 +0200 Subject: [PATCH 002/104] [wip] Standardize ssh config --- data/hooks/conf_regen/03-ssh | 2 +- locales/en.json | 6 ++++++ src/yunohost/tools.py | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index a469b7a66..e3e03877e 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -7,7 +7,7 @@ do_pre_regen() { cd /usr/share/yunohost/templates/ssh - # only overwrite SSH configuration on an ISO installation + # Don't overwrite configuration if from_script if [[ ! -f /etc/yunohost/from_script ]]; then # do not listen to IPv6 if unavailable [[ -f /proc/net/if_inet6 ]] \ diff --git a/locales/en.json b/locales/en.json index 6ce22ca80..d63fc4a69 100644 --- a/locales/en.json +++ b/locales/en.json @@ -292,6 +292,12 @@ "migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now :(.", "migration_0006_disclaimer": "Yunohost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.", "migration_0006_done": "Your root password have been replaced by your admin password.", + "migration_0007_general_warning": "To ensure a global security of your server, YunoHost recommends to let it manage the SSH configuration of your server. Your current SSH configuration differs from common default configuration. If you let YunoHost reconfigure it, the way to access with SSH to your server could change after this migration:", + "migration_0007_port": "- you will have to connect using port 22 instead of your custom SSH port. Feel free to reconfigure it", + "migration_0007_root": "- you will not be able to connect with root user, instead you will have to use admin user.", + "migration_0007_dsa": "- you might need to invalidate a warning and to recheck fingerprint of your server, because DSA key will be disabled.", + "migration_0007_risk": "If you agree to let YunoHost replace your configuration and change the way to access your server, make the migration else skip it.", + "migration_0007_no_risk": "No major change in the way has been found, but it's difficult to be sure. If you agree to let YunoHost replace your configuration and change the way to access your server, make the migration else skip it.", "migrations_backward": "Migrating backward.", "migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}", "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 271947b3d..a0549321a 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -440,6 +440,12 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, service_start("yunohost-firewall") service_regen_conf(force=True) + + # Restore specific ssh conf + bkp_sshd_conf = '/etc/ssh/sshd_config.to_restore' + if os.path.exists(bkp_sshd_conf): + os.rename(bkp_sshd_conf, '/etc/ssh/sshd_config') + logger.success(m18n.n('yunohost_configured')) logger.warning(m18n.n('recommend_to_add_first_user')) From 7f3a35dac0bd33a15247ca3df866551953d9a39c Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 26 Aug 2018 14:38:46 +0200 Subject: [PATCH 003/104] [enh] Don't change ssh conf if not specified directly --- src/yunohost/service.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 5b7680a80..9ab301933 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -39,8 +39,8 @@ from moulinette import m18n from moulinette.core import MoulinetteError from moulinette.utils import log, filesystem -from yunohost.hook import hook_callback from yunohost.log import is_unit_operation +from yunohost.hook import hook_callback, hook_list BASE_CONF_PATH = '/home/yunohost.conf' BACKUP_CONF_DIR = os.path.join(BASE_CONF_PATH, 'backup') @@ -422,6 +422,12 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, # return the arguments to pass to the script return pre_args + [service_pending_path, ] + # Don't regen SSH if not specifically specified + if not names: + names = hook_list('conf_regen', list_by='name', + show_info=False)['hooks'] + names.remove('ssh') + pre_result = hook_callback('conf_regen', names, pre_callback=_pre_call) # Update the services name From f0d0a715869af02b2d0594265d04c6c6efe37ba2 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 26 Aug 2018 14:54:07 +0200 Subject: [PATCH 004/104] [wip] Ask user for keeping or not sshd config --- .../0006_manage_sshd_config.py | 56 ++++++++++++++ .../data_migrations/0007_reset_sshd_config.py | 77 +++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 src/yunohost/data_migrations/0006_manage_sshd_config.py create mode 100644 src/yunohost/data_migrations/0007_reset_sshd_config.py diff --git a/src/yunohost/data_migrations/0006_manage_sshd_config.py b/src/yunohost/data_migrations/0006_manage_sshd_config.py new file mode 100644 index 000000000..413c3fefe --- /dev/null +++ b/src/yunohost/data_migrations/0006_manage_sshd_config.py @@ -0,0 +1,56 @@ +import subprocess +import os + +from shutil import copyfile + +from moulinette import m18n +from moulinette.core import MoulinetteError +from moulinette.utils.log import getActionLogger + +from yunohost.tools import Migration +from yunohost.service import service_regen_conf, _get_conf_hashes, + _calculate_hash + +logger = getActionLogger('yunohost.migration') + + +class MyMigration(Migration): + """ + Ensure SSH conf is managed by YunoHost, reapply initial change and setup an + extension dir + """ + + def migrate(self): + + # Create sshd_config.d dir + if not os.path.exists('/etc/ssh/sshd_config.d'): + mkdir('/etc/ssh/sshd_config.d', '0755', uid='root', gid='root') + + # Manage SSHd in all case + if os.path.exists('/etc/yunohost/from_script'): + rm('/etc/yunohost/from_script') + copyfile('/etc/ssh/sshd_config', '/etc/ssh/sshd_config.restore') + service_regen_conf(names=['ssh'], force=True) + os.rename('/etc/ssh/sshd_config.restore', '/etc/ssh/sshd_config') + + # If custom conf, add 'Include' instruction + ynh_hash = _get_conf_hashes('ssh')['/etc/ssh/sshd_config'] + current_hash = _calculate_hash('/etc/ssh/sshd_config') + if ynh_hash == current_hash: + return + + add_include = False + include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' + for line in open('/etc/ssh/sshd_config'): + if re.match(root_rgx, line) is not None: + add_include = True + break + + if add_include: + with open("/etc/ssh/sshd_config", "a") as conf: + conf.write('Include sshd_config.d/*') + + def backward(self): + + raise MoulinetteError(m18n.n("migration_0006_backward_impossible")) + diff --git a/src/yunohost/data_migrations/0007_reset_sshd_config.py b/src/yunohost/data_migrations/0007_reset_sshd_config.py new file mode 100644 index 000000000..701b60e3c --- /dev/null +++ b/src/yunohost/data_migrations/0007_reset_sshd_config.py @@ -0,0 +1,77 @@ +import subprocess +import os + +from shutil import copyfile + +from moulinette import m18n +from moulinette.core import MoulinetteError +from moulinette.utils.log import getActionLogger + +from yunohost.tools import Migration +from yunohost.service import service_regen_conf + +logger = getActionLogger('yunohost.migration') + + +class MyMigration(Migration): + "Reset SSH conf to the YunoHost one" + + mode = "manual" + + def migrate(self): + service_regen_conf(names=['ssh'], force=True) + + def backward(self): + + raise MoulinetteError(m18n.n("migration_0007_backward_impossible")) + + @property + def disclaimer(self): + + # Avoid having a super long disclaimer + ynh_hash = _get_conf_hashes('ssh')['/etc/ssh/sshd_config'] + current_hash = _calculate_hash('/etc/ssh/sshd_config') + if ynh_hash == current_hash: + return None + + # Detect major risk to migrate to the new configuration + dsa = False + port_rgx = r'^[ \t]*Port[ \t]+(\d+)[ \t]*(?:#.*)?$' + root_rgx = r'^[ \t]*PermitRootLogin[ \t]([\w-]*)[ \t]*(?:#.*)?$' + dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$' + for line in open('/etc/ssh/sshd_config'): + + ports = re.findall(port_rgx, line) + + root_login = re.match(root_rgx, line) + if root_login is not None: + root_login = root_login.group(1) + + if not dsa and re.match(dsa_rgx, line): + dsa = True + + if len(ports) == 0: + ports = ['22'] + + port = ports != ['22'] + + root_user = root_login in ['yes'] + + # Build message + message = m18n.n("migration_0007_general_warning") + + if port: + message += "\n\n" + m18n.n("migration_0007_port") + + if root_user: + message += "\n\n" + m18n.n("migration_0007_root") + + if dsa: + message += "\n\n" + m18n.n("migration_0007_dsa") + + if port or root_user or dsa: + message += "\n\n" + m18n.n("migration_0007_risk") + else: + message += "\n\n" + m18n.n("migration_0007_no_risk") + + return message From c2b225d3765938a5d4536badbdb1700ee1064801 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 26 Aug 2018 19:57:48 +0200 Subject: [PATCH 005/104] [fix] A lot of bug on the wip work on sshd migration --- data/templates/ssh/sshd_config | 2 ++ locales/en.json | 2 ++ .../0006_manage_sshd_config.py | 13 +++++--- .../data_migrations/0007_reset_sshd_config.py | 32 ++++++++++++------- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index 8c5a7fb95..b79ffd3bf 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -94,3 +94,5 @@ Match User sftpusers AllowTcpForwarding no GatewayPorts no X11Forwarding no + +Include sshd_config.d/* diff --git a/locales/en.json b/locales/en.json index d63fc4a69..94e8a6384 100644 --- a/locales/en.json +++ b/locales/en.json @@ -274,6 +274,8 @@ "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_0006_manage_sshd_config": "Manage SSH conf in a better way", + "migration_description_0007_reset_sshd_config": "Reset SSH conf to the YunoHost default conf", "migration_0003_backward_impossible": "The stretch migration cannot be reverted.", "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", "migration_0003_patching_sources_list": "Patching the sources.lists ...", diff --git a/src/yunohost/data_migrations/0006_manage_sshd_config.py b/src/yunohost/data_migrations/0006_manage_sshd_config.py index 413c3fefe..d4740192e 100644 --- a/src/yunohost/data_migrations/0006_manage_sshd_config.py +++ b/src/yunohost/data_migrations/0006_manage_sshd_config.py @@ -1,15 +1,17 @@ import subprocess import os +import re from shutil import copyfile from moulinette import m18n from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger +from moulinette.utils.filesystem import mkdir, rm from yunohost.tools import Migration -from yunohost.service import service_regen_conf, _get_conf_hashes, - _calculate_hash +from yunohost.service import service_regen_conf, _get_conf_hashes, \ + _calculate_hash, _run_service_command logger = getActionLogger('yunohost.migration') @@ -24,7 +26,7 @@ class MyMigration(Migration): # Create sshd_config.d dir if not os.path.exists('/etc/ssh/sshd_config.d'): - mkdir('/etc/ssh/sshd_config.d', '0755', uid='root', gid='root') + mkdir('/etc/ssh/sshd_config.d', 0755, uid='root', gid='root') # Manage SSHd in all case if os.path.exists('/etc/yunohost/from_script'): @@ -42,7 +44,7 @@ class MyMigration(Migration): add_include = False include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' for line in open('/etc/ssh/sshd_config'): - if re.match(root_rgx, line) is not None: + if re.match(include_rgx, line) is not None: add_include = True break @@ -50,6 +52,9 @@ class MyMigration(Migration): with open("/etc/ssh/sshd_config", "a") as conf: conf.write('Include sshd_config.d/*') + if not _run_service_command('restart', 'ssh'): + self.backward() + def backward(self): raise MoulinetteError(m18n.n("migration_0006_backward_impossible")) diff --git a/src/yunohost/data_migrations/0007_reset_sshd_config.py b/src/yunohost/data_migrations/0007_reset_sshd_config.py index 701b60e3c..a9fb9fa14 100644 --- a/src/yunohost/data_migrations/0007_reset_sshd_config.py +++ b/src/yunohost/data_migrations/0007_reset_sshd_config.py @@ -1,5 +1,6 @@ import subprocess import os +import re from shutil import copyfile @@ -8,7 +9,7 @@ from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from yunohost.tools import Migration -from yunohost.service import service_regen_conf +from yunohost.service import service_regen_conf, _get_conf_hashes, _calculate_hash logger = getActionLogger('yunohost.migration') @@ -16,8 +17,6 @@ logger = getActionLogger('yunohost.migration') class MyMigration(Migration): "Reset SSH conf to the YunoHost one" - mode = "manual" - def migrate(self): service_regen_conf(names=['ssh'], force=True) @@ -26,26 +25,37 @@ class MyMigration(Migration): raise MoulinetteError(m18n.n("migration_0007_backward_impossible")) @property - def disclaimer(self): + def mode(self): # Avoid having a super long disclaimer - ynh_hash = _get_conf_hashes('ssh')['/etc/ssh/sshd_config'] + ynh_hash = _get_conf_hashes('ssh') + if '/etc/ssh/sshd_config' in ynh_hash: + ynh_hash = ynh_hash['/etc/ssh/sshd_config'] current_hash = _calculate_hash('/etc/ssh/sshd_config') if ynh_hash == current_hash: + return "auto" + + return "manual" + + + @property + def disclaimer(self): + + if self.mode == "auto": return None # Detect major risk to migrate to the new configuration dsa = False + ports = [] + root_login = [] port_rgx = r'^[ \t]*Port[ \t]+(\d+)[ \t]*(?:#.*)?$' - root_rgx = r'^[ \t]*PermitRootLogin[ \t]([\w-]*)[ \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('/etc/ssh/sshd_config'): - ports = re.findall(port_rgx, line) + ports = ports + re.findall(port_rgx, line) - root_login = re.match(root_rgx, line) - if root_login is not None: - root_login = root_login.group(1) + root_login = root_login + re.findall(root_rgx, line) if not dsa and re.match(dsa_rgx, line): dsa = True @@ -55,7 +65,7 @@ class MyMigration(Migration): port = ports != ['22'] - root_user = root_login in ['yes'] + root_user = root_login and root_login[-1] != 'no' # Build message message = m18n.n("migration_0007_general_warning") From 4e92a36322dae60021738c0d5ef71e67247a45a8 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 26 Aug 2018 20:20:35 +0200 Subject: [PATCH 006/104] [fix] Backward if can't restart --- locales/en.json | 2 ++ .../0006_manage_sshd_config.py | 33 +++++++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/locales/en.json b/locales/en.json index 94e8a6384..df69ca1a4 100644 --- a/locales/en.json +++ b/locales/en.json @@ -294,6 +294,8 @@ "migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now :(.", "migration_0006_disclaimer": "Yunohost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.", "migration_0006_done": "Your root password have been replaced by your admin password.", + "migration_0006_cancelled": "YunoHost has failed to improve the way your ssh conf is managed.", + "migration_0006_cannot_restart": "SSH can't be restarted after we tried to cancel the migration 6.", "migration_0007_general_warning": "To ensure a global security of your server, YunoHost recommends to let it manage the SSH configuration of your server. Your current SSH configuration differs from common default configuration. If you let YunoHost reconfigure it, the way to access with SSH to your server could change after this migration:", "migration_0007_port": "- you will have to connect using port 22 instead of your custom SSH port. Feel free to reconfigure it", "migration_0007_root": "- you will not be able to connect with root user, instead you will have to use admin user.", diff --git a/src/yunohost/data_migrations/0006_manage_sshd_config.py b/src/yunohost/data_migrations/0006_manage_sshd_config.py index d4740192e..cd9204846 100644 --- a/src/yunohost/data_migrations/0006_manage_sshd_config.py +++ b/src/yunohost/data_migrations/0006_manage_sshd_config.py @@ -31,31 +31,36 @@ class MyMigration(Migration): # Manage SSHd in all case if os.path.exists('/etc/yunohost/from_script'): rm('/etc/yunohost/from_script') - copyfile('/etc/ssh/sshd_config', '/etc/ssh/sshd_config.restore') + copyfile('/etc/ssh/sshd_config', '/etc/ssh/sshd_config.bkp') service_regen_conf(names=['ssh'], force=True) - os.rename('/etc/ssh/sshd_config.restore', '/etc/ssh/sshd_config') + copyfile('/etc/ssh/sshd_config.bkp', '/etc/ssh/sshd_config') # If custom conf, add 'Include' instruction ynh_hash = _get_conf_hashes('ssh')['/etc/ssh/sshd_config'] current_hash = _calculate_hash('/etc/ssh/sshd_config') - if ynh_hash == current_hash: - return + if ynh_hash != current_hash: - add_include = False - include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' - for line in open('/etc/ssh/sshd_config'): - if re.match(include_rgx, line) is not None: - add_include = True - break + add_include = False + include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' + for line in open('/etc/ssh/sshd_config'): + if re.match(include_rgx, line) is not None: + add_include = True + break - if add_include: - with open("/etc/ssh/sshd_config", "a") as conf: - conf.write('Include sshd_config.d/*') + if add_include: + with open("/etc/ssh/sshd_config", "a") as conf: + conf.write('Include sshd_config.d/*') + # Restart ssh and backward if it fail if not _run_service_command('restart', 'ssh'): self.backward() + raise MoulinetteError(m18n.n("migration_0006_cancel")) + def backward(self): + # We don't backward completely but it should be enough - raise MoulinetteError(m18n.n("migration_0006_backward_impossible")) + copyfile('/etc/ssh/sshd_config.bkp', '/etc/ssh/sshd_config') + if not _run_service_command('restart', 'ssh'): + raise MoulinetteError(m18n.n("migration_0006_cannot_restart")) From 4602439c013d9db6f9a92f34899788fab0df9f56 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 26 Aug 2018 20:29:11 +0200 Subject: [PATCH 007/104] [fix] Pep8 and define SSHD_CONF --- .../0006_manage_sshd_config.py | 25 +++++++++---------- .../data_migrations/0007_reset_sshd_config.py | 8 ++---- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/yunohost/data_migrations/0006_manage_sshd_config.py b/src/yunohost/data_migrations/0006_manage_sshd_config.py index cd9204846..4af486b6e 100644 --- a/src/yunohost/data_migrations/0006_manage_sshd_config.py +++ b/src/yunohost/data_migrations/0006_manage_sshd_config.py @@ -1,4 +1,3 @@ -import subprocess import os import re @@ -15,6 +14,8 @@ from yunohost.service import service_regen_conf, _get_conf_hashes, \ logger = getActionLogger('yunohost.migration') +SSHD_CONF = '/etc/ssh/sshd_config' + class MyMigration(Migration): """ @@ -25,30 +26,30 @@ class MyMigration(Migration): def migrate(self): # Create sshd_config.d dir - if not os.path.exists('/etc/ssh/sshd_config.d'): - mkdir('/etc/ssh/sshd_config.d', 0755, uid='root', gid='root') + if not os.path.exists(SSHD_CONF + '.d'): + mkdir(SSHD_CONF + '.d', 0755, uid='root', gid='root') # Manage SSHd in all case if os.path.exists('/etc/yunohost/from_script'): rm('/etc/yunohost/from_script') - copyfile('/etc/ssh/sshd_config', '/etc/ssh/sshd_config.bkp') + copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp') service_regen_conf(names=['ssh'], force=True) - copyfile('/etc/ssh/sshd_config.bkp', '/etc/ssh/sshd_config') + copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) # If custom conf, add 'Include' instruction - ynh_hash = _get_conf_hashes('ssh')['/etc/ssh/sshd_config'] - current_hash = _calculate_hash('/etc/ssh/sshd_config') + ynh_hash = _get_conf_hashes('ssh')[SSHD_CONF] + current_hash = _calculate_hash(SSHD_CONF) + include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' if ynh_hash != current_hash: add_include = False - include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' - for line in open('/etc/ssh/sshd_config'): + for line in open(SSHD_CONF): if re.match(include_rgx, line) is not None: add_include = True break if add_include: - with open("/etc/ssh/sshd_config", "a") as conf: + with open(SSHD_CONF, "a") as conf: conf.write('Include sshd_config.d/*') # Restart ssh and backward if it fail @@ -56,11 +57,9 @@ class MyMigration(Migration): self.backward() raise MoulinetteError(m18n.n("migration_0006_cancel")) - def backward(self): # We don't backward completely but it should be enough - copyfile('/etc/ssh/sshd_config.bkp', '/etc/ssh/sshd_config') + copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) if not _run_service_command('restart', 'ssh'): raise MoulinetteError(m18n.n("migration_0006_cannot_restart")) - diff --git a/src/yunohost/data_migrations/0007_reset_sshd_config.py b/src/yunohost/data_migrations/0007_reset_sshd_config.py index a9fb9fa14..5a097968d 100644 --- a/src/yunohost/data_migrations/0007_reset_sshd_config.py +++ b/src/yunohost/data_migrations/0007_reset_sshd_config.py @@ -1,15 +1,12 @@ -import subprocess -import os import re -from shutil import copyfile - from moulinette import m18n from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from yunohost.tools import Migration -from yunohost.service import service_regen_conf, _get_conf_hashes, _calculate_hash +from yunohost.service import service_regen_conf, _get_conf_hashes, \ + _calculate_hash logger = getActionLogger('yunohost.migration') @@ -37,7 +34,6 @@ class MyMigration(Migration): return "manual" - @property def disclaimer(self): From 8e0086d49397e701efbbc2ec4f31ab044b8e5920 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 26 Aug 2018 23:40:26 +0200 Subject: [PATCH 008/104] [fix] Allow user to trigger the moment when they remove dsa --- data/hooks/conf_regen/03-ssh | 5 +++++ data/templates/ssh/sshd_config | 8 +++----- .../data_migrations/0006_manage_sshd_config.py | 11 +++++++++++ .../data_migrations/0007_reset_sshd_config.py | 7 +++++-- src/yunohost/settings.py | 1 + 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index e3e03877e..563394d40 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -13,6 +13,11 @@ do_pre_regen() { [[ -f /proc/net/if_inet6 ]] \ || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config + # Add DSA HostKey to let user remove it with migration 7 + if [[ "$(yunohost settings 'service.ssh._deprecated_dsa_hostkey')" == "True" ]]; then + sed -i '/HostKey \/etc\/ssh\/ssh_host_rsa_key/a HostKey /etc/ssh/ssh_host_dsa_key' sshd_config + fi + install -D -m 644 sshd_config "${pending_dir}/etc/ssh/sshd_config" fi } diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index b79ffd3bf..66aacc5f0 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -9,14 +9,12 @@ ListenAddress 0.0.0.0 Protocol 2 # HostKeys for protocol version 2 HostKey /etc/ssh/ssh_host_rsa_key -HostKey /etc/ssh/ssh_host_dsa_key +HostKey /etc/ssh/ssh_host_ecdsa_key +HostKey /etc/ssh/ssh_host_ed25519_key + #Privilege Separation is turned on for security UsePrivilegeSeparation yes -# Lifetime and size of ephemeral version 1 server key -KeyRegenerationInterval 3600 -ServerKeyBits 768 - # Logging SyslogFacility AUTH LogLevel INFO diff --git a/src/yunohost/data_migrations/0006_manage_sshd_config.py b/src/yunohost/data_migrations/0006_manage_sshd_config.py index 4af486b6e..13b0bbadf 100644 --- a/src/yunohost/data_migrations/0006_manage_sshd_config.py +++ b/src/yunohost/data_migrations/0006_manage_sshd_config.py @@ -11,6 +11,7 @@ from moulinette.utils.filesystem import mkdir, rm from yunohost.tools import Migration from yunohost.service import service_regen_conf, _get_conf_hashes, \ _calculate_hash, _run_service_command +from yunohost.settings import settings_set logger = getActionLogger('yunohost.migration') @@ -25,6 +26,16 @@ class MyMigration(Migration): def migrate(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._deprecated_dsa_hostkey", True) + # Create sshd_config.d dir if not os.path.exists(SSHD_CONF + '.d'): mkdir(SSHD_CONF + '.d', 0755, uid='root', gid='root') diff --git a/src/yunohost/data_migrations/0007_reset_sshd_config.py b/src/yunohost/data_migrations/0007_reset_sshd_config.py index 5a097968d..af8f83ce7 100644 --- a/src/yunohost/data_migrations/0007_reset_sshd_config.py +++ b/src/yunohost/data_migrations/0007_reset_sshd_config.py @@ -7,6 +7,7 @@ from moulinette.utils.log import getActionLogger from yunohost.tools import Migration from yunohost.service import service_regen_conf, _get_conf_hashes, \ _calculate_hash +from yunohost.settings import settings_set, settings_get logger = getActionLogger('yunohost.migration') @@ -15,6 +16,7 @@ class MyMigration(Migration): "Reset SSH conf to the YunoHost one" def migrate(self): + settings_set("service.ssh._deprecated_dsa_hostkey", False) service_regen_conf(names=['ssh'], force=True) def backward(self): @@ -29,7 +31,8 @@ class MyMigration(Migration): if '/etc/ssh/sshd_config' in ynh_hash: ynh_hash = ynh_hash['/etc/ssh/sshd_config'] current_hash = _calculate_hash('/etc/ssh/sshd_config') - if ynh_hash == current_hash: + dsa = settings_get("service.ssh._deprecated_dsa_hostkey") + if ynh_hash == current_hash and not dsa: return "auto" return "manual" @@ -53,7 +56,7 @@ class MyMigration(Migration): root_login = root_login + re.findall(root_rgx, line) - if not dsa and re.match(dsa_rgx, line): + if not dsa and re.match(dsa_rgx, line) is not None: dsa = True if len(ports) == 0: diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index d2526316e..1539435c6 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -39,6 +39,7 @@ DEFAULTS = OrderedDict([ # -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._deprecated_dsa_hostkey", {"type": "bool", "default": False}), ]) From b5896e88c3db60569233da8628b38dc81e7d8cd4 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Sat, 15 Sep 2018 15:14:51 +0200 Subject: [PATCH 009/104] [enh] Display only used fingerprint in bootprompt --- bin/yunoprompt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/yunoprompt b/bin/yunoprompt index 2705dbcdc..41fb83899 100755 --- a/bin/yunoprompt +++ b/bin/yunoprompt @@ -5,7 +5,7 @@ ip=$(hostname --all-ip-address) # Fetch SSH fingerprints i=0 -for key in /etc/ssh/ssh_host_*_key.pub ; do +for key in /etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key.pub ; do output=$(ssh-keygen -l -f $key) fingerprint[$i]=" - $(echo $output | cut -d' ' -f2) $(echo $output| cut -d' ' -f4)" i=$(($i + 1)) From b4e72173cd4b076ceb36d1513e0278ffcce0af72 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Sat, 15 Sep 2018 15:24:03 +0200 Subject: [PATCH 010/104] [fix] If some ssh keys are missing --- bin/yunoprompt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/yunoprompt b/bin/yunoprompt index 41fb83899..a86d29558 100755 --- a/bin/yunoprompt +++ b/bin/yunoprompt @@ -5,7 +5,7 @@ ip=$(hostname --all-ip-address) # Fetch SSH fingerprints i=0 -for key in /etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key.pub ; do +for key in /etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key.pub 2> /dev/null ; do output=$(ssh-keygen -l -f $key) fingerprint[$i]=" - $(echo $output | cut -d' ' -f2) $(echo $output| cut -d' ' -f4)" i=$(($i + 1)) From 07e5ead0382bb8cb6066801a08451f6bde17f2fd Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Sun, 16 Sep 2018 18:37:43 +0200 Subject: [PATCH 011/104] [fix] Sometimes I need to sleep --- bin/yunoprompt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/yunoprompt b/bin/yunoprompt index a86d29558..bca5c2cb3 100755 --- a/bin/yunoprompt +++ b/bin/yunoprompt @@ -5,7 +5,7 @@ ip=$(hostname --all-ip-address) # Fetch SSH fingerprints i=0 -for key in /etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key.pub 2> /dev/null ; do +for key in $(ls /etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key.pub 2> /dev/null) ; do output=$(ssh-keygen -l -f $key) fingerprint[$i]=" - $(echo $output | cut -d' ' -f2) $(echo $output| cut -d' ' -f4)" i=$(($i + 1)) From 31c8b88f4470aca75afd5bb5f9f720da2cddbeae Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 21 Sep 2018 15:57:59 +0000 Subject: [PATCH 012/104] Wordin --- locales/en.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/locales/en.json b/locales/en.json index df69ca1a4..a206b9fb8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -294,14 +294,14 @@ "migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now :(.", "migration_0006_disclaimer": "Yunohost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.", "migration_0006_done": "Your root password have been replaced by your admin password.", - "migration_0006_cancelled": "YunoHost has failed to improve the way your ssh conf is managed.", - "migration_0006_cannot_restart": "SSH can't be restarted after we tried to cancel the migration 6.", - "migration_0007_general_warning": "To ensure a global security of your server, YunoHost recommends to let it manage the SSH configuration of your server. Your current SSH configuration differs from common default configuration. If you let YunoHost reconfigure it, the way to access with SSH to your server could change after this migration:", - "migration_0007_port": "- you will have to connect using port 22 instead of your custom SSH port. Feel free to reconfigure it", - "migration_0007_root": "- you will not be able to connect with root user, instead you will have to use admin user.", - "migration_0007_dsa": "- you might need to invalidate a warning and to recheck fingerprint of your server, because DSA key will be disabled.", - "migration_0007_risk": "If you agree to let YunoHost replace your configuration and change the way to access your server, make the migration else skip it.", - "migration_0007_no_risk": "No major change in the way has been found, but it's difficult to be sure. If you agree to let YunoHost replace your configuration and change the way to access your server, make the migration else skip it.", + "migration_0006_cancelled": "YunoHost has failed to improve the way your SSH conf is managed.", + "migration_0006_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.", + "migration_0007_general_warning": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH configuration differs from the recommended configuration. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change in the following way:", + "migration_0007_port": " - you will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it ;", + "migration_0007_root": " - you will not be able to connect with root user, instead you will have to use the admin user ;", + "migration_0007_dsa": " - you might need to invalidate a warning and to recheck the fingerprint of your server, because DSA key will be disabled ;", + "migration_0007_risk": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration though it is not recommended.", + "migration_0007_no_risk": "No major risk has been indentified about overriding your SSH configuration - but it's difficult to be sure. If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration though it is not recommended.", "migrations_backward": "Migrating backward.", "migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}", "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", From 68906a1e982adb9785d521c4d3ff21a47dd99d6a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Oct 2018 22:16:36 +0200 Subject: [PATCH 013/104] Improve comments --- .../0006_manage_sshd_config.py | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/yunohost/data_migrations/0006_manage_sshd_config.py b/src/yunohost/data_migrations/0006_manage_sshd_config.py index 13b0bbadf..68ee020fd 100644 --- a/src/yunohost/data_migrations/0006_manage_sshd_config.py +++ b/src/yunohost/data_migrations/0006_manage_sshd_config.py @@ -20,8 +20,14 @@ SSHD_CONF = '/etc/ssh/sshd_config' class MyMigration(Migration): """ - Ensure SSH conf is managed by YunoHost, reapply initial change and setup an - extension dir + This is an automatic migration, that ensure SSH conf is managed by YunoHost + (even if the "from_script" flag is present) + + If the from_script flag exists, then we keep the current SSH conf such that it + will appear as "manually modified" to the regenconf. + + The admin can then choose in the next migration (manual, thi time) wether or + not to actually use the recommended configuration. """ def migrate(self): @@ -40,25 +46,34 @@ class MyMigration(Migration): if not os.path.exists(SSHD_CONF + '.d'): mkdir(SSHD_CONF + '.d', 0755, uid='root', gid='root') - # Manage SSHd in all case + # 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') service_regen_conf(names=['ssh'], force=True) copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) - # If custom conf, add 'Include' instruction + # If we detect the conf as manually modified ynh_hash = _get_conf_hashes('ssh')[SSHD_CONF] current_hash = _calculate_hash(SSHD_CONF) - include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' if ynh_hash != current_hash: + # And if there's not already an "Include ssh_config.d/*" directive + include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' add_include = False for line in open(SSHD_CONF): if re.match(include_rgx, line) is not None: add_include = True break + # We add an "Include sshd_config.d/*" directive if add_include: with open(SSHD_CONF, "a") as conf: conf.write('Include sshd_config.d/*') @@ -69,8 +84,8 @@ class MyMigration(Migration): raise MoulinetteError(m18n.n("migration_0006_cancel")) def backward(self): - # We don't backward completely but it should be enough + # We don't backward completely but it should be enough copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) if not _run_service_command('restart', 'ssh'): raise MoulinetteError(m18n.n("migration_0006_cannot_restart")) From 7b6bf6f4b890f8f1b1303f691258ea290f6bc278 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Oct 2018 20:27:24 +0000 Subject: [PATCH 014/104] Missing 'get' --- data/hooks/conf_regen/03-ssh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index 563394d40..2c9261193 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -14,7 +14,7 @@ do_pre_regen() { || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config # Add DSA HostKey to let user remove it with migration 7 - if [[ "$(yunohost settings 'service.ssh._deprecated_dsa_hostkey')" == "True" ]]; then + if [[ "$(yunohost settings get 'service.ssh._deprecated_dsa_hostkey')" == "True" ]]; then sed -i '/HostKey \/etc\/ssh\/ssh_host_rsa_key/a HostKey /etc/ssh/ssh_host_dsa_key' sshd_config fi From e8393a3d26777bdce09ffb77efb06ba6e8fcb754 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Oct 2018 20:47:47 +0000 Subject: [PATCH 015/104] Improve comments, naming and descriptions --- locales/en.json | 4 ++-- ...006_ssh_conf_managed_by_yunohost_step1.py} | 12 +++++++---- ...007_ssh_conf_managed_by_yunohost_step2.py} | 21 ++++++++++++++++--- 3 files changed, 28 insertions(+), 9 deletions(-) rename src/yunohost/data_migrations/{0006_manage_sshd_config.py => 0006_ssh_conf_managed_by_yunohost_step1.py} (86%) rename src/yunohost/data_migrations/{0007_reset_sshd_config.py => 0007_ssh_conf_managed_by_yunohost_step2.py} (76%) diff --git a/locales/en.json b/locales/en.json index a206b9fb8..803d5c937 100644 --- a/locales/en.json +++ b/locales/en.json @@ -274,8 +274,8 @@ "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_0006_manage_sshd_config": "Manage SSH conf in a better way", - "migration_description_0007_reset_sshd_config": "Reset SSH conf to the YunoHost default conf", + "migration_description_0006_ssh_conf_managed_by_yunohost_step1": "Let the SSH configuration be managed by YunoHost (step 1, automatic)", + "migration_description_0007_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)", "migration_0003_backward_impossible": "The stretch migration cannot be reverted.", "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", "migration_0003_patching_sources_list": "Patching the sources.lists ...", diff --git a/src/yunohost/data_migrations/0006_manage_sshd_config.py b/src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py similarity index 86% rename from src/yunohost/data_migrations/0006_manage_sshd_config.py rename to src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py index 68ee020fd..c3a503492 100644 --- a/src/yunohost/data_migrations/0006_manage_sshd_config.py +++ b/src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py @@ -20,14 +20,18 @@ SSHD_CONF = '/etc/ssh/sshd_config' class MyMigration(Migration): """ - This is an automatic migration, that ensure SSH conf is managed by YunoHost - (even if the "from_script" flag is present) + 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. - The admin can then choose in the next migration (manual, thi time) wether or - not to actually use the recommended configuration. + 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 migrate(self): diff --git a/src/yunohost/data_migrations/0007_reset_sshd_config.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py similarity index 76% rename from src/yunohost/data_migrations/0007_reset_sshd_config.py rename to src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py index af8f83ce7..10e319b2d 100644 --- a/src/yunohost/data_migrations/0007_reset_sshd_config.py +++ b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py @@ -13,7 +13,18 @@ logger = getActionLogger('yunohost.migration') class MyMigration(Migration): - "Reset SSH conf to the YunoHost one" + """ + 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. + """ def migrate(self): settings_set("service.ssh._deprecated_dsa_hostkey", False) @@ -26,7 +37,10 @@ class MyMigration(Migration): @property def mode(self): - # Avoid having a super long disclaimer + # 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') if '/etc/ssh/sshd_config' in ynh_hash: ynh_hash = ynh_hash['/etc/ssh/sshd_config'] @@ -43,7 +57,8 @@ class MyMigration(Migration): if self.mode == "auto": return None - # Detect major risk to migrate to the new configuration + # Detect key things to be aware of before enabling the + # recommended configuration dsa = False ports = [] root_login = [] From 6145199564728e6e6a53b73a3131caf5377599d9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Oct 2018 21:16:51 +0000 Subject: [PATCH 016/104] Improve semantic / simplify a few things --- locales/en.json | 10 ++--- ...0007_ssh_conf_managed_by_yunohost_step2.py | 37 ++++++++----------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/locales/en.json b/locales/en.json index 803d5c937..54792a827 100644 --- a/locales/en.json +++ b/locales/en.json @@ -296,12 +296,12 @@ "migration_0006_done": "Your root password have been replaced by your admin password.", "migration_0006_cancelled": "YunoHost has failed to improve the way your SSH conf is managed.", "migration_0006_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.", - "migration_0007_general_warning": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH configuration differs from the recommended configuration. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change in the following way:", + "migration_0007_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH configuration differs from the recommended configuration. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change in the following way:", "migration_0007_port": " - you will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it ;", - "migration_0007_root": " - you will not be able to connect with root user, instead you will have to use the admin user ;", - "migration_0007_dsa": " - you might need to invalidate a warning and to recheck the fingerprint of your server, because DSA key will be disabled ;", - "migration_0007_risk": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration though it is not recommended.", - "migration_0007_no_risk": "No major risk has been indentified about overriding your SSH configuration - but it's difficult to be sure. If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration though it is not recommended.", + "migration_0007_root": " - you will not be able to connect as root through SSH. Instead you should use the admin user ;", + "migration_0007_dsa": " - the DSA key will be disabled. Hence, you might need to invalidate a warning from your SSH client, and recheck the fingerprint of your server ;", + "migration_0007_warning": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", + "migration_0007_no_warning": "No major risk has been indentified about overriding your SSH configuration - but we can't be absolutely sure ;) ! If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", "migrations_backward": "Migrating backward.", "migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}", "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", diff --git a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py index 10e319b2d..c6355ac61 100644 --- a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py +++ b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py @@ -11,6 +11,7 @@ from yunohost.settings import settings_set, settings_get logger = getActionLogger('yunohost.migration') +SSHD_CONF = '/etc/ssh/sshd_config' class MyMigration(Migration): """ @@ -41,10 +42,8 @@ class MyMigration(Migration): # 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') - if '/etc/ssh/sshd_config' in ynh_hash: - ynh_hash = ynh_hash['/etc/ssh/sshd_config'] - current_hash = _calculate_hash('/etc/ssh/sshd_config') + ynh_hash = _get_conf_hashes('ssh').get(SSHD_CONF, None) + current_hash = _calculate_hash(SSHD_CONF) dsa = settings_get("service.ssh._deprecated_dsa_hostkey") if ynh_hash == current_hash and not dsa: return "auto" @@ -59,43 +58,39 @@ class MyMigration(Migration): # Detect key things to be aware of before enabling the # recommended configuration - dsa = False + 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('/etc/ssh/sshd_config'): + 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 and re.match(dsa_rgx, line) is not None: - dsa = True + if not dsa_key_enabled and re.match(dsa_rgx, line) is not None: + dsa_key_enabled = True - if len(ports) == 0: - ports = ['22'] - - port = ports != ['22'] - - root_user = root_login and root_login[-1] != 'no' + custom_port = ports != ['22'] and ports != [] + root_login_enabled = root_login and root_login[-1] != 'no' # Build message - message = m18n.n("migration_0007_general_warning") + message = m18n.n("migration_0007_general_disclaimer") - if port: + if custom_port: message += "\n\n" + m18n.n("migration_0007_port") - if root_user: + if root_login_enabled: message += "\n\n" + m18n.n("migration_0007_root") - if dsa: + if dsa_key_enabled: message += "\n\n" + m18n.n("migration_0007_dsa") - if port or root_user or dsa: - message += "\n\n" + m18n.n("migration_0007_risk") + if custom_port or root_login_enabled or dsa_key_enabled: + message += "\n\n" + m18n.n("migration_0007_warning") else: - message += "\n\n" + m18n.n("migration_0007_no_risk") + message += "\n\n" + m18n.n("migration_0007_no_warning") return message From 2aa0d2b55bb35c92cce3ba49d0cc217f8e765236 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Oct 2018 23:30:34 +0200 Subject: [PATCH 017/104] Spooky warning is spooky --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 54792a827..53250b0b1 100644 --- a/locales/en.json +++ b/locales/en.json @@ -299,7 +299,7 @@ "migration_0007_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH configuration differs from the recommended configuration. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change in the following way:", "migration_0007_port": " - you will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it ;", "migration_0007_root": " - you will not be able to connect as root through SSH. Instead you should use the admin user ;", - "migration_0007_dsa": " - the DSA key will be disabled. Hence, you might need to invalidate a warning from your SSH client, and recheck the fingerprint of your server ;", + "migration_0007_dsa": " - the DSA key will be disabled. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server ;", "migration_0007_warning": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", "migration_0007_no_warning": "No major risk has been indentified about overriding your SSH configuration - but we can't be absolutely sure ;) ! If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", "migrations_backward": "Migrating backward.", From e596758184204d095da220f5ad20147e6a06160d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 25 Oct 2018 23:34:39 +0200 Subject: [PATCH 018/104] Add a comment about /etc/ssh/sshd_config.to_restor --- src/yunohost/tools.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index a0549321a..678049900 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -442,6 +442,11 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, service_regen_conf(force=True) # Restore specific ssh conf + # c.f. the install script and in particular + # https://github.com/YunoHost/install_script/pull/50 + # The user can now choose during the install to keep + # the initial, existing sshd configuration + # instead of YunoHost's recommended conf bkp_sshd_conf = '/etc/ssh/sshd_config.to_restore' if os.path.exists(bkp_sshd_conf): os.rename(bkp_sshd_conf, '/etc/ssh/sshd_config') From 325678f541d40d833675823aa7de27bcad338bb9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 27 Nov 2018 23:55:15 +0100 Subject: [PATCH 019/104] More explicit name for setting --- data/hooks/conf_regen/03-ssh | 4 ++-- .../0006_ssh_conf_managed_by_yunohost_step1.py | 10 +++++----- .../0007_ssh_conf_managed_by_yunohost_step2.py | 4 ++-- src/yunohost/settings.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index 2c9261193..37b92e3fe 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -13,8 +13,8 @@ do_pre_regen() { [[ -f /proc/net/if_inet6 ]] \ || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config - # Add DSA HostKey to let user remove it with migration 7 - if [[ "$(yunohost settings get 'service.ssh._deprecated_dsa_hostkey')" == "True" ]]; then + # Support legacy setting (this setting might be disabled by a user during a migration) + if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then sed -i '/HostKey \/etc\/ssh\/ssh_host_rsa_key/a HostKey /etc/ssh/ssh_host_dsa_key' sshd_config fi diff --git a/src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py b/src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py index c3a503492..751f56fac 100644 --- a/src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py +++ b/src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py @@ -23,13 +23,13 @@ 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. """ @@ -44,15 +44,15 @@ class MyMigration(Migration): dsa = True break if dsa: - settings_set("service.ssh._deprecated_dsa_hostkey", True) + settings_set("service.ssh.allow_deprecated_dsa_hostkey", True) # Create sshd_config.d dir if not os.path.exists(SSHD_CONF + '.d'): mkdir(SSHD_CONF + '.d', 0755, uid='root', gid='root') # 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* + # 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 diff --git a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py index c6355ac61..20267d9e8 100644 --- a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py +++ b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py @@ -28,7 +28,7 @@ class MyMigration(Migration): """ def migrate(self): - settings_set("service.ssh._deprecated_dsa_hostkey", False) + settings_set("service.ssh.allow_deprecated_dsa_hostkey", False) service_regen_conf(names=['ssh'], force=True) def backward(self): @@ -44,7 +44,7 @@ class MyMigration(Migration): # (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._deprecated_dsa_hostkey") + dsa = settings_get("service.ssh.allow_deprecated_dsa_hostkey") if ynh_hash == current_hash and not dsa: return "auto" diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 1539435c6..391893b4e 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -39,7 +39,7 @@ DEFAULTS = OrderedDict([ # -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._deprecated_dsa_hostkey", {"type": "bool", "default": False}), + ("service.ssh.allow_deprecated_dsa_hostkey", {"type": "bool", "default": False}), ]) From 23893c43b3431b0fc5094f7b1a2521187243810a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 00:08:53 +0100 Subject: [PATCH 020/104] Increment migrations number --- locales/en.json | 20 +++++++++---------- ...007_ssh_conf_managed_by_yunohost_step1.py} | 4 ++-- ...008_ssh_conf_managed_by_yunohost_step2.py} | 14 ++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) rename src/yunohost/data_migrations/{0006_ssh_conf_managed_by_yunohost_step1.py => 0007_ssh_conf_managed_by_yunohost_step1.py} (96%) rename src/yunohost/data_migrations/{0007_ssh_conf_managed_by_yunohost_step2.py => 0008_ssh_conf_managed_by_yunohost_step2.py} (86%) diff --git a/locales/en.json b/locales/en.json index 53250b0b1..e657d38d1 100644 --- a/locales/en.json +++ b/locales/en.json @@ -274,8 +274,8 @@ "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_0006_ssh_conf_managed_by_yunohost_step1": "Let the SSH configuration be managed by YunoHost (step 1, automatic)", - "migration_description_0007_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)", + "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_0003_backward_impossible": "The stretch migration cannot be reverted.", "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", "migration_0003_patching_sources_list": "Patching the sources.lists ...", @@ -294,14 +294,14 @@ "migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now :(.", "migration_0006_disclaimer": "Yunohost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.", "migration_0006_done": "Your root password have been replaced by your admin password.", - "migration_0006_cancelled": "YunoHost has failed to improve the way your SSH conf is managed.", - "migration_0006_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.", - "migration_0007_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH configuration differs from the recommended configuration. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change in the following way:", - "migration_0007_port": " - you will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it ;", - "migration_0007_root": " - you will not be able to connect as root through SSH. Instead you should use the admin user ;", - "migration_0007_dsa": " - the DSA key will be disabled. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server ;", - "migration_0007_warning": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", - "migration_0007_no_warning": "No major risk has been indentified about overriding your SSH configuration - but we can't be absolutely sure ;) ! If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", + "migration_0007_cancelled": "YunoHost has failed to improve the way your SSH conf 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 configuration differs from the recommended configuration. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change in the following way:", + "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 disabled. 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 agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", + "migration_0008_no_warning": "No major risk has been indentified about overriding your SSH configuration - but we can't be absolutely sure ;) ! If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", "migrations_backward": "Migrating backward.", "migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}", "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", diff --git a/src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py similarity index 96% rename from src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py rename to src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py index 751f56fac..82d1fc634 100644 --- a/src/yunohost/data_migrations/0006_ssh_conf_managed_by_yunohost_step1.py +++ b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py @@ -85,11 +85,11 @@ class MyMigration(Migration): # Restart ssh and backward if it fail if not _run_service_command('restart', 'ssh'): self.backward() - raise MoulinetteError(m18n.n("migration_0006_cancel")) + raise MoulinetteError(m18n.n("migration_0007_cancel")) def backward(self): # We don't backward completely but it should be enough copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) if not _run_service_command('restart', 'ssh'): - raise MoulinetteError(m18n.n("migration_0006_cannot_restart")) + raise MoulinetteError(m18n.n("migration_0007_cannot_restart")) diff --git a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py similarity index 86% rename from src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py rename to src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py index 20267d9e8..c53154192 100644 --- a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step2.py +++ b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py @@ -33,7 +33,7 @@ class MyMigration(Migration): def backward(self): - raise MoulinetteError(m18n.n("migration_0007_backward_impossible")) + raise MoulinetteError(m18n.n("migration_0008_backward_impossible")) @property def mode(self): @@ -77,20 +77,20 @@ class MyMigration(Migration): root_login_enabled = root_login and root_login[-1] != 'no' # Build message - message = m18n.n("migration_0007_general_disclaimer") + message = m18n.n("migration_0008_general_disclaimer") if custom_port: - message += "\n\n" + m18n.n("migration_0007_port") + message += "\n\n" + m18n.n("migration_0008_port") if root_login_enabled: - message += "\n\n" + m18n.n("migration_0007_root") + message += "\n\n" + m18n.n("migration_0008_root") if dsa_key_enabled: - message += "\n\n" + m18n.n("migration_0007_dsa") + 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_0007_warning") + message += "\n\n" + m18n.n("migration_0008_warning") else: - message += "\n\n" + m18n.n("migration_0007_no_warning") + message += "\n\n" + m18n.n("migration_0008_no_warning") return message From fad4ff090a9f58ed2864097c1c49f192e67c9ba8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 00:26:25 +0100 Subject: [PATCH 021/104] Use templating for more robustness about which SSH keys are enabled --- data/hooks/conf_regen/03-ssh | 8 ++++++-- data/templates/ssh/sshd_config | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index 37b92e3fe..74064a631 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -2,6 +2,8 @@ set -e +. /usr/share/yunohost/helpers.d/utils + do_pre_regen() { pending_dir=$1 @@ -14,11 +16,13 @@ do_pre_regen() { || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config # Support legacy setting (this setting might be disabled by a user during a migration) + ssh_keys=$(ls /etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key 2>/dev/null) if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then - sed -i '/HostKey \/etc\/ssh\/ssh_host_rsa_key/a HostKey /etc/ssh/ssh_host_dsa_key' sshd_config + ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)" fi - install -D -m 644 sshd_config "${pending_dir}/etc/ssh/sshd_config" + export $ssh_keys + ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config" fi } diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index 66aacc5f0..36bd9167d 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -8,9 +8,9 @@ ListenAddress :: ListenAddress 0.0.0.0 Protocol 2 # HostKeys for protocol version 2 -HostKey /etc/ssh/ssh_host_rsa_key -HostKey /etc/ssh/ssh_host_ecdsa_key -HostKey /etc/ssh/ssh_host_ed25519_key +{% for key in ssh_keys %} +HostKey {{ key }} +{% endfor %} #Privilege Separation is turned on for security UsePrivilegeSeparation yes From 3d81f032e9aba1d4b2155c8b74300b9bf0d307c0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 17:50:20 +0000 Subject: [PATCH 022/104] Fixes following tests (some sshd_config options do not exists or are deprecated) --- data/helpers.d/utils | 1 + data/hooks/conf_regen/03-ssh | 2 +- data/templates/ssh/sshd_config | 21 +++++++------------ ...0007_ssh_conf_managed_by_yunohost_step1.py | 2 +- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/data/helpers.d/utils b/data/helpers.d/utils index eef9f2a8e..b280c3b21 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -272,6 +272,7 @@ ynh_local_curl () { ynh_render_template() { local template_path=$1 local output_path=$2 + mkdir -p "$(dirname $output_path)" # Taken from https://stackoverflow.com/a/35009576 python2.7 -c 'import os, sys, jinja2; sys.stdout.write( jinja2.Template(sys.stdin.read() diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index 74064a631..a9ed0ee48 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -21,7 +21,7 @@ do_pre_regen() { ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)" fi - export $ssh_keys + export ssh_keys ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config" fi } diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index 36bd9167d..ed9a3136e 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -1,16 +1,14 @@ -# Package generated configuration file -# See the sshd_config(5) manpage for details +# This configuration has been automatically generated +# by YunoHost -# What ports, IPs and protocols we listen for +Protocol 2 Port 22 -# Use these options to restrict which interfaces/protocols sshd will bind to + ListenAddress :: ListenAddress 0.0.0.0 -Protocol 2 -# HostKeys for protocol version 2 -{% for key in ssh_keys %} -HostKey {{ key }} -{% endfor %} + +{% for key in ssh_keys.split() %} +HostKey {{ key }}{% endfor %} #Privilege Separation is turned on for security UsePrivilegeSeparation yes @@ -24,14 +22,11 @@ LoginGraceTime 120 PermitRootLogin no StrictModes yes -RSAAuthentication yes PubkeyAuthentication yes #AuthorizedKeysFile %h/.ssh/authorized_keys # Don't read the user's ~/.rhosts and ~/.shosts files IgnoreRhosts yes -# For this to work you will also need host keys in /etc/ssh_known_hosts -RhostsRSAAuthentication no # similar for protocol version 2 HostbasedAuthentication no # Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication @@ -92,5 +87,3 @@ Match User sftpusers AllowTcpForwarding no GatewayPorts no X11Forwarding no - -Include sshd_config.d/* 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 index 82d1fc634..95e67894c 100644 --- 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 @@ -65,7 +65,7 @@ class MyMigration(Migration): copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) # If we detect the conf as manually modified - ynh_hash = _get_conf_hashes('ssh')[SSHD_CONF] + ynh_hash = _get_conf_hashes('ssh').get(SSHD_CONF, None) current_hash = _calculate_hash(SSHD_CONF) if ynh_hash != current_hash: From f295c83fd3cd50f8c96e156e4a9267fc1f21be45 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 18:59:34 +0000 Subject: [PATCH 023/104] Order of keys matter, ed25519 is recommended --- bin/yunoprompt | 2 +- data/hooks/conf_regen/03-ssh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/yunoprompt b/bin/yunoprompt index bca5c2cb3..2b2a6cfb2 100755 --- a/bin/yunoprompt +++ b/bin/yunoprompt @@ -5,7 +5,7 @@ ip=$(hostname --all-ip-address) # Fetch SSH fingerprints i=0 -for key in $(ls /etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key.pub 2> /dev/null) ; do +for key in $(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key.pub 2> /dev/null) ; do output=$(ssh-keygen -l -f $key) fingerprint[$i]=" - $(echo $output | cut -d' ' -f2) $(echo $output| cut -d' ' -f4)" i=$(($i + 1)) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index a9ed0ee48..e60b3022f 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -16,7 +16,7 @@ do_pre_regen() { || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config # Support legacy setting (this setting might be disabled by a user during a migration) - ssh_keys=$(ls /etc/ssh/ssh_host_{rsa,ecdsa,ed25519}_key 2>/dev/null) + ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null) if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)" fi From 25efab7f2a91243ef55438fc76cd2deb13fec3dd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 19:45:19 +0000 Subject: [PATCH 024/104] We can't have include blocks in sshd_config :| --- .../0007_ssh_conf_managed_by_yunohost_step1.py | 18 ------------------ 1 file changed, 18 deletions(-) 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 index 95e67894c..73cb162b6 100644 --- 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 @@ -64,24 +64,6 @@ class MyMigration(Migration): service_regen_conf(names=['ssh'], force=True) copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) - # If we detect the conf as manually modified - ynh_hash = _get_conf_hashes('ssh').get(SSHD_CONF, None) - current_hash = _calculate_hash(SSHD_CONF) - if ynh_hash != current_hash: - - # And if there's not already an "Include ssh_config.d/*" directive - include_rgx = r'^[ \t]*Include[ \t]+sshd_config\.d/\*[ \t]*(?:#.*)?$' - add_include = False - for line in open(SSHD_CONF): - if re.match(include_rgx, line) is not None: - add_include = True - break - - # We add an "Include sshd_config.d/*" directive - if add_include: - with open(SSHD_CONF, "a") as conf: - conf.write('Include sshd_config.d/*') - # Restart ssh and backward if it fail if not _run_service_command('restart', 'ssh'): self.backward() From 6a812190c5ffaae014229088b85217a064a379fa Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 20:27:42 +0000 Subject: [PATCH 025/104] Enforce permissions for /etc/ssh/sshd_config --- data/hooks/conf_regen/03-ssh | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index e60b3022f..dac21b19b 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -15,8 +15,9 @@ do_pre_regen() { [[ -f /proc/net/if_inet6 ]] \ || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config - # Support legacy setting (this setting might be disabled by a user during a migration) ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null) + + # Support legacy setting (this setting might be disabled by a user during a migration) if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)" fi @@ -27,12 +28,15 @@ do_pre_regen() { } do_post_regen() { - regen_conf_files=$1 - - if [[ ! -f /etc/yunohost/from_script ]]; then - [[ -z "$regen_conf_files" ]] \ - || sudo service ssh restart - fi + regen_conf_files=$1 + if [[ ! -f /etc/yunohost/from_script ]]; then + if [[ -n "$regen_conf_files" ]]; + then + sudo service ssh restart + chown root:root "/etc/ssh/sshd_config" + chmod 644 "/etc/ssh/sshd_config" + fi + fi } FORCE=${2:-0} From 4db65682eb468edc81ae043f2da03909437f557d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 20:42:39 +0000 Subject: [PATCH 026/104] Fix IPv6 handling in ssh regen conf script --- data/hooks/conf_regen/03-ssh | 5 +++-- data/templates/ssh/sshd_config | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index dac21b19b..271ad9bb8 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -11,9 +11,9 @@ do_pre_regen() { # Don't overwrite configuration if from_script if [[ ! -f /etc/yunohost/from_script ]]; then + # do not listen to IPv6 if unavailable - [[ -f /proc/net/if_inet6 ]] \ - || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config + [[ -f /proc/net/if_inet6 ]] && ipv6_enabled=true || ipv6_enabled=false ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null) @@ -23,6 +23,7 @@ do_pre_regen() { fi export ssh_keys + export ipv6_enabled ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config" fi } diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index ed9a3136e..9d6c078b9 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -4,7 +4,7 @@ Protocol 2 Port 22 -ListenAddress :: +{% if ipv6_enabled == "true" %}ListenAddress ::{% endif %} ListenAddress 0.0.0.0 {% for key in ssh_keys.split() %} From 0576b17442282867a6b011a41cdaf7bcfafaaad3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 21:03:28 +0000 Subject: [PATCH 027/104] Simplify code / indentation levels --- data/hooks/conf_regen/03-ssh | 53 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index 271ad9bb8..76fab7cd4 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -5,39 +5,44 @@ set -e . /usr/share/yunohost/helpers.d/utils do_pre_regen() { - pending_dir=$1 + pending_dir=$1 - cd /usr/share/yunohost/templates/ssh + # If the (legacy) 'from_script' flag is here, + # we won't touch anything in the ssh config. + [[ ! -f /etc/yunohost/from_script ]] || return - # Don't overwrite configuration if from_script - if [[ ! -f /etc/yunohost/from_script ]]; then + cd /usr/share/yunohost/templates/ssh - # do not listen to IPv6 if unavailable - [[ -f /proc/net/if_inet6 ]] && ipv6_enabled=true || ipv6_enabled=false + # do not listen to IPv6 if unavailable + [[ -f /proc/net/if_inet6 ]] && ipv6_enabled=true || ipv6_enabled=false - ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null) + ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null) - # Support legacy setting (this setting might be disabled by a user during a migration) - if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then - ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)" - fi + # Support legacy setting (this setting might be disabled by a user during a migration) + if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then + ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)" + fi - export ssh_keys - export ipv6_enabled - ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config" - fi + export ssh_keys + export ipv6_enabled + ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config" } do_post_regen() { - regen_conf_files=$1 - if [[ ! -f /etc/yunohost/from_script ]]; then - if [[ -n "$regen_conf_files" ]]; - then - sudo service ssh restart - chown root:root "/etc/ssh/sshd_config" - chmod 644 "/etc/ssh/sshd_config" - fi - fi + regen_conf_files=$1 + + # If the (legacy) 'from_script' flag is here, + # we won't touch anything in the ssh config. + [[ ! -f /etc/yunohost/from_script ]] || return + + # If no file changed, there's nothing to do + [[ -n "$regen_conf_files" ]] || return + + # Enforce permissions for /etc/ssh/sshd_config + chown root:root "/etc/ssh/sshd_config" + chmod 644 "/etc/ssh/sshd_config" + + systemctl restart ssh } FORCE=${2:-0} From 90e542a9315115f1f7489bbf704433e722ee18dc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 28 Nov 2018 21:30:26 +0000 Subject: [PATCH 028/104] Allow root login on local networks --- data/templates/ssh/sshd_config | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index 9d6c078b9..cfc101ffa 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -87,3 +87,11 @@ Match User sftpusers AllowTcpForwarding no GatewayPorts no X11Forwarding no + +# root login is allowed on local networks +# It's meant to be a backup solution in case LDAP is down and +# user admin can't be used... +# If the server is a VPS, it's expected that the owner of the +# server has access to a web console through which to log in. +Match Address 192.168.0.0/16,10.0.0.0/16 + PermitRootLogin yes From 847d18293a6c375d062f342425a34d05c62bbd22 Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Fri, 30 Nov 2018 15:47:42 +0100 Subject: [PATCH 029/104] [enh] Add other private ip network and link local --- data/templates/ssh/sshd_config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index cfc101ffa..360920751 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -93,5 +93,5 @@ Match User sftpusers # user admin can't be used... # If the server is a VPS, it's expected that the owner of the # server has access to a web console through which to log in. -Match Address 192.168.0.0/16,10.0.0.0/16 +Match Address 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,169.254.0.0/16,fe80::/10,fd00::/8 PermitRootLogin yes From 447372d07c88172309e122116b5f4086fba82a3e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 3 Dec 2018 17:03:22 +0100 Subject: [PATCH 030/104] [enh] Clean + harden sshd config using Mozilla recommendation (#590) * Clean sshd_config + harden using Mozilla recommendation * Order of keys matter, ed25519 is recommended --- data/hooks/conf_regen/03-ssh | 8 ++- data/templates/ssh/sshd_config | 101 +++++++++++++-------------------- 2 files changed, 48 insertions(+), 61 deletions(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index 76fab7cd4..dafa4327e 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -12,10 +12,16 @@ do_pre_regen() { [[ ! -f /etc/yunohost/from_script ]] || return cd /usr/share/yunohost/templates/ssh - + # do not listen to IPv6 if unavailable [[ -f /proc/net/if_inet6 ]] && ipv6_enabled=true || ipv6_enabled=false + # Support legacy setting (this setting might be disabled by a user during a migration) + ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null) + if [[ "$(yunohost settings get 'service.ssh.allow_deprecated_dsa_hostkey')" == "True" ]]; then + ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null)" + fi + ssh_keys=$(ls /etc/ssh/ssh_host_{ed25519,rsa,ecdsa}_key 2>/dev/null) # Support legacy setting (this setting might be disabled by a user during a migration) diff --git a/data/templates/ssh/sshd_config b/data/templates/ssh/sshd_config index 360920751..ed870e5dc 100644 --- a/data/templates/ssh/sshd_config +++ b/data/templates/ssh/sshd_config @@ -10,77 +10,58 @@ ListenAddress 0.0.0.0 {% for key in ssh_keys.split() %} HostKey {{ key }}{% endfor %} -#Privilege Separation is turned on for security -UsePrivilegeSeparation yes +# ############################################## +# Stuff recommended by Mozilla "modern" compat' +# https://infosec.mozilla.org/guidelines/openssh +# ############################################## -# Logging +# Keys, ciphers and MACS +KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256 +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr +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 + +# 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 -LogLevel INFO +LogLevel VERBOSE + +# ####################### +# Authentication settings +# ####################### + +# Comment from Mozilla about the motivation behind disabling root login +# +# Root login is not allowed for auditing reasons. This is because it's difficult to track which process belongs to which root user: +# +# On Linux, user sessions are tracking using a kernel-side session id, however, this session id is not recorded by OpenSSH. +# Additionally, only tools such as systemd and auditd record the process session id. +# On other OSes, the user session id is not necessarily recorded at all kernel-side. +# Using regular users in combination with /bin/su or /usr/bin/sudo ensure a clear audit track. -# Authentication: LoginGraceTime 120 PermitRootLogin no StrictModes yes - PubkeyAuthentication yes -#AuthorizedKeysFile %h/.ssh/authorized_keys - -# Don't read the user's ~/.rhosts and ~/.shosts files -IgnoreRhosts yes -# similar for protocol version 2 -HostbasedAuthentication no -# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication -#IgnoreUserKnownHosts yes - -# To enable empty passwords, change to yes (NOT RECOMMENDED) PermitEmptyPasswords no - -# Change to yes to enable challenge-response passwords (beware issues with -# some PAM modules and threads) ChallengeResponseAuthentication no - -# Change to no to disable tunnelled clear text passwords -#PasswordAuthentication yes - -# Kerberos options -#KerberosAuthentication no -#KerberosGetAFSToken no -#KerberosOrLocalPasswd yes -#KerberosTicketCleanup yes - -# GSSAPI options -#GSSAPIAuthentication no -#GSSAPICleanupCredentials yes - -X11Forwarding yes -X11DisplayOffset 10 -PrintMotd no -PrintLastLog yes -TCPKeepAlive yes -#UseLogin no - -# keep ssh sessions fresh -ClientAliveInterval 60 - -#MaxStartups 10:30:60 -Banner /etc/issue.net - -# Allow client to pass locale environment variables -AcceptEnv LANG LC_* - -Subsystem sftp internal-sftp - -# Set this to 'yes' to enable PAM authentication, account processing, -# and session processing. If this is enabled, PAM authentication will -# be allowed through the ChallengeResponseAuthentication and -# PasswordAuthentication. Depending on your PAM configuration, -# PAM authentication via ChallengeResponseAuthentication may bypass -# the setting of "PermitRootLogin without-password". -# If you just want the PAM account and session checks to run without -# PAM authentication, then enable this but set PasswordAuthentication -# and ChallengeResponseAuthentication to 'no'. UsePAM yes +# Change to no to disable tunnelled clear text passwords +# (i.e. everybody will need to authenticate using ssh keys) +#PasswordAuthentication yes + +# Post-login stuff +Banner /etc/issue.net +PrintMotd no +PrintLastLog yes +ClientAliveInterval 60 +AcceptEnv LANG LC_* + +# SFTP stuff +Subsystem sftp internal-sftp Match User sftpusers ForceCommand internal-sftp ChrootDirectory /home/%u From 4f05cd5b2b2f12fddf67712c3c263c89fbf4678a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 3 Dec 2018 16:56:28 +0000 Subject: [PATCH 031/104] Uh for some reason we need to return *0* explicitly --- data/hooks/conf_regen/03-ssh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index dafa4327e..34cb441b4 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -9,7 +9,7 @@ do_pre_regen() { # If the (legacy) 'from_script' flag is here, # we won't touch anything in the ssh config. - [[ ! -f /etc/yunohost/from_script ]] || return + [[ ! -f /etc/yunohost/from_script ]] || return 0 cd /usr/share/yunohost/templates/ssh @@ -39,10 +39,10 @@ do_post_regen() { # If the (legacy) 'from_script' flag is here, # we won't touch anything in the ssh config. - [[ ! -f /etc/yunohost/from_script ]] || return + [[ ! -f /etc/yunohost/from_script ]] || return 0 # If no file changed, there's nothing to do - [[ -n "$regen_conf_files" ]] || return + [[ -n "$regen_conf_files" ]] || return 0 # Enforce permissions for /etc/ssh/sshd_config chown root:root "/etc/ssh/sshd_config" From e871ff3b20307069ccc28eede7bb31e1f91561aa Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 3 Dec 2018 17:04:43 +0000 Subject: [PATCH 032/104] Semantics --- src/yunohost/tools.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 678049900..c5b35a6b5 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -441,15 +441,18 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, service_regen_conf(force=True) - # Restore specific ssh conf - # c.f. the install script and in particular + # Restore original ssh conf, as chosen by the + # admin during the initial install + # + # c.f. the install script and in particular # https://github.com/YunoHost/install_script/pull/50 # The user can now choose during the install to keep # the initial, existing sshd configuration # instead of YunoHost's recommended conf - bkp_sshd_conf = '/etc/ssh/sshd_config.to_restore' - if os.path.exists(bkp_sshd_conf): - os.rename(bkp_sshd_conf, '/etc/ssh/sshd_config') + # + original_sshd_conf = '/etc/ssh/sshd_config.before_yunohost' + if os.path.exists(original_sshd_conf): + os.rename(original_sshd_conf, '/etc/ssh/sshd_config') logger.success(m18n.n('yunohost_configured')) From 3949333599dfcad37799c83e6765b4f75a56bf02 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 3 Dec 2018 17:56:01 +0000 Subject: [PATCH 033/104] We need to explicitly ask for the ssh conf to be generated --- src/yunohost/tools.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index c5b35a6b5..6810e5397 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -453,6 +453,10 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, original_sshd_conf = '/etc/ssh/sshd_config.before_yunohost' if os.path.exists(original_sshd_conf): os.rename(original_sshd_conf, '/etc/ssh/sshd_config') + else: + # We need to explicitly ask the regen conf to regen ssh + # (by default, i.e. first argument = None, it won't because it's too touchy) + service_regen_conf(names=["ssh"], force=True) logger.success(m18n.n('yunohost_configured')) From 3a2464dfb46bc2d6cba7cbe3862a694b6cd5258c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 18:27:18 +0100 Subject: [PATCH 034/104] Skip migrations one at a time --- src/yunohost/tools.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index baa614fa5..add114b99 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -938,6 +938,10 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai operation_logger.success() + # Skip migrations one at a time + if skip: + break + # special case where we want to go back from the start if target == 0: state["last_run_migration"] = None From 3ca8c089e116b0d6a5344ffdec6ff106ec25140c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 18:33:39 +0100 Subject: [PATCH 035/104] Have a function to initialize migrations --- src/yunohost/tools.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index add114b99..f6535d85c 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -432,7 +432,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, _install_appslist_fetch_cron() # Init migrations (skip them, no need to run them on a fresh system) - tools_migrations_migrate(skip=True, auto=True) + _skip_all_migrations() os.system('touch /etc/yunohost/installed') @@ -948,7 +948,6 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai write_to_json(MIGRATIONS_STATE_PATH, state) - def tools_migrations_state(): """ Show current migration state @@ -1048,6 +1047,25 @@ def _load_migration(migration_file): raise MoulinetteError(errno.EINVAL, m18n.n('migrations_error_failed_to_load_migration', number=number, name=name)) +def _skip_all_migrations(): + """ + Skip all pending migrations. + This is meant to be used during postinstall to + initialize the migration system. + """ + state = tools_migrations_state() + + # load all migrations + migrations = _get_migrations_list() + migrations = sorted(migrations, key=lambda x: x.number) + last_migration = migrations[-1] + + state["last_run_migration"] = { + "number": last_migration.number, + "name": last_migration.name + } + write_to_json(MIGRATIONS_STATE_PATH, state) + class Migration(object): From 791ac440375515b6ca4a1c5d53c2f1529bd9b47d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 22:10:06 +0100 Subject: [PATCH 036/104] Add success message after running migrations --- locales/en.json | 1 + src/yunohost/tools.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/locales/en.json b/locales/en.json index 6ce22ca80..3bb36ae9d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -305,6 +305,7 @@ "migrations_show_currently_running_migration": "Running migration {number} {name}...", "migrations_show_last_migration": "Last ran migration is {}", "migrations_skip_migration": "Skipping migration {number} {name}...", + "migrations_success": "Successfully ran migration {number} {name}!", "migrations_to_be_ran_manually": "Migration {number} {name} has to be ran manually. Please go to Tools > Migrations on the webadmin, or run `yunohost tools migrations migrate`.", "migrations_need_to_accept_disclaimer": "To run the migration {number} {name}, 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.", "monitor_disabled": "The server monitoring has been disabled", diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index f6535d85c..fbadb3b20 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -924,6 +924,9 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai logger.error(msg, exc_info=1) operation_logger.error(msg) break + else: + logger.success(m18n.n('migrations_success', + number=migration.number, name=migration.name)) else: # if skip logger.warn(m18n.n('migrations_skip_migration', From 0fa374db40171f6412b2cc4f10d1bb2dd7ffc014 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 22:11:06 +0100 Subject: [PATCH 037/104] This message should be info --- 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 fbadb3b20..3bffe3606 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -903,7 +903,7 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai if not skip: - logger.warn(m18n.n('migrations_show_currently_running_migration', + logger.info(m18n.n('migrations_show_currently_running_migration', number=migration.number, name=migration.name)) try: From 783ecf8f4158876be6192abf576f9acbef379268 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 18:24:53 +0100 Subject: [PATCH 038/104] Manage migration of auto/manual and disclaimer on a per-migration basis --- src/yunohost/tools.py | 47 +++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 3bffe3606..d8cd53f56 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -872,31 +872,34 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai else: # can't happen, this case is handle before raise Exception() - # If we are migrating in "automatic mode" (i.e. from debian - # configure during an upgrade of the package) but we are asked to run - # migrations is to be ran manually by the user - manual_migrations = [m for m in migrations if m.mode == "manual"] - if not skip and auto and manual_migrations: - for m in manual_migrations: - logger.warn(m18n.n('migrations_to_be_ran_manually', - number=m.number, - name=m.name)) - return - - # If some migrations have disclaimers, require the --accept-disclaimer - # option - migrations_with_disclaimer = [m for m in migrations if m.disclaimer] - if not skip and not accept_disclaimer and migrations_with_disclaimer: - for m in migrations_with_disclaimer: - logger.warn(m18n.n('migrations_need_to_accept_disclaimer', - number=m.number, - name=m.name, - disclaimer=m.disclaimer)) - return - # effectively run selected migrations for migration in migrations: + if not skip: + # If we are migrating in "automatic mode" (i.e. from debian configure + # during an upgrade of the package) but we are asked to run migrations + # to be ran manually by the user, stop there and ask the user to + # run the migration manually. + if auto and migration.mode == "manual": + logger.warn(m18n.n('migrations_to_be_ran_manually', + number=migration.number, + name=migration.name)) + break + + # If some migrations have disclaimers, + if migration.disclaimer: + # require the --accept-disclaimer option. Otherwise, stop everything + # here and display the disclaimer + if not accept_disclaimer: + logger.warn(m18n.n('migrations_need_to_accept_disclaimer', + number=migration.number, + name=migration.name, + disclaimer=migration.disclaimer)) + break + # --accept-disclaimer will only work for the first migration + else: + accept_disclaimer = False + # Start register change on system operation_logger= OperationLogger('tools_migrations_migrate_' + mode) operation_logger.start() From df56cf90aa18235ee5103681dc76ff5dece841e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Fri, 7 Dec 2018 18:28:30 +0100 Subject: [PATCH 039/104] Ignore test directory --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6dd427aba..75f4ae6ea 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ pip-log.txt # moulinette lib src/yunohost/locales + +# Test +src/yunohost/tests/apps From 1ac97c46adc4518eefdf2a43b57c86e9626b087d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 7 Dec 2018 19:12:47 +0100 Subject: [PATCH 040/104] Explicit root password change each time admin password is changed --- locales/en.json | 2 +- .../data_migrations/0006_sync_admin_and_root_passwords.py | 2 +- src/yunohost/tools.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index 6ce22ca80..6a2852f80 100644 --- a/locales/en.json +++ b/locales/en.json @@ -291,7 +291,6 @@ "migration_0005_postgresql_96_not_installed": "Postgresql 9.4 has been found to be installed, but not postgresql 9.6 !? Something weird might have happened on your system :( ...", "migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now :(.", "migration_0006_disclaimer": "Yunohost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.", - "migration_0006_done": "Your root password have been replaced by your admin password.", "migrations_backward": "Migrating backward.", "migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}", "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", @@ -375,6 +374,7 @@ "restore_running_hooks": "Running restoration hooks...", "restore_system_part_failed": "Unable to restore the '{part:s}' system part", "root_password_desynchronized": "The admin password has been changed, but YunoHost was unable to propagate this on the root password !", + "root_password_replaced_by_admin_password": "Your root password have been replaced by your admin password.", "server_shutdown": "The server will shutdown", "server_shutdown_confirm": "The server will shutdown immediatly, are you sure? [{answers:s}]", "server_reboot": "The server will reboot", 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 index 366363f22..ee3aeefcb 100644 --- a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py +++ b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py @@ -23,7 +23,7 @@ class MyMigration(Migration): new_hash = self._get_admin_hash() self._replace_root_hash(new_hash) - logger.info(m18n.n("migration_0006_done")) + logger.info(m18n.n("root_password_replaced_by_admin_password")) def backward(self): pass diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index baa614fa5..fea1f8398 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -155,6 +155,8 @@ def tools_adminpw(auth, new_password, check_strength=True): except IOError as e: logger.warning(m18n.n('root_password_desynchronized')) return + + logger.info(m18n.n("root_password_replaced_by_admin_password")) logger.success(m18n.n('admin_password_changed')) From 03b1a2867ce4f8c0cce772c951c2f8f89acfa635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Fri, 7 Dec 2018 16:35:01 +0100 Subject: [PATCH 041/104] Fix tests --- locales/en.json | 2 + src/yunohost/settings.py | 7 ++- src/yunohost/tests/test_backuprestore.py | 68 ++++++++++-------------- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/locales/en.json b/locales/en.json index 6ce22ca80..980dfb071 100644 --- a/locales/en.json +++ b/locales/en.json @@ -194,6 +194,8 @@ "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_password_admin_strength": "Admin password strength", + "global_settings_setting_security_password_user_strength": "User password strength", "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discarding it and save it in /etc/yunohost/unkown_settings.json", "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.", "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index d2526316e..ee2f35785 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -95,7 +95,12 @@ def settings_set(key, value): elif key_type == "int": if not isinstance(value, int) or isinstance(value, bool): if isinstance(value, str): - value=int(value) + try: + value=int(value) + except: + raise MoulinetteError(errno.EINVAL, m18n.n( + 'global_settings_bad_type_for_setting', setting=key, + received_type=type(value).__name__, expected_type=key_type)) else: raise MoulinetteError(errno.EINVAL, m18n.n( 'global_settings_bad_type_for_setting', setting=key, diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 1071c1642..c60dcb517 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -195,7 +195,7 @@ def add_archive_system_from_2p4(): def test_backup_only_ldap(): # Create the backup - backup_create(ignore_system=False, ignore_apps=True, system=["conf_ldap"]) + backup_create(system=["conf_ldap"]) archives = backup_list()["archives"] assert len(archives) == 1 @@ -212,7 +212,7 @@ def test_backup_system_part_that_does_not_exists(mocker): # Create the backup with pytest.raises(MoulinetteError): - backup_create(ignore_system=False, ignore_apps=True, system=["yolol"]) + backup_create(system=["yolol"]) m18n.n.assert_any_call('backup_hook_unknown', hook="yolol") m18n.n.assert_any_call('backup_nothings_done') @@ -224,7 +224,7 @@ def test_backup_system_part_that_does_not_exists(mocker): def test_backup_and_restore_all_sys(): # Create the backup - backup_create(ignore_system=False, ignore_apps=True) + backup_create(system=[]) archives = backup_list()["archives"] assert len(archives) == 1 @@ -241,7 +241,7 @@ def test_backup_and_restore_all_sys(): # Restore the backup backup_restore(auth, name=archives[0], force=True, - ignore_system=False, ignore_apps=True) + system=[]) # Check ssowat conf is back assert os.path.exists("/etc/ssowat/conf.json") @@ -255,21 +255,19 @@ def test_backup_and_restore_all_sys(): def test_restore_system_from_Ynh2p4(monkeypatch, mocker): # Backup current system - backup_create(ignore_system=False, ignore_apps=True) + backup_create(system=[]) archives = backup_list()["archives"] assert len(archives) == 2 # Restore system archive from 2.4 try: backup_restore(auth, name=backup_list()["archives"][1], - ignore_system=False, - ignore_apps=True, + system=[], force=True) finally: # Restore system as it was backup_restore(auth, name=backup_list()["archives"][0], - ignore_system=False, - ignore_apps=True, + system=[], force=True) ############################################################################### @@ -293,7 +291,7 @@ def test_backup_script_failure_handling(monkeypatch, mocker): mocker.spy(m18n, "n") with pytest.raises(MoulinetteError): - backup_create(ignore_system=True, ignore_apps=False, apps=["backup_recommended_app"]) + backup_create(system=None, apps=["backup_recommended_app"]) m18n.n.assert_any_call('backup_app_failed', app='backup_recommended_app') @@ -313,7 +311,7 @@ def test_backup_not_enough_free_space(monkeypatch, mocker): mocker.spy(m18n, "n") with pytest.raises(MoulinetteError): - backup_create(ignore_system=True, ignore_apps=False, apps=["backup_recommended_app"]) + backup_create(system=None, apps=["backup_recommended_app"]) m18n.n.assert_any_call('not_enough_disk_space', path=ANY) @@ -325,7 +323,7 @@ def test_backup_app_not_installed(mocker): mocker.spy(m18n, "n") with pytest.raises(MoulinetteError): - backup_create(ignore_system=True, ignore_apps=False, apps=["wordpress"]) + backup_create(system=None, apps=["wordpress"]) m18n.n.assert_any_call("unbackup_app", app="wordpress") m18n.n.assert_any_call('backup_nothings_done') @@ -341,7 +339,7 @@ def test_backup_app_with_no_backup_script(mocker): mocker.spy(m18n, "n") with pytest.raises(MoulinetteError): - backup_create(ignore_system=True, ignore_apps=False, apps=["backup_recommended_app"]) + backup_create(system=None, apps=["backup_recommended_app"]) m18n.n.assert_any_call("backup_with_no_backup_script_for_app", app="backup_recommended_app") m18n.n.assert_any_call('backup_nothings_done') @@ -359,7 +357,7 @@ def test_backup_app_with_no_restore_script(mocker): # Backuping an app with no restore script will only display a warning to the # user... - backup_create(ignore_system=True, ignore_apps=False, apps=["backup_recommended_app"]) + backup_create(system=None, apps=["backup_recommended_app"]) m18n.n.assert_any_call("backup_with_no_restore_script_for_app", app="backup_recommended_app") @@ -368,7 +366,7 @@ def test_backup_app_with_no_restore_script(mocker): def test_backup_with_different_output_directory(): # Create the backup - backup_create(ignore_system=False, ignore_apps=True, system=["conf_ssh"], + backup_create(system=["conf_ssh"], output_directory="/opt/test_backup_output_directory", name="backup") @@ -385,7 +383,7 @@ def test_backup_with_different_output_directory(): @pytest.mark.clean_opt_dir def test_backup_with_no_compress(): # Create the backup - backup_create(ignore_system=False, ignore_apps=True, system=["conf_nginx"], + backup_create(system=["conf_nginx"], output_directory="/opt/test_backup_output_directory", no_compress=True, name="backup") @@ -400,9 +398,7 @@ def test_backup_with_no_compress(): @pytest.mark.with_wordpress_archive_from_2p4 def test_restore_app_wordpress_from_Ynh2p4(): - backup_restore(auth, name=backup_list()["archives"][0], - ignore_system=True, - ignore_apps=False, + backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["wordpress"]) @@ -420,9 +416,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker): assert not _is_installed("wordpress") with pytest.raises(MoulinetteError): - backup_restore(auth, name=backup_list()["archives"][0], - ignore_system=True, - ignore_apps=False, + backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_app_failed', app='wordpress') @@ -443,9 +437,7 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker): assert not _is_installed("wordpress") with pytest.raises(MoulinetteError): - backup_restore(auth, name=backup_list()["archives"][0], - ignore_system=True, - ignore_apps=False, + backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_not_enough_disk_space', @@ -464,9 +456,7 @@ def test_restore_app_not_in_backup(mocker): mocker.spy(m18n, "n") with pytest.raises(MoulinetteError): - backup_restore(auth, name=backup_list()["archives"][0], - ignore_system=True, - ignore_apps=False, + backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["yoloswag"]) m18n.n.assert_any_call('backup_archive_app_not_found', app="yoloswag") @@ -479,18 +469,14 @@ def test_restore_app_already_installed(mocker): assert not _is_installed("wordpress") - backup_restore(auth, name=backup_list()["archives"][0], - ignore_system=True, - ignore_apps=False, + backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["wordpress"]) assert _is_installed("wordpress") mocker.spy(m18n, "n") with pytest.raises(MoulinetteError): - backup_restore(auth, name=backup_list()["archives"][0], - ignore_system=True, - ignore_apps=False, + backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["wordpress"]) m18n.n.assert_any_call('restore_already_installed_app', app="wordpress") @@ -520,7 +506,7 @@ def test_backup_and_restore_with_ynh_restore(): def _test_backup_and_restore_app(app): # Create a backup of this app - backup_create(ignore_system=True, ignore_apps=False, apps=[app]) + backup_create(system=None, apps=[app]) archives = backup_list()["archives"] assert len(archives) == 1 @@ -535,8 +521,8 @@ def _test_backup_and_restore_app(app): assert not app_is_installed(app) # Restore the app - backup_restore(auth, name=archives[0], ignore_system=True, - ignore_apps=False, apps=[app]) + backup_restore(auth, system=None, name=archives[0], + apps=[app]) assert app_is_installed(app) @@ -554,8 +540,7 @@ def test_restore_archive_with_no_json(mocker): mocker.spy(m18n, "n") with pytest.raises(MoulinetteError): - backup_restore(auth, name="badbackup", force=True, - ignore_system=False, ignore_apps=False) + backup_restore(auth, name="badbackup", force=True) m18n.n.assert_any_call('backup_invalid_archive') @@ -565,9 +550,10 @@ def test_backup_binds_are_readonly(monkeypatch): self.manager = backup_manager self._organize_files() + confssh = os.path.join(self.work_dir, "conf/ssh") output = subprocess.check_output("touch %s/test 2>&1 || true" % confssh, - shell=True) + shell=True, env={'LANG' : 'en_US.UTF-8'}) assert "Read-only file system" in output @@ -580,4 +566,4 @@ def test_backup_binds_are_readonly(monkeypatch): custom_mount_and_backup) # Create the backup - backup_create(ignore_system=False, ignore_apps=True) + backup_create(system=[]) From 1f13676d06717dbaa38530df0e4430d476be6f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Fri, 7 Dec 2018 23:14:57 +0100 Subject: [PATCH 042/104] Fix LDAP authenticator after backup --- src/yunohost/tests/test_changeurl.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/yunohost/tests/test_changeurl.py b/src/yunohost/tests/test_changeurl.py index 737b68a6d..36d8028c9 100644 --- a/src/yunohost/tests/test_changeurl.py +++ b/src/yunohost/tests/test_changeurl.py @@ -19,8 +19,10 @@ maindomain = _get_maindomain() def setup_function(function): - pass - + # For some reason the nginx reload can take some time to propagate + time.sleep(1) + global auth + auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) def teardown_function(function): app_remove(auth, "change_url_app") From f93aa40c2e2ed2a071ce2556bfac08ea5712c959 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 8 Dec 2018 00:18:02 +0100 Subject: [PATCH 043/104] Use 127.0.0.1 + domain header for local HTTP requests --- src/yunohost/tests/test_changeurl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/tests/test_changeurl.py b/src/yunohost/tests/test_changeurl.py index 36d8028c9..13f8aca5d 100644 --- a/src/yunohost/tests/test_changeurl.py +++ b/src/yunohost/tests/test_changeurl.py @@ -40,7 +40,7 @@ def check_changeurl_app(path): assert appmap[maindomain][path + "/"]["id"] == "change_url_app" - r = requests.get("https://%s%s/" % (maindomain, path), verify=False) + r = requests.get("https://127.0.0.1%s/" % path, headers={"domain": maindomain}, verify=False) assert r.status_code == 200 From c2631d74cde608e3a060637391d34bf4ac05e411 Mon Sep 17 00:00:00 2001 From: nqb Date: Sat, 8 Dec 2018 04:39:30 +0100 Subject: [PATCH 044/104] fix change quotes around CAA value for letsencrypt.org --- src/yunohost/domain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index dd2eda4a3..7b387618a 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -340,7 +340,7 @@ def _build_dns_conf(domain, ttl=3600): {"type": "TXT", "name": "_dmarc", "value": "\"v=DMARC1; p=none\"", "ttl": 3600} ], "extra": [ - {"type": "CAA", "name": "@", "value": "128 issue 'letsencrypt.org'", "ttl": 3600}, + {"type": "CAA", "name": "@", "value": "128 issue \"letsencrypt.org\"", "ttl": 3600}, ], } """ @@ -397,7 +397,7 @@ def _build_dns_conf(domain, ttl=3600): # Extra extra = [ - ["@", ttl, "CAA", "128 issue 'letsencrypt.org'"] + ["@", ttl, "CAA", '128 issue "letsencrypt.org"'] ] return { From 830341a687e2e0aeac3304f8508c20fc1b47f9c4 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 9 Dec 2018 19:27:44 +0100 Subject: [PATCH 045/104] [enh] Display date correctly --- data/actionsmap/yunohost.yml | 5 ----- debian/control | 2 +- src/yunohost/app.py | 14 ++++++-------- src/yunohost/log.py | 13 ++++++++++--- src/yunohost/monitor.py | 2 +- src/yunohost/service.py | 17 ++++++++--------- 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index a6bc8ec3e..ba5f1d505 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -470,11 +470,6 @@ app: listlists: action_help: List registered application lists api: GET /appslists - arguments: - -H: - full: --human-readable - help: Return the lastUpdate with a human readable date - action: store_true ### app_removelist() removelist: diff --git a/debian/control b/debian/control index 9f72bf11a..f0423c4a3 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Package: yunohost Architecture: all Depends: ${python:Depends}, ${misc:Depends} , moulinette (>= 2.7.1), ssowat (>= 2.7.1) - , python-psutil, python-requests, python-dnspython, python-openssl + , python-psutil, python-requests, python-dnspython, python-openssl, python-tz , python-apt, python-miniupnpc, python-dbus, python-jinja2 , glances , dnsutils, bind9utils, unzip, git, curl, cron, wget, jq diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 66d83db9c..d5121a418 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -37,6 +37,7 @@ import pwd import grp from collections import OrderedDict import datetime +import pytz from moulinette import msignals, m18n, msettings from moulinette.core import MoulinetteError @@ -67,7 +68,7 @@ re_app_instance_name = re.compile( ) -def app_listlists(human_readable=False): +def app_listlists(): """ List fetched lists @@ -84,13 +85,10 @@ def app_listlists(human_readable=False): # Get the list appslist_list = _read_appslist_list() - # Human readable date - if human_readable: - for app in appslist_list: - now_for_humans = datetime.datetime.fromtimestamp( - appslist_list[app].get("lastUpdate")) - appslist_list[app]["lastUpdate"] = now_for_humans.strftime( - '%Y-%m-%d %H:%M:%S') + for app in appslist_list: + last_update = datetime.datetime.utcfromtimestamp( + appslist_list[app].get("lastUpdate")) + appslist_list[app]["lastUpdate"] = last_update.replace(tzinfo=pytz.utc) return appslist_list diff --git a/src/yunohost/log.py b/src/yunohost/log.py index c105b8279..c75300d3d 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -28,6 +28,7 @@ import os import yaml import errno import collections +import pytz from datetime import datetime from logging import FileHandler, getLogger, Formatter @@ -100,7 +101,7 @@ def log_list(category=[], limit=None): except ValueError: pass else: - entry["started_at"] = log_datetime + entry["started_at"] = log_datetime.replace(tzinfo=pytz.utc) result[category].append(entry) @@ -182,6 +183,10 @@ def log_display(path, number=50, share=False): metadata = yaml.safe_load(md_file) infos['metadata_path'] = md_path infos['metadata'] = metadata + if 'started_at' in infos['metadata']: + infos['metadata']['started_at'] = infos['metadata']['started_at'].replace(tzinfo=pytz.utc) + if 'ended_at' in infos['metadata']: + infos['metadata']['ended_at'] = infos['metadata']['ended_at'].replace(tzinfo=pytz.utc) if 'log_path' in metadata: log_path = metadata['log_path'] except yaml.YAMLError: @@ -316,7 +321,8 @@ class OperationLogger(object): """ if self.started_at is None: - self.started_at = datetime.now() + self.started_at = datetime.utcnow() + self.started_at = self.started_at.replace(tzinfo=pytz.utc) self.flush() self._register_log() @@ -408,7 +414,8 @@ class OperationLogger(object): return if error is not None and not isinstance(error, basestring): error = str(error) - self.ended_at = datetime.now() + self.ended_at = datetime.utcnow() + self.ended_at = self.ended_at.replace(tzinfo=pytz.utc) self._error = error self._success = error is None if self.logger is not None: diff --git a/src/yunohost/monitor.py b/src/yunohost/monitor.py index fc10a4fbc..cad467ed5 100644 --- a/src/yunohost/monitor.py +++ b/src/yunohost/monitor.py @@ -283,7 +283,7 @@ def monitor_system(units=None, human_readable=False): elif u == 'process': result[u] = json.loads(glances.getProcessCount()) elif u == 'uptime': - result[u] = (str(datetime.now() - datetime.fromtimestamp(psutil.boot_time())).split('.')[0]) + result[u] = (str(datetime.utcnow() - datetime.utcfromtimestamp(psutil.boot_time())).split('.')[0]) elif u == 'infos': result[u] = json.loads(glances.getSystem()) else: diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 5b7680a80..73383cb44 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -31,6 +31,7 @@ import subprocess import errno import shutil import hashlib +import pytz from difflib import unified_diff from datetime import datetime @@ -248,10 +249,7 @@ def service_status(names=[]): 'status': "unknown", 'loaded': "unknown", 'active': "unknown", - 'active_at': { - "timestamp": "unknown", - "human": "unknown", - }, + 'active_at': "unknown", 'description': "Error: failed to get information for this service, it doesn't exists for systemd", 'service_file_path': "unknown", } @@ -273,13 +271,14 @@ def service_status(names=[]): 'status': str(status.get("SubState", "unknown")), 'loaded': "enabled" if str(status.get("LoadState", "unknown")) == "loaded" else str(status.get("LoadState", "unknown")), 'active': str(status.get("ActiveState", "unknown")), - 'active_at': { - "timestamp": str(status.get("ActiveEnterTimestamp", "unknown")), - "human": datetime.fromtimestamp(status["ActiveEnterTimestamp"] / 1000000).strftime("%F %X") if "ActiveEnterTimestamp" in status else "unknown", - }, 'description': description, 'service_file_path': str(status.get("FragmentPath", "unknown")), } + if "ActiveEnterTimestamp" in status: + result[name]['active_at'] = datetime.utcfromtimestamp(status["ActiveEnterTimestamp"] / 1000000) + result[name]['active_at'] = result[name]['active_at'].replace(tzinfo=pytz.utc) + else: + result[name]['active_at'] = "unknown" if len(names) == 1: return result[names[0]] @@ -293,7 +292,7 @@ def _get_service_information_from_systemd(service): d = dbus.SystemBus() - systemd = d.get_object('org.freedesktop.systemd1','/org/freedesktop/systemd1') + systemd = d.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1') manager = dbus.Interface(systemd, 'org.freedesktop.systemd1.Manager') try: From c417c628948e1bbe928fd213e13fec0d2d554ce8 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 9 Dec 2018 19:35:38 +0100 Subject: [PATCH 046/104] [fix] Bad comment --- src/yunohost/app.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index d5121a418..e107d7ac9 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -72,9 +72,6 @@ def app_listlists(): """ List fetched lists - Keyword argument: - human_readable -- Show human readable dates - """ # Migrate appslist system if needed From 7754f2722a9eb3b1aace286fb6ccdc9f97c9983b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 3 Dec 2018 14:02:12 +0100 Subject: [PATCH 047/104] Return instead of break, otherwise warning is shown --- data/helpers.d/package | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/package b/data/helpers.d/package index 22adb9b15..8b672d701 100644 --- a/data/helpers.d/package +++ b/data/helpers.d/package @@ -15,7 +15,7 @@ ynh_wait_dpkg_free() { # Sleep an exponential time at each round sleep $(( try * try )) else - break + return 0 fi done echo "apt still used, but timeout reached !" From cd3ed6af6fa6e590ef3736ed61c7d3962740a9ef Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 7 Dec 2018 19:12:47 +0100 Subject: [PATCH 048/104] Explicit root password change each time admin password is changed --- locales/en.json | 2 +- .../data_migrations/0006_sync_admin_and_root_passwords.py | 2 +- src/yunohost/tools.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index 6ce22ca80..6a2852f80 100644 --- a/locales/en.json +++ b/locales/en.json @@ -291,7 +291,6 @@ "migration_0005_postgresql_96_not_installed": "Postgresql 9.4 has been found to be installed, but not postgresql 9.6 !? Something weird might have happened on your system :( ...", "migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now :(.", "migration_0006_disclaimer": "Yunohost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.", - "migration_0006_done": "Your root password have been replaced by your admin password.", "migrations_backward": "Migrating backward.", "migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}", "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", @@ -375,6 +374,7 @@ "restore_running_hooks": "Running restoration hooks...", "restore_system_part_failed": "Unable to restore the '{part:s}' system part", "root_password_desynchronized": "The admin password has been changed, but YunoHost was unable to propagate this on the root password !", + "root_password_replaced_by_admin_password": "Your root password have been replaced by your admin password.", "server_shutdown": "The server will shutdown", "server_shutdown_confirm": "The server will shutdown immediatly, are you sure? [{answers:s}]", "server_reboot": "The server will reboot", 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 index 366363f22..ee3aeefcb 100644 --- a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py +++ b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py @@ -23,7 +23,7 @@ class MyMigration(Migration): new_hash = self._get_admin_hash() self._replace_root_hash(new_hash) - logger.info(m18n.n("migration_0006_done")) + logger.info(m18n.n("root_password_replaced_by_admin_password")) def backward(self): pass diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index baa614fa5..fea1f8398 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -155,6 +155,8 @@ def tools_adminpw(auth, new_password, check_strength=True): except IOError as e: logger.warning(m18n.n('root_password_desynchronized')) return + + logger.info(m18n.n("root_password_replaced_by_admin_password")) logger.success(m18n.n('admin_password_changed')) From ccd9c1631ea30f4314681662e5ab91f842548f62 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 18:27:18 +0100 Subject: [PATCH 049/104] Skip migrations one at a time --- src/yunohost/tools.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index fea1f8398..fce2f1569 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -940,6 +940,10 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai operation_logger.success() + # Skip migrations one at a time + if skip: + break + # special case where we want to go back from the start if target == 0: state["last_run_migration"] = None From 8c3905c5d3de183227d9836c753b7fbdf64c261d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 18:33:39 +0100 Subject: [PATCH 050/104] Have a function to initialize migrations --- src/yunohost/tools.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index fce2f1569..ebb9516f1 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -434,7 +434,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, _install_appslist_fetch_cron() # Init migrations (skip them, no need to run them on a fresh system) - tools_migrations_migrate(skip=True, auto=True) + _skip_all_migrations() os.system('touch /etc/yunohost/installed') @@ -950,7 +950,6 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai write_to_json(MIGRATIONS_STATE_PATH, state) - def tools_migrations_state(): """ Show current migration state @@ -1050,6 +1049,25 @@ def _load_migration(migration_file): raise MoulinetteError(errno.EINVAL, m18n.n('migrations_error_failed_to_load_migration', number=number, name=name)) +def _skip_all_migrations(): + """ + Skip all pending migrations. + This is meant to be used during postinstall to + initialize the migration system. + """ + state = tools_migrations_state() + + # load all migrations + migrations = _get_migrations_list() + migrations = sorted(migrations, key=lambda x: x.number) + last_migration = migrations[-1] + + state["last_run_migration"] = { + "number": last_migration.number, + "name": last_migration.name + } + write_to_json(MIGRATIONS_STATE_PATH, state) + class Migration(object): From fee5b1efa108e705451d7eeb05ba28715ccc79d1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 22:10:06 +0100 Subject: [PATCH 051/104] Add success message after running migrations --- locales/en.json | 1 + src/yunohost/tools.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/locales/en.json b/locales/en.json index 6a2852f80..bc90c93a6 100644 --- a/locales/en.json +++ b/locales/en.json @@ -304,6 +304,7 @@ "migrations_show_currently_running_migration": "Running migration {number} {name}...", "migrations_show_last_migration": "Last ran migration is {}", "migrations_skip_migration": "Skipping migration {number} {name}...", + "migrations_success": "Successfully ran migration {number} {name}!", "migrations_to_be_ran_manually": "Migration {number} {name} has to be ran manually. Please go to Tools > Migrations on the webadmin, or run `yunohost tools migrations migrate`.", "migrations_need_to_accept_disclaimer": "To run the migration {number} {name}, 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.", "monitor_disabled": "The server monitoring has been disabled", diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index ebb9516f1..42e5cd690 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -926,6 +926,9 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai logger.error(msg, exc_info=1) operation_logger.error(msg) break + else: + logger.success(m18n.n('migrations_success', + number=migration.number, name=migration.name)) else: # if skip logger.warn(m18n.n('migrations_skip_migration', From 3d5cb7e3d17ca8e9d4e3cecba1b017323bb21dc4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 22:11:06 +0100 Subject: [PATCH 052/104] This message should be info --- 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 42e5cd690..63863a57f 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -905,7 +905,7 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai if not skip: - logger.warn(m18n.n('migrations_show_currently_running_migration', + logger.info(m18n.n('migrations_show_currently_running_migration', number=migration.number, name=migration.name)) try: From 34c3968501bf4131e4472c45a4dd337c5f77e5d7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 6 Dec 2018 18:24:53 +0100 Subject: [PATCH 053/104] Manage migration of auto/manual and disclaimer on a per-migration basis --- src/yunohost/tools.py | 47 +++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 63863a57f..78e641189 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -874,31 +874,34 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai else: # can't happen, this case is handle before raise Exception() - # If we are migrating in "automatic mode" (i.e. from debian - # configure during an upgrade of the package) but we are asked to run - # migrations is to be ran manually by the user - manual_migrations = [m for m in migrations if m.mode == "manual"] - if not skip and auto and manual_migrations: - for m in manual_migrations: - logger.warn(m18n.n('migrations_to_be_ran_manually', - number=m.number, - name=m.name)) - return - - # If some migrations have disclaimers, require the --accept-disclaimer - # option - migrations_with_disclaimer = [m for m in migrations if m.disclaimer] - if not skip and not accept_disclaimer and migrations_with_disclaimer: - for m in migrations_with_disclaimer: - logger.warn(m18n.n('migrations_need_to_accept_disclaimer', - number=m.number, - name=m.name, - disclaimer=m.disclaimer)) - return - # effectively run selected migrations for migration in migrations: + if not skip: + # If we are migrating in "automatic mode" (i.e. from debian configure + # during an upgrade of the package) but we are asked to run migrations + # to be ran manually by the user, stop there and ask the user to + # run the migration manually. + if auto and migration.mode == "manual": + logger.warn(m18n.n('migrations_to_be_ran_manually', + number=migration.number, + name=migration.name)) + break + + # If some migrations have disclaimers, + if migration.disclaimer: + # require the --accept-disclaimer option. Otherwise, stop everything + # here and display the disclaimer + if not accept_disclaimer: + logger.warn(m18n.n('migrations_need_to_accept_disclaimer', + number=migration.number, + name=migration.name, + disclaimer=migration.disclaimer)) + break + # --accept-disclaimer will only work for the first migration + else: + accept_disclaimer = False + # Start register change on system operation_logger= OperationLogger('tools_migrations_migrate_' + mode) operation_logger.start() From b71ee3a9a0d89fd6405c78308c74c2856cc01d05 Mon Sep 17 00:00:00 2001 From: nqb Date: Sat, 8 Dec 2018 04:39:30 +0100 Subject: [PATCH 054/104] fix change quotes around CAA value for letsencrypt.org --- src/yunohost/domain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index dd2eda4a3..7b387618a 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -340,7 +340,7 @@ def _build_dns_conf(domain, ttl=3600): {"type": "TXT", "name": "_dmarc", "value": "\"v=DMARC1; p=none\"", "ttl": 3600} ], "extra": [ - {"type": "CAA", "name": "@", "value": "128 issue 'letsencrypt.org'", "ttl": 3600}, + {"type": "CAA", "name": "@", "value": "128 issue \"letsencrypt.org\"", "ttl": 3600}, ], } """ @@ -397,7 +397,7 @@ def _build_dns_conf(domain, ttl=3600): # Extra extra = [ - ["@", ttl, "CAA", "128 issue 'letsencrypt.org'"] + ["@", ttl, "CAA", '128 issue "letsencrypt.org"'] ] return { From bda028f2b25928840b5c9d9fb4b38456451ff67b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 9 Dec 2018 19:59:05 +0000 Subject: [PATCH 055/104] Update changelog for 3.3.3 --- debian/changelog | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/debian/changelog b/debian/changelog index 084d7f096..3abd967e1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,17 +1,19 @@ +yunohost (3.3.3) stable; urgency=low + + * [fix] ynh_wait_dpkg_free displaying a warning despite everything being okay (#593) + * [fix] Quotes for recommended CAA DNS record (#596) + * [fix] Manual migration and disclaimer behaviors (#594) + * [fix] Explicit root password change each time admin password is changed + + -- Alexandre Aubin Sun, 09 Dec 2018 20:58:00 +0000 + yunohost (3.3.2) stable; urgency=low * [fix] Regen nginx conf to be sure it integrates OCSP Stapling (#588) * [fix] Broken new settings and options to control passwords checks / constrains (#589) * [fix] Log dyndns update only if we really update something (#591) - -- Alexandre Aubin Sun, 02 Dev 2018 17:23:00 +0000 - -yunohost (3.3.2) stable; urgency=low - - * [fix] Log dyndns update only if we really update something (#591) - * [fix] Broken new settings and options to control passwords checks / constrains (#589) - - -- Alexandre Aubin Sun, 02 Dev 2018 17:17:00 +0000 + -- Alexandre Aubin Sun, 02 Dec 2018 17:23:00 +0000 yunohost (3.3.1) stable; urgency=low From 60843edc0f6bd6d59bb785f620c95958d71f0108 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 9 Dec 2018 21:08:36 +0100 Subject: [PATCH 056/104] [enh] Use aware UTC date in backup settings and app --- src/yunohost/app.py | 3 +-- src/yunohost/backup.py | 6 ++++-- src/yunohost/settings.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index e107d7ac9..0664a24b2 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1762,8 +1762,7 @@ def _get_app_status(app_id, format_date=False): if not v: status[f] = '-' else: - status[f] = time.strftime(m18n.n('format_datetime_short'), - time.gmtime(v)) + status[f] = datetime.utcfromtimestamp(v).replace(tzinfo=pytz.utc) return status diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 6ef9a0554..492687eaf 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -33,6 +33,8 @@ import shutil import subprocess import csv import tempfile +import pytz +from datetime import datetime from glob import glob from collections import OrderedDict @@ -2266,8 +2268,8 @@ def backup_info(name, with_details=False, human_readable=False): result = { 'path': archive_file, - 'created_at': time.strftime(m18n.n('format_datetime_short'), - time.gmtime(info['created_at'])), + 'created_at': datetime.utcfromtimestamp(info['created_at']) + .replace(tzinfo=pytz.utc), 'description': info['description'], 'size': size, } diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index d2526316e..3227ad3e3 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -154,7 +154,7 @@ def settings_reset_all(): # addition but we'll see if this is a common need. # Another solution would be to use etckeeper and integrate those # modification inside of it and take advantage of its git history - old_settings_backup_path = SETTINGS_PATH_OTHER_LOCATION % datetime.now().strftime("%F_%X") + old_settings_backup_path = SETTINGS_PATH_OTHER_LOCATION % datetime.utcnow().strftime("%F_%X") _save_settings(settings, location=old_settings_backup_path) for value in settings.values(): From e43bcf253cef4137120e0fc8faa498ebc015a020 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 9 Dec 2018 21:24:59 +0100 Subject: [PATCH 057/104] [fix] Avoid -1 remaining_days --- src/yunohost/certificate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 8da6ab52a..9975a349f 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -154,7 +154,7 @@ def _certificate_install_selfsigned(domain_list, force=False): args={'force': force}) # Paths of files and folder we'll need - date_tag = datetime.now().strftime("%Y%m%d.%H%M%S") + date_tag = datetime.utcnow().strftime("%Y%m%d.%H%M%S") new_cert_folder = "%s/%s-history/%s-selfsigned" % ( CERT_FOLDER, domain, date_tag) @@ -587,7 +587,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): logger.debug("Saving the key and signed certificate...") # Create corresponding directory - date_tag = datetime.now().strftime("%Y%m%d.%H%M%S") + date_tag = datetime.utcnow().strftime("%Y%m%d.%H%M%S") if staging: folder_flag = "staging" @@ -674,7 +674,7 @@ def _get_status(domain): cert_subject = cert.get_subject().CN cert_issuer = cert.get_issuer().CN valid_up_to = datetime.strptime(cert.get_notAfter(), "%Y%m%d%H%M%SZ") - days_remaining = (valid_up_to - datetime.now()).days + days_remaining = (valid_up_to - datetime.utcnow()).days if cert_issuer == _name_self_CA(): CA_type = { @@ -816,7 +816,7 @@ def _backup_current_cert(domain): cert_folder_domain = os.path.join(CERT_FOLDER, domain) - date_tag = datetime.now().strftime("%Y%m%d.%H%M%S") + date_tag = datetime.utcnow().strftime("%Y%m%d.%H%M%S") backup_folder = "%s-backups/%s" % (cert_folder_domain, date_tag) shutil.copytree(cert_folder_domain, backup_folder) From fa5a226339964d3ee0489ebfaecee83b67a0e82a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 9 Dec 2018 21:26:37 +0100 Subject: [PATCH 058/104] Add post_cert_update hook each time certificate is updated (#586) --- src/yunohost/certificate.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 8da6ab52a..adb546329 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -810,6 +810,9 @@ def _enable_certificate(domain, new_cert_folder): _run_service_command("reload", "nginx") + from yunohost.hook import hook_callback + hook_callback('post_cert_update', args=[domain]) + def _backup_current_cert(domain): logger.debug("Backuping existing certificate for domain %s", domain) From 08818757cc356d5fd3da9d70a4cd49c66934759d Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Sun, 9 Dec 2018 21:27:43 +0100 Subject: [PATCH 059/104] [enh] Do not fail on backup and restore if some missing files are not mandatory (#576) --- data/helpers.d/filesystem | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/data/helpers.d/filesystem b/data/helpers.d/filesystem index badc0e997..b5ae36a75 100644 --- a/data/helpers.d/filesystem +++ b/data/helpers.d/filesystem @@ -10,12 +10,13 @@ CAN_BIND=${CAN_BIND:-1} # # If DEST is ended by a slash it complete this path with the basename of SRC. # -# usage: ynh_backup src [dest [is_big [arg]]] +# usage: ynh_backup src [dest [is_big [not_mandatory [arg]]]] # | arg: src - file or directory to bind or symlink or copy. it shouldn't be in # the backup dir. # | arg: dest - destination file or directory inside the # backup dir # | arg: is_big - 1 to indicate data are big (mail, video, image ...) +# | arg: not_mandatory - 1 to indicate that if the file is missing, the backup can ignore it. # | arg: arg - Deprecated arg # # example: @@ -46,6 +47,7 @@ ynh_backup() { local SRC_PATH="$1" local DEST_PATH="${2:-}" local IS_BIG="${3:-0}" + local NOT_MANDATORY="${4:-0}" BACKUP_CORE_ONLY=${BACKUP_CORE_ONLY:-0} # If backing up core only (used by ynh_backup_before_upgrade), @@ -60,15 +62,17 @@ ynh_backup() { # ============================================================================== # Be sure the source path is not empty [[ -e "${SRC_PATH}" ]] || { - echo "!!! Source path '${SRC_PATH}' does not exist !!!" >&2 - - # This is a temporary fix for fail2ban config files missing after the migration to stretch. - if echo "${SRC_PATH}" | grep --quiet "/etc/fail2ban" + if [ "$NOT_MANDATORY" == "0" ] then - touch "${SRC_PATH}" - echo "The missing file will be replaced by a dummy one for the backup !!!" >&2 + # This is a temporary fix for fail2ban config files missing after the migration to stretch. + if echo "${SRC_PATH}" | grep --quiet "/etc/fail2ban" + then + touch "${SRC_PATH}" + echo "The missing file will be replaced by a dummy one for the backup !!!" >&2 + else + return 1 else - return 1 + return 0 fi } @@ -176,12 +180,13 @@ with open(sys.argv[1], 'r') as backup_file: # Use the registered path in backup_list by ynh_backup to restore the file at # the good place. # -# usage: ynh_restore_file ORIGIN_PATH [ DEST_PATH ] +# usage: ynh_restore_file ORIGIN_PATH [ DEST_PATH [NOT_MANDATORY]] # | arg: ORIGIN_PATH - Path where was located the file or the directory before # to be backuped or relative path to $YNH_CWD where it is located in the backup archive # | arg: DEST_PATH - Path where restore the file or the dir, if unspecified, # the destination will be ORIGIN_PATH or if the ORIGIN_PATH doesn't exist in # the archive, the destination will be searched into backup.csv +# | arg: NOT_MANDATORY - 1 to indicate that if the file is missing, the restore process can ignore it. # # If DEST_PATH already exists and is lighter than 500 Mo, a backup will be made in # /home/yunohost.conf/backup/. Otherwise, the existing file is removed. @@ -201,10 +206,16 @@ ynh_restore_file () { local ARCHIVE_PATH="$YNH_CWD${ORIGIN_PATH}" # Default value for DEST_PATH = /$ORIGIN_PATH local DEST_PATH="${2:-$ORIGIN_PATH}" + local NOT_MANDATORY="${3:-0}" # If ARCHIVE_PATH doesn't exist, search for a corresponding path in CSV if [ ! -d "$ARCHIVE_PATH" ] && [ ! -f "$ARCHIVE_PATH" ] && [ ! -L "$ARCHIVE_PATH" ]; then - ARCHIVE_PATH="$YNH_BACKUP_DIR/$(_get_archive_path \"$ORIGIN_PATH\")" + if [ "$NOT_MANDATORY" == "0" ] + then + ARCHIVE_PATH="$YNH_BACKUP_DIR/$(_get_archive_path \"$ORIGIN_PATH\")" + else + return 0 + fi fi # Move the old directory if it already exists From db1b00e706a9c4f1ea16a95b6ca3c8184f831789 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 9 Dec 2018 23:48:56 +0100 Subject: [PATCH 060/104] [enh] Use UTC naive format and manage it in moulinette --- src/yunohost/app.py | 6 ++---- src/yunohost/backup.py | 6 ++---- src/yunohost/log.py | 8 +++----- src/yunohost/monitor.py | 2 +- src/yunohost/service.py | 4 +--- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 0664a24b2..79af7faf2 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -37,7 +37,6 @@ import pwd import grp from collections import OrderedDict import datetime -import pytz from moulinette import msignals, m18n, msettings from moulinette.core import MoulinetteError @@ -83,9 +82,8 @@ def app_listlists(): appslist_list = _read_appslist_list() for app in appslist_list: - last_update = datetime.datetime.utcfromtimestamp( + appslist_list[app]["lastUpdate"] = datetime.datetime.utcfromtimestamp( appslist_list[app].get("lastUpdate")) - appslist_list[app]["lastUpdate"] = last_update.replace(tzinfo=pytz.utc) return appslist_list @@ -1762,7 +1760,7 @@ def _get_app_status(app_id, format_date=False): if not v: status[f] = '-' else: - status[f] = datetime.utcfromtimestamp(v).replace(tzinfo=pytz.utc) + status[f] = datetime.utcfromtimestamp(v) return status diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 492687eaf..528b2e24f 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -33,7 +33,6 @@ import shutil import subprocess import csv import tempfile -import pytz from datetime import datetime from glob import glob from collections import OrderedDict @@ -301,7 +300,7 @@ class BackupManager(): (string) A backup name created from current date 'YYMMDD-HHMMSS' """ # FIXME: case where this name already exist - return time.strftime('%Y%m%d-%H%M%S') + return time.strftime('%Y%m%d-%H%M%S', time.gmtime()) def _init_work_dir(self): """Initialize preparation directory @@ -2268,8 +2267,7 @@ def backup_info(name, with_details=False, human_readable=False): result = { 'path': archive_file, - 'created_at': datetime.utcfromtimestamp(info['created_at']) - .replace(tzinfo=pytz.utc), + 'created_at': datetime.utcfromtimestamp(info['created_at']), 'description': info['description'], 'size': size, } diff --git a/src/yunohost/log.py b/src/yunohost/log.py index c75300d3d..af19090c8 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -101,7 +101,7 @@ def log_list(category=[], limit=None): except ValueError: pass else: - entry["started_at"] = log_datetime.replace(tzinfo=pytz.utc) + entry["started_at"] = log_datetime result[category].append(entry) @@ -321,8 +321,7 @@ class OperationLogger(object): """ if self.started_at is None: - self.started_at = datetime.utcnow() - self.started_at = self.started_at.replace(tzinfo=pytz.utc) + self.started_at = datetime.now(tz=pytz.utc) self.flush() self._register_log() @@ -414,8 +413,7 @@ class OperationLogger(object): return if error is not None and not isinstance(error, basestring): error = str(error) - self.ended_at = datetime.utcnow() - self.ended_at = self.ended_at.replace(tzinfo=pytz.utc) + self.ended_at = datetime.now(tz=pytz.utc) self._error = error self._success = error is None if self.logger is not None: diff --git a/src/yunohost/monitor.py b/src/yunohost/monitor.py index cad467ed5..fc10a4fbc 100644 --- a/src/yunohost/monitor.py +++ b/src/yunohost/monitor.py @@ -283,7 +283,7 @@ def monitor_system(units=None, human_readable=False): elif u == 'process': result[u] = json.loads(glances.getProcessCount()) elif u == 'uptime': - result[u] = (str(datetime.utcnow() - datetime.utcfromtimestamp(psutil.boot_time())).split('.')[0]) + result[u] = (str(datetime.now() - datetime.fromtimestamp(psutil.boot_time())).split('.')[0]) elif u == 'infos': result[u] = json.loads(glances.getSystem()) else: diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 73383cb44..fd75d0235 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -31,7 +31,6 @@ import subprocess import errno import shutil import hashlib -import pytz from difflib import unified_diff from datetime import datetime @@ -276,7 +275,6 @@ def service_status(names=[]): } if "ActiveEnterTimestamp" in status: result[name]['active_at'] = datetime.utcfromtimestamp(status["ActiveEnterTimestamp"] / 1000000) - result[name]['active_at'] = result[name]['active_at'].replace(tzinfo=pytz.utc) else: result[name]['active_at'] = "unknown" @@ -926,7 +924,7 @@ def _process_regen_conf(system_conf, new_conf=None, save=True): """ if save: backup_path = os.path.join(BACKUP_CONF_DIR, '{0}-{1}'.format( - system_conf.lstrip('/'), time.strftime("%Y%m%d.%H%M%S"))) + system_conf.lstrip('/'), datetime.utcnow().strftime("%Y%m%d.%H%M%S"))) backup_dir = os.path.dirname(backup_path) if not os.path.isdir(backup_dir): From cafd0dd884d9a10ea761efdef7bb1a1938082092 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 9 Dec 2018 23:55:51 +0100 Subject: [PATCH 061/104] [enh] Remove pytz --- debian/control | 2 +- src/yunohost/log.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/debian/control b/debian/control index f0423c4a3..9f72bf11a 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ Package: yunohost Architecture: all Depends: ${python:Depends}, ${misc:Depends} , moulinette (>= 2.7.1), ssowat (>= 2.7.1) - , python-psutil, python-requests, python-dnspython, python-openssl, python-tz + , python-psutil, python-requests, python-dnspython, python-openssl , python-apt, python-miniupnpc, python-dbus, python-jinja2 , glances , dnsutils, bind9utils, unzip, git, curl, cron, wget, jq diff --git a/src/yunohost/log.py b/src/yunohost/log.py index af19090c8..f11c587d1 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -28,7 +28,6 @@ import os import yaml import errno import collections -import pytz from datetime import datetime from logging import FileHandler, getLogger, Formatter @@ -184,9 +183,9 @@ def log_display(path, number=50, share=False): infos['metadata_path'] = md_path infos['metadata'] = metadata if 'started_at' in infos['metadata']: - infos['metadata']['started_at'] = infos['metadata']['started_at'].replace(tzinfo=pytz.utc) + infos['metadata']['started_at'] = infos['metadata']['started_at'] if 'ended_at' in infos['metadata']: - infos['metadata']['ended_at'] = infos['metadata']['ended_at'].replace(tzinfo=pytz.utc) + infos['metadata']['ended_at'] = infos['metadata']['ended_at'] if 'log_path' in metadata: log_path = metadata['log_path'] except yaml.YAMLError: @@ -321,7 +320,7 @@ class OperationLogger(object): """ if self.started_at is None: - self.started_at = datetime.now(tz=pytz.utc) + self.started_at = datetime.utcnow() self.flush() self._register_log() @@ -413,7 +412,7 @@ class OperationLogger(object): return if error is not None and not isinstance(error, basestring): error = str(error) - self.ended_at = datetime.now(tz=pytz.utc) + self.ended_at = datetime.utcnow() self._error = error self._success = error is None if self.logger is not None: From 7732136b7508a3056ae79a1745312f604098463a Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 9 Dec 2018 23:58:06 +0100 Subject: [PATCH 062/104] [enh] Remove unneeded code --- src/yunohost/log.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index f11c587d1..80a7a134f 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -182,10 +182,6 @@ def log_display(path, number=50, share=False): metadata = yaml.safe_load(md_file) infos['metadata_path'] = md_path infos['metadata'] = metadata - if 'started_at' in infos['metadata']: - infos['metadata']['started_at'] = infos['metadata']['started_at'] - if 'ended_at' in infos['metadata']: - infos['metadata']['ended_at'] = infos['metadata']['ended_at'] if 'log_path' in metadata: log_path = metadata['log_path'] except yaml.YAMLError: From 76be566607405618ae535e7c8884125403044cf7 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 10 Dec 2018 01:19:14 +0100 Subject: [PATCH 063/104] [fix] Bad time convesion in a debug message --- src/yunohost/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 528b2e24f..33fbccb7f 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -860,7 +860,7 @@ class RestoreManager(): raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive')) else: logger.debug("restoring from backup '%s' created on %s", self.name, - time.ctime(self.info['created_at'])) + datetime.utcfromtimestamp(self.info['created_at'])) def _postinstall_if_needed(self): """ From 96a6e370a37b0c44d46df430dd740aac67cac2b4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 10 Dec 2018 02:53:27 +0100 Subject: [PATCH 064/104] [tests] Fix some issues with maindomain changing sometimes --- src/yunohost/tests/test_backuprestore.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index c60dcb517..6dfbff9b7 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -15,7 +15,7 @@ from yunohost.domain import _get_maindomain from moulinette.core import MoulinetteError # Get main domain -maindomain = _get_maindomain() +maindomain = "" # Instantiate LDAP Authenticator AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous') @@ -24,6 +24,9 @@ auth = None def setup_function(function): + global maindomain + maindomain = _get_maindomain() + print "" global auth From d06d1cdee4b5cb62c1ec9906c972fab89b16e802 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 10 Dec 2018 03:33:40 +0100 Subject: [PATCH 065/104] Explicit that we don't backup/restore apps --- src/yunohost/tests/test_backuprestore.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 6dfbff9b7..c9d4c6b15 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -198,7 +198,7 @@ def add_archive_system_from_2p4(): def test_backup_only_ldap(): # Create the backup - backup_create(system=["conf_ldap"]) + backup_create(system=["conf_ldap"], apps=None) archives = backup_list()["archives"] assert len(archives) == 1 @@ -215,7 +215,7 @@ def test_backup_system_part_that_does_not_exists(mocker): # Create the backup with pytest.raises(MoulinetteError): - backup_create(system=["yolol"]) + backup_create(system=["yolol"], apps=None) m18n.n.assert_any_call('backup_hook_unknown', hook="yolol") m18n.n.assert_any_call('backup_nothings_done') @@ -227,7 +227,7 @@ def test_backup_system_part_that_does_not_exists(mocker): def test_backup_and_restore_all_sys(): # Create the backup - backup_create(system=[]) + backup_create(system=[], apps=None) archives = backup_list()["archives"] assert len(archives) == 1 @@ -244,7 +244,7 @@ def test_backup_and_restore_all_sys(): # Restore the backup backup_restore(auth, name=archives[0], force=True, - system=[]) + system=[], apps=None) # Check ssowat conf is back assert os.path.exists("/etc/ssowat/conf.json") @@ -258,7 +258,7 @@ def test_backup_and_restore_all_sys(): def test_restore_system_from_Ynh2p4(monkeypatch, mocker): # Backup current system - backup_create(system=[]) + backup_create(system=[], apps=None) archives = backup_list()["archives"] assert len(archives) == 2 @@ -266,11 +266,13 @@ def test_restore_system_from_Ynh2p4(monkeypatch, mocker): try: backup_restore(auth, name=backup_list()["archives"][1], system=[], + apps=None, force=True) finally: # Restore system as it was backup_restore(auth, name=backup_list()["archives"][0], system=[], + apps=None, force=True) ############################################################################### @@ -369,7 +371,7 @@ def test_backup_app_with_no_restore_script(mocker): def test_backup_with_different_output_directory(): # Create the backup - backup_create(system=["conf_ssh"], + backup_create(system=["conf_ssh"], apps=None, output_directory="/opt/test_backup_output_directory", name="backup") @@ -386,7 +388,7 @@ def test_backup_with_different_output_directory(): @pytest.mark.clean_opt_dir def test_backup_with_no_compress(): # Create the backup - backup_create(system=["conf_nginx"], + backup_create(system=["conf_nginx"], apps=None, output_directory="/opt/test_backup_output_directory", no_compress=True, name="backup") From 4d4b14b82c8eb19d5dba375379fccaa28f7de5e6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 10 Dec 2018 16:23:30 +0100 Subject: [PATCH 066/104] Change message about conf now being managed by service -> yunohost (#597) --- locales/en.json | 2 +- src/yunohost/service.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index 78df35c33..819a28008 100644 --- a/locales/en.json +++ b/locales/en.json @@ -403,7 +403,7 @@ "service_conf_file_remove_failed": "Unable to remove the configuration file '{conf}'", "service_conf_file_removed": "The configuration file '{conf}' has been removed", "service_conf_file_updated": "The configuration file '{conf}' has been updated", - "service_conf_new_managed_file": "The configuration file '{conf}' is now managed by the service {service}.", + "service_conf_now_managed_by_yunohost": "The configuration file '{conf}' is now managed by YunoHost.", "service_conf_up_to_date": "The configuration is already up-to-date for service '{service}'", "service_conf_updated": "The configuration has been updated for service '{service}'", "service_conf_would_be_updated": "The configuration would have been updated for service '{service}'", diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 9ab301933..47d53e0ac 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -508,8 +508,8 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, # we assume that it is safe to regen it, since the file is backuped # anyway (by default in _regen), as long as we warn the user # appropriately. - logger.info(m18n.n('service_conf_new_managed_file', - conf=system_path, service=service)) + logger.info(m18n.n('service_conf_now_managed_by_yunohost', + conf=system_path)) regenerated = _regen(system_path, pending_path) conf_status = 'new' elif force: From fe15e600fd2b0f2b413c19ab92a7c4edf0cb1898 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Dec 2018 02:02:11 +0000 Subject: [PATCH 067/104] Small UX improvement about link to log sharing in webadmin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 819a28008..98ae9a170 100644 --- a/locales/en.json +++ b/locales/en.json @@ -211,7 +211,7 @@ "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": "The operation '{desc}' has failed ! To get help, please provide the full log of this operation", + "log_link_to_failed_log": "The operation '{desc}' has failed ! To get help, please provide the full log of this operation by clicking here", "log_help_to_get_failed_log": "The operation '{desc}' has failed ! To get help, please share the full log of this operation using the command 'yunohost log display {name} --share'", "log_category_404": "The log category '{category}' does not exist", "log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list to see all available operation logs'", From c97582938760f43559f15383f0c483563fa09fbb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Dec 2018 15:54:50 +0100 Subject: [PATCH 068/104] [fix] Missing 'fi' as found by M. Martin --- data/helpers.d/filesystem | 1 + 1 file changed, 1 insertion(+) diff --git a/data/helpers.d/filesystem b/data/helpers.d/filesystem index b5ae36a75..5b55b752f 100644 --- a/data/helpers.d/filesystem +++ b/data/helpers.d/filesystem @@ -71,6 +71,7 @@ ynh_backup() { echo "The missing file will be replaced by a dummy one for the backup !!!" >&2 else return 1 + fi else return 0 fi From 777108d84416fb3a4a90fb06c831ebbb763669c9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Dec 2018 18:39:15 +0000 Subject: [PATCH 069/104] Ask confirmation before installing low-quality, experimental or third party apps --- data/actionsmap/yunohost.yml | 4 ++++ locales/en.json | 4 ++++ src/yunohost/app.py | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index ba5f1d505..79cc7cdaa 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -557,6 +557,10 @@ app: full: --no-remove-on-failure help: Debug option to avoid removing the app on a failed installation action: store_true + -f: + full: --force + help: Do not ask confirmation if the app is not safe to use (low quality, experimental or 3rd party) + action: store_true ### app_remove() TODO: Write help remove: diff --git a/locales/en.json b/locales/en.json index 98ae9a170..8e4e18497 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1,4 +1,5 @@ { + "aborting": "Aborting.", "action_invalid": "Invalid action '{action:s}'", "admin_password": "Administration password", "admin_password_change_failed": "Unable to change password", @@ -132,6 +133,9 @@ "certmanager_no_cert_file": "Unable to read certificate file for domain {domain:s} (file: {file:s})", "certmanager_self_ca_conf_file_not_found": "Configuration file not found for self-signing authority (file: {file:s})", "certmanager_unable_to_parse_self_CA_name": "Unable to parse name of self-signing authority (file: {file:s})", + "confirm_app_install_warning": "Warning : this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway ? [{answers:s}] ", + "confirm_app_install_danger": "WARNING ! This application is still experimental (if not explicitly not working) and it is likely to break your system ! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk ? [{answers:s}] ", + "confirm_app_install_thirdparty": "WARNING ! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk ? [{answers:s}] ", "custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}", "custom_appslist_name_required": "You must provide a name for your custom app list", "diagnosis_debian_version_error": "Can't retrieve the Debian version: {error}", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index e26700c49..e9a4b67f1 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -689,7 +689,7 @@ def app_upgrade(auth, app=[], url=None, file=None): @is_unit_operation() -def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on_failure=False): +def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on_failure=False, force=False): """ Install apps @@ -698,7 +698,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on label -- Custom name for the app args -- Serialize arguments for app installation no_remove_on_failure -- Debug option to avoid removing the app on a failed installation - + force -- Do not ask for confirmation when installing experimental / low-quality apps """ from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.log import OperationLogger @@ -718,9 +718,38 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on }, } - if app in app_list(raw=True) or ('@' in app) or ('http://' in app) or ('https://' in app): + def confirm_install(confirm): + + # Ignore if there's nothing for confirm (good quality app), if --force is used + # or if request on the API (confirm already implemented on the API side) + if confirm is None or force or msettings.get('interface') == 'api': + return + + answer = msignals.prompt(m18n.n('confirm_app_install_'+confirm, + answers='Y/N')) + if answer.upper() != "Y": + raise MoulinetteError(errno.EINVAL, m18n.n("aborting")) + + + raw_app_list = app_list(raw=True) + if app in raw_app_list or ('@' in app) or ('http://' in app) or ('https://' in app): + if app in raw_app_list: + state = raw_app_list[app].get("state", "notworking") + level = raw_app_list[app].get("level", None) + confirm = "danger" + if state in ["working", "validated"]: + if isinstance(level, int) and level >= 3: + confirm = None + elif isinstance(level, int) and level > 0: + confirm = "warning" + else: + confirm = "thirdparty" + + confirm_install(confirm) + manifest, extracted_app_folder = _fetch_app_from_git(app) elif os.path.exists(app): + confirm_install("thirdparty") manifest, extracted_app_folder = _extract_app_from_file(app) else: raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown')) From d7f25bec2ab05faa6f69a9a37f75b570eb7430f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Tue, 11 Dec 2018 20:18:23 +0100 Subject: [PATCH 070/104] Remove hack for authenticator It's solved in the moulinette with #185 --- src/yunohost/tests/test_changeurl.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/yunohost/tests/test_changeurl.py b/src/yunohost/tests/test_changeurl.py index 13f8aca5d..eb10cd604 100644 --- a/src/yunohost/tests/test_changeurl.py +++ b/src/yunohost/tests/test_changeurl.py @@ -19,10 +19,8 @@ maindomain = _get_maindomain() def setup_function(function): - # For some reason the nginx reload can take some time to propagate - time.sleep(1) - global auth - auth = init_authenticator(AUTH_IDENTIFIER, AUTH_PARAMETERS) + pass + def teardown_function(function): app_remove(auth, "change_url_app") From 3749bc9790e16db46956e938761b4b9715d124a5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 18:31:47 +0000 Subject: [PATCH 071/104] Missing locale key for new setting about DSA hostkey --- locales/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/en.json b/locales/en.json index e54e3c168..f1db76e07 100644 --- a/locales/en.json +++ b/locales/en.json @@ -197,6 +197,7 @@ "global_settings_setting_security_password_admin_strength": "Admin password strength", "global_settings_setting_security_password_user_strength": "User password strength", "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discarding it and save it in /etc/yunohost/unkown_settings.json", + "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Allow the use of (deprecated) DSA hostkey for the SSH daemon configuration", "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.", "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind 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 - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", From 0179bf2005da83483dc15426f72fa0f2a182a60b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 18:32:55 +0000 Subject: [PATCH 072/104] Fix misc stuff about utcfromtimestamp usage (tests were broken) --- src/yunohost/app.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 79af7faf2..296f21361 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -36,7 +36,7 @@ import glob import pwd import grp from collections import OrderedDict -import datetime +from datetime import datetime from moulinette import msignals, m18n, msettings from moulinette.core import MoulinetteError @@ -81,9 +81,11 @@ def app_listlists(): # Get the list appslist_list = _read_appslist_list() - for app in appslist_list: - appslist_list[app]["lastUpdate"] = datetime.datetime.utcfromtimestamp( - appslist_list[app].get("lastUpdate")) + # Convert 'lastUpdate' timestamp to datetime + for name, infos in appslist_list.items(): + if infos["lastUpdate"] is None: + infos["lastUpdate"] = 0 + infos["lastUpdate"] = datetime.utcfromtimestamp(infos["lastUpdate"]) return appslist_list From f2e184d8b29ae2e3cdbef1f9d69becaf983db647 Mon Sep 17 00:00:00 2001 From: Irina LAMBLA Date: Fri, 31 Aug 2018 18:51:16 +0200 Subject: [PATCH 073/104] moulinette MoulinetteError oneline --- src/yunohost/app.py | 36 +++++++++---------- src/yunohost/backup.py | 14 ++++---- src/yunohost/certificate.py | 2 +- .../0002_migrate_to_tsig_sha256.py | 2 +- .../0003_migrate_to_stretch.py | 10 +++--- .../0005_postgresql_9p4_to_9p6.py | 4 +-- src/yunohost/domain.py | 12 +++---- src/yunohost/dyndns.py | 6 ++-- src/yunohost/firewall.py | 8 ++--- src/yunohost/hook.py | 4 +-- src/yunohost/monitor.py | 18 +++++----- src/yunohost/service.py | 12 +++---- src/yunohost/ssh.py | 4 +-- src/yunohost/tools.py | 14 ++++---- src/yunohost/user.py | 14 ++++---- 15 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 296f21361..4637b36e3 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -212,7 +212,7 @@ def app_removelist(operation_logger, name): # Make sure we know this appslist if name not in appslists.keys(): - raise MoulinetteError(errno.ENOENT, m18n.n('appslist_unknown', appslist=name)) + raise MoulinetteError('appslist_unknown', appslist=name) operation_logger.start() @@ -459,7 +459,7 @@ def app_change_url(operation_logger, auth, app, domain, path): m18n.n('app_not_installed', app=app)) if not os.path.exists(os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url")): - raise MoulinetteError(errno.EINVAL, m18n.n("app_change_no_change_url_script", app_name=app)) + raise MoulinetteError("app_change_no_change_url_script", app_name=app) old_domain = app_setting(app, "domain") old_path = app_setting(app, "path") @@ -471,7 +471,7 @@ def app_change_url(operation_logger, auth, app, domain, path): path = normalize_url_path(path) if (domain, path) == (old_domain, old_path): - raise MoulinetteError(errno.EINVAL, m18n.n("app_change_url_identical_domains", domain=domain, path=path)) + raise MoulinetteError("app_change_url_identical_domains", domain=domain, path=path) # WARNING / FIXME : checkurl will modify the settings # (this is a non intuitive behavior that should be changed) @@ -547,7 +547,7 @@ def app_change_url(operation_logger, auth, app, domain, path): stderr=subprocess.STDOUT, shell=True).rstrip() - raise MoulinetteError(errno.EINVAL, m18n.n("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors)) + raise MoulinetteError("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors) logger.success(m18n.n("app_change_url_success", app=app, domain=domain, path=path)) @@ -573,7 +573,7 @@ def app_upgrade(auth, app=[], url=None, file=None): try: app_list() except MoulinetteError: - raise MoulinetteError(errno.ENODATA, m18n.n('app_no_upgrade')) + raise MoulinetteError('app_no_upgrade') upgraded_apps = [] @@ -684,7 +684,7 @@ def app_upgrade(auth, app=[], url=None, file=None): operation_logger.success() if not upgraded_apps: - raise MoulinetteError(errno.ENODATA, m18n.n('app_no_upgrade')) + raise MoulinetteError('app_no_upgrade') app_ssowatconf(auth) @@ -730,12 +730,12 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on elif os.path.exists(app): manifest, extracted_app_folder = _extract_app_from_file(app) else: - raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown')) + raise MoulinetteError('app_unknown') status['remote'] = manifest.get('remote', {}) # Check ID if 'id' not in manifest or '__' in manifest['id']: - raise MoulinetteError(errno.EINVAL, m18n.n('app_id_invalid')) + raise MoulinetteError('app_id_invalid') app_id = manifest['id'] @@ -1139,7 +1139,7 @@ def app_makedefault(operation_logger, auth, app, domain=None): domain = app_domain operation_logger.related_to.append(('domain',domain)) elif domain not in domain_list(auth)['domains']: - raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown')) + raise MoulinetteError('domain_unknown') operation_logger.start() if '/' in app_map(raw=True)[domain]: @@ -1262,7 +1262,7 @@ def app_register_url(auth, app, domain, path): app_label=app_label, )) - raise MoulinetteError(errno.EINVAL, m18n.n('app_location_unavailable', apps="\n".join(apps))) + raise MoulinetteError('app_location_unavailable', apps="\n".join(apps)) app_setting(app, 'domain', value=domain) app_setting(app, 'path', value=path) @@ -1300,7 +1300,7 @@ def app_checkurl(auth, url, app=None): apps_map = app_map(raw=True) if domain not in domain_list(auth)['domains']: - raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown')) + raise MoulinetteError('domain_unknown') if domain in apps_map: # Loop through apps @@ -1349,10 +1349,10 @@ def app_initdb(user, password=None, db=None, sql=None): mysql_root_pwd = open('/etc/yunohost/mysql').read().rstrip() mysql_command = 'mysql -u root -p%s -e "CREATE DATABASE %s ; GRANT ALL PRIVILEGES ON %s.* TO \'%s\'@localhost IDENTIFIED BY \'%s\';"' % (mysql_root_pwd, db, db, user, password) if os.system(mysql_command) != 0: - raise MoulinetteError(errno.EIO, m18n.n('mysql_db_creation_failed')) + raise MoulinetteError('mysql_db_creation_failed') if sql is not None: if os.system('mysql -u %s -p%s %s < %s' % (user, password, db, sql)) != 0: - raise MoulinetteError(errno.EIO, m18n.n('mysql_db_init_failed')) + raise MoulinetteError('mysql_db_init_failed') if return_pwd: return password @@ -1738,7 +1738,7 @@ def _get_app_status(app_id, format_date=False): """ app_setting_path = APPS_SETTING_PATH + app_id if not os.path.isdir(app_setting_path): - raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown')) + raise MoulinetteError('app_unknown') status = {} try: @@ -1803,7 +1803,7 @@ def _extract_app_from_file(path, remove=False): extract_result = 1 if extract_result != 0: - raise MoulinetteError(errno.EINVAL, m18n.n('app_extraction_failed')) + raise MoulinetteError('app_extraction_failed') try: extracted_app_folder = APP_TMP_FOLDER @@ -1814,7 +1814,7 @@ def _extract_app_from_file(path, remove=False): manifest = json.loads(str(json_manifest.read())) manifest['lastUpdate'] = int(time.time()) except IOError: - raise MoulinetteError(errno.EIO, m18n.n('app_install_files_invalid')) + raise MoulinetteError('app_install_files_invalid') except ValueError as e: raise MoulinetteError(errno.EINVAL, m18n.n('app_manifest_invalid', error=e.strerror)) @@ -1933,7 +1933,7 @@ def _fetch_app_from_git(app): app_info['manifest']['lastUpdate'] = app_info['lastUpdate'] manifest = app_info['manifest'] else: - raise MoulinetteError(errno.EINVAL, m18n.n('app_unknown')) + raise MoulinetteError('app_unknown') if 'git' not in app_info: raise MoulinetteError(errno.EINVAL, @@ -2312,7 +2312,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): app_label=app_label, )) - raise MoulinetteError(errno.EINVAL, m18n.n('app_location_unavailable', apps="\n".join(apps))) + raise MoulinetteError('app_location_unavailable', apps="\n".join(apps)) # (We save this normalized path so that the install script have a # standard path format to deal with no matter what the user inputted) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 33fbccb7f..d8197fed4 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -507,7 +507,7 @@ class BackupManager(): if not successfull_apps and not successfull_system: filesystem.rm(self.work_dir, True, True) - raise MoulinetteError(errno.EINVAL, m18n.n('backup_nothings_done')) + raise MoulinetteError('backup_nothings_done') # Add unlisted files from backup tmp dir self._add_to_list_to_backup('backup.csv') @@ -857,7 +857,7 @@ class RestoreManager(): self.info["system"] = self.info["hooks"] except IOError: logger.debug("unable to load '%s'", info_file, exc_info=1) - raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive')) + raise MoulinetteError('backup_invalid_archive') else: logger.debug("restoring from backup '%s' created on %s", self.name, datetime.utcfromtimestamp(self.info['created_at'])) @@ -1442,7 +1442,7 @@ class BackupMethod(object): @property def method_name(self): """Return the string name of a BackupMethod (eg "tar" or "copy")""" - raise MoulinetteError(errno.EINVAL, m18n.n('backup_abstract_method')) + raise MoulinetteError('backup_abstract_method') @property def name(self): @@ -2132,7 +2132,7 @@ def backup_restore(auth, name, system=[], apps=[], force=False): if i == 'y' or i == 'Y': force = True if not force: - raise MoulinetteError(errno.EEXIST, m18n.n('restore_failed')) + raise MoulinetteError('restore_failed') # TODO Partial app restore could not work if ldap is not restored before # TODO repair mysql if broken and it's a complete restore @@ -2159,7 +2159,7 @@ def backup_restore(auth, name, system=[], apps=[], force=False): if restore_manager.success: logger.success(m18n.n('restore_complete')) else: - raise MoulinetteError(errno.EINVAL, m18n.n('restore_nothings_done')) + raise MoulinetteError('restore_nothings_done') return restore_manager.targets.results @@ -2240,7 +2240,7 @@ def backup_info(name, with_details=False, human_readable=False): except KeyError: logger.debug("unable to retrieve '%s' inside the archive", info_file, exc_info=1) - raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive')) + raise MoulinetteError('backup_invalid_archive') else: shutil.move(os.path.join(info_dir, 'info.json'), info_file) finally: @@ -2253,7 +2253,7 @@ def backup_info(name, with_details=False, human_readable=False): info = json.load(f) except: logger.debug("unable to load '%s'", info_file, exc_info=1) - raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive')) + raise MoulinetteError('backup_invalid_archive') # Retrieve backup size size = info.get('size', 0) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 5c3bf8c9e..59b7a72c3 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -581,7 +581,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): try: intermediate_certificate = requests.get(INTERMEDIATE_CERTIFICATE_URL, timeout=30).text except requests.exceptions.Timeout as e: - raise MoulinetteError(errno.EINVAL, m18n.n('certmanager_couldnt_fetch_intermediate_cert')) + raise MoulinetteError('certmanager_couldnt_fetch_intermediate_cert') # Now save the key and signed certificate logger.debug("Saving the key and signed certificate...") diff --git a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py index 5cbc4494f..aad6771be 100644 --- a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py +++ b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py @@ -52,7 +52,7 @@ class MyMigration(Migration): 'public_key_sha512': base64.b64encode(public_key_sha512), }, timeout=30) except requests.ConnectionError: - raise MoulinetteError(errno.ENETUNREACH, m18n.n('no_internet_connection')) + raise MoulinetteError('no_internet_connection') if r.status_code != 201: try: diff --git a/src/yunohost/data_migrations/0003_migrate_to_stretch.py b/src/yunohost/data_migrations/0003_migrate_to_stretch.py index 26f91ae0b..474a26db5 100644 --- a/src/yunohost/data_migrations/0003_migrate_to_stretch.py +++ b/src/yunohost/data_migrations/0003_migrate_to_stretch.py @@ -30,7 +30,7 @@ class MyMigration(Migration): def backward(self): - raise MoulinetteError(m18n.n("migration_0003_backward_impossible")) + raise MoulinetteError("migration_0003_backward_impossible") def migrate(self): @@ -57,7 +57,7 @@ class MyMigration(Migration): self.apt_dist_upgrade(conf_flags=["old", "miss", "def"]) _run_service_command("start", "mysql") if self.debian_major_version() == 8: - raise MoulinetteError(m18n.n("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile)) + raise MoulinetteError("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile) # Specific upgrade for fail2ban... logger.info(m18n.n("migration_0003_fail2ban_upgrade")) @@ -107,11 +107,11 @@ class MyMigration(Migration): # would still be in 2.x... if not self.debian_major_version() == 8 \ and not self.yunohost_major_version() == 2: - raise MoulinetteError(m18n.n("migration_0003_not_jessie")) + raise MoulinetteError("migration_0003_not_jessie") # Have > 1 Go free space on /var/ ? if free_space_in_directory("/var/") / (1024**3) < 1.0: - raise MoulinetteError(m18n.n("migration_0003_not_enough_free_space")) + raise MoulinetteError("migration_0003_not_enough_free_space") # Check system is up to date # (but we don't if 'stretch' is already in the sources.list ... @@ -120,7 +120,7 @@ class MyMigration(Migration): self.apt_update() apt_list_upgradable = check_output("apt list --upgradable -a") if "upgradable" in apt_list_upgradable: - raise MoulinetteError(m18n.n("migration_0003_system_not_fully_up_to_date")) + raise MoulinetteError("migration_0003_system_not_fully_up_to_date") @property def disclaimer(self): diff --git a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py index 871edcd19..f03a93ef9 100644 --- a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py +++ b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py @@ -20,10 +20,10 @@ class MyMigration(Migration): return if not self.package_is_installed("postgresql-9.6"): - raise MoulinetteError(m18n.n("migration_0005_postgresql_96_not_installed")) + raise MoulinetteError("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 MoulinetteError(m18n.n("migration_0005_not_enough_space", path="/var/lib/postgresql/")) + raise MoulinetteError("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) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 7b387618a..21a7992ae 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -78,7 +78,7 @@ def domain_add(operation_logger, auth, domain, dyndns=False): try: auth.validate_uniqueness({'virtualdomain': domain}) except MoulinetteError: - raise MoulinetteError(errno.EEXIST, m18n.n('domain_exists')) + raise MoulinetteError('domain_exists') operation_logger.start() @@ -110,7 +110,7 @@ def domain_add(operation_logger, auth, domain, dyndns=False): } if not auth.add('virtualdomain=%s,ou=domains' % domain, attr_dict): - raise MoulinetteError(errno.EIO, m18n.n('domain_creation_failed')) + raise MoulinetteError('domain_creation_failed') # Don't regen these conf if we're still in postinstall if os.path.exists('/etc/yunohost/installed'): @@ -147,11 +147,11 @@ def domain_remove(operation_logger, auth, domain, force=False): from yunohost.app import app_ssowatconf if not force and domain not in domain_list(auth)['domains']: - raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown')) + raise MoulinetteError('domain_unknown') # Check domain is not the main domain if domain == _get_maindomain(): - raise MoulinetteError(errno.EINVAL, m18n.n('domain_cannot_remove_main')) + raise MoulinetteError('domain_cannot_remove_main') # Check if apps are installed on the domain for app in os.listdir('/etc/yunohost/apps/'): @@ -169,7 +169,7 @@ def domain_remove(operation_logger, auth, domain, force=False): if auth.remove('virtualdomain=' + domain + ',ou=domains') or force: os.system('rm -rf /etc/yunohost/certs/%s' % domain) else: - raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed')) + raise MoulinetteError('domain_deletion_failed') service_regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix']) app_ssowatconf(auth) @@ -246,7 +246,7 @@ def _get_conflicting_apps(auth, domain, path): # Abort if domain is unknown if domain not in domain_list(auth)['domains']: - raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown')) + raise MoulinetteError('domain_unknown') # This import cannot be put on top of file because it would create a # recursive import... diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index dd652119f..8bf2d254f 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -161,7 +161,7 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom try: r = requests.post('https://%s/key/%s?key_algo=hmac-sha512' % (subscribe_host, base64.b64encode(key)), data={'subdomain': domain}, timeout=30) except requests.ConnectionError: - raise MoulinetteError(errno.ENETUNREACH, m18n.n('no_internet_connection')) + raise MoulinetteError('no_internet_connection') if r.status_code != 201: try: error = json.loads(r.text)['error'] @@ -202,7 +202,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, keys = glob.glob('/etc/yunohost/dyndns/K{0}.+*.private'.format(domain)) if not keys: - raise MoulinetteError(errno.EIO, m18n.n('dyndns_key_not_found')) + raise MoulinetteError('dyndns_key_not_found') key = keys[0] @@ -329,7 +329,7 @@ def dyndns_removecron(): try: os.remove("/etc/cron.d/yunohost-dyndns") except: - raise MoulinetteError(errno.EIO, m18n.n('dyndns_cron_remove_failed')) + raise MoulinetteError('dyndns_cron_remove_failed') logger.success(m18n.n('dyndns_cron_removed')) diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index 0e1cf6e76..878083bc7 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -268,7 +268,7 @@ def firewall_reload(skip_upnp=False): reloaded = True if not reloaded: - raise MoulinetteError(errno.ESRCH, m18n.n('firewall_reload_failed')) + raise MoulinetteError('firewall_reload_failed') hook_callback('post_iptable_rules', args=[upnp, os.path.exists("/proc/net/if_inet6")]) @@ -338,7 +338,7 @@ def firewall_upnp(action='status', no_refresh=False): if action == 'status': no_refresh = True else: - raise MoulinetteError(errno.EINVAL, m18n.n('action_invalid', action=action)) + raise MoulinetteError('action_invalid', action=action) # Refresh port mapping using UPnP if not no_refresh: @@ -407,7 +407,7 @@ def firewall_upnp(action='status', no_refresh=False): firewall_reload(skip_upnp=True) if action == 'enable' and not enabled: - raise MoulinetteError(errno.ENXIO, m18n.n('upnp_port_open_failed')) + raise MoulinetteError('upnp_port_open_failed') return {'enabled': enabled} @@ -419,7 +419,7 @@ def firewall_stop(): """ if os.system("iptables -w -P INPUT ACCEPT") != 0: - raise MoulinetteError(errno.ESRCH, m18n.n('iptables_unavailable')) + raise MoulinetteError('iptables_unavailable') os.system("iptables -w -F") os.system("iptables -w -X") diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 46c25d50d..b7457878e 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -112,7 +112,7 @@ def hook_info(action, name): }) if not hooks: - raise MoulinetteError(errno.EINVAL, m18n.n('hook_name_unknown', name=name)) + raise MoulinetteError('hook_name_unknown', name=name) return { 'action': action, 'name': name, @@ -174,7 +174,7 @@ def hook_list(action, list_by='name', show_info=False): # Add only the name d.add(name) else: - raise MoulinetteError(errno.EINVAL, m18n.n('hook_list_by_invalid')) + raise MoulinetteError('hook_list_by_invalid') def _append_folder(d, folder): # Iterate over and add hook from a folder diff --git a/src/yunohost/monitor.py b/src/yunohost/monitor.py index fc10a4fbc..b01e36238 100644 --- a/src/yunohost/monitor.py +++ b/src/yunohost/monitor.py @@ -83,7 +83,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False): result_dname = dn if len(devices) == 0: if mountpoint is not None: - raise MoulinetteError(errno.ENODEV, m18n.n('mountpoint_unknown')) + raise MoulinetteError('mountpoint_unknown') return result # Retrieve monitoring for unit(s) @@ -141,7 +141,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False): for dname in devices_names: _set(dname, 'not-available') else: - raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown', unit=u)) + raise MoulinetteError('unit_unknown', unit=u) if result_dname is not None: return result[result_dname] @@ -237,7 +237,7 @@ def monitor_network(units=None, human_readable=False): 'gateway': gateway, } else: - raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown', unit=u)) + raise MoulinetteError('unit_unknown', unit=u) if len(units) == 1: return result[units[0]] @@ -287,7 +287,7 @@ def monitor_system(units=None, human_readable=False): elif u == 'infos': result[u] = json.loads(glances.getSystem()) else: - raise MoulinetteError(errno.EINVAL, m18n.n('unit_unknown', unit=u)) + raise MoulinetteError('unit_unknown', unit=u) if len(units) == 1 and type(result[units[0]]) is not str: return result[units[0]] @@ -303,7 +303,7 @@ def monitor_update_stats(period): """ if period not in ['day', 'week', 'month']: - raise MoulinetteError(errno.EINVAL, m18n.n('monitor_period_invalid')) + raise MoulinetteError('monitor_period_invalid') stats = _retrieve_stats(period) if not stats: @@ -321,7 +321,7 @@ def monitor_update_stats(period): else: monitor = _monitor_all(p, 0) if not monitor: - raise MoulinetteError(errno.ENODATA, m18n.n('monitor_stats_no_update')) + raise MoulinetteError('monitor_stats_no_update') stats['timestamp'].append(time.time()) @@ -386,7 +386,7 @@ def monitor_show_stats(period, date=None): """ if period not in ['day', 'week', 'month']: - raise MoulinetteError(errno.EINVAL, m18n.n('monitor_period_invalid')) + raise MoulinetteError('monitor_period_invalid') result = _retrieve_stats(period, date) if result is False: @@ -470,8 +470,8 @@ def _get_glances_api(): from yunohost.service import service_status if service_status('glances')['status'] != 'running': - raise MoulinetteError(errno.EPERM, m18n.n('monitor_not_enabled')) - raise MoulinetteError(errno.EIO, m18n.n('monitor_glances_con_failed')) + raise MoulinetteError('monitor_not_enabled') + raise MoulinetteError('monitor_glances_con_failed') def _extract_inet(string, skip_netmask=False, skip_loopback=True): diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 83c36d0f8..2c85b7047 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -85,7 +85,7 @@ def service_add(name, status=None, log=None, runlevel=None, need_lock=False, des _save_services(services) except: # we'll get a logger.warning with more details in _save_services - raise MoulinetteError(errno.EIO, m18n.n('service_add_failed', service=name)) + raise MoulinetteError('service_add_failed', service=name) logger.success(m18n.n('service_added', service=name)) @@ -103,13 +103,13 @@ def service_remove(name): try: del services[name] except KeyError: - raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=name)) + raise MoulinetteError('service_unknown', service=name) try: _save_services(services) except: # we'll get a logger.warning with more details in _save_services - raise MoulinetteError(errno.EIO, m18n.n('service_remove_failed', service=name)) + raise MoulinetteError('service_remove_failed', service=name) logger.success(m18n.n('service_removed', service=name)) @@ -320,10 +320,10 @@ def service_log(name, number=50): services = _get_services() if name not in services.keys(): - raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=name)) + raise MoulinetteError('service_unknown', service=name) if 'log' not in services[name]: - raise MoulinetteError(errno.EPERM, m18n.n('service_no_log', service=name)) + raise MoulinetteError('service_no_log', service=name) log_list = services[name]['log'] @@ -609,7 +609,7 @@ def _run_service_command(action, service): """ services = _get_services() if service not in services.keys(): - raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=service)) + raise MoulinetteError('service_unknown', service=service) possible_actions = ['start', 'stop', 'restart', 'reload', 'enable', 'disable'] if action not in possible_actions: diff --git a/src/yunohost/ssh.py b/src/yunohost/ssh.py index 5ddebfc2f..41ac64293 100644 --- a/src/yunohost/ssh.py +++ b/src/yunohost/ssh.py @@ -23,7 +23,7 @@ def user_ssh_allow(auth, username): # TODO it would be good to support different kind of shells if not _get_user_for_ssh(auth, username): - raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username)) + raise MoulinetteError('user_unknown', user=username) auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/bash'}) @@ -42,7 +42,7 @@ def user_ssh_disallow(auth, username): # TODO it would be good to support different kind of shells if not _get_user_for_ssh(auth, username): - raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username)) + raise MoulinetteError('user_unknown', user=username) auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/false'}) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 397e51eb2..5f82c467b 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -112,7 +112,7 @@ def tools_ldapinit(): pwd.getpwnam("admin") except KeyError: logger.error(m18n.n('ldap_init_failed_to_create_admin')) - raise MoulinetteError(errno.EINVAL, m18n.n('installation_failed')) + raise MoulinetteError('installation_failed') logger.success(m18n.n('ldap_initialized')) return auth @@ -176,7 +176,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): # Check domain exists if new_domain not in domain_list(auth)['domains']: - raise MoulinetteError(errno.EINVAL, m18n.n('domain_unknown')) + raise MoulinetteError('domain_unknown') operation_logger.related_to.append(('domain', new_domain)) operation_logger.start() @@ -199,7 +199,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): _set_maindomain(new_domain) except Exception as e: logger.warning("%s" % e, exc_info=1) - raise MoulinetteError(errno.EPERM, m18n.n('maindomain_change_failed')) + raise MoulinetteError('maindomain_change_failed') _set_hostname(new_domain) @@ -248,7 +248,7 @@ def _set_hostname(hostname, pretty_hostname=None): if p.returncode != 0: logger.warning(command) logger.warning(out) - raise MoulinetteError(errno.EIO, m18n.n('domain_hostname_failed')) + raise MoulinetteError('domain_hostname_failed') else: logger.debug(out) @@ -483,7 +483,7 @@ def tools_update(ignore_apps=False, ignore_packages=False): # Update APT cache logger.debug(m18n.n('updating_apt_cache')) if not cache.update(): - raise MoulinetteError(errno.EPERM, m18n.n('update_cache_failed')) + raise MoulinetteError('update_cache_failed') cache.open(None) cache.upgrade(True) @@ -807,7 +807,7 @@ def tools_migrations_list(pending=False, done=False): # Check for option conflict if pending and done: - raise MoulinetteError(errno.EINVAL, m18n.n("migrations_list_conflict_pending_done")) + raise MoulinetteError("migrations_list_conflict_pending_done") # Get all migrations migrations = _get_migrations_list() @@ -864,7 +864,7 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai # validate input, target must be "0" or a valid number elif target != 0 and target not in all_migration_numbers: - raise MoulinetteError(errno.EINVAL, m18n.n('migrations_bad_value_for_target', ", ".join(map(str, all_migration_numbers)))) + raise MoulinetteError('migrations_bad_value_for_target', ", ".join(map(str, all_migration_numbers))) logger.debug(m18n.n('migrations_current_target', target)) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index bb39dd58e..d074c81c1 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -130,7 +130,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas # Validate uniqueness of username in system users all_existing_usernames = {x.pw_name for x in pwd.getpwall()} if username in all_existing_usernames: - raise MoulinetteError(errno.EEXIST, m18n.n('system_username_exists')) + raise MoulinetteError('system_username_exists') main_domain = _get_maindomain() aliases = [ @@ -226,7 +226,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas return {'fullname': fullname, 'username': username, 'mail': mail} - raise MoulinetteError(169, m18n.n('user_creation_failed')) + raise MoulinetteError('user_creation_failed') @is_unit_operation([('username', 'user')]) @@ -257,7 +257,7 @@ def user_delete(operation_logger, auth, username, purge=False): if purge: subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) else: - raise MoulinetteError(169, m18n.n('user_deletion_failed')) + raise MoulinetteError('user_deletion_failed') app_ssowatconf(auth) @@ -296,7 +296,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, # Populate user informations result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch) if not result: - raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username)) + raise MoulinetteError('user_unknown', user=username) user = result[0] # Get modifications from arguments @@ -389,7 +389,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, app_ssowatconf(auth) return user_info(auth, username) else: - raise MoulinetteError(169, m18n.n('user_update_failed')) + raise MoulinetteError('user_update_failed') def user_info(auth, username): @@ -414,7 +414,7 @@ def user_info(auth, username): if result: user = result[0] else: - raise MoulinetteError(errno.EINVAL, m18n.n('user_unknown', user=username)) + raise MoulinetteError('user_unknown', user=username) result_dict = { 'username': user['uid'][0], @@ -470,7 +470,7 @@ def user_info(auth, username): if result: return result_dict else: - raise MoulinetteError(167, m18n.n('user_info_failed')) + raise MoulinetteError('user_info_failed') # # SSH subcategory From 4bc3427e3efecf7f788f1c90f457696d898ab2ce Mon Sep 17 00:00:00 2001 From: Irina LAMBLA Date: Wed, 3 Oct 2018 15:32:04 +0200 Subject: [PATCH 074/104] moulinette MoulinetteError multilignes" sed -i "N; s@MoulinetteError(.*m18n\.n(\n* *\(.*\)))@MoulinetteError(\1)@g" --- src/yunohost/app.py | 81 +++++++------------ src/yunohost/backup.py | 70 ++++++---------- src/yunohost/certificate.py | 51 ++++-------- .../0002_migrate_to_tsig_sha256.py | 4 +- src/yunohost/domain.py | 3 +- src/yunohost/dyndns.py | 12 +-- src/yunohost/hook.py | 6 +- src/yunohost/log.py | 3 +- src/yunohost/monitor.py | 6 +- src/yunohost/settings.py | 6 +- src/yunohost/tools.py | 7 +- src/yunohost/user.py | 12 +-- 12 files changed, 89 insertions(+), 172 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 4637b36e3..92aae5336 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -125,14 +125,12 @@ def app_fetchlist(url=None, name=None): appslists_to_be_fetched = [name] operation_logger.success() else: - raise MoulinetteError(errno.EINVAL, - m18n.n('custom_appslist_name_required')) + raise MoulinetteError('custom_appslist_name_required') # If a name is given, look for an appslist with that name and fetch it elif name is not None: if name not in appslists.keys(): - raise MoulinetteError(errno.EINVAL, - m18n.n('appslist_unknown', appslist=name)) + raise MoulinetteError('appslist_unknown', appslist=name) else: appslists_to_be_fetched = [name] @@ -343,8 +341,7 @@ def app_info(app, show_status=False, raw=False): """ if not _is_installed(app): - raise MoulinetteError(errno.EINVAL, - m18n.n('app_not_installed', app=app)) + raise MoulinetteError('app_not_installed', app=app) app_setting_path = APPS_SETTING_PATH + app @@ -402,8 +399,7 @@ def app_map(app=None, raw=False, user=None): if app is not None: if not _is_installed(app): - raise MoulinetteError(errno.EINVAL, - m18n.n('app_not_installed', app=app)) + raise MoulinetteError('app_not_installed', app=app) apps = [app, ] else: apps = os.listdir(APPS_SETTING_PATH) @@ -455,8 +451,7 @@ def app_change_url(operation_logger, auth, app, domain, path): installed = _is_installed(app) if not installed: - raise MoulinetteError(errno.ENOPKG, - m18n.n('app_not_installed', app=app)) + raise MoulinetteError('app_not_installed', app=app) if not os.path.exists(os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url")): raise MoulinetteError("app_change_no_change_url_script", app_name=app) @@ -593,8 +588,7 @@ def app_upgrade(auth, app=[], url=None, file=None): logger.info(m18n.n('app_upgrade_app_name', app=app_instance_name)) installed = _is_installed(app_instance_name) if not installed: - raise MoulinetteError(errno.ENOPKG, - m18n.n('app_not_installed', app=app_instance_name)) + raise MoulinetteError('app_not_installed', app=app_instance_name) if app_instance_name in upgraded_apps: continue @@ -746,8 +740,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on instance_number = _installed_instance_number(app_id, last=True) + 1 if instance_number > 1: if 'multi_instance' not in manifest or not is_true(manifest['multi_instance']): - raise MoulinetteError(errno.EEXIST, - m18n.n('app_already_installed', app=app_id)) + raise MoulinetteError('app_already_installed', app=app_id) # Change app_id to the forked app id app_instance_name = app_id + '__' + str(instance_number) @@ -888,8 +881,7 @@ def app_remove(operation_logger, auth, app): """ from yunohost.hook import hook_exec, hook_remove, hook_callback if not _is_installed(app): - raise MoulinetteError(errno.EINVAL, - m18n.n('app_not_installed', app=app)) + raise MoulinetteError('app_not_installed', app=app) operation_logger.start() @@ -1152,8 +1144,7 @@ def app_makedefault(operation_logger, auth, app, domain=None): with open('/etc/ssowat/conf.json.persistent') as json_conf: ssowat_conf = json.loads(str(json_conf.read())) except ValueError as e: - raise MoulinetteError(errno.EINVAL, - m18n.n('ssowat_persistent_conf_read_error', error=e.strerror)) + raise MoulinetteError('ssowat_persistent_conf_read_error', error=e.strerror) except IOError: ssowat_conf = {} @@ -1166,8 +1157,7 @@ def app_makedefault(operation_logger, auth, app, domain=None): with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) except IOError as e: - raise MoulinetteError(errno.EPERM, - m18n.n('ssowat_persistent_conf_write_error', error=e.strerror)) + raise MoulinetteError('ssowat_persistent_conf_write_error', error=e.strerror) os.system('chmod 644 /etc/ssowat/conf.json.persistent') @@ -1219,8 +1209,7 @@ def app_checkport(port): if tools_port_available(port): logger.success(m18n.n('port_available', port=int(port))) else: - raise MoulinetteError(errno.EINVAL, - m18n.n('port_unavailable', port=int(port))) + raise MoulinetteError('port_unavailable', port=int(port)) def app_register_url(auth, app, domain, path): @@ -1247,8 +1236,7 @@ def app_register_url(auth, app, domain, path): if installed: settings = _get_app_settings(app) if "path" in settings.keys() and "domain" in settings.keys(): - raise MoulinetteError(errno.EINVAL, - m18n.n('app_already_installed_cant_change_url')) + raise MoulinetteError('app_already_installed_cant_change_url') # Check the url is available conflicts = _get_conflicting_apps(auth, domain, path) @@ -1458,8 +1446,7 @@ def app_ssowatconf(auth): def app_change_label(auth, app, new_label): installed = _is_installed(app) if not installed: - raise MoulinetteError(errno.ENOPKG, - m18n.n('app_not_installed', app=app)) + raise MoulinetteError('app_not_installed', app=app) app_setting(app, "label", value=new_label) @@ -1640,8 +1627,7 @@ def app_config_apply(app, args): installed = _is_installed(app) if not installed: - raise MoulinetteError(errno.ENOPKG, - m18n.n('app_not_installed', app=app)) + raise MoulinetteError('app_not_installed', app=app_id) config_panel = os.path.join(APPS_SETTING_PATH, app, 'config_panel.json') config_script = os.path.join(APPS_SETTING_PATH, app, 'scripts', 'config') @@ -1699,8 +1685,7 @@ def _get_app_settings(app_id): """ if not _is_installed(app_id): - raise MoulinetteError(errno.EINVAL, - m18n.n('app_not_installed', app=app_id)) + raise MoulinetteError('app_not_installed', app=app_id) try: with open(os.path.join( APPS_SETTING_PATH, app_id, 'settings.yml')) as f: @@ -1816,8 +1801,7 @@ def _extract_app_from_file(path, remove=False): except IOError: raise MoulinetteError('app_install_files_invalid') except ValueError as e: - raise MoulinetteError(errno.EINVAL, - m18n.n('app_manifest_invalid', error=e.strerror)) + raise MoulinetteError('app_manifest_invalid', error=e.strerror) logger.debug(m18n.n('done')) @@ -1885,8 +1869,7 @@ def _fetch_app_from_git(app): 'wget', '-qO', app_tmp_archive, tarball_url]) except subprocess.CalledProcessError: logger.exception('unable to download %s', tarball_url) - raise MoulinetteError(errno.EIO, - m18n.n('app_sources_fetch_failed')) + raise MoulinetteError('app_sources_fetch_failed') else: manifest, extracted_app_folder = _extract_app_from_file( app_tmp_archive, remove=True) @@ -1909,11 +1892,9 @@ def _fetch_app_from_git(app): with open(extracted_app_folder + '/manifest.json') as f: manifest = json.loads(str(f.read())) except subprocess.CalledProcessError: - raise MoulinetteError(errno.EIO, - m18n.n('app_sources_fetch_failed')) + raise MoulinetteError('app_sources_fetch_failed') except ValueError as e: - raise MoulinetteError(errno.EIO, - m18n.n('app_manifest_invalid', error=e.strerror)) + raise MoulinetteError('app_manifest_invalid', error=e.strerror) else: logger.debug(m18n.n('done')) @@ -1936,8 +1917,7 @@ def _fetch_app_from_git(app): raise MoulinetteError('app_unknown') if 'git' not in app_info: - raise MoulinetteError(errno.EINVAL, - m18n.n('app_unsupported_remote_type')) + raise MoulinetteError('app_unsupported_remote_type') url = app_info['git']['url'] if 'github.com' in url: @@ -1949,8 +1929,7 @@ def _fetch_app_from_git(app): 'wget', '-qO', app_tmp_archive, tarball_url]) except subprocess.CalledProcessError: logger.exception('unable to download %s', tarball_url) - raise MoulinetteError(errno.EIO, - m18n.n('app_sources_fetch_failed')) + raise MoulinetteError('app_sources_fetch_failed') else: manifest, extracted_app_folder = _extract_app_from_file( app_tmp_archive, remove=True) @@ -1966,11 +1945,9 @@ def _fetch_app_from_git(app): with open(extracted_app_folder + '/manifest.json') as f: manifest = json.loads(str(f.read())) except subprocess.CalledProcessError: - raise MoulinetteError(errno.EIO, - m18n.n('app_sources_fetch_failed')) + raise MoulinetteError('app_sources_fetch_failed') except ValueError as e: - raise MoulinetteError(errno.EIO, - m18n.n('app_manifest_invalid', error=e.strerror)) + raise MoulinetteError('app_manifest_invalid', error=e.strerror) else: logger.debug(m18n.n('done')) @@ -2237,8 +2214,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): # Validate argument value if (arg_value is None or arg_value == '') \ and not arg.get('optional', False): - raise MoulinetteError(errno.EINVAL, - m18n.n('app_argument_required', name=arg_name)) + raise MoulinetteError('app_argument_required', name=arg_name) elif arg_value is None: args_dict[arg_name] = '' continue @@ -2462,8 +2438,7 @@ def _read_appslist_list(): try: appslists = json.loads(appslists_json) except ValueError: - raise MoulinetteError(errno.EBADR, - m18n.n('appslist_corrupted_json', filename=APPSLISTS_JSON)) + raise MoulinetteError('appslist_corrupted_json', filename=APPSLISTS_JSON) return appslists @@ -2493,15 +2468,13 @@ def _register_new_appslist(url, name): # Check if name conflicts with an existing list if name in appslist_list: - raise MoulinetteError(errno.EEXIST, - m18n.n('appslist_name_already_tracked', name=name)) + raise MoulinetteError('appslist_name_already_tracked', name=name) # Check if url conflicts with an existing list known_appslist_urls = [appslist["url"] for _, appslist in appslist_list.items()] if url in known_appslist_urls: - raise MoulinetteError(errno.EEXIST, - m18n.n('appslist_url_already_tracked', url=url)) + raise MoulinetteError('appslist_url_already_tracked', url=url) logger.debug("Registering new appslist %s at %s" % (name, url)) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index d8197fed4..31acca188 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -327,8 +327,7 @@ class BackupManager(): logger.debug("temporary directory for backup '%s' already exists", self.work_dir) # FIXME May be we should clean the workdir here - raise MoulinetteError( - errno.EIO, m18n.n('backup_output_directory_not_empty')) + raise MoulinetteError('backup_output_directory_not_empty') ########################################################################### # Backup target management # @@ -880,8 +879,7 @@ class RestoreManager(): logger.debug("unable to retrieve current_host from the backup", exc_info=1) # FIXME include the current_host by default ? - raise MoulinetteError(errno.EIO, - m18n.n('backup_invalid_archive')) + raise MoulinetteError('backup_invalid_archive') logger.debug("executing the post-install...") tools_postinstall(domain, 'yunohost', True) @@ -1010,8 +1008,7 @@ class RestoreManager(): subprocess.call(['rmdir', self.work_dir]) logger.debug("Unmount dir: {}".format(self.work_dir)) else: - raise MoulinetteError(errno.EIO, - m18n.n('restore_removing_tmp_dir_failed')) + raise MoulinetteError('restore_removing_tmp_dir_failed') elif os.path.isdir(self.work_dir): logger.debug("temporary restore directory '%s' already exists", self.work_dir) @@ -1019,8 +1016,7 @@ class RestoreManager(): if ret == 0: logger.debug("Delete dir: {}".format(self.work_dir)) else: - raise MoulinetteError(errno.EIO, - m18n.n('restore_removing_tmp_dir_failed')) + raise MoulinetteError('restore_removing_tmp_dir_failed') filesystem.mkdir(self.work_dir, parents=True) @@ -1524,8 +1520,7 @@ class BackupMethod(object): """ if self.need_mount(): if self._recursive_umount(self.work_dir) > 0: - raise MoulinetteError(errno.EINVAL, - m18n.n('backup_cleaning_failed')) + raise MoulinetteError('backup_cleaning_failed') if self.manager.is_tmp_work_dir: filesystem.rm(self.work_dir, True, True) @@ -1567,8 +1562,7 @@ class BackupMethod(object): if free_space < backup_size: logger.debug('Not enough space at %s (free: %s / needed: %d)', self.repo, free_space, backup_size) - raise MoulinetteError(errno.EIO, m18n.n( - 'not_enough_disk_space', path=self.repo)) + raise MoulinetteError('not_enough_disk_space', path=self.repo) def _organize_files(self): """ @@ -1656,12 +1650,10 @@ class BackupMethod(object): i = msignals.prompt(m18n.n('backup_ask_for_copying_if_needed', answers='y/N', size=str(size))) except NotImplemented: - raise MoulinetteError(errno.EIO, - m18n.n('backup_unable_to_organize_files')) + raise MoulinetteError('backup_unable_to_organize_files') else: if i != 'y' and i != 'Y': - raise MoulinetteError(errno.EIO, - m18n.n('backup_unable_to_organize_files')) + raise MoulinetteError('backup_unable_to_organize_files') # Copy unbinded path logger.debug(m18n.n('backup_copying_to_organize_the_archive', @@ -1751,8 +1743,7 @@ class CopyBackupMethod(BackupMethod): super(CopyBackupMethod, self).mount() if not os.path.isdir(self.repo): - raise MoulinetteError(errno.EIO, - m18n.n('backup_no_uncompress_archive_dir')) + raise MoulinetteError('backup_no_uncompress_archive_dir') filesystem.mkdir(self.work_dir, parent=True) ret = subprocess.call(["mount", "-r", "--rbind", self.repo, @@ -1763,8 +1754,7 @@ class CopyBackupMethod(BackupMethod): logger.warning(m18n.n("bind_mouting_disable")) subprocess.call(["mountpoint", "-q", dest, "&&", "umount", "-R", dest]) - raise MoulinetteError(errno.EIO, - m18n.n('backup_cant_mount_uncompress_archive')) + raise MoulinetteError('backup_cant_mount_uncompress_archive') class TarBackupMethod(BackupMethod): @@ -1809,8 +1799,7 @@ class TarBackupMethod(BackupMethod): except: logger.debug("unable to open '%s' for writing", self._archive_file, exc_info=1) - raise MoulinetteError(errno.EIO, - m18n.n('backup_archive_open_failed')) + raise MoulinetteError('backup_archive_open_failed') # Add files to the archive try: @@ -1821,8 +1810,7 @@ class TarBackupMethod(BackupMethod): tar.close() except IOError: logger.error(m18n.n('backup_archive_writing_error'), exc_info=1) - raise MoulinetteError(errno.EIO, - m18n.n('backup_creation_failed')) + raise MoulinetteError('backup_creation_failed') # Move info file shutil.copy(os.path.join(self.work_dir, 'info.json'), @@ -1850,8 +1838,7 @@ class TarBackupMethod(BackupMethod): except: logger.debug("cannot open backup archive '%s'", self._archive_file, exc_info=1) - raise MoulinetteError(errno.EIO, - m18n.n('backup_archive_open_failed')) + raise MoulinetteError('backup_archive_open_failed') tar.close() # Mount the tarball @@ -1912,12 +1899,10 @@ class BorgBackupMethod(BackupMethod): super(CopyBackupMethod, self).backup() # TODO run borg create command - raise MoulinetteError( - errno.EIO, m18n.n('backup_borg_not_implemented')) + raise MoulinetteError('backup_borg_not_implemented') def mount(self, mnt_path): - raise MoulinetteError( - errno.EIO, m18n.n('backup_borg_not_implemented')) + raise MoulinetteError('backup_borg_not_implemented') class CustomBackupMethod(BackupMethod): @@ -1963,8 +1948,7 @@ class CustomBackupMethod(BackupMethod): ret = hook_callback('backup_method', [self.method], args=self._get_args('backup')) if ret['failed']: - raise MoulinetteError(errno.EIO, - m18n.n('backup_custom_backup_error')) + raise MoulinetteError('backup_custom_backup_error') def mount(self, restore_manager): """ @@ -1977,8 +1961,7 @@ class CustomBackupMethod(BackupMethod): ret = hook_callback('backup_method', [self.method], args=self._get_args('mount')) if ret['failed']: - raise MoulinetteError(errno.EIO, - m18n.n('backup_custom_mount_error')) + raise MoulinetteError('backup_custom_mount_error') def _get_args(self, action): """Return the arguments to give to the custom script""" @@ -2014,8 +1997,7 @@ def backup_create(name=None, description=None, methods=[], # Validate there is no archive with the same name if name and name in backup_list()['archives']: - raise MoulinetteError(errno.EINVAL, - m18n.n('backup_archive_name_exists')) + raise MoulinetteError('backup_archive_name_exists') # Validate output_directory option if output_directory: @@ -2025,17 +2007,14 @@ def backup_create(name=None, description=None, methods=[], if output_directory.startswith(ARCHIVES_PATH) or \ re.match(r'^/(|(bin|boot|dev|etc|lib|root|run|sbin|sys|usr|var)(|/.*))$', output_directory): - raise MoulinetteError(errno.EINVAL, - m18n.n('backup_output_directory_forbidden')) + raise MoulinetteError('backup_output_directory_forbidden') # Check that output directory is empty if os.path.isdir(output_directory) and no_compress and \ os.listdir(output_directory): - raise MoulinetteError(errno.EIO, - m18n.n('backup_output_directory_not_empty')) + raise MoulinetteError('backup_output_directory_not_empty') elif no_compress: - raise MoulinetteError(errno.EINVAL, - m18n.n('backup_output_directory_required')) + raise MoulinetteError('backup_output_directory_required') # Define methods (retro-compat) if not methods: @@ -2217,8 +2196,7 @@ def backup_info(name, with_details=False, human_readable=False): # Check file exist (even if it's a broken symlink) if not os.path.lexists(archive_file): - raise MoulinetteError(errno.EIO, - m18n.n('backup_archive_name_unknown', name=name)) + raise MoulinetteError('backup_archive_name_unknown', name=name) # If symlink, retrieve the real path if os.path.islink(archive_file): @@ -2292,8 +2270,8 @@ def backup_delete(name): """ if name not in backup_list()["archives"]: - raise MoulinetteError(errno.EIO, m18n.n('backup_archive_name_unknown', - name=name)) + raise MoulinetteError('backup_archive_name_unknown', + name=name) hook_callback('pre_backup_delete', args=[name]) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 59b7a72c3..6c9b890ca 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -172,8 +172,7 @@ def _certificate_install_selfsigned(domain_list, force=False): status = _get_status(domain) if status["summary"]["code"] in ('good', 'great'): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_attempt_to_replace_valid_cert', domain=domain)) + raise MoulinetteError('certmanager_attempt_to_replace_valid_cert', domain=domain) operation_logger.start() @@ -203,8 +202,7 @@ def _certificate_install_selfsigned(domain_list, force=False): if p.returncode != 0: logger.warning(out) - raise MoulinetteError( - errno.EIO, m18n.n('domain_cert_gen_failed')) + raise MoulinetteError('domain_cert_gen_failed') else: logger.debug(out) @@ -262,14 +260,12 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F for domain in domain_list: yunohost_domains_list = yunohost.domain.domain_list(auth)['domains'] if domain not in yunohost_domains_list: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_domain_unknown', domain=domain)) + raise MoulinetteError('certmanager_domain_unknown', domain=domain) # Is it self-signed? status = _get_status(domain) if not force and status["CA_type"]["code"] != "self-signed": - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_domain_cert_not_selfsigned', domain=domain)) + raise MoulinetteError('certmanager_domain_cert_not_selfsigned', domain=domain) if staging: logger.warning( @@ -349,25 +345,21 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal # Is it in Yunohost dmomain list? if domain not in yunohost.domain.domain_list(auth)['domains']: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_domain_unknown', domain=domain)) + raise MoulinetteError('certmanager_domain_unknown', domain=domain) status = _get_status(domain) # Does it expire soon? if status["validity"] > VALIDITY_LIMIT and not force: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_attempt_to_renew_valid_cert', domain=domain)) + raise MoulinetteError('certmanager_attempt_to_renew_valid_cert', domain=domain) # Does it have a Let's Encrypt cert? if status["CA_type"]["code"] != "lets-encrypt": - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_attempt_to_renew_nonLE_cert', domain=domain)) + raise MoulinetteError('certmanager_attempt_to_renew_nonLE_cert', domain=domain) # Check ACME challenge configured for given domain if not _check_acme_challenge_configuration(domain): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_acme_not_configured_for_domain', domain=domain)) + raise MoulinetteError('certmanager_acme_not_configured_for_domain', domain=domain) if staging: logger.warning( @@ -563,19 +555,16 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): CA=certification_authority) except ValueError as e: if "urn:acme:error:rateLimited" in str(e): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_hit_rate_limit', domain=domain)) + raise MoulinetteError('certmanager_hit_rate_limit', domain=domain) else: logger.error(str(e)) _display_debug_information(domain) - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_cert_signing_failed')) + raise MoulinetteError('certmanager_cert_signing_failed') except Exception as e: logger.error(str(e)) - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_cert_signing_failed')) + raise MoulinetteError('certmanager_cert_signing_failed') import requests # lazy loading this module for performance reasons try: @@ -624,8 +613,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): status_summary = _get_status(domain)["summary"] if status_summary["code"] != "great": - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_certificate_fetching_or_enabling_failed', domain=domain)) + raise MoulinetteError('certmanager_certificate_fetching_or_enabling_failed', domain=domain) def _prepare_certificate_signing_request(domain, key_file, output_folder): @@ -658,8 +646,7 @@ def _get_status(domain): cert_file = os.path.join(CERT_FOLDER, domain, "crt.pem") if not os.path.isfile(cert_file): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_no_cert_file', domain=domain, file=cert_file)) + raise MoulinetteError('certmanager_no_cert_file', domain=domain, file=cert_file) from OpenSSL import crypto # lazy loading this module for performance reasons try: @@ -668,8 +655,7 @@ def _get_status(domain): except Exception as exception: import traceback traceback.print_exc(file=sys.stdout) - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_cannot_read_cert', domain=domain, file=cert_file, reason=exception)) + raise MoulinetteError('certmanager_cannot_read_cert', domain=domain, file=cert_file, reason=exception) cert_subject = cert.get_subject().CN cert_issuer = cert.get_issuer().CN @@ -830,13 +816,11 @@ def _check_domain_is_ready_for_ACME(domain): # Check if IP from DNS matches public IP if not _dns_ip_match_public_ip(public_ip, domain): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_domain_dns_ip_differs_from_public_ip', domain=domain)) + raise MoulinetteError('certmanager_domain_dns_ip_differs_from_public_ip', domain=domain) # Check if domain seems to be accessible through HTTP? if not _domain_is_accessible_through_HTTP(public_ip, domain): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_domain_http_not_working', domain=domain)) + raise MoulinetteError('certmanager_domain_http_not_working', domain=domain) def _get_dns_ip(domain): @@ -845,8 +829,7 @@ def _get_dns_ip(domain): resolver.nameservers = DNS_RESOLVERS answers = resolver.query(domain, "A") except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_error_no_A_record', domain=domain)) + raise MoulinetteError('certmanager_error_no_A_record', domain=domain) return str(answers[0]) diff --git a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py index aad6771be..188c1dffc 100644 --- a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py +++ b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py @@ -70,8 +70,8 @@ class MyMigration(Migration): # Migration didn't succeed, so we rollback and raise an exception os.system("mv /etc/yunohost/dyndns/*+165* /tmp") - raise MoulinetteError(m18n.n('migrate_tsig_failed', domain=domain, - error_code=str(r.status_code), error=error)) + raise MoulinetteError('migrate_tsig_failed', domain=domain, + error_code=str(r.status_code), error=error) # remove old certificates os.system("mv /etc/yunohost/dyndns/*+157* /tmp") diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 21a7992ae..98ebf3239 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -162,8 +162,7 @@ def domain_remove(operation_logger, auth, domain, force=False): continue else: if app_domain == domain: - raise MoulinetteError(errno.EPERM, - m18n.n('domain_uninstall_app_first')) + raise MoulinetteError('domain_uninstall_app_first') operation_logger.start() if auth.remove('virtualdomain=' + domain + ',ou=domains') or force: diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 8bf2d254f..09f672662 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -136,8 +136,7 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom # Verify if domain is available if not _dyndns_available(subscribe_host, domain): - raise MoulinetteError(errno.ENOENT, - m18n.n('dyndns_unavailable', domain=domain)) + raise MoulinetteError('dyndns_unavailable', domain=domain) operation_logger.start() @@ -167,8 +166,7 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom error = json.loads(r.text)['error'] except: error = "Server error, code: %s. (Message: \"%s\")" % (r.status_code, r.text) - raise MoulinetteError(errno.EPERM, - m18n.n('dyndns_registration_failed', error=error)) + raise MoulinetteError('dyndns_registration_failed', error=error) logger.success(m18n.n('dyndns_registered')) @@ -302,8 +300,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, command = ["/usr/bin/nsupdate", "-k", key, DYNDNS_ZONE] subprocess.check_call(command) except subprocess.CalledProcessError: - raise MoulinetteError(errno.EPERM, - m18n.n('dyndns_ip_update_failed')) + raise MoulinetteError('dyndns_ip_update_failed') logger.success(m18n.n('dyndns_ip_updated')) @@ -359,5 +356,4 @@ def _guess_current_dyndns_domain(dyn_host): else: return (_domain, path) - raise MoulinetteError(errno.EINVAL, - m18n.n('dyndns_no_domain_registered')) + raise MoulinetteError('dyndns_no_domain_registered') diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index b7457878e..302ffe302 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -384,14 +384,12 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, # Check and return process' return code if returncode is None: if raise_on_error: - raise MoulinetteError( - errno.EIO, m18n.n('hook_exec_not_terminated', path=path)) + raise MoulinetteError('hook_exec_not_terminated', path=path) else: logger.error(m18n.n('hook_exec_not_terminated', path=path)) return 1 elif raise_on_error and returncode != 0: - raise MoulinetteError( - errno.EIO, m18n.n('hook_exec_failed', path=path)) + raise MoulinetteError('hook_exec_failed', path=path) return returncode diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 80a7a134f..d31231c72 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -148,8 +148,7 @@ def log_display(path, number=50, share=False): log_path = base_path + LOG_FILE_EXT if not os.path.exists(md_path) and not os.path.exists(log_path): - raise MoulinetteError(errno.EINVAL, - m18n.n('log_does_exists', log=path)) + raise MoulinetteError('log_does_exists', log=path) infos = {} diff --git a/src/yunohost/monitor.py b/src/yunohost/monitor.py index b01e36238..0839c6ae7 100644 --- a/src/yunohost/monitor.py +++ b/src/yunohost/monitor.py @@ -390,11 +390,9 @@ def monitor_show_stats(period, date=None): result = _retrieve_stats(period, date) if result is False: - raise MoulinetteError(errno.ENOENT, - m18n.n('monitor_stats_file_not_found')) + raise MoulinetteError('monitor_stats_file_not_found') elif result is None: - raise MoulinetteError(errno.EINVAL, - m18n.n('monitor_stats_period_unavailable')) + raise MoulinetteError('monitor_stats_period_unavailable') return result diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index fcb08485e..d49c1af35 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -83,8 +83,7 @@ def settings_set(key, value): settings = _get_settings() if key not in settings: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'global_settings_key_doesnt_exists', settings_key=key)) + raise MoulinetteError('global_settings_key_doesnt_exists', settings_key=key) key_type = settings[key]["type"] @@ -138,8 +137,7 @@ def settings_reset(key): settings = _get_settings() if key not in settings: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'global_settings_key_doesnt_exists', settings_key=key)) + raise MoulinetteError('global_settings_key_doesnt_exists', settings_key=key) settings[key]["value"] = settings[key]["default"] _save_settings(settings) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 5f82c467b..1c7581365 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -406,8 +406,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, if p.returncode != 0: logger.warning(out) - raise MoulinetteError(errno.EPERM, - m18n.n('yunohost_ca_creation_failed')) + raise MoulinetteError('yunohost_ca_creation_failed') else: logger.debug(out) @@ -1070,8 +1069,8 @@ def _load_migration(migration_file): import traceback traceback.print_exc() - raise MoulinetteError(errno.EINVAL, m18n.n('migrations_error_failed_to_load_migration', - number=number, name=name)) + raise MoulinetteError('migrations_error_failed_to_load_migration', + number=number, name=name) def _skip_all_migrations(): """ diff --git a/src/yunohost/user.py b/src/yunohost/user.py index d074c81c1..c6752d2dc 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -188,8 +188,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas with open('/etc/ssowat/conf.json.persistent') as json_conf: ssowat_conf = json.loads(str(json_conf.read())) except ValueError as e: - raise MoulinetteError(errno.EINVAL, - m18n.n('ssowat_persistent_conf_read_error', error=e.strerror)) + raise MoulinetteError('ssowat_persistent_conf_read_error', error=e.strerror) except IOError: ssowat_conf = {} @@ -199,8 +198,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) except IOError as e: - raise MoulinetteError(errno.EPERM, - m18n.n('ssowat_persistent_conf_write_error', error=e.strerror)) + raise MoulinetteError('ssowat_persistent_conf_write_error', error=e.strerror) if auth.add('uid=%s,ou=users' % username, attr_dict): # Invalidate passwd to take user creation into account @@ -355,8 +353,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, if len(user['mail']) > 1 and mail in user['mail'][1:]: user['mail'].remove(mail) else: - raise MoulinetteError(errno.EINVAL, - m18n.n('mail_alias_remove_failed', mail=mail)) + raise MoulinetteError('mail_alias_remove_failed', mail=mail) new_attr_dict['mail'] = user['mail'] if add_mailforward: @@ -375,8 +372,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]: user['maildrop'].remove(mail) else: - raise MoulinetteError(errno.EINVAL, - m18n.n('mail_forward_remove_failed', mail=mail)) + raise MoulinetteError('mail_forward_remove_failed', mail=mail) new_attr_dict['maildrop'] = user['maildrop'] if mailbox_quota is not None: From 0468d6b900acd199053ab977e06716030e0f2bb7 Mon Sep 17 00:00:00 2001 From: Irina LAMBLA Date: Fri, 9 Nov 2018 17:50:39 +0100 Subject: [PATCH 075/104] sed 3 lines deletion of line breaks to be able to use "sed" : sed -i "N; s@MoulinetteError(.*m18n\.n(\n* *@MoulinetteError(m18n.n(@g" --- src/yunohost/backup.py | 13 +++++-------- src/yunohost/certificate.py | 3 +-- src/yunohost/dyndns.py | 8 +++----- src/yunohost/hook.py | 3 +-- src/yunohost/service.py | 5 ++--- src/yunohost/settings.py | 28 +++++++++++----------------- src/yunohost/user.py | 9 +++------ 7 files changed, 26 insertions(+), 43 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 31acca188..9893def33 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1083,14 +1083,12 @@ class RestoreManager(): return True elif free_space > needed_space: # TODO Add --force options to avoid the error raising - raise MoulinetteError(errno.EIO, - m18n.n('restore_may_be_not_enough_disk_space', + raise MoulinetteError(m18n.n('restore_may_be_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin)) else: - raise MoulinetteError(errno.EIO, - m18n.n('restore_not_enough_disk_space', + raise MoulinetteError(m18n.n('restore_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin)) @@ -1143,7 +1141,7 @@ class RestoreManager(): newlines.append(row) except (IOError, OSError, csv.Error) as e: - raise MoulinetteError(errno.EIO,m18n.n('error_reading_file', + raise MoulinetteError(m18n.n('error_reading_file', file=backup_csv, error=str(e))) @@ -2204,9 +2202,8 @@ def backup_info(name, with_details=False, human_readable=False): # Raise exception if link is broken (e.g. on unmounted external storage) if not os.path.exists(archive_file): - raise MoulinetteError(errno.EIO, - m18n.n('backup_archive_broken_link', - path=archive_file)) + raise MoulinetteError('backup_archive_broken_link', + path=archive_file) info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 6c9b890ca..61eeca319 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -476,8 +476,7 @@ location ^~ '/.well-known/acme-challenge/' contents = f.read() if '/.well-known/acme-challenge' in contents: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_conflicting_nginx_file', filepath=path)) + raise MoulinetteError('certmanager_conflicting_nginx_file', filepath=path) # Write the conf if os.path.exists(nginx_conf_file): diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 09f672662..d9643054e 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -106,9 +106,8 @@ def _dyndns_available(provider, domain): expected_status_code=None) except MoulinetteError as e: logger.error(str(e)) - raise MoulinetteError(errno.EIO, - m18n.n('dyndns_could_not_check_available', - domain=domain, provider=provider)) + raise MoulinetteError('dyndns_could_not_check_available', + domain=domain, provider=provider) return r == u"Domain %s is available" % domain @@ -130,8 +129,7 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom # Verify if domain is provided by subscribe_host if not _dyndns_provides(subscribe_host, domain): - raise MoulinetteError(errno.ENOENT, - m18n.n('dyndns_domain_not_provided', + raise MoulinetteError(m18n.n('dyndns_domain_not_provided', domain=domain, provider=subscribe_host)) # Verify if domain is available diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 302ffe302..fef5199d9 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -255,8 +255,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, try: hl = hooks_names[n] except KeyError: - raise MoulinetteError(errno.EINVAL, - m18n.n('hook_name_unknown', n)) + raise MoulinetteError('hook_name_unknown', n) # Iterate over hooks with this name for h in hl: # Update hooks dict diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 2c85b7047..14d16a73d 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -431,9 +431,8 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, names = pre_result['succeed'].keys() if not names: - raise MoulinetteError(errno.EIO, - m18n.n('service_regenconf_failed', - services=', '.join(pre_result['failed']))) + raise MoulinetteError('service_regenconf_failed', + services=', '.join(pre_result['failed'])) # Set the processing method _regen = _process_regen_conf if not dry_run else lambda *a, **k: True diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index d49c1af35..ca9a98561 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -54,7 +54,7 @@ def settings_get(key, full=False): settings = _get_settings() if key not in settings: - raise MoulinetteError(errno.EINVAL, m18n.n( + raise MoulinetteError(m18n.n( 'global_settings_key_doesnt_exists', settings_key=key)) if full: @@ -89,9 +89,8 @@ def settings_set(key, value): if key_type == "bool": if not isinstance(value, bool): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'global_settings_bad_type_for_setting', setting=key, - received_type=type(value).__name__, expected_type=key_type)) + raise MoulinetteError('global_settings_bad_type_for_setting', setting=key, + received_type=type(value).__name__, expected_type=key_type) elif key_type == "int": if not isinstance(value, int) or isinstance(value, bool): if isinstance(value, str): @@ -102,24 +101,21 @@ def settings_set(key, value): 'global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type)) else: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'global_settings_bad_type_for_setting', setting=key, + raise MoulinetteError(m18n.n('global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type)) elif key_type == "string": if not isinstance(value, basestring): - raise MoulinetteError(errno.EINVAL, m18n.n( - 'global_settings_bad_type_for_setting', setting=key, + raise MoulinetteError(m18n.n('global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type)) elif key_type == "enum": if value not in settings[key]["choices"]: - raise MoulinetteError(errno.EINVAL, m18n.n( + raise MoulinetteError(m18n.n( 'global_settings_bad_choice_for_enum', setting=key, received_type=type(value).__name__, expected_type=", ".join(settings[key]["choices"]))) else: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'global_settings_unknown_type', setting=key, - unknown_type=key_type)) + raise MoulinetteError('global_settings_unknown_type', setting=key, + unknown_type=key_type) settings[key]["value"] = value @@ -213,7 +209,7 @@ def _get_settings(): setting_key=key)) unknown_settings[key] = value except Exception as e: - raise MoulinetteError(errno.EIO, m18n.n('global_settings_cant_open_settings', reason=e), + raise MoulinetteError(m18n.n('global_settings_cant_open_settings', reason=e), exc_info=1) if unknown_settings: @@ -235,14 +231,12 @@ def _save_settings(settings, location=SETTINGS_PATH): try: result = json.dumps(settings_without_description, indent=4) except Exception as e: - raise MoulinetteError(errno.EINVAL, - m18n.n('global_settings_cant_serialize_settings', reason=e), + raise MoulinetteError(m18n.n('global_settings_cant_serialize_settings', reason=e), exc_info=1) try: with open(location, "w") as settings_fd: settings_fd.write(result) except Exception as e: - raise MoulinetteError(errno.EIO, - m18n.n('global_settings_cant_write_settings', reason=e), + raise MoulinetteError(m18n.n('global_settings_cant_write_settings', reason=e), exc_info=1) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index c6752d2dc..78bfbf047 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -145,8 +145,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas # Check that the mail domain exists if mail.split("@")[1] not in domain_list(auth)['domains']: - raise MoulinetteError(errno.EINVAL, - m18n.n('mail_domain_unknown', + raise MoulinetteError(m18n.n('mail_domain_unknown', domain=mail.split("@")[1])) operation_logger.start() @@ -325,8 +324,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, ] auth.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: - raise MoulinetteError(errno.EINVAL, - m18n.n('mail_domain_unknown', + raise MoulinetteError(m18n.n('mail_domain_unknown', domain=mail[mail.find('@') + 1:])) if mail in aliases: raise MoulinetteError(errno.EEXIST,m18n.n('mail_unavailable')) @@ -340,8 +338,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, for mail in add_mailalias: auth.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: - raise MoulinetteError(errno.EINVAL, - m18n.n('mail_domain_unknown', + raise MoulinetteError(m18n.n('mail_domain_unknown', domain=mail[mail.find('@') + 1:])) user['mail'].append(mail) new_attr_dict['mail'] = user['mail'] From fab662f4ef8d4f039ec2ca3c42f6707d419854c3 Mon Sep 17 00:00:00 2001 From: Irina LAMBLA Date: Fri, 9 Nov 2018 19:07:23 +0100 Subject: [PATCH 076/104] The rest The ones that sed didn't find. Modified by hand. --- src/yunohost/app.py | 52 ++++++++++----------------------- src/yunohost/backup.py | 4 +-- src/yunohost/certificate.py | 3 +- src/yunohost/domain.py | 6 ++-- src/yunohost/dyndns.py | 4 +-- src/yunohost/hook.py | 2 +- src/yunohost/log.py | 2 +- src/yunohost/service.py | 23 ++++----------- src/yunohost/tools.py | 16 ++++------ src/yunohost/user.py | 3 +- src/yunohost/utils/yunopaste.py | 9 ++---- 11 files changed, 36 insertions(+), 88 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 92aae5336..5e7860992 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -184,9 +184,7 @@ def app_fetchlist(url=None, name=None): with open(list_file, "w") as f: f.write(appslist) except Exception as e: - raise MoulinetteError(errno.EIO, - "Error while writing appslist %s: %s" % - (name, str(e))) + raise MoulinetteError("Error while writing appslist %s: %s" % (name, str(e))) now = int(time.time()) appslists[name]["lastUpdate"] = now @@ -843,9 +841,9 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on if install_retcode == -1: msg = m18n.n('operation_interrupted') + " " + error_msg - raise MoulinetteError(errno.EINTR, msg) + raise MoulinetteError(msg) msg = error_msg - raise MoulinetteError(errno.EIO, msg) + raise MoulinetteError(msg) # Clean hooks and add new ones hook_remove(app_instance_name) @@ -1135,10 +1133,7 @@ def app_makedefault(operation_logger, auth, app, domain=None): operation_logger.start() if '/' in app_map(raw=True)[domain]: - raise MoulinetteError(errno.EEXIST, - m18n.n('app_make_default_location_already_used', - app=app, domain=app_domain, - other_app=app_map(raw=True)[domain]["/"]["id"])) + raise MoulinetteError('app_make_default_location_already_used', app=app, domain=app_domain, other_app=app_map(raw=True)[domain]["/"]["id"]) try: with open('/etc/ssowat/conf.json.persistent') as json_conf: @@ -1298,14 +1293,10 @@ def app_checkurl(auth, url, app=None): installed = True continue if path == p: - raise MoulinetteError(errno.EINVAL, - m18n.n('app_location_already_used', - app=a["id"], path=path)) + raise MoulinetteError('app_location_already_used', app=a["id"], path=path) # can't install "/a/b/" if "/a/" exists elif path.startswith(p) or p.startswith(path): - raise MoulinetteError(errno.EPERM, - m18n.n('app_location_install_failed', - other_path=p, other_app=a['id'])) + raise MoulinetteError('app_location_install_failed', other_path=p, other_app=a['id')) if app is not None and not installed: app_setting(app, 'domain', value=domain) @@ -1482,7 +1473,7 @@ def app_action_run(app, action, args=None): actions = {x["id"]: x for x in actions} if action not in actions: - raise MoulinetteError(errno.EINVAL, "action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys()))) + raise MoulinetteError("action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys()))) action_declaration = actions[action] @@ -2066,7 +2057,7 @@ def _check_manifest_requirements(manifest, app_instance_name): yunohost_req = requirements.get('yunohost', None) if (not yunohost_req or not packages.SpecifierSet(yunohost_req) & '>= 2.3.6'): - raise MoulinetteError(errno.EINVAL, '{0}{1}'.format( + raise MoulinetteError('{0}{1}'.format( m18n.g('colon', m18n.n('app_incompatible'), app=app_instance_name), m18n.n('app_package_need_update', app=app_instance_name))) elif not requirements: @@ -2079,9 +2070,7 @@ def _check_manifest_requirements(manifest, app_instance_name): versions = packages.get_installed_version( *requirements.keys(), strict=True, as_dict=True) except packages.PackageException as e: - raise MoulinetteError(errno.EINVAL, - m18n.n('app_requirements_failed', - error=str(e), app=app_instance_name)) + raise MoulinetteError('app_requirements_failed', error=str(e), app=app_instance_name) # Iterate over requirements for pkgname, spec in requirements.items(): @@ -2221,28 +2210,20 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): # Validate argument choice if arg_choices and arg_value not in arg_choices: - raise MoulinetteError(errno.EINVAL, - m18n.n('app_argument_choice_invalid', - name=arg_name, choices=', '.join(arg_choices))) + raise MoulinetteError('app_argument_choice_invalid', name=arg_name, choices=', '.join(arg_choices)) # Validate argument type if arg_type == 'domain': if arg_value not in domain_list(auth)['domains']: - raise MoulinetteError(errno.EINVAL, - m18n.n('app_argument_invalid', - name=arg_name, error=m18n.n('domain_unknown'))) + raise MoulinetteError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown')) elif arg_type == 'user': try: user_info(auth, arg_value) except MoulinetteError as e: - raise MoulinetteError(errno.EINVAL, - m18n.n('app_argument_invalid', - name=arg_name, error=e.strerror)) + raise MoulinetteError('app_argument_invalid', name=arg_name, error=e.strerror) elif arg_type == 'app': if not _is_installed(arg_value): - raise MoulinetteError(errno.EINVAL, - m18n.n('app_argument_invalid', - name=arg_name, error=m18n.n('app_unknown'))) + raise MoulinetteError('app_argument_invalid', name=arg_name, error=m18n.n('app_unknown')) elif arg_type == 'boolean': if isinstance(arg_value, bool): arg_value = 1 if arg_value else 0 @@ -2252,9 +2233,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): elif str(arg_value).lower() in ["0", "no", "n"]: arg_value = 0 else: - raise MoulinetteError(errno.EINVAL, - m18n.n('app_argument_choice_invalid', - name=arg_name, choices='yes, no, y, n, 1, 0')) + raise MoulinetteError('app_argument_choice_invalid', name=arg_name, choices='yes, no, y, n, 1, 0') elif arg_type == 'password': from yunohost.utils.password import assert_password_is_strong_enough assert_password_is_strong_enough('user', arg_value) @@ -2453,8 +2432,7 @@ def _write_appslist_list(appslist_lists): with open(APPSLISTS_JSON, "w") as f: json.dump(appslist_lists, f) except Exception as e: - raise MoulinetteError(errno.EIO, - "Error while writing list of appslist %s: %s" % + raise MoulinetteError("Error while writing list of appslist %s: %s" % (APPSLISTS_JSON, str(e))) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 9893def33..84d89dbca 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -2295,9 +2295,7 @@ def _create_archive_dir(): """ Create the YunoHost archives directory if doesn't exist """ if not os.path.isdir(ARCHIVES_PATH): if os.path.lexists(ARCHIVES_PATH): - raise MoulinetteError(errno.EINVAL, - m18n.n('backup_output_symlink_dir_broken', - path=ARCHIVES_PATH)) + raise MoulinetteError('backup_output_symlink_dir_broken', path=ARCHIVES_PATH) os.mkdir(ARCHIVES_PATH, 0750) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 61eeca319..e75aebcc7 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -106,8 +106,7 @@ def certificate_status(auth, domain_list, full=False): for domain in domain_list: # Is it in Yunohost domain list? if domain not in yunohost_domains_list: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'certmanager_domain_unknown', domain=domain)) + raise MoulinetteError('certmanager_domain_unknown', domain=domain) certificates = {} diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 98ebf3239..7be214914 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -87,16 +87,14 @@ def domain_add(operation_logger, auth, domain, dyndns=False): # Do not allow to subscribe to multiple dyndns domains... if os.path.exists('/etc/cron.d/yunohost-dyndns'): - raise MoulinetteError(errno.EPERM, - m18n.n('domain_dyndns_already_subscribed')) + raise MoulinetteError('domain_dyndns_already_subscribed') from yunohost.dyndns import dyndns_subscribe, _dyndns_provides # Check that this domain can effectively be provided by # dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st) if not _dyndns_provides("dyndns.yunohost.org", domain): - raise MoulinetteError(errno.EINVAL, - m18n.n('domain_dyndns_root_unknown')) + raise MoulinetteError('domain_dyndns_root_unknown') # Actually subscribe dyndns_subscribe(domain=domain) diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index d9643054e..cda55f5e0 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -77,9 +77,7 @@ def _dyndns_provides(provider, domain): dyndomains = download_json('https://%s/domains' % provider, timeout=30) except MoulinetteError as e: logger.error(str(e)) - raise MoulinetteError(errno.EIO, - m18n.n('dyndns_could_not_check_provide', - domain=domain, provider=provider)) + raise MoulinetteError('dyndns_could_not_check_provide', domain=domain, provider=provider) # Extract 'dyndomain' from 'domain', e.g. 'nohost.me' from 'foo.nohost.me' dyndomain = '.'.join(domain.split('.')[1:]) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index fef5199d9..665d2fe40 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -318,7 +318,7 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, if path[0] != '/': path = os.path.realpath(path) if not os.path.isfile(path): - raise MoulinetteError(errno.EIO, m18n.g('file_not_exist', path=path)) + raise MoulinetteError('file_not_exist', path=path) # Construct command variables cmd_args = '' diff --git a/src/yunohost/log.py b/src/yunohost/log.py index d31231c72..3df630b14 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -188,7 +188,7 @@ def log_display(path, number=50, share=False): if os.path.exists(log_path): logger.warning(error) else: - raise MoulinetteError(errno.EINVAL, error) + raise MoulinetteError(error) # Display logs if exist if os.path.exists(log_path): diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 14d16a73d..140c107be 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -130,10 +130,7 @@ def service_start(names): logger.success(m18n.n('service_started', service=name)) else: if service_status(name)['status'] != 'running': - raise MoulinetteError(errno.EPERM, - m18n.n('service_start_failed', - service=name, - logs=_get_journalctl_logs(name))) + raise MoulinetteError('service_start_failed', service=name, logs=_get_journalctl_logs(name)) logger.debug(m18n.n('service_already_started', service=name)) @@ -152,10 +149,7 @@ def service_stop(names): logger.success(m18n.n('service_stopped', service=name)) else: if service_status(name)['status'] != 'inactive': - raise MoulinetteError(errno.EPERM, - m18n.n('service_stop_failed', - service=name, - logs=_get_journalctl_logs(name))) + raise MoulinetteError('service_stop_failed', service=name, logs=_get_journalctl_logs(name)) logger.debug(m18n.n('service_already_stopped', service=name)) @is_unit_operation() @@ -174,10 +168,7 @@ def service_enable(operation_logger, names): if _run_service_command('enable', name): logger.success(m18n.n('service_enabled', service=name)) else: - raise MoulinetteError(errno.EPERM, - m18n.n('service_enable_failed', - service=name, - logs=_get_journalctl_logs(name))) + raise MoulinetteError('service_enable_failed', service=name, logs=_get_journalctl_logs(name)) def service_disable(names): @@ -194,10 +185,7 @@ def service_disable(names): if _run_service_command('disable', name): logger.success(m18n.n('service_disabled', service=name)) else: - raise MoulinetteError(errno.EPERM, - m18n.n('service_disable_failed', - service=name, - logs=_get_journalctl_logs(name))) + raise MoulinetteError('service_disable_failed', service=name, logs=_get_journalctl_logs(name)) def service_status(names=[]): @@ -220,8 +208,7 @@ def service_status(names=[]): for name in names: if check_names and name not in services.keys(): - raise MoulinetteError(errno.EINVAL, - m18n.n('service_unknown', service=name)) + raise MoulinetteError('service_unknown', service=name) # this "service" isn't a service actually so we skip it # diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 1c7581365..8e7da9939 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -139,8 +139,7 @@ def tools_adminpw(auth, new_password, check_strength=True): auth.update("cn=admin", { "userPassword": new_hash, }) except: logger.exception('unable to change admin password') - raise MoulinetteError(errno.EPERM, - m18n.n('admin_password_change_failed')) + raise MoulinetteError('admin_password_change_failed') else: # Write as root password try: @@ -289,8 +288,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, # Do some checks at first if os.path.isfile('/etc/yunohost/installed'): - raise MoulinetteError(errno.EPERM, - m18n.n('yunohost_already_installed')) + raise MoulinetteError('yunohost_already_installed') # Check password if not force_password: @@ -319,9 +317,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, dyndns = True # If not, abort the postinstall else: - raise MoulinetteError(errno.EEXIST, - m18n.n('dyndns_unavailable', - domain=domain)) + raise MoulinetteError('dyndns_unavailable', domain=domain) else: dyndns = False else: @@ -364,8 +360,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, with open('/etc/ssowat/conf.json.persistent') as json_conf: ssowat_conf = json.loads(str(json_conf.read())) except ValueError as e: - raise MoulinetteError(errno.EINVAL, - m18n.n('ssowat_persistent_conf_read_error', error=str(e))) + raise MoulinetteError('ssowat_persistent_conf_read_error', error=str(e)) except IOError: ssowat_conf = {} @@ -378,8 +373,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) except IOError as e: - raise MoulinetteError(errno.EPERM, - m18n.n('ssowat_persistent_conf_write_error', error=str(e))) + raise MoulinetteError('ssowat_persistent_conf_write_error', error=str(e)) os.system('chmod 644 /etc/ssowat/conf.json.persistent') diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 78bfbf047..c8aee153f 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -71,8 +71,7 @@ def user_list(auth, fields=None): if attr in keys: attrs.append(attr) else: - raise MoulinetteError(errno.EINVAL, - m18n.n('field_invalid', attr)) + raise MoulinetteError('field_invalid', attrz1) else: attrs = ['uid', 'cn', 'mail', 'mailuserquota', 'loginShell'] diff --git a/src/yunohost/utils/yunopaste.py b/src/yunohost/utils/yunopaste.py index 2b53062d1..475f29f79 100644 --- a/src/yunohost/utils/yunopaste.py +++ b/src/yunohost/utils/yunopaste.py @@ -13,17 +13,14 @@ def yunopaste(data): try: r = requests.post("%s/documents" % paste_server, data=data, timeout=30) except Exception as e: - raise MoulinetteError(errno.EIO, - "Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e)) + raise MoulinetteError("Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e)) if r.status_code != 200: - raise MoulinetteError(errno.EIO, - "Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text)) + raise MoulinetteError("Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text)) try: url = json.loads(r.text)["key"] except: - raise MoulinetteError(errno.EIO, - "Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text) + raise MoulinetteError("Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text) return "%s/raw/%s" % (paste_server, url) From 9eec09da81e6927e03f459b8c9b7f58f07a182f5 Mon Sep 17 00:00:00 2001 From: Irina LAMBLA Date: Sat, 17 Nov 2018 14:45:24 +0100 Subject: [PATCH 077/104] - "m18n.n" --- src/yunohost/backup.py | 14 +++----------- src/yunohost/settings.py | 25 ++++++++++--------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 84d89dbca..08544a1a6 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1083,15 +1083,9 @@ class RestoreManager(): return True elif free_space > needed_space: # TODO Add --force options to avoid the error raising - raise MoulinetteError(m18n.n('restore_may_be_not_enough_disk_space', - free_space=free_space, - needed_space=needed_space, - margin=margin)) + raise MoulinetteError('restore_may_be_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) else: - raise MoulinetteError(m18n.n('restore_not_enough_disk_space', - free_space=free_space, - needed_space=needed_space, - margin=margin)) + raise MoulinetteError('restore_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) ########################################################################### # "Actual restore" (reverse step of the backup collect part) # @@ -1141,9 +1135,7 @@ class RestoreManager(): newlines.append(row) except (IOError, OSError, csv.Error) as e: - raise MoulinetteError(m18n.n('error_reading_file', - file=backup_csv, - error=str(e))) + raise MoulinetteError('error_reading_file', file=backup_csv, error=str(e)) if not contains_php5: return diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index ca9a98561..5d875afb9 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -54,8 +54,7 @@ def settings_get(key, full=False): settings = _get_settings() if key not in settings: - raise MoulinetteError(m18n.n( - 'global_settings_key_doesnt_exists', settings_key=key)) + raise MoulinetteError('global_settings_key_doesnt_exists', settings_key=key) if full: return settings[key] @@ -101,18 +100,17 @@ def settings_set(key, value): 'global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type)) else: - raise MoulinetteError(m18n.n('global_settings_bad_type_for_setting', setting=key, - received_type=type(value).__name__, expected_type=key_type)) + raise MoulinetteError('global_settings_bad_type_for_setting', setting=key, + received_type=type(value).__name__, expected_type=key_type) elif key_type == "string": if not isinstance(value, basestring): - raise MoulinetteError(m18n.n('global_settings_bad_type_for_setting', setting=key, - received_type=type(value).__name__, expected_type=key_type)) + raise MoulinetteError('global_settings_bad_type_for_setting', setting=key, + received_type=type(value).__name__, expected_type=key_type) elif key_type == "enum": if value not in settings[key]["choices"]: - raise MoulinetteError(m18n.n( - 'global_settings_bad_choice_for_enum', setting=key, + raise MoulinetteError('global_settings_bad_choice_for_enum', setting=key, received_type=type(value).__name__, - expected_type=", ".join(settings[key]["choices"]))) + expected_type=", ".join(settings[key]["choices"])) else: raise MoulinetteError('global_settings_unknown_type', setting=key, unknown_type=key_type) @@ -209,8 +207,7 @@ def _get_settings(): setting_key=key)) unknown_settings[key] = value except Exception as e: - raise MoulinetteError(m18n.n('global_settings_cant_open_settings', reason=e), - exc_info=1) + raise MoulinetteError('global_settings_cant_open_settings', reason=e, exc_info=1) if unknown_settings: try: @@ -231,12 +228,10 @@ def _save_settings(settings, location=SETTINGS_PATH): try: result = json.dumps(settings_without_description, indent=4) except Exception as e: - raise MoulinetteError(m18n.n('global_settings_cant_serialize_settings', reason=e), - exc_info=1) + raise MoulinetteError('global_settings_cant_serialize_settings', reason=e, exc_info=1) try: with open(location, "w") as settings_fd: settings_fd.write(result) except Exception as e: - raise MoulinetteError(m18n.n('global_settings_cant_write_settings', reason=e), - exc_info=1) + raise MoulinetteError('global_settings_cant_write_settings', reason=e, exc_info=1) From 410e757732b9ce46c58774c05a87b06f19ba1d1a Mon Sep 17 00:00:00 2001 From: Irina LAMBLA Date: Fri, 30 Nov 2018 17:28:15 +0100 Subject: [PATCH 078/104] corrections --- src/yunohost/app.py | 4 ++-- src/yunohost/dyndns.py | 3 +-- src/yunohost/user.py | 9 ++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5e7860992..96c235835 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1296,7 +1296,7 @@ def app_checkurl(auth, url, app=None): raise MoulinetteError('app_location_already_used', app=a["id"], path=path) # can't install "/a/b/" if "/a/" exists elif path.startswith(p) or p.startswith(path): - raise MoulinetteError('app_location_install_failed', other_path=p, other_app=a['id')) + raise MoulinetteError('app_location_install_failed', other_path=p, other_app=a['id']) if app is not None and not installed: app_setting(app, 'domain', value=domain) @@ -1618,7 +1618,7 @@ def app_config_apply(app, args): installed = _is_installed(app) if not installed: - raise MoulinetteError('app_not_installed', app=app_id) + raise MoulinetteError('app_not_installed', app=app) config_panel = os.path.join(APPS_SETTING_PATH, app, 'config_panel.json') config_script = os.path.join(APPS_SETTING_PATH, app, 'scripts', 'config') diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index cda55f5e0..93f3a7a38 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -127,8 +127,7 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom # Verify if domain is provided by subscribe_host if not _dyndns_provides(subscribe_host, domain): - raise MoulinetteError(m18n.n('dyndns_domain_not_provided', - domain=domain, provider=subscribe_host)) + raise MoulinetteError('dyndns_domain_not_provided', domain=domain, provider=subscribe_host) # Verify if domain is available if not _dyndns_available(subscribe_host, domain): diff --git a/src/yunohost/user.py b/src/yunohost/user.py index c8aee153f..67cfe6cf1 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -71,7 +71,7 @@ def user_list(auth, fields=None): if attr in keys: attrs.append(attr) else: - raise MoulinetteError('field_invalid', attrz1) + raise MoulinetteError('field_invalid', attr) else: attrs = ['uid', 'cn', 'mail', 'mailuserquota', 'loginShell'] @@ -140,7 +140,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas ] if mail in aliases: - raise MoulinetteError(errno.EEXIST,m18n.n('mail_unavailable')) + raise MoulinetteError('mail_unavailable') # Check that the mail domain exists if mail.split("@")[1] not in domain_list(auth)['domains']: @@ -326,7 +326,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, raise MoulinetteError(m18n.n('mail_domain_unknown', domain=mail[mail.find('@') + 1:])) if mail in aliases: - raise MoulinetteError(errno.EEXIST,m18n.n('mail_unavailable')) + raise MoulinetteError('mail_unavailable') del user['mail'][0] new_attr_dict['mail'] = [mail] + user['mail'] @@ -337,8 +337,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, for mail in add_mailalias: auth.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: - raise MoulinetteError(m18n.n('mail_domain_unknown', - domain=mail[mail.find('@') + 1:])) + raise MoulinetteError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) user['mail'].append(mail) new_attr_dict['mail'] = user['mail'] From c4b77b2894e47847f59bf4bf058690b1cb6b24f4 Mon Sep 17 00:00:00 2001 From: Irina LAMBLA Date: Sat, 1 Dec 2018 00:12:16 +0100 Subject: [PATCH 079/104] add YunohostError --- src/yunohost/utils/error.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/yunohost/utils/error.py diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py new file mode 100644 index 000000000..a2b6f5055 --- /dev/null +++ b/src/yunohost/utils/error.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +""" License + + Copyright (C) 2018 YUNOHOST.ORG + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, see http://www.gnu.org/licenses + +""" + +from moulinette.core import MoulinetteError + +class YunohostError(MoulinetteError): + """Yunohost base exception""" + def __init__(self, key, *args, **kwargs): + msg = m18n.n(key, *args, **kwargs) + super(MoulinetteError, self).__init__(msg) + From 4ba309a2217389622f0362f5b10e550e2180b88e Mon Sep 17 00:00:00 2001 From: Irina LAMBLA Date: Fri, 7 Dec 2018 23:56:57 +0100 Subject: [PATCH 080/104] modif MoulinetteError to YunohostError --- src/yunohost/app.py | 132 +++++++++--------- src/yunohost/backup.py | 82 +++++------ src/yunohost/certificate.py | 42 +++--- .../0002_migrate_to_tsig_sha256.py | 8 +- .../0003_migrate_to_stretch.py | 12 +- .../0005_postgresql_9p4_to_9p6.py | 6 +- .../0006_sync_admin_and_root_passwords.py | 2 +- src/yunohost/domain.py | 22 +-- src/yunohost/dyndns.py | 26 ++-- src/yunohost/firewall.py | 10 +- src/yunohost/hook.py | 16 +-- src/yunohost/log.py | 6 +- src/yunohost/monitor.py | 26 ++-- src/yunohost/service.py | 26 ++-- src/yunohost/settings.py | 24 ++-- src/yunohost/ssh.py | 6 +- src/yunohost/tests/test_appslist.py | 12 +- src/yunohost/tests/test_appurl.py | 8 +- src/yunohost/tests/test_backuprestore.py | 23 +-- src/yunohost/tests/test_changeurl.py | 4 +- src/yunohost/tests/test_settings.py | 28 ++-- src/yunohost/tools.py | 38 ++--- src/yunohost/user.py | 36 ++--- src/yunohost/utils/error.py | 1 + src/yunohost/utils/password.py | 4 +- src/yunohost/utils/yunopaste.py | 8 +- 26 files changed, 305 insertions(+), 303 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 96c235835..f4e48de45 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -39,7 +39,7 @@ from collections import OrderedDict from datetime import datetime from moulinette import msignals, m18n, msettings -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_json @@ -125,12 +125,12 @@ def app_fetchlist(url=None, name=None): appslists_to_be_fetched = [name] operation_logger.success() else: - raise MoulinetteError('custom_appslist_name_required') + raise YunohostError('custom_appslist_name_required') # If a name is given, look for an appslist with that name and fetch it elif name is not None: if name not in appslists.keys(): - raise MoulinetteError('appslist_unknown', appslist=name) + raise YunohostError('appslist_unknown', appslist=name) else: appslists_to_be_fetched = [name] @@ -184,7 +184,7 @@ def app_fetchlist(url=None, name=None): with open(list_file, "w") as f: f.write(appslist) except Exception as e: - raise MoulinetteError("Error while writing appslist %s: %s" % (name, str(e))) + raise YunohostError("Error while writing appslist %s: %s" % (name, str(e))) now = int(time.time()) appslists[name]["lastUpdate"] = now @@ -208,7 +208,7 @@ def app_removelist(operation_logger, name): # Make sure we know this appslist if name not in appslists.keys(): - raise MoulinetteError('appslist_unknown', appslist=name) + raise YunohostError('appslist_unknown', appslist=name) operation_logger.start() @@ -339,7 +339,7 @@ def app_info(app, show_status=False, raw=False): """ if not _is_installed(app): - raise MoulinetteError('app_not_installed', app=app) + raise YunohostError('app_not_installed', app=app) app_setting_path = APPS_SETTING_PATH + app @@ -397,7 +397,7 @@ def app_map(app=None, raw=False, user=None): if app is not None: if not _is_installed(app): - raise MoulinetteError('app_not_installed', app=app) + raise YunohostError('app_not_installed', app=app) apps = [app, ] else: apps = os.listdir(APPS_SETTING_PATH) @@ -449,10 +449,10 @@ def app_change_url(operation_logger, auth, app, domain, path): installed = _is_installed(app) if not installed: - raise MoulinetteError('app_not_installed', app=app) + raise YunohostError('app_not_installed', app=app) if not os.path.exists(os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url")): - raise MoulinetteError("app_change_no_change_url_script", app_name=app) + raise YunohostError("app_change_no_change_url_script", app_name=app) old_domain = app_setting(app, "domain") old_path = app_setting(app, "path") @@ -464,7 +464,7 @@ def app_change_url(operation_logger, auth, app, domain, path): path = normalize_url_path(path) if (domain, path) == (old_domain, old_path): - raise MoulinetteError("app_change_url_identical_domains", domain=domain, path=path) + raise YunohostError("app_change_url_identical_domains", domain=domain, path=path) # WARNING / FIXME : checkurl will modify the settings # (this is a non intuitive behavior that should be changed) @@ -540,7 +540,7 @@ def app_change_url(operation_logger, auth, app, domain, path): stderr=subprocess.STDOUT, shell=True).rstrip() - raise MoulinetteError("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors) + raise YunohostError("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors) logger.success(m18n.n("app_change_url_success", app=app, domain=domain, path=path)) @@ -565,8 +565,8 @@ def app_upgrade(auth, app=[], url=None, file=None): try: app_list() - except MoulinetteError: - raise MoulinetteError('app_no_upgrade') + except YunohostError: + raise YunohostError('app_no_upgrade') upgraded_apps = [] @@ -586,7 +586,7 @@ def app_upgrade(auth, app=[], url=None, file=None): logger.info(m18n.n('app_upgrade_app_name', app=app_instance_name)) installed = _is_installed(app_instance_name) if not installed: - raise MoulinetteError('app_not_installed', app=app_instance_name) + raise YunohostError('app_not_installed', app=app_instance_name) if app_instance_name in upgraded_apps: continue @@ -676,7 +676,7 @@ def app_upgrade(auth, app=[], url=None, file=None): operation_logger.success() if not upgraded_apps: - raise MoulinetteError('app_no_upgrade') + raise YunohostError('app_no_upgrade') app_ssowatconf(auth) @@ -722,12 +722,12 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on elif os.path.exists(app): manifest, extracted_app_folder = _extract_app_from_file(app) else: - raise MoulinetteError('app_unknown') + raise YunohostError('app_unknown') status['remote'] = manifest.get('remote', {}) # Check ID if 'id' not in manifest or '__' in manifest['id']: - raise MoulinetteError('app_id_invalid') + raise YunohostError('app_id_invalid') app_id = manifest['id'] @@ -738,7 +738,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on instance_number = _installed_instance_number(app_id, last=True) + 1 if instance_number > 1: if 'multi_instance' not in manifest or not is_true(manifest['multi_instance']): - raise MoulinetteError('app_already_installed', app=app_id) + raise YunohostError('app_already_installed', app=app_id) # Change app_id to the forked app id app_instance_name = app_id + '__' + str(instance_number) @@ -841,9 +841,9 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on if install_retcode == -1: msg = m18n.n('operation_interrupted') + " " + error_msg - raise MoulinetteError(msg) + raise YunohostError(msg) msg = error_msg - raise MoulinetteError(msg) + raise YunohostError(msg) # Clean hooks and add new ones hook_remove(app_instance_name) @@ -879,7 +879,7 @@ def app_remove(operation_logger, auth, app): """ from yunohost.hook import hook_exec, hook_remove, hook_callback if not _is_installed(app): - raise MoulinetteError('app_not_installed', app=app) + raise YunohostError('app_not_installed', app=app) operation_logger.start() @@ -969,7 +969,7 @@ def app_addaccess(auth, apps, users=[]): if allowed_user not in allowed_users: try: user_info(auth, allowed_user) - except MoulinetteError: + except YunohostError: logger.warning(m18n.n('user_unknown', user=allowed_user)) continue allowed_users.add(allowed_user) @@ -1129,17 +1129,17 @@ def app_makedefault(operation_logger, auth, app, domain=None): domain = app_domain operation_logger.related_to.append(('domain',domain)) elif domain not in domain_list(auth)['domains']: - raise MoulinetteError('domain_unknown') + raise YunohostError('domain_unknown') operation_logger.start() if '/' in app_map(raw=True)[domain]: - raise MoulinetteError('app_make_default_location_already_used', app=app, domain=app_domain, other_app=app_map(raw=True)[domain]["/"]["id"]) + raise YunohostError('app_make_default_location_already_used', app=app, domain=app_domain, other_app=app_map(raw=True)[domain]["/"]["id"]) try: with open('/etc/ssowat/conf.json.persistent') as json_conf: ssowat_conf = json.loads(str(json_conf.read())) except ValueError as e: - raise MoulinetteError('ssowat_persistent_conf_read_error', error=e.strerror) + raise YunohostError('ssowat_persistent_conf_read_error', error=e.strerror) except IOError: ssowat_conf = {} @@ -1152,7 +1152,7 @@ def app_makedefault(operation_logger, auth, app, domain=None): with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) except IOError as e: - raise MoulinetteError('ssowat_persistent_conf_write_error', error=e.strerror) + raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror) os.system('chmod 644 /etc/ssowat/conf.json.persistent') @@ -1204,7 +1204,7 @@ def app_checkport(port): if tools_port_available(port): logger.success(m18n.n('port_available', port=int(port))) else: - raise MoulinetteError('port_unavailable', port=int(port)) + raise YunohostError('port_unavailable', port=int(port)) def app_register_url(auth, app, domain, path): @@ -1231,7 +1231,7 @@ def app_register_url(auth, app, domain, path): if installed: settings = _get_app_settings(app) if "path" in settings.keys() and "domain" in settings.keys(): - raise MoulinetteError('app_already_installed_cant_change_url') + raise YunohostError('app_already_installed_cant_change_url') # Check the url is available conflicts = _get_conflicting_apps(auth, domain, path) @@ -1245,7 +1245,7 @@ def app_register_url(auth, app, domain, path): app_label=app_label, )) - raise MoulinetteError('app_location_unavailable', apps="\n".join(apps)) + raise YunohostError('app_location_unavailable', apps="\n".join(apps)) app_setting(app, 'domain', value=domain) app_setting(app, 'path', value=path) @@ -1283,7 +1283,7 @@ def app_checkurl(auth, url, app=None): apps_map = app_map(raw=True) if domain not in domain_list(auth)['domains']: - raise MoulinetteError('domain_unknown') + raise YunohostError('domain_unknown') if domain in apps_map: # Loop through apps @@ -1293,10 +1293,10 @@ def app_checkurl(auth, url, app=None): installed = True continue if path == p: - raise MoulinetteError('app_location_already_used', app=a["id"], path=path) + raise YunohostError('app_location_already_used', app=a["id"], path=path) # can't install "/a/b/" if "/a/" exists elif path.startswith(p) or p.startswith(path): - raise MoulinetteError('app_location_install_failed', other_path=p, other_app=a['id']) + raise YunohostError('app_location_install_failed', other_path=p, other_app=a['id']) if app is not None and not installed: app_setting(app, 'domain', value=domain) @@ -1328,10 +1328,10 @@ def app_initdb(user, password=None, db=None, sql=None): mysql_root_pwd = open('/etc/yunohost/mysql').read().rstrip() mysql_command = 'mysql -u root -p%s -e "CREATE DATABASE %s ; GRANT ALL PRIVILEGES ON %s.* TO \'%s\'@localhost IDENTIFIED BY \'%s\';"' % (mysql_root_pwd, db, db, user, password) if os.system(mysql_command) != 0: - raise MoulinetteError('mysql_db_creation_failed') + raise YunohostError('mysql_db_creation_failed') if sql is not None: if os.system('mysql -u %s -p%s %s < %s' % (user, password, db, sql)) != 0: - raise MoulinetteError('mysql_db_init_failed') + raise YunohostError('mysql_db_init_failed') if return_pwd: return password @@ -1437,7 +1437,7 @@ def app_ssowatconf(auth): def app_change_label(auth, app, new_label): installed = _is_installed(app) if not installed: - raise MoulinetteError('app_not_installed', app=app) + raise YunohostError('app_not_installed', app=app) app_setting(app, "label", value=new_label) @@ -1473,7 +1473,7 @@ def app_action_run(app, action, args=None): actions = {x["id"]: x for x in actions} if action not in actions: - raise MoulinetteError("action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys()))) + raise YunohostError("action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys()))) action_declaration = actions[action] @@ -1511,7 +1511,7 @@ def app_action_run(app, action, args=None): ) if retcode not in action_declaration.get("accepted_return_codes", [0]): - raise MoulinetteError(retcode, "Error while executing action '%s' of app '%s': return code %s" % (action, app, retcode)) + raise YunohostError(retcode, "Error while executing action '%s' of app '%s': return code %s" % (action, app, retcode)) os.remove(path) @@ -1618,7 +1618,7 @@ def app_config_apply(app, args): installed = _is_installed(app) if not installed: - raise MoulinetteError('app_not_installed', app=app) + raise YunohostError('app_not_installed', app=app) config_panel = os.path.join(APPS_SETTING_PATH, app, 'config_panel.json') config_script = os.path.join(APPS_SETTING_PATH, app, 'scripts', 'config') @@ -1676,7 +1676,7 @@ def _get_app_settings(app_id): """ if not _is_installed(app_id): - raise MoulinetteError('app_not_installed', app=app_id) + raise YunohostError('app_not_installed', app=app_id) try: with open(os.path.join( APPS_SETTING_PATH, app_id, 'settings.yml')) as f: @@ -1714,7 +1714,7 @@ def _get_app_status(app_id, format_date=False): """ app_setting_path = APPS_SETTING_PATH + app_id if not os.path.isdir(app_setting_path): - raise MoulinetteError('app_unknown') + raise YunohostError('app_unknown') status = {} try: @@ -1779,7 +1779,7 @@ def _extract_app_from_file(path, remove=False): extract_result = 1 if extract_result != 0: - raise MoulinetteError('app_extraction_failed') + raise YunohostError('app_extraction_failed') try: extracted_app_folder = APP_TMP_FOLDER @@ -1790,9 +1790,9 @@ def _extract_app_from_file(path, remove=False): manifest = json.loads(str(json_manifest.read())) manifest['lastUpdate'] = int(time.time()) except IOError: - raise MoulinetteError('app_install_files_invalid') + raise YunohostError('app_install_files_invalid') except ValueError as e: - raise MoulinetteError('app_manifest_invalid', error=e.strerror) + raise YunohostError('app_manifest_invalid', error=e.strerror) logger.debug(m18n.n('done')) @@ -1860,7 +1860,7 @@ def _fetch_app_from_git(app): 'wget', '-qO', app_tmp_archive, tarball_url]) except subprocess.CalledProcessError: logger.exception('unable to download %s', tarball_url) - raise MoulinetteError('app_sources_fetch_failed') + raise YunohostError('app_sources_fetch_failed') else: manifest, extracted_app_folder = _extract_app_from_file( app_tmp_archive, remove=True) @@ -1883,9 +1883,9 @@ def _fetch_app_from_git(app): with open(extracted_app_folder + '/manifest.json') as f: manifest = json.loads(str(f.read())) except subprocess.CalledProcessError: - raise MoulinetteError('app_sources_fetch_failed') + raise YunohostError('app_sources_fetch_failed') except ValueError as e: - raise MoulinetteError('app_manifest_invalid', error=e.strerror) + raise YunohostError('app_manifest_invalid', error=e.strerror) else: logger.debug(m18n.n('done')) @@ -1905,10 +1905,10 @@ def _fetch_app_from_git(app): app_info['manifest']['lastUpdate'] = app_info['lastUpdate'] manifest = app_info['manifest'] else: - raise MoulinetteError('app_unknown') + raise YunohostError('app_unknown') if 'git' not in app_info: - raise MoulinetteError('app_unsupported_remote_type') + raise YunohostError('app_unsupported_remote_type') url = app_info['git']['url'] if 'github.com' in url: @@ -1920,7 +1920,7 @@ def _fetch_app_from_git(app): 'wget', '-qO', app_tmp_archive, tarball_url]) except subprocess.CalledProcessError: logger.exception('unable to download %s', tarball_url) - raise MoulinetteError('app_sources_fetch_failed') + raise YunohostError('app_sources_fetch_failed') else: manifest, extracted_app_folder = _extract_app_from_file( app_tmp_archive, remove=True) @@ -1936,9 +1936,9 @@ def _fetch_app_from_git(app): with open(extracted_app_folder + '/manifest.json') as f: manifest = json.loads(str(f.read())) except subprocess.CalledProcessError: - raise MoulinetteError('app_sources_fetch_failed') + raise YunohostError('app_sources_fetch_failed') except ValueError as e: - raise MoulinetteError('app_manifest_invalid', error=e.strerror) + raise YunohostError('app_manifest_invalid', error=e.strerror) else: logger.debug(m18n.n('done')) @@ -2057,7 +2057,7 @@ def _check_manifest_requirements(manifest, app_instance_name): yunohost_req = requirements.get('yunohost', None) if (not yunohost_req or not packages.SpecifierSet(yunohost_req) & '>= 2.3.6'): - raise MoulinetteError('{0}{1}'.format( + raise YunohostError('{0}{1}'.format( m18n.g('colon', m18n.n('app_incompatible'), app=app_instance_name), m18n.n('app_package_need_update', app=app_instance_name))) elif not requirements: @@ -2070,13 +2070,13 @@ def _check_manifest_requirements(manifest, app_instance_name): versions = packages.get_installed_version( *requirements.keys(), strict=True, as_dict=True) except packages.PackageException as e: - raise MoulinetteError('app_requirements_failed', error=str(e), app=app_instance_name) + raise YunohostError('app_requirements_failed', error=str(e), app=app_instance_name) # Iterate over requirements for pkgname, spec in requirements.items(): version = versions[pkgname] if version not in packages.SpecifierSet(spec): - raise MoulinetteError( + raise YunohostError( errno.EINVAL, m18n.n('app_requirements_unmeet', pkgname=pkgname, version=version, spec=spec, app=app_instance_name)) @@ -2203,27 +2203,27 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): # Validate argument value if (arg_value is None or arg_value == '') \ and not arg.get('optional', False): - raise MoulinetteError('app_argument_required', name=arg_name) + raise YunohostError('app_argument_required', name=arg_name) elif arg_value is None: args_dict[arg_name] = '' continue # Validate argument choice if arg_choices and arg_value not in arg_choices: - raise MoulinetteError('app_argument_choice_invalid', name=arg_name, choices=', '.join(arg_choices)) + raise YunohostError('app_argument_choice_invalid', name=arg_name, choices=', '.join(arg_choices)) # Validate argument type if arg_type == 'domain': if arg_value not in domain_list(auth)['domains']: - raise MoulinetteError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown')) + raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown')) elif arg_type == 'user': try: user_info(auth, arg_value) - except MoulinetteError as e: - raise MoulinetteError('app_argument_invalid', name=arg_name, error=e.strerror) + except YunohostError as e: + raise YunohostError('app_argument_invalid', name=arg_name, error=e.strerror) elif arg_type == 'app': if not _is_installed(arg_value): - raise MoulinetteError('app_argument_invalid', name=arg_name, error=m18n.n('app_unknown')) + raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('app_unknown')) elif arg_type == 'boolean': if isinstance(arg_value, bool): arg_value = 1 if arg_value else 0 @@ -2233,7 +2233,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): elif str(arg_value).lower() in ["0", "no", "n"]: arg_value = 0 else: - raise MoulinetteError('app_argument_choice_invalid', name=arg_name, choices='yes, no, y, n, 1, 0') + raise YunohostError('app_argument_choice_invalid', name=arg_name, choices='yes, no, y, n, 1, 0') elif arg_type == 'password': from yunohost.utils.password import assert_password_is_strong_enough assert_password_is_strong_enough('user', arg_value) @@ -2267,7 +2267,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): app_label=app_label, )) - raise MoulinetteError('app_location_unavailable', apps="\n".join(apps)) + raise YunohostError('app_location_unavailable', apps="\n".join(apps)) # (We save this normalized path so that the install script have a # standard path format to deal with no matter what the user inputted) @@ -2417,7 +2417,7 @@ def _read_appslist_list(): try: appslists = json.loads(appslists_json) except ValueError: - raise MoulinetteError('appslist_corrupted_json', filename=APPSLISTS_JSON) + raise YunohostError('appslist_corrupted_json', filename=APPSLISTS_JSON) return appslists @@ -2432,7 +2432,7 @@ def _write_appslist_list(appslist_lists): with open(APPSLISTS_JSON, "w") as f: json.dump(appslist_lists, f) except Exception as e: - raise MoulinetteError("Error while writing list of appslist %s: %s" % + raise YunohostError("Error while writing list of appslist %s: %s" % (APPSLISTS_JSON, str(e))) @@ -2446,13 +2446,13 @@ def _register_new_appslist(url, name): # Check if name conflicts with an existing list if name in appslist_list: - raise MoulinetteError('appslist_name_already_tracked', name=name) + raise YunohostError('appslist_name_already_tracked', name=name) # Check if url conflicts with an existing list known_appslist_urls = [appslist["url"] for _, appslist in appslist_list.items()] if url in known_appslist_urls: - raise MoulinetteError('appslist_url_already_tracked', url=url) + raise YunohostError('appslist_url_already_tracked', url=url) logger.debug("Registering new appslist %s at %s" % (name, url)) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 08544a1a6..24313d416 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -38,7 +38,7 @@ from glob import glob from collections import OrderedDict from moulinette import msignals, m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils import filesystem from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_file @@ -308,14 +308,14 @@ class BackupManager(): Ensure the working directory exists and is empty exception: - backup_output_directory_not_empty -- (MoulinetteError) Raised if the + 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 -- (MoulinetteError) + (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 -- (MoulinetteError) Raised + (TODO) backup_cant_create_working_directory -- (YunohostError) Raised if iyunohost can't create the working directory """ @@ -327,7 +327,7 @@ class BackupManager(): logger.debug("temporary directory for backup '%s' already exists", self.work_dir) # FIXME May be we should clean the workdir here - raise MoulinetteError('backup_output_directory_not_empty') + raise YunohostError('backup_output_directory_not_empty') ########################################################################### # Backup target management # @@ -493,7 +493,7 @@ class BackupManager(): copied here Exceptions: - "backup_nothings_done" -- (MoulinetteError) This exception is raised if + "backup_nothings_done" -- (YunohostError) This exception is raised if nothing has been listed. """ @@ -506,7 +506,7 @@ class BackupManager(): if not successfull_apps and not successfull_system: filesystem.rm(self.work_dir, True, True) - raise MoulinetteError('backup_nothings_done') + raise YunohostError('backup_nothings_done') # Add unlisted files from backup tmp dir self._add_to_list_to_backup('backup.csv') @@ -856,7 +856,7 @@ class RestoreManager(): self.info["system"] = self.info["hooks"] except IOError: logger.debug("unable to load '%s'", info_file, exc_info=1) - raise MoulinetteError('backup_invalid_archive') + raise YunohostError('backup_invalid_archive') else: logger.debug("restoring from backup '%s' created on %s", self.name, datetime.utcfromtimestamp(self.info['created_at'])) @@ -879,7 +879,7 @@ class RestoreManager(): logger.debug("unable to retrieve current_host from the backup", exc_info=1) # FIXME include the current_host by default ? - raise MoulinetteError('backup_invalid_archive') + raise YunohostError('backup_invalid_archive') logger.debug("executing the post-install...") tools_postinstall(domain, 'yunohost', True) @@ -1008,7 +1008,7 @@ class RestoreManager(): subprocess.call(['rmdir', self.work_dir]) logger.debug("Unmount dir: {}".format(self.work_dir)) else: - raise MoulinetteError('restore_removing_tmp_dir_failed') + raise YunohostError('restore_removing_tmp_dir_failed') elif os.path.isdir(self.work_dir): logger.debug("temporary restore directory '%s' already exists", self.work_dir) @@ -1016,7 +1016,7 @@ class RestoreManager(): if ret == 0: logger.debug("Delete dir: {}".format(self.work_dir)) else: - raise MoulinetteError('restore_removing_tmp_dir_failed') + raise YunohostError('restore_removing_tmp_dir_failed') filesystem.mkdir(self.work_dir, parents=True) @@ -1083,9 +1083,9 @@ class RestoreManager(): return True elif free_space > needed_space: # TODO Add --force options to avoid the error raising - raise MoulinetteError('restore_may_be_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) + raise YunohostError('restore_may_be_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) else: - raise MoulinetteError('restore_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) + raise YunohostError('restore_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) ########################################################################### # "Actual restore" (reverse step of the backup collect part) # @@ -1135,7 +1135,7 @@ class RestoreManager(): newlines.append(row) except (IOError, OSError, csv.Error) as e: - raise MoulinetteError('error_reading_file', file=backup_csv, error=str(e)) + raise YunohostError('error_reading_file', file=backup_csv, error=str(e)) if not contains_php5: return @@ -1428,7 +1428,7 @@ class BackupMethod(object): @property def method_name(self): """Return the string name of a BackupMethod (eg "tar" or "copy")""" - raise MoulinetteError('backup_abstract_method') + raise YunohostError('backup_abstract_method') @property def name(self): @@ -1510,7 +1510,7 @@ class BackupMethod(object): """ if self.need_mount(): if self._recursive_umount(self.work_dir) > 0: - raise MoulinetteError('backup_cleaning_failed') + raise YunohostError('backup_cleaning_failed') if self.manager.is_tmp_work_dir: filesystem.rm(self.work_dir, True, True) @@ -1552,7 +1552,7 @@ class BackupMethod(object): if free_space < backup_size: logger.debug('Not enough space at %s (free: %s / needed: %d)', self.repo, free_space, backup_size) - raise MoulinetteError('not_enough_disk_space', path=self.repo) + raise YunohostError('not_enough_disk_space', path=self.repo) def _organize_files(self): """ @@ -1640,10 +1640,10 @@ class BackupMethod(object): i = msignals.prompt(m18n.n('backup_ask_for_copying_if_needed', answers='y/N', size=str(size))) except NotImplemented: - raise MoulinetteError('backup_unable_to_organize_files') + raise YunohostError('backup_unable_to_organize_files') else: if i != 'y' and i != 'Y': - raise MoulinetteError('backup_unable_to_organize_files') + raise YunohostError('backup_unable_to_organize_files') # Copy unbinded path logger.debug(m18n.n('backup_copying_to_organize_the_archive', @@ -1733,7 +1733,7 @@ class CopyBackupMethod(BackupMethod): super(CopyBackupMethod, self).mount() if not os.path.isdir(self.repo): - raise MoulinetteError('backup_no_uncompress_archive_dir') + raise YunohostError('backup_no_uncompress_archive_dir') filesystem.mkdir(self.work_dir, parent=True) ret = subprocess.call(["mount", "-r", "--rbind", self.repo, @@ -1744,7 +1744,7 @@ class CopyBackupMethod(BackupMethod): logger.warning(m18n.n("bind_mouting_disable")) subprocess.call(["mountpoint", "-q", dest, "&&", "umount", "-R", dest]) - raise MoulinetteError('backup_cant_mount_uncompress_archive') + raise YunohostError('backup_cant_mount_uncompress_archive') class TarBackupMethod(BackupMethod): @@ -1789,7 +1789,7 @@ class TarBackupMethod(BackupMethod): except: logger.debug("unable to open '%s' for writing", self._archive_file, exc_info=1) - raise MoulinetteError('backup_archive_open_failed') + raise YunohostError('backup_archive_open_failed') # Add files to the archive try: @@ -1800,7 +1800,7 @@ class TarBackupMethod(BackupMethod): tar.close() except IOError: logger.error(m18n.n('backup_archive_writing_error'), exc_info=1) - raise MoulinetteError('backup_creation_failed') + raise YunohostError('backup_creation_failed') # Move info file shutil.copy(os.path.join(self.work_dir, 'info.json'), @@ -1828,7 +1828,7 @@ class TarBackupMethod(BackupMethod): except: logger.debug("cannot open backup archive '%s'", self._archive_file, exc_info=1) - raise MoulinetteError('backup_archive_open_failed') + raise YunohostError('backup_archive_open_failed') tar.close() # Mount the tarball @@ -1889,10 +1889,10 @@ class BorgBackupMethod(BackupMethod): super(CopyBackupMethod, self).backup() # TODO run borg create command - raise MoulinetteError('backup_borg_not_implemented') + raise YunohostError('backup_borg_not_implemented') def mount(self, mnt_path): - raise MoulinetteError('backup_borg_not_implemented') + raise YunohostError('backup_borg_not_implemented') class CustomBackupMethod(BackupMethod): @@ -1938,7 +1938,7 @@ class CustomBackupMethod(BackupMethod): ret = hook_callback('backup_method', [self.method], args=self._get_args('backup')) if ret['failed']: - raise MoulinetteError('backup_custom_backup_error') + raise YunohostError('backup_custom_backup_error') def mount(self, restore_manager): """ @@ -1951,7 +1951,7 @@ class CustomBackupMethod(BackupMethod): ret = hook_callback('backup_method', [self.method], args=self._get_args('mount')) if ret['failed']: - raise MoulinetteError('backup_custom_mount_error') + raise YunohostError('backup_custom_mount_error') def _get_args(self, action): """Return the arguments to give to the custom script""" @@ -1987,7 +1987,7 @@ def backup_create(name=None, description=None, methods=[], # Validate there is no archive with the same name if name and name in backup_list()['archives']: - raise MoulinetteError('backup_archive_name_exists') + raise YunohostError('backup_archive_name_exists') # Validate output_directory option if output_directory: @@ -1997,14 +1997,14 @@ def backup_create(name=None, description=None, methods=[], if output_directory.startswith(ARCHIVES_PATH) or \ re.match(r'^/(|(bin|boot|dev|etc|lib|root|run|sbin|sys|usr|var)(|/.*))$', output_directory): - raise MoulinetteError('backup_output_directory_forbidden') + raise YunohostError('backup_output_directory_forbidden') # Check that output directory is empty if os.path.isdir(output_directory) and no_compress and \ os.listdir(output_directory): - raise MoulinetteError('backup_output_directory_not_empty') + raise YunohostError('backup_output_directory_not_empty') elif no_compress: - raise MoulinetteError('backup_output_directory_required') + raise YunohostError('backup_output_directory_required') # Define methods (retro-compat) if not methods: @@ -2101,7 +2101,7 @@ def backup_restore(auth, name, system=[], apps=[], force=False): if i == 'y' or i == 'Y': force = True if not force: - raise MoulinetteError('restore_failed') + raise YunohostError('restore_failed') # TODO Partial app restore could not work if ldap is not restored before # TODO repair mysql if broken and it's a complete restore @@ -2128,7 +2128,7 @@ def backup_restore(auth, name, system=[], apps=[], force=False): if restore_manager.success: logger.success(m18n.n('restore_complete')) else: - raise MoulinetteError('restore_nothings_done') + raise YunohostError('restore_nothings_done') return restore_manager.targets.results @@ -2164,7 +2164,7 @@ def backup_list(with_info=False, human_readable=False): for a in result: try: d[a] = backup_info(a, human_readable=human_readable) - except MoulinetteError, e: + except YunohostError, e: logger.warning('%s: %s' % (a, e.strerror)) result = d @@ -2186,7 +2186,7 @@ def backup_info(name, with_details=False, human_readable=False): # Check file exist (even if it's a broken symlink) if not os.path.lexists(archive_file): - raise MoulinetteError('backup_archive_name_unknown', name=name) + raise YunohostError('backup_archive_name_unknown', name=name) # If symlink, retrieve the real path if os.path.islink(archive_file): @@ -2194,7 +2194,7 @@ def backup_info(name, with_details=False, human_readable=False): # Raise exception if link is broken (e.g. on unmounted external storage) if not os.path.exists(archive_file): - raise MoulinetteError('backup_archive_broken_link', + raise YunohostError('backup_archive_broken_link', path=archive_file) info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name) @@ -2207,7 +2207,7 @@ def backup_info(name, with_details=False, human_readable=False): except KeyError: logger.debug("unable to retrieve '%s' inside the archive", info_file, exc_info=1) - raise MoulinetteError('backup_invalid_archive') + raise YunohostError('backup_invalid_archive') else: shutil.move(os.path.join(info_dir, 'info.json'), info_file) finally: @@ -2220,7 +2220,7 @@ def backup_info(name, with_details=False, human_readable=False): info = json.load(f) except: logger.debug("unable to load '%s'", info_file, exc_info=1) - raise MoulinetteError('backup_invalid_archive') + raise YunohostError('backup_invalid_archive') # Retrieve backup size size = info.get('size', 0) @@ -2259,7 +2259,7 @@ def backup_delete(name): """ if name not in backup_list()["archives"]: - raise MoulinetteError('backup_archive_name_unknown', + raise YunohostError('backup_archive_name_unknown', name=name) hook_callback('pre_backup_delete', args=[name]) @@ -2287,7 +2287,7 @@ def _create_archive_dir(): """ Create the YunoHost archives directory if doesn't exist """ if not os.path.isdir(ARCHIVES_PATH): if os.path.lexists(ARCHIVES_PATH): - raise MoulinetteError('backup_output_symlink_dir_broken', path=ARCHIVES_PATH) + raise YunohostError('backup_output_symlink_dir_broken', path=ARCHIVES_PATH) os.mkdir(ARCHIVES_PATH, 0750) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index e75aebcc7..ca27d59df 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -37,7 +37,7 @@ from datetime import datetime from yunohost.vendor.acme_tiny.acme_tiny import get_crt as sign_certificate -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from yunohost.utils.network import get_public_ip @@ -106,7 +106,7 @@ def certificate_status(auth, domain_list, full=False): for domain in domain_list: # Is it in Yunohost domain list? if domain not in yunohost_domains_list: - raise MoulinetteError('certmanager_domain_unknown', domain=domain) + raise YunohostError('certmanager_domain_unknown', domain=domain) certificates = {} @@ -171,7 +171,7 @@ def _certificate_install_selfsigned(domain_list, force=False): status = _get_status(domain) if status["summary"]["code"] in ('good', 'great'): - raise MoulinetteError('certmanager_attempt_to_replace_valid_cert', domain=domain) + raise YunohostError('certmanager_attempt_to_replace_valid_cert', domain=domain) operation_logger.start() @@ -201,7 +201,7 @@ def _certificate_install_selfsigned(domain_list, force=False): if p.returncode != 0: logger.warning(out) - raise MoulinetteError('domain_cert_gen_failed') + raise YunohostError('domain_cert_gen_failed') else: logger.debug(out) @@ -259,12 +259,12 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F for domain in domain_list: yunohost_domains_list = yunohost.domain.domain_list(auth)['domains'] if domain not in yunohost_domains_list: - raise MoulinetteError('certmanager_domain_unknown', domain=domain) + raise YunohostError('certmanager_domain_unknown', domain=domain) # Is it self-signed? status = _get_status(domain) if not force and status["CA_type"]["code"] != "self-signed": - raise MoulinetteError('certmanager_domain_cert_not_selfsigned', domain=domain) + raise YunohostError('certmanager_domain_cert_not_selfsigned', domain=domain) if staging: logger.warning( @@ -344,21 +344,21 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal # Is it in Yunohost dmomain list? if domain not in yunohost.domain.domain_list(auth)['domains']: - raise MoulinetteError('certmanager_domain_unknown', domain=domain) + raise YunohostError('certmanager_domain_unknown', domain=domain) status = _get_status(domain) # Does it expire soon? if status["validity"] > VALIDITY_LIMIT and not force: - raise MoulinetteError('certmanager_attempt_to_renew_valid_cert', domain=domain) + raise YunohostError('certmanager_attempt_to_renew_valid_cert', domain=domain) # Does it have a Let's Encrypt cert? if status["CA_type"]["code"] != "lets-encrypt": - raise MoulinetteError('certmanager_attempt_to_renew_nonLE_cert', domain=domain) + raise YunohostError('certmanager_attempt_to_renew_nonLE_cert', domain=domain) # Check ACME challenge configured for given domain if not _check_acme_challenge_configuration(domain): - raise MoulinetteError('certmanager_acme_not_configured_for_domain', domain=domain) + raise YunohostError('certmanager_acme_not_configured_for_domain', domain=domain) if staging: logger.warning( @@ -475,7 +475,7 @@ location ^~ '/.well-known/acme-challenge/' contents = f.read() if '/.well-known/acme-challenge' in contents: - raise MoulinetteError('certmanager_conflicting_nginx_file', filepath=path) + raise YunohostError('certmanager_conflicting_nginx_file', filepath=path) # Write the conf if os.path.exists(nginx_conf_file): @@ -553,22 +553,22 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): CA=certification_authority) except ValueError as e: if "urn:acme:error:rateLimited" in str(e): - raise MoulinetteError('certmanager_hit_rate_limit', domain=domain) + raise YunohostError('certmanager_hit_rate_limit', domain=domain) else: logger.error(str(e)) _display_debug_information(domain) - raise MoulinetteError('certmanager_cert_signing_failed') + raise YunohostError('certmanager_cert_signing_failed') except Exception as e: logger.error(str(e)) - raise MoulinetteError('certmanager_cert_signing_failed') + raise YunohostError('certmanager_cert_signing_failed') import requests # lazy loading this module for performance reasons try: intermediate_certificate = requests.get(INTERMEDIATE_CERTIFICATE_URL, timeout=30).text except requests.exceptions.Timeout as e: - raise MoulinetteError('certmanager_couldnt_fetch_intermediate_cert') + raise YunohostError('certmanager_couldnt_fetch_intermediate_cert') # Now save the key and signed certificate logger.debug("Saving the key and signed certificate...") @@ -611,7 +611,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): status_summary = _get_status(domain)["summary"] if status_summary["code"] != "great": - raise MoulinetteError('certmanager_certificate_fetching_or_enabling_failed', domain=domain) + raise YunohostError('certmanager_certificate_fetching_or_enabling_failed', domain=domain) def _prepare_certificate_signing_request(domain, key_file, output_folder): @@ -644,7 +644,7 @@ def _get_status(domain): cert_file = os.path.join(CERT_FOLDER, domain, "crt.pem") if not os.path.isfile(cert_file): - raise MoulinetteError('certmanager_no_cert_file', domain=domain, file=cert_file) + raise YunohostError('certmanager_no_cert_file', domain=domain, file=cert_file) from OpenSSL import crypto # lazy loading this module for performance reasons try: @@ -653,7 +653,7 @@ def _get_status(domain): except Exception as exception: import traceback traceback.print_exc(file=sys.stdout) - raise MoulinetteError('certmanager_cannot_read_cert', domain=domain, file=cert_file, reason=exception) + raise YunohostError('certmanager_cannot_read_cert', domain=domain, file=cert_file, reason=exception) cert_subject = cert.get_subject().CN cert_issuer = cert.get_issuer().CN @@ -814,11 +814,11 @@ def _check_domain_is_ready_for_ACME(domain): # Check if IP from DNS matches public IP if not _dns_ip_match_public_ip(public_ip, domain): - raise MoulinetteError('certmanager_domain_dns_ip_differs_from_public_ip', domain=domain) + raise YunohostError('certmanager_domain_dns_ip_differs_from_public_ip', domain=domain) # Check if domain seems to be accessible through HTTP? if not _domain_is_accessible_through_HTTP(public_ip, domain): - raise MoulinetteError('certmanager_domain_http_not_working', domain=domain) + raise YunohostError('certmanager_domain_http_not_working', domain=domain) def _get_dns_ip(domain): @@ -827,7 +827,7 @@ def _get_dns_ip(domain): resolver.nameservers = DNS_RESOLVERS answers = resolver.query(domain, "A") except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN): - raise MoulinetteError('certmanager_error_no_A_record', domain=domain) + raise YunohostError('certmanager_error_no_A_record', domain=domain) return str(answers[0]) diff --git a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py index 188c1dffc..e81b20e5f 100644 --- a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py +++ b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py @@ -7,7 +7,7 @@ import json import errno from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from yunohost.tools import Migration @@ -29,7 +29,7 @@ class MyMigration(Migration): try: (domain, private_key_path) = _guess_current_dyndns_domain(dyn_host) assert "+157" in private_key_path - except (MoulinetteError, AssertionError): + except (YunohostError, AssertionError): logger.info(m18n.n("migrate_tsig_not_needed")) return @@ -52,7 +52,7 @@ class MyMigration(Migration): 'public_key_sha512': base64.b64encode(public_key_sha512), }, timeout=30) except requests.ConnectionError: - raise MoulinetteError('no_internet_connection') + raise YunohostError('no_internet_connection') if r.status_code != 201: try: @@ -70,7 +70,7 @@ class MyMigration(Migration): # Migration didn't succeed, so we rollback and raise an exception os.system("mv /etc/yunohost/dyndns/*+165* /tmp") - raise MoulinetteError('migrate_tsig_failed', domain=domain, + raise YunohostError('migrate_tsig_failed', domain=domain, error_code=str(r.status_code), error=error) # remove old certificates diff --git a/src/yunohost/data_migrations/0003_migrate_to_stretch.py b/src/yunohost/data_migrations/0003_migrate_to_stretch.py index 474a26db5..80d182fbe 100644 --- a/src/yunohost/data_migrations/0003_migrate_to_stretch.py +++ b/src/yunohost/data_migrations/0003_migrate_to_stretch.py @@ -3,7 +3,7 @@ import os from shutil import copy2 from moulinette import m18n, msettings -from moulinette.core import MoulinetteError +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 @@ -30,7 +30,7 @@ class MyMigration(Migration): def backward(self): - raise MoulinetteError("migration_0003_backward_impossible") + raise YunohostError("migration_0003_backward_impossible") def migrate(self): @@ -57,7 +57,7 @@ class MyMigration(Migration): self.apt_dist_upgrade(conf_flags=["old", "miss", "def"]) _run_service_command("start", "mysql") if self.debian_major_version() == 8: - raise MoulinetteError("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile) + 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")) @@ -107,11 +107,11 @@ class MyMigration(Migration): # would still be in 2.x... if not self.debian_major_version() == 8 \ and not self.yunohost_major_version() == 2: - raise MoulinetteError("migration_0003_not_jessie") + raise YunohostError("migration_0003_not_jessie") # Have > 1 Go free space on /var/ ? if free_space_in_directory("/var/") / (1024**3) < 1.0: - raise MoulinetteError("migration_0003_not_enough_free_space") + raise YunohostError("migration_0003_not_enough_free_space") # Check system is up to date # (but we don't if 'stretch' is already in the sources.list ... @@ -120,7 +120,7 @@ class MyMigration(Migration): self.apt_update() apt_list_upgradable = check_output("apt list --upgradable -a") if "upgradable" in apt_list_upgradable: - raise MoulinetteError("migration_0003_system_not_fully_up_to_date") + raise YunohostError("migration_0003_system_not_fully_up_to_date") @property def disclaimer(self): diff --git a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py index f03a93ef9..0208b717e 100644 --- a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py +++ b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py @@ -1,7 +1,7 @@ import subprocess from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from yunohost.tools import Migration @@ -20,10 +20,10 @@ class MyMigration(Migration): return if not self.package_is_installed("postgresql-9.6"): - raise MoulinetteError("migration_0005_postgresql_96_not_installed") + 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 MoulinetteError("migration_0005_not_enough_space", path="/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) 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 index ee3aeefcb..1149f28d5 100644 --- a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py +++ b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py @@ -5,7 +5,7 @@ import string import subprocess from moulinette import m18n -from moulinette.core import MoulinetteError +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 diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 7be214914..e28a24afe 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -30,7 +30,7 @@ import yaml import errno from moulinette import m18n, msettings -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger import yunohost.certificate @@ -77,8 +77,8 @@ def domain_add(operation_logger, auth, domain, dyndns=False): try: auth.validate_uniqueness({'virtualdomain': domain}) - except MoulinetteError: - raise MoulinetteError('domain_exists') + except YunohostError: + raise YunohostError('domain_exists') operation_logger.start() @@ -87,14 +87,14 @@ def domain_add(operation_logger, auth, domain, dyndns=False): # Do not allow to subscribe to multiple dyndns domains... if os.path.exists('/etc/cron.d/yunohost-dyndns'): - raise MoulinetteError('domain_dyndns_already_subscribed') + raise YunohostError('domain_dyndns_already_subscribed') from yunohost.dyndns import dyndns_subscribe, _dyndns_provides # Check that this domain can effectively be provided by # dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st) if not _dyndns_provides("dyndns.yunohost.org", domain): - raise MoulinetteError('domain_dyndns_root_unknown') + raise YunohostError('domain_dyndns_root_unknown') # Actually subscribe dyndns_subscribe(domain=domain) @@ -108,7 +108,7 @@ def domain_add(operation_logger, auth, domain, dyndns=False): } if not auth.add('virtualdomain=%s,ou=domains' % domain, attr_dict): - raise MoulinetteError('domain_creation_failed') + raise YunohostError('domain_creation_failed') # Don't regen these conf if we're still in postinstall if os.path.exists('/etc/yunohost/installed'): @@ -145,11 +145,11 @@ def domain_remove(operation_logger, auth, domain, force=False): from yunohost.app import app_ssowatconf if not force and domain not in domain_list(auth)['domains']: - raise MoulinetteError('domain_unknown') + raise YunohostError('domain_unknown') # Check domain is not the main domain if domain == _get_maindomain(): - raise MoulinetteError('domain_cannot_remove_main') + raise YunohostError('domain_cannot_remove_main') # Check if apps are installed on the domain for app in os.listdir('/etc/yunohost/apps/'): @@ -160,13 +160,13 @@ def domain_remove(operation_logger, auth, domain, force=False): continue else: if app_domain == domain: - raise MoulinetteError('domain_uninstall_app_first') + raise YunohostError('domain_uninstall_app_first') operation_logger.start() if auth.remove('virtualdomain=' + domain + ',ou=domains') or force: os.system('rm -rf /etc/yunohost/certs/%s' % domain) else: - raise MoulinetteError('domain_deletion_failed') + raise YunohostError('domain_deletion_failed') service_regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix']) app_ssowatconf(auth) @@ -243,7 +243,7 @@ def _get_conflicting_apps(auth, domain, path): # Abort if domain is unknown if domain not in domain_list(auth)['domains']: - raise MoulinetteError('domain_unknown') + raise YunohostError('domain_unknown') # This import cannot be put on top of file because it would create a # recursive import... diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 93f3a7a38..13dd4b43d 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -33,7 +33,7 @@ import errno import subprocess from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_file, write_to_file, rm from moulinette.utils.network import download_json @@ -75,9 +75,9 @@ def _dyndns_provides(provider, domain): # Dyndomains will be a list of domains supported by the provider # e.g. [ "nohost.me", "noho.st" ] dyndomains = download_json('https://%s/domains' % provider, timeout=30) - except MoulinetteError as e: + except YunohostError as e: logger.error(str(e)) - raise MoulinetteError('dyndns_could_not_check_provide', domain=domain, provider=provider) + raise YunohostError('dyndns_could_not_check_provide', domain=domain, provider=provider) # Extract 'dyndomain' from 'domain', e.g. 'nohost.me' from 'foo.nohost.me' dyndomain = '.'.join(domain.split('.')[1:]) @@ -102,9 +102,9 @@ def _dyndns_available(provider, domain): try: r = download_json('https://%s/test/%s' % (provider, domain), expected_status_code=None) - except MoulinetteError as e: + except YunohostError as e: logger.error(str(e)) - raise MoulinetteError('dyndns_could_not_check_available', + raise YunohostError('dyndns_could_not_check_available', domain=domain, provider=provider) return r == u"Domain %s is available" % domain @@ -127,11 +127,11 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom # Verify if domain is provided by subscribe_host if not _dyndns_provides(subscribe_host, domain): - raise MoulinetteError('dyndns_domain_not_provided', domain=domain, provider=subscribe_host) + raise YunohostError('dyndns_domain_not_provided', domain=domain, provider=subscribe_host) # Verify if domain is available if not _dyndns_available(subscribe_host, domain): - raise MoulinetteError('dyndns_unavailable', domain=domain) + raise YunohostError('dyndns_unavailable', domain=domain) operation_logger.start() @@ -155,13 +155,13 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom try: r = requests.post('https://%s/key/%s?key_algo=hmac-sha512' % (subscribe_host, base64.b64encode(key)), data={'subdomain': domain}, timeout=30) except requests.ConnectionError: - raise MoulinetteError('no_internet_connection') + raise YunohostError('no_internet_connection') if r.status_code != 201: try: error = json.loads(r.text)['error'] except: error = "Server error, code: %s. (Message: \"%s\")" % (r.status_code, r.text) - raise MoulinetteError('dyndns_registration_failed', error=error) + raise YunohostError('dyndns_registration_failed', error=error) logger.success(m18n.n('dyndns_registered')) @@ -195,7 +195,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, keys = glob.glob('/etc/yunohost/dyndns/K{0}.+*.private'.format(domain)) if not keys: - raise MoulinetteError('dyndns_key_not_found') + raise YunohostError('dyndns_key_not_found') key = keys[0] @@ -295,7 +295,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, command = ["/usr/bin/nsupdate", "-k", key, DYNDNS_ZONE] subprocess.check_call(command) except subprocess.CalledProcessError: - raise MoulinetteError('dyndns_ip_update_failed') + raise YunohostError('dyndns_ip_update_failed') logger.success(m18n.n('dyndns_ip_updated')) @@ -321,7 +321,7 @@ def dyndns_removecron(): try: os.remove("/etc/cron.d/yunohost-dyndns") except: - raise MoulinetteError('dyndns_cron_remove_failed') + raise YunohostError('dyndns_cron_remove_failed') logger.success(m18n.n('dyndns_cron_removed')) @@ -351,4 +351,4 @@ def _guess_current_dyndns_domain(dyn_host): else: return (_domain, path) - raise MoulinetteError('dyndns_no_domain_registered') + raise YunohostError('dyndns_no_domain_registered') diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index 878083bc7..fb1804659 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -34,7 +34,7 @@ except ImportError: sys.exit(1) from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils import process from moulinette.utils.log import getActionLogger from moulinette.utils.text import prependlines @@ -268,7 +268,7 @@ def firewall_reload(skip_upnp=False): reloaded = True if not reloaded: - raise MoulinetteError('firewall_reload_failed') + raise YunohostError('firewall_reload_failed') hook_callback('post_iptable_rules', args=[upnp, os.path.exists("/proc/net/if_inet6")]) @@ -338,7 +338,7 @@ def firewall_upnp(action='status', no_refresh=False): if action == 'status': no_refresh = True else: - raise MoulinetteError('action_invalid', action=action) + raise YunohostError('action_invalid', action=action) # Refresh port mapping using UPnP if not no_refresh: @@ -407,7 +407,7 @@ def firewall_upnp(action='status', no_refresh=False): firewall_reload(skip_upnp=True) if action == 'enable' and not enabled: - raise MoulinetteError('upnp_port_open_failed') + raise YunohostError('upnp_port_open_failed') return {'enabled': enabled} @@ -419,7 +419,7 @@ def firewall_stop(): """ if os.system("iptables -w -P INPUT ACCEPT") != 0: - raise MoulinetteError('iptables_unavailable') + raise YunohostError('iptables_unavailable') os.system("iptables -w -F") os.system("iptables -w -X") diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 665d2fe40..901321926 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -30,7 +30,7 @@ import tempfile from glob import iglob from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils import log HOOK_FOLDER = '/usr/share/yunohost/hooks/' @@ -112,7 +112,7 @@ def hook_info(action, name): }) if not hooks: - raise MoulinetteError('hook_name_unknown', name=name) + raise YunohostError('hook_name_unknown', name=name) return { 'action': action, 'name': name, @@ -174,7 +174,7 @@ def hook_list(action, list_by='name', show_info=False): # Add only the name d.add(name) else: - raise MoulinetteError('hook_list_by_invalid') + raise YunohostError('hook_list_by_invalid') def _append_folder(d, folder): # Iterate over and add hook from a folder @@ -255,7 +255,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, try: hl = hooks_names[n] except KeyError: - raise MoulinetteError('hook_name_unknown', n) + raise YunohostError('hook_name_unknown', n) # Iterate over hooks with this name for h in hl: # Update hooks dict @@ -281,7 +281,7 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, path=path, args=args) hook_exec(path, args=hook_args, chdir=chdir, env=env, no_trace=no_trace, raise_on_error=True) - except MoulinetteError as e: + except YunohostError as e: state = 'failed' logger.error(e.strerror, exc_info=1) post_callback(name=name, priority=priority, path=path, @@ -318,7 +318,7 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, if path[0] != '/': path = os.path.realpath(path) if not os.path.isfile(path): - raise MoulinetteError('file_not_exist', path=path) + raise YunohostError('file_not_exist', path=path) # Construct command variables cmd_args = '' @@ -383,12 +383,12 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, # Check and return process' return code if returncode is None: if raise_on_error: - raise MoulinetteError('hook_exec_not_terminated', path=path) + raise YunohostError('hook_exec_not_terminated', path=path) else: logger.error(m18n.n('hook_exec_not_terminated', path=path)) return 1 elif raise_on_error and returncode != 0: - raise MoulinetteError('hook_exec_failed', path=path) + raise YunohostError('hook_exec_failed', path=path) return returncode diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 3df630b14..3c7ff02f3 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -34,7 +34,7 @@ from logging import FileHandler, getLogger, Formatter from sys import exc_info from moulinette import m18n, msettings -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_file @@ -148,7 +148,7 @@ def log_display(path, number=50, share=False): log_path = base_path + LOG_FILE_EXT if not os.path.exists(md_path) and not os.path.exists(log_path): - raise MoulinetteError('log_does_exists', log=path) + raise YunohostError('log_does_exists', log=path) infos = {} @@ -188,7 +188,7 @@ def log_display(path, number=50, share=False): if os.path.exists(log_path): logger.warning(error) else: - raise MoulinetteError(error) + raise YunohostError(error) # Display logs if exist if os.path.exists(log_path): diff --git a/src/yunohost/monitor.py b/src/yunohost/monitor.py index 0839c6ae7..4fef2ca61 100644 --- a/src/yunohost/monitor.py +++ b/src/yunohost/monitor.py @@ -38,7 +38,7 @@ import cPickle as pickle from datetime import datetime from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from yunohost.utils.network import get_public_ip @@ -83,7 +83,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False): result_dname = dn if len(devices) == 0: if mountpoint is not None: - raise MoulinetteError('mountpoint_unknown') + raise YunohostError('mountpoint_unknown') return result # Retrieve monitoring for unit(s) @@ -141,7 +141,7 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False): for dname in devices_names: _set(dname, 'not-available') else: - raise MoulinetteError('unit_unknown', unit=u) + raise YunohostError('unit_unknown', unit=u) if result_dname is not None: return result[result_dname] @@ -237,7 +237,7 @@ def monitor_network(units=None, human_readable=False): 'gateway': gateway, } else: - raise MoulinetteError('unit_unknown', unit=u) + raise YunohostError('unit_unknown', unit=u) if len(units) == 1: return result[units[0]] @@ -287,7 +287,7 @@ def monitor_system(units=None, human_readable=False): elif u == 'infos': result[u] = json.loads(glances.getSystem()) else: - raise MoulinetteError('unit_unknown', unit=u) + raise YunohostError('unit_unknown', unit=u) if len(units) == 1 and type(result[units[0]]) is not str: return result[units[0]] @@ -303,7 +303,7 @@ def monitor_update_stats(period): """ if period not in ['day', 'week', 'month']: - raise MoulinetteError('monitor_period_invalid') + raise YunohostError('monitor_period_invalid') stats = _retrieve_stats(period) if not stats: @@ -321,7 +321,7 @@ def monitor_update_stats(period): else: monitor = _monitor_all(p, 0) if not monitor: - raise MoulinetteError('monitor_stats_no_update') + raise YunohostError('monitor_stats_no_update') stats['timestamp'].append(time.time()) @@ -386,13 +386,13 @@ def monitor_show_stats(period, date=None): """ if period not in ['day', 'week', 'month']: - raise MoulinetteError('monitor_period_invalid') + raise YunohostError('monitor_period_invalid') result = _retrieve_stats(period, date) if result is False: - raise MoulinetteError('monitor_stats_file_not_found') + raise YunohostError('monitor_stats_file_not_found') elif result is None: - raise MoulinetteError('monitor_stats_period_unavailable') + raise YunohostError('monitor_stats_period_unavailable') return result @@ -440,7 +440,7 @@ def monitor_disable(): if glances['loaded'] != 'disabled': try: service_disable('glances') - except MoulinetteError as e: + except YunohostError as e: logger.warning(e.strerror) # Remove crontab @@ -468,8 +468,8 @@ def _get_glances_api(): from yunohost.service import service_status if service_status('glances')['status'] != 'running': - raise MoulinetteError('monitor_not_enabled') - raise MoulinetteError('monitor_glances_con_failed') + raise YunohostError('monitor_not_enabled') + raise YunohostError('monitor_glances_con_failed') def _extract_inet(string, skip_netmask=False, skip_loopback=True): diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 140c107be..082b32eab 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -36,7 +36,7 @@ from difflib import unified_diff from datetime import datetime from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils import log, filesystem from yunohost.log import is_unit_operation @@ -85,7 +85,7 @@ def service_add(name, status=None, log=None, runlevel=None, need_lock=False, des _save_services(services) except: # we'll get a logger.warning with more details in _save_services - raise MoulinetteError('service_add_failed', service=name) + raise YunohostError('service_add_failed', service=name) logger.success(m18n.n('service_added', service=name)) @@ -103,13 +103,13 @@ def service_remove(name): try: del services[name] except KeyError: - raise MoulinetteError('service_unknown', service=name) + raise YunohostError('service_unknown', service=name) try: _save_services(services) except: # we'll get a logger.warning with more details in _save_services - raise MoulinetteError('service_remove_failed', service=name) + raise YunohostError('service_remove_failed', service=name) logger.success(m18n.n('service_removed', service=name)) @@ -130,7 +130,7 @@ def service_start(names): logger.success(m18n.n('service_started', service=name)) else: if service_status(name)['status'] != 'running': - raise MoulinetteError('service_start_failed', service=name, logs=_get_journalctl_logs(name)) + raise YunohostError('service_start_failed', service=name, logs=_get_journalctl_logs(name)) logger.debug(m18n.n('service_already_started', service=name)) @@ -149,7 +149,7 @@ def service_stop(names): logger.success(m18n.n('service_stopped', service=name)) else: if service_status(name)['status'] != 'inactive': - raise MoulinetteError('service_stop_failed', service=name, logs=_get_journalctl_logs(name)) + raise YunohostError('service_stop_failed', service=name, logs=_get_journalctl_logs(name)) logger.debug(m18n.n('service_already_stopped', service=name)) @is_unit_operation() @@ -168,7 +168,7 @@ def service_enable(operation_logger, names): if _run_service_command('enable', name): logger.success(m18n.n('service_enabled', service=name)) else: - raise MoulinetteError('service_enable_failed', service=name, logs=_get_journalctl_logs(name)) + raise YunohostError('service_enable_failed', service=name, logs=_get_journalctl_logs(name)) def service_disable(names): @@ -185,7 +185,7 @@ def service_disable(names): if _run_service_command('disable', name): logger.success(m18n.n('service_disabled', service=name)) else: - raise MoulinetteError('service_disable_failed', service=name, logs=_get_journalctl_logs(name)) + raise YunohostError('service_disable_failed', service=name, logs=_get_journalctl_logs(name)) def service_status(names=[]): @@ -208,7 +208,7 @@ def service_status(names=[]): for name in names: if check_names and name not in services.keys(): - raise MoulinetteError('service_unknown', service=name) + raise YunohostError('service_unknown', service=name) # this "service" isn't a service actually so we skip it # @@ -307,10 +307,10 @@ def service_log(name, number=50): services = _get_services() if name not in services.keys(): - raise MoulinetteError('service_unknown', service=name) + raise YunohostError('service_unknown', service=name) if 'log' not in services[name]: - raise MoulinetteError('service_no_log', service=name) + raise YunohostError('service_no_log', service=name) log_list = services[name]['log'] @@ -418,7 +418,7 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, names = pre_result['succeed'].keys() if not names: - raise MoulinetteError('service_regenconf_failed', + raise YunohostError('service_regenconf_failed', services=', '.join(pre_result['failed'])) # Set the processing method @@ -595,7 +595,7 @@ def _run_service_command(action, service): """ services = _get_services() if service not in services.keys(): - raise MoulinetteError('service_unknown', service=service) + raise YunohostError('service_unknown', service=service) possible_actions = ['start', 'stop', 'restart', 'reload', 'enable', 'disable'] if action not in possible_actions: diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 5d875afb9..8783188a1 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -6,7 +6,7 @@ from datetime import datetime from collections import OrderedDict from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger logger = getActionLogger('yunohost.settings') @@ -54,7 +54,7 @@ def settings_get(key, full=False): settings = _get_settings() if key not in settings: - raise MoulinetteError('global_settings_key_doesnt_exists', settings_key=key) + raise YunohostError('global_settings_key_doesnt_exists', settings_key=key) if full: return settings[key] @@ -82,13 +82,13 @@ def settings_set(key, value): settings = _get_settings() if key not in settings: - raise MoulinetteError('global_settings_key_doesnt_exists', settings_key=key) + raise YunohostError('global_settings_key_doesnt_exists', settings_key=key) key_type = settings[key]["type"] if key_type == "bool": if not isinstance(value, bool): - raise MoulinetteError('global_settings_bad_type_for_setting', setting=key, + raise YunohostError('global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type) elif key_type == "int": if not isinstance(value, int) or isinstance(value, bool): @@ -100,19 +100,19 @@ def settings_set(key, value): 'global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type)) else: - raise MoulinetteError('global_settings_bad_type_for_setting', setting=key, + raise YunohostError('global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type) elif key_type == "string": if not isinstance(value, basestring): - raise MoulinetteError('global_settings_bad_type_for_setting', setting=key, + raise YunohostError('global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type) elif key_type == "enum": if value not in settings[key]["choices"]: - raise MoulinetteError('global_settings_bad_choice_for_enum', setting=key, + raise YunohostError('global_settings_bad_choice_for_enum', setting=key, received_type=type(value).__name__, expected_type=", ".join(settings[key]["choices"])) else: - raise MoulinetteError('global_settings_unknown_type', setting=key, + raise YunohostError('global_settings_unknown_type', setting=key, unknown_type=key_type) settings[key]["value"] = value @@ -131,7 +131,7 @@ def settings_reset(key): settings = _get_settings() if key not in settings: - raise MoulinetteError('global_settings_key_doesnt_exists', settings_key=key) + raise YunohostError('global_settings_key_doesnt_exists', settings_key=key) settings[key]["value"] = settings[key]["default"] _save_settings(settings) @@ -207,7 +207,7 @@ def _get_settings(): setting_key=key)) unknown_settings[key] = value except Exception as e: - raise MoulinetteError('global_settings_cant_open_settings', reason=e, exc_info=1) + raise YunohostError('global_settings_cant_open_settings', reason=e, exc_info=1) if unknown_settings: try: @@ -228,10 +228,10 @@ def _save_settings(settings, location=SETTINGS_PATH): try: result = json.dumps(settings_without_description, indent=4) except Exception as e: - raise MoulinetteError('global_settings_cant_serialize_settings', reason=e, exc_info=1) + raise YunohostError('global_settings_cant_serialize_settings', reason=e, exc_info=1) try: with open(location, "w") as settings_fd: settings_fd.write(result) except Exception as e: - raise MoulinetteError('global_settings_cant_write_settings', reason=e, exc_info=1) + raise YunohostError('global_settings_cant_write_settings', reason=e, exc_info=1) diff --git a/src/yunohost/ssh.py b/src/yunohost/ssh.py index 41ac64293..e284736c4 100644 --- a/src/yunohost/ssh.py +++ b/src/yunohost/ssh.py @@ -7,7 +7,7 @@ import pwd import subprocess from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.filesystem import read_file, write_to_file, chown, chmod, mkdir SSHD_CONFIG_PATH = "/etc/ssh/sshd_config" @@ -23,7 +23,7 @@ def user_ssh_allow(auth, username): # TODO it would be good to support different kind of shells if not _get_user_for_ssh(auth, username): - raise MoulinetteError('user_unknown', user=username) + raise YunohostError('user_unknown', user=username) auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/bash'}) @@ -42,7 +42,7 @@ def user_ssh_disallow(auth, username): # TODO it would be good to support different kind of shells if not _get_user_for_ssh(auth, username): - raise MoulinetteError('user_unknown', user=username) + raise YunohostError('user_unknown', user=username) auth.update('uid=%s,ou=users' % username, {'loginShell': '/bin/false'}) diff --git a/src/yunohost/tests/test_appslist.py b/src/yunohost/tests/test_appslist.py index 6b7141f4a..7a6f27d6c 100644 --- a/src/yunohost/tests/test_appslist.py +++ b/src/yunohost/tests/test_appslist.py @@ -5,7 +5,7 @@ import requests_mock import glob import time -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from yunohost.app import app_fetchlist, app_removelist, app_listlists, _using_legacy_appslist_system, _migrate_appslist_system, _register_new_appslist @@ -79,7 +79,7 @@ def test_appslist_list_register_conflict_name(): """ _register_new_appslist("https://lol.com/appslist.json", "dummy") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): _register_new_appslist("https://lol.com/appslist2.json", "dummy") appslist_dict = app_listlists() @@ -94,7 +94,7 @@ def test_appslist_list_register_conflict_url(): """ _register_new_appslist("https://lol.com/appslist.json", "dummy") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): _register_new_appslist("https://lol.com/appslist.json", "plopette") appslist_dict = app_listlists() @@ -161,7 +161,7 @@ def test_appslist_fetch_unknownlist(): assert app_listlists() == {} - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): app_fetchlist(name="swag") @@ -170,7 +170,7 @@ def test_appslist_fetch_url_but_no_name(): Do a fetchlist with url given, but no name given """ - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): app_fetchlist(url=URL_OFFICIAL_APP_LIST) @@ -270,7 +270,7 @@ def test_appslist_remove_unknown(): Attempt to remove an unknown list """ - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): app_removelist("dummy") diff --git a/src/yunohost/tests/test_appurl.py b/src/yunohost/tests/test_appurl.py index dc1dbc29b..58c345dea 100644 --- a/src/yunohost/tests/test_appurl.py +++ b/src/yunohost/tests/test_appurl.py @@ -1,6 +1,6 @@ import pytest -from moulinette.core import MoulinetteError, init_authenticator +from yunohost.utils.error import YunohostError, init_authenticator from yunohost.app import app_install, app_remove from yunohost.domain import _get_maindomain, domain_url_available, _normalize_domain_path @@ -43,7 +43,7 @@ def test_urlavailable(): assert domain_url_available(auth, maindomain, "/macnuggets") # We don't know the domain yolo.swag - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): assert domain_url_available(auth, "yolo.swag", "/macnuggets") @@ -55,13 +55,13 @@ def test_registerurl(): assert not domain_url_available(auth, maindomain, "/urlregisterapp") # Try installing at same location - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): app_install(auth, "./tests/apps/register_url_app_ynh", args="domain=%s&path=%s" % (maindomain, "/urlregisterapp")) def test_registerurl_baddomain(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): app_install(auth, "./tests/apps/register_url_app_ynh", args="domain=%s&path=%s" % ("yolo.swag", "/urlregisterapp")) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index c9d4c6b15..abe5bb1c8 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -12,7 +12,7 @@ from yunohost.app import app_install, app_remove, app_ssowatconf from yunohost.app import _is_installed from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete from yunohost.domain import _get_maindomain -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError # Get main domain maindomain = "" @@ -214,7 +214,7 @@ def test_backup_system_part_that_does_not_exists(mocker): mocker.spy(m18n, "n") # Create the backup - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_create(system=["yolol"], apps=None) m18n.n.assert_any_call('backup_hook_unknown', hook="yolol") @@ -295,7 +295,7 @@ def test_backup_script_failure_handling(monkeypatch, mocker): monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec) mocker.spy(m18n, "n") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_create(system=None, apps=["backup_recommended_app"]) m18n.n.assert_any_call('backup_app_failed', app='backup_recommended_app') @@ -315,7 +315,7 @@ def test_backup_not_enough_free_space(monkeypatch, mocker): mocker.spy(m18n, "n") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_create(system=None, apps=["backup_recommended_app"]) m18n.n.assert_any_call('not_enough_disk_space', path=ANY) @@ -327,7 +327,7 @@ def test_backup_app_not_installed(mocker): mocker.spy(m18n, "n") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_create(system=None, apps=["wordpress"]) m18n.n.assert_any_call("unbackup_app", app="wordpress") @@ -343,8 +343,9 @@ def test_backup_app_with_no_backup_script(mocker): mocker.spy(m18n, "n") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_create(system=None, apps=["backup_recommended_app"]) +>>>>>>> modif YunohostError to YunohostError m18n.n.assert_any_call("backup_with_no_backup_script_for_app", app="backup_recommended_app") m18n.n.assert_any_call('backup_nothings_done') @@ -420,7 +421,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker): assert not _is_installed("wordpress") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["wordpress"]) @@ -441,7 +442,7 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker): assert not _is_installed("wordpress") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["wordpress"]) @@ -460,7 +461,7 @@ def test_restore_app_not_in_backup(mocker): mocker.spy(m18n, "n") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["yoloswag"]) @@ -480,7 +481,7 @@ def test_restore_app_already_installed(mocker): assert _is_installed("wordpress") mocker.spy(m18n, "n") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_restore(auth, system=None, name=backup_list()["archives"][0], apps=["wordpress"]) @@ -544,7 +545,7 @@ def test_restore_archive_with_no_json(mocker): assert "badbackup" in backup_list()["archives"] mocker.spy(m18n, "n") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): backup_restore(auth, name="badbackup", force=True) m18n.n.assert_any_call('backup_invalid_archive') diff --git a/src/yunohost/tests/test_changeurl.py b/src/yunohost/tests/test_changeurl.py index eb10cd604..826ec659b 100644 --- a/src/yunohost/tests/test_changeurl.py +++ b/src/yunohost/tests/test_changeurl.py @@ -6,7 +6,7 @@ from moulinette.core import init_authenticator from yunohost.app import app_install, app_change_url, app_remove, app_map from yunohost.domain import _get_maindomain -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError # Instantiate LDAP Authenticator AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous') @@ -57,5 +57,5 @@ def test_appchangeurl_sameurl(): install_changeurl_app("/changeurl") check_changeurl_app("/changeurl") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): app_change_url(auth, "change_url_app", maindomain, "changeurl") diff --git a/src/yunohost/tests/test_settings.py b/src/yunohost/tests/test_settings.py index 746f5a9d4..afb9faee9 100644 --- a/src/yunohost/tests/test_settings.py +++ b/src/yunohost/tests/test_settings.py @@ -2,7 +2,7 @@ import os import json import pytest -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from yunohost.settings import settings_get, settings_list, _get_settings, \ settings_set, settings_reset, settings_reset_all, \ @@ -46,7 +46,7 @@ def test_settings_get_full_enum(): def test_settings_get_doesnt_exists(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_get("doesnt.exists") @@ -70,39 +70,39 @@ def test_settings_set_enum(): def test_settings_set_doesexit(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("doesnt.exist", True) def test_settings_set_bad_type_bool(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.bool", 42) - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.bool", "pouet") def test_settings_set_bad_type_int(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.int", True) - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.int", "pouet") def test_settings_set_bad_type_string(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.string", True) - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.string", 42) def test_settings_set_bad_value_enum(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.enum", True) - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.enum", "e") - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.enum", 42) - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_set("example.enum", "pouet") @@ -119,7 +119,7 @@ def test_reset(): def test_settings_reset_doesexit(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): settings_reset("doesnt.exist") diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 8e7da9939..51c19ff34 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -40,7 +40,7 @@ import apt import apt.progress from moulinette import msettings, msignals, m18n -from moulinette.core import MoulinetteError, init_authenticator +from yunohost.utils.error import YunohostError, init_authenticator from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output from moulinette.utils.filesystem import read_json, write_to_json @@ -112,7 +112,7 @@ def tools_ldapinit(): pwd.getpwnam("admin") except KeyError: logger.error(m18n.n('ldap_init_failed_to_create_admin')) - raise MoulinetteError('installation_failed') + raise YunohostError('installation_failed') logger.success(m18n.n('ldap_initialized')) return auth @@ -139,7 +139,7 @@ def tools_adminpw(auth, new_password, check_strength=True): auth.update("cn=admin", { "userPassword": new_hash, }) except: logger.exception('unable to change admin password') - raise MoulinetteError('admin_password_change_failed') + raise YunohostError('admin_password_change_failed') else: # Write as root password try: @@ -175,7 +175,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): # Check domain exists if new_domain not in domain_list(auth)['domains']: - raise MoulinetteError('domain_unknown') + raise YunohostError('domain_unknown') operation_logger.related_to.append(('domain', new_domain)) operation_logger.start() @@ -198,7 +198,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): _set_maindomain(new_domain) except Exception as e: logger.warning("%s" % e, exc_info=1) - raise MoulinetteError('maindomain_change_failed') + raise YunohostError('maindomain_change_failed') _set_hostname(new_domain) @@ -247,7 +247,7 @@ def _set_hostname(hostname, pretty_hostname=None): if p.returncode != 0: logger.warning(command) logger.warning(out) - raise MoulinetteError('domain_hostname_failed') + raise YunohostError('domain_hostname_failed') else: logger.debug(out) @@ -288,7 +288,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, # Do some checks at first if os.path.isfile('/etc/yunohost/installed'): - raise MoulinetteError('yunohost_already_installed') + raise YunohostError('yunohost_already_installed') # Check password if not force_password: @@ -317,7 +317,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, dyndns = True # If not, abort the postinstall else: - raise MoulinetteError('dyndns_unavailable', domain=domain) + raise YunohostError('dyndns_unavailable', domain=domain) else: dyndns = False else: @@ -360,7 +360,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, with open('/etc/ssowat/conf.json.persistent') as json_conf: ssowat_conf = json.loads(str(json_conf.read())) except ValueError as e: - raise MoulinetteError('ssowat_persistent_conf_read_error', error=str(e)) + raise YunohostError('ssowat_persistent_conf_read_error', error=str(e)) except IOError: ssowat_conf = {} @@ -373,7 +373,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) except IOError as e: - raise MoulinetteError('ssowat_persistent_conf_write_error', error=str(e)) + raise YunohostError('ssowat_persistent_conf_write_error', error=str(e)) os.system('chmod 644 /etc/ssowat/conf.json.persistent') @@ -400,7 +400,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, if p.returncode != 0: logger.warning(out) - raise MoulinetteError('yunohost_ca_creation_failed') + raise YunohostError('yunohost_ca_creation_failed') else: logger.debug(out) @@ -476,7 +476,7 @@ def tools_update(ignore_apps=False, ignore_packages=False): # Update APT cache logger.debug(m18n.n('updating_apt_cache')) if not cache.update(): - raise MoulinetteError('update_cache_failed') + raise YunohostError('update_cache_failed') cache.open(None) cache.upgrade(True) @@ -495,7 +495,7 @@ def tools_update(ignore_apps=False, ignore_packages=False): if not ignore_apps: try: app_fetchlist() - except MoulinetteError: + except YunohostError: # FIXME : silent exception !? pass @@ -626,7 +626,7 @@ def tools_diagnosis(auth, private=False): diagnosis['system'] = OrderedDict() try: disks = monitor_disk(units=['filesystem'], human_readable=True) - except (MoulinetteError, Fault) as e: + except (YunohostError, Fault) as e: logger.warning(m18n.n('diagnosis_monitor_disk_error', error=format(e)), exc_info=1) else: diagnosis['system']['disks'] = {} @@ -642,7 +642,7 @@ def tools_diagnosis(auth, private=False): try: system = monitor_system(units=['cpu', 'memory'], human_readable=True) - except MoulinetteError as e: + except YunohostError as e: logger.warning(m18n.n('diagnosis_monitor_system_error', error=format(e)), exc_info=1) else: diagnosis['system']['memory'] = { @@ -668,7 +668,7 @@ def tools_diagnosis(auth, private=False): # YNH Applications try: applications = app_list()['apps'] - except MoulinetteError as e: + except YunohostError as e: diagnosis['applications'] = m18n.n('diagnosis_no_apps') else: diagnosis['applications'] = {} @@ -800,7 +800,7 @@ def tools_migrations_list(pending=False, done=False): # Check for option conflict if pending and done: - raise MoulinetteError("migrations_list_conflict_pending_done") + raise YunohostError("migrations_list_conflict_pending_done") # Get all migrations migrations = _get_migrations_list() @@ -857,7 +857,7 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai # validate input, target must be "0" or a valid number elif target != 0 and target not in all_migration_numbers: - raise MoulinetteError('migrations_bad_value_for_target', ", ".join(map(str, all_migration_numbers))) + raise YunohostError('migrations_bad_value_for_target', ", ".join(map(str, all_migration_numbers))) logger.debug(m18n.n('migrations_current_target', target)) @@ -1063,7 +1063,7 @@ def _load_migration(migration_file): import traceback traceback.print_exc() - raise MoulinetteError('migrations_error_failed_to_load_migration', + raise YunohostError('migrations_error_failed_to_load_migration', number=number, name=name) def _skip_all_migrations(): diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 67cfe6cf1..313835fe0 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -34,7 +34,7 @@ import string import subprocess from moulinette import m18n -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from yunohost.service import service_status from yunohost.log import is_unit_operation @@ -71,7 +71,7 @@ def user_list(auth, fields=None): if attr in keys: attrs.append(attr) else: - raise MoulinetteError('field_invalid', attr) + raise YunohostError('field_invalid', attr) else: attrs = ['uid', 'cn', 'mail', 'mailuserquota', 'loginShell'] @@ -129,7 +129,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas # Validate uniqueness of username in system users all_existing_usernames = {x.pw_name for x in pwd.getpwall()} if username in all_existing_usernames: - raise MoulinetteError('system_username_exists') + raise YunohostError('system_username_exists') main_domain = _get_maindomain() aliases = [ @@ -140,11 +140,11 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas ] if mail in aliases: - raise MoulinetteError('mail_unavailable') + raise YunohostError('mail_unavailable') # Check that the mail domain exists if mail.split("@")[1] not in domain_list(auth)['domains']: - raise MoulinetteError(m18n.n('mail_domain_unknown', + raise YunohostError(m18n.n('mail_domain_unknown', domain=mail.split("@")[1])) operation_logger.start() @@ -186,7 +186,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas with open('/etc/ssowat/conf.json.persistent') as json_conf: ssowat_conf = json.loads(str(json_conf.read())) except ValueError as e: - raise MoulinetteError('ssowat_persistent_conf_read_error', error=e.strerror) + raise YunohostError('ssowat_persistent_conf_read_error', error=e.strerror) except IOError: ssowat_conf = {} @@ -196,7 +196,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) except IOError as e: - raise MoulinetteError('ssowat_persistent_conf_write_error', error=e.strerror) + raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror) if auth.add('uid=%s,ou=users' % username, attr_dict): # Invalidate passwd to take user creation into account @@ -222,7 +222,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas return {'fullname': fullname, 'username': username, 'mail': mail} - raise MoulinetteError('user_creation_failed') + raise YunohostError('user_creation_failed') @is_unit_operation([('username', 'user')]) @@ -253,7 +253,7 @@ def user_delete(operation_logger, auth, username, purge=False): if purge: subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) else: - raise MoulinetteError('user_deletion_failed') + raise YunohostError('user_deletion_failed') app_ssowatconf(auth) @@ -292,7 +292,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, # Populate user informations result = auth.search(base='ou=users,dc=yunohost,dc=org', filter='uid=' + username, attrs=attrs_to_fetch) if not result: - raise MoulinetteError('user_unknown', user=username) + raise YunohostError('user_unknown', user=username) user = result[0] # Get modifications from arguments @@ -323,10 +323,10 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, ] auth.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: - raise MoulinetteError(m18n.n('mail_domain_unknown', + raise YunohostError(m18n.n('mail_domain_unknown', domain=mail[mail.find('@') + 1:])) if mail in aliases: - raise MoulinetteError('mail_unavailable') + raise YunohostError('mail_unavailable') del user['mail'][0] new_attr_dict['mail'] = [mail] + user['mail'] @@ -337,7 +337,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, for mail in add_mailalias: auth.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: - raise MoulinetteError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) + raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) user['mail'].append(mail) new_attr_dict['mail'] = user['mail'] @@ -348,7 +348,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, if len(user['mail']) > 1 and mail in user['mail'][1:]: user['mail'].remove(mail) else: - raise MoulinetteError('mail_alias_remove_failed', mail=mail) + raise YunohostError('mail_alias_remove_failed', mail=mail) new_attr_dict['mail'] = user['mail'] if add_mailforward: @@ -367,7 +367,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, if len(user['maildrop']) > 1 and mail in user['maildrop'][1:]: user['maildrop'].remove(mail) else: - raise MoulinetteError('mail_forward_remove_failed', mail=mail) + raise YunohostError('mail_forward_remove_failed', mail=mail) new_attr_dict['maildrop'] = user['maildrop'] if mailbox_quota is not None: @@ -380,7 +380,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, app_ssowatconf(auth) return user_info(auth, username) else: - raise MoulinetteError('user_update_failed') + raise YunohostError('user_update_failed') def user_info(auth, username): @@ -405,7 +405,7 @@ def user_info(auth, username): if result: user = result[0] else: - raise MoulinetteError('user_unknown', user=username) + raise YunohostError('user_unknown', user=username) result_dict = { 'username': user['uid'][0], @@ -461,7 +461,7 @@ def user_info(auth, username): if result: return result_dict else: - raise MoulinetteError('user_info_failed') + raise YunohostError('user_info_failed') # # SSH subcategory diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py index a2b6f5055..479176ee3 100644 --- a/src/yunohost/utils/error.py +++ b/src/yunohost/utils/error.py @@ -20,6 +20,7 @@ """ from moulinette.core import MoulinetteError +from moulinette.__init__ import m18n class YunohostError(MoulinetteError): """Yunohost base exception""" diff --git a/src/yunohost/utils/password.py b/src/yunohost/utils/password.py index 6e8f5ba0a..b86fa23b1 100644 --- a/src/yunohost/utils/password.py +++ b/src/yunohost/utils/password.py @@ -84,14 +84,14 @@ class PasswordValidator(object): import errno import logging from moulinette import m18n - from moulinette.core import MoulinetteError + 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": - raise MoulinetteError(1, m18n.n(msg)) + raise YunohostError(1, m18n.n(msg)) def validation_summary(self, password): """ diff --git a/src/yunohost/utils/yunopaste.py b/src/yunohost/utils/yunopaste.py index 475f29f79..7a3847873 100644 --- a/src/yunohost/utils/yunopaste.py +++ b/src/yunohost/utils/yunopaste.py @@ -4,7 +4,7 @@ import requests import json import errno -from moulinette.core import MoulinetteError +from yunohost.utils.error import YunohostError def yunopaste(data): @@ -13,14 +13,14 @@ def yunopaste(data): try: r = requests.post("%s/documents" % paste_server, data=data, timeout=30) except Exception as e: - raise MoulinetteError("Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e)) + raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e)) if r.status_code != 200: - raise MoulinetteError("Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text)) + raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text)) try: url = json.loads(r.text)["key"] except: - raise MoulinetteError("Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text) + raise YunohostError("Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text) return "%s/raw/%s" % (paste_server, url) From 24cf090221e0a50c72acb856869de51cba4d2f56 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 01:53:53 +0000 Subject: [PATCH 081/104] Fix YunohostError definition --- src/yunohost/utils/error.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py index 479176ee3..8a73e52b7 100644 --- a/src/yunohost/utils/error.py +++ b/src/yunohost/utils/error.py @@ -20,11 +20,11 @@ """ from moulinette.core import MoulinetteError -from moulinette.__init__ import m18n +from moulinette import m18n class YunohostError(MoulinetteError): """Yunohost base exception""" def __init__(self, key, *args, **kwargs): msg = m18n.n(key, *args, **kwargs) - super(MoulinetteError, self).__init__(msg) + super(YunohostError, self).__init__(msg) From 07ffe6f592ed8c7454a941fd4dc82314f58bbc09 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 02:00:58 +0000 Subject: [PATCH 082/104] Fix misc typo and forgotten m18n.n --- src/yunohost/backup.py | 2 +- src/yunohost/user.py | 6 ++---- src/yunohost/utils/password.py | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 24313d416..a44819a33 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -2164,7 +2164,7 @@ def backup_list(with_info=False, human_readable=False): for a in result: try: d[a] = backup_info(a, human_readable=human_readable) - except YunohostError, e: + except YunohostError as e: logger.warning('%s: %s' % (a, e.strerror)) result = d diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 313835fe0..1cec8922d 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -144,8 +144,7 @@ def user_create(operation_logger, auth, username, firstname, lastname, mail, pas # Check that the mail domain exists if mail.split("@")[1] not in domain_list(auth)['domains']: - raise YunohostError(m18n.n('mail_domain_unknown', - domain=mail.split("@")[1])) + raise YunohostError('mail_domain_unknown', domain=mail.split("@")[1]) operation_logger.start() @@ -323,8 +322,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, ] auth.validate_uniqueness({'mail': mail}) if mail[mail.find('@') + 1:] not in domains: - raise YunohostError(m18n.n('mail_domain_unknown', - domain=mail[mail.find('@') + 1:])) + raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) if mail in aliases: raise YunohostError('mail_unavailable') diff --git a/src/yunohost/utils/password.py b/src/yunohost/utils/password.py index b86fa23b1..ddc59cbe9 100644 --- a/src/yunohost/utils/password.py +++ b/src/yunohost/utils/password.py @@ -91,7 +91,7 @@ class PasswordValidator(object): status, msg = self.validation_summary(password) if status == "error": - raise YunohostError(1, m18n.n(msg)) + raise YunohostError(msg) def validation_summary(self, password): """ From 2dd1d8ccfe59bbbf32813172ead3d04b283c490c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 02:49:36 +0000 Subject: [PATCH 083/104] Unused errno + remaining unecessary m18n.n --- src/yunohost/app.py | 8 +++----- src/yunohost/backup.py | 1 - src/yunohost/certificate.py | 1 - .../data_migrations/0002_migrate_to_tsig_sha256.py | 1 - src/yunohost/domain.py | 1 - src/yunohost/dyndns.py | 1 - src/yunohost/firewall.py | 1 - src/yunohost/hook.py | 1 - src/yunohost/log.py | 1 - src/yunohost/monitor.py | 1 - src/yunohost/service.py | 1 - src/yunohost/settings.py | 1 - src/yunohost/ssh.py | 1 - src/yunohost/tools.py | 1 - src/yunohost/user.py | 1 - src/yunohost/utils/password.py | 2 -- src/yunohost/utils/yunopaste.py | 1 - 17 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index f4e48de45..510b60e95 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -30,7 +30,6 @@ import yaml import time import re import urlparse -import errno import subprocess import glob import pwd @@ -2076,10 +2075,9 @@ def _check_manifest_requirements(manifest, app_instance_name): for pkgname, spec in requirements.items(): version = versions[pkgname] if version not in packages.SpecifierSet(spec): - raise YunohostError( - errno.EINVAL, m18n.n('app_requirements_unmeet', - pkgname=pkgname, version=version, - spec=spec, app=app_instance_name)) + raise YunohostError('app_requirements_unmeet', + pkgname=pkgname, version=version, + spec=spec, app=app_instance_name) def _parse_args_from_manifest(manifest, action, args={}, auth=None): diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index a44819a33..6ed73d343 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -26,7 +26,6 @@ import os import re import json -import errno import time import tarfile import shutil diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index ca27d59df..cdcf4bbe9 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -24,7 +24,6 @@ import os import sys -import errno import shutil import pwd import grp diff --git a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py index e81b20e5f..39e4d2afe 100644 --- a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py +++ b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py @@ -4,7 +4,6 @@ import requests import base64 import time import json -import errno from moulinette import m18n from yunohost.utils.error import YunohostError diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index e28a24afe..83da7e18c 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -27,7 +27,6 @@ import os import re import json import yaml -import errno from moulinette import m18n, msettings from yunohost.utils.error import YunohostError diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 13dd4b43d..e83280b19 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -29,7 +29,6 @@ import json import glob import time import base64 -import errno import subprocess from moulinette import m18n diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index fb1804659..f7c6c0875 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -26,7 +26,6 @@ import os import sys import yaml -import errno try: import miniupnpc except ImportError: diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 901321926..5a6c4c154 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -25,7 +25,6 @@ """ import os import re -import errno import tempfile from glob import iglob diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 3c7ff02f3..7b32f18dd 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -26,7 +26,6 @@ import os import yaml -import errno import collections from datetime import datetime diff --git a/src/yunohost/monitor.py b/src/yunohost/monitor.py index 4fef2ca61..469ac41f1 100644 --- a/src/yunohost/monitor.py +++ b/src/yunohost/monitor.py @@ -31,7 +31,6 @@ import calendar import subprocess import xmlrpclib import os.path -import errno import os import dns.resolver import cPickle as pickle diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 082b32eab..b2c18c74e 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -28,7 +28,6 @@ import time import yaml import json import subprocess -import errno import shutil import hashlib diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 8783188a1..12b046146 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -1,6 +1,5 @@ import os import json -import errno from datetime import datetime from collections import OrderedDict diff --git a/src/yunohost/ssh.py b/src/yunohost/ssh.py index e284736c4..a9e5babf7 100644 --- a/src/yunohost/ssh.py +++ b/src/yunohost/ssh.py @@ -2,7 +2,6 @@ import re import os -import errno import pwd import subprocess diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 51c19ff34..6102b9f7a 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -27,7 +27,6 @@ import re import os import yaml import json -import errno import logging import subprocess import pwd diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 1cec8922d..ea8dc30a9 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -27,7 +27,6 @@ import os import re import pwd import json -import errno import crypt import random import string diff --git a/src/yunohost/utils/password.py b/src/yunohost/utils/password.py index ddc59cbe9..9cee56d53 100644 --- a/src/yunohost/utils/password.py +++ b/src/yunohost/utils/password.py @@ -81,9 +81,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 errno import logging - from moulinette import m18n from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger diff --git a/src/yunohost/utils/yunopaste.py b/src/yunohost/utils/yunopaste.py index 7a3847873..e349ebc32 100644 --- a/src/yunohost/utils/yunopaste.py +++ b/src/yunohost/utils/yunopaste.py @@ -2,7 +2,6 @@ import requests import json -import errno from yunohost.utils.error import YunohostError From e97b07403f72b772ba557e9ed4b41e7e15df2e71 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 13:23:53 +0000 Subject: [PATCH 084/104] Fixes following rebase --- .../0007_ssh_conf_managed_by_yunohost_step1.py | 6 +++--- .../0008_ssh_conf_managed_by_yunohost_step2.py | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) 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 index 73cb162b6..306e47672 100644 --- 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 @@ -4,7 +4,6 @@ import re from shutil import copyfile from moulinette import m18n -from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import mkdir, rm @@ -12,6 +11,7 @@ from yunohost.tools import Migration from yunohost.service import service_regen_conf, _get_conf_hashes, \ _calculate_hash, _run_service_command from yunohost.settings import settings_set +from yunohost.utils.error import YunohostError logger = getActionLogger('yunohost.migration') @@ -67,11 +67,11 @@ class MyMigration(Migration): # Restart ssh and backward if it fail if not _run_service_command('restart', 'ssh'): self.backward() - raise MoulinetteError(m18n.n("migration_0007_cancel")) + raise YunohostError("migration_0007_cancel") def backward(self): # We don't backward completely but it should be enough copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) if not _run_service_command('restart', 'ssh'): - raise MoulinetteError(m18n.n("migration_0007_cannot_restart")) + raise YunohostError("migration_0007_cannot_restart") 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 index c53154192..5d8fee89c 100644 --- 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 @@ -1,13 +1,12 @@ import re -from moulinette import m18n -from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from yunohost.tools import Migration from yunohost.service import service_regen_conf, _get_conf_hashes, \ _calculate_hash from yunohost.settings import settings_set, settings_get +from yunohost.utils.error import YunohostError logger = getActionLogger('yunohost.migration') @@ -33,7 +32,7 @@ class MyMigration(Migration): def backward(self): - raise MoulinetteError(m18n.n("migration_0008_backward_impossible")) + raise YunohostError("migration_0008_backward_impossible") @property def mode(self): From caa2d0d91b325ffcf1e867052e4989c0e052f825 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 14:23:11 +0000 Subject: [PATCH 085/104] MoulinetteError / OSError never accepted keyword arguments such as exc_info :/ ... --- src/yunohost/settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 12b046146..fbf998d3a 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -206,7 +206,7 @@ def _get_settings(): setting_key=key)) unknown_settings[key] = value except Exception as e: - raise YunohostError('global_settings_cant_open_settings', reason=e, exc_info=1) + raise YunohostError('global_settings_cant_open_settings', reason=e) if unknown_settings: try: @@ -227,10 +227,10 @@ def _save_settings(settings, location=SETTINGS_PATH): try: result = json.dumps(settings_without_description, indent=4) except Exception as e: - raise YunohostError('global_settings_cant_serialize_settings', reason=e, exc_info=1) + raise YunohostError('global_settings_cant_serialize_settings', reason=e) try: with open(location, "w") as settings_fd: settings_fd.write(result) except Exception as e: - raise YunohostError('global_settings_cant_write_settings', reason=e, exc_info=1) + raise YunohostError('global_settings_cant_write_settings', reason=e) From 18c33db8b60779ac4c66b7a6efde5973cbe7136f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 19:07:25 +0000 Subject: [PATCH 086/104] MoulinetteError -> YunohostError --- src/yunohost/settings.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index fbf998d3a..c48f567ab 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -95,9 +95,10 @@ def settings_set(key, value): try: value=int(value) except: - raise MoulinetteError(errno.EINVAL, m18n.n( - 'global_settings_bad_type_for_setting', setting=key, - received_type=type(value).__name__, expected_type=key_type)) + raise YunohostError('global_settings_bad_type_for_setting', + setting=key, + received_type=type(value).__name__, + expected_type=key_type) else: raise YunohostError('global_settings_bad_type_for_setting', setting=key, received_type=type(value).__name__, expected_type=key_type) From 712f742ef6cc790b87358a2901fa3ee83c5ade2e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 19:10:17 +0000 Subject: [PATCH 087/104] Woopsies typo --- src/yunohost/tests/test_backuprestore.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index abe5bb1c8..a2356318d 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -345,7 +345,6 @@ def test_backup_app_with_no_backup_script(mocker): with pytest.raises(YunohostError): backup_create(system=None, apps=["backup_recommended_app"]) ->>>>>>> modif YunohostError to YunohostError m18n.n.assert_any_call("backup_with_no_backup_script_for_app", app="backup_recommended_app") m18n.n.assert_any_call('backup_nothings_done') From b93e96d33bb999fd0e96ef6e6c1293d0ddc85d49 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 12 Dec 2018 19:38:05 +0000 Subject: [PATCH 088/104] Fix tests + allow to bypass m18n in YunohostError if a raw message is provided --- src/yunohost/app.py | 4 ++-- src/yunohost/tests/test_appurl.py | 4 ++-- src/yunohost/tools.py | 3 ++- src/yunohost/utils/error.py | 9 ++++++--- src/yunohost/utils/yunopaste.py | 6 +++--- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 510b60e95..3177407c1 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -840,9 +840,9 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on if install_retcode == -1: msg = m18n.n('operation_interrupted') + " " + error_msg - raise YunohostError(msg) + raise YunohostError(msg, __raw_msg__=True) msg = error_msg - raise YunohostError(msg) + raise YunohostError(msg, __raw_msg__=True) # Clean hooks and add new ones hook_remove(app_instance_name) diff --git a/src/yunohost/tests/test_appurl.py b/src/yunohost/tests/test_appurl.py index 58c345dea..85f290b5d 100644 --- a/src/yunohost/tests/test_appurl.py +++ b/src/yunohost/tests/test_appurl.py @@ -1,7 +1,7 @@ import pytest -from yunohost.utils.error import YunohostError, init_authenticator - +from moulinette.core import init_authenticator +from yunohost.utils.error import YunohostError from yunohost.app import app_install, app_remove from yunohost.domain import _get_maindomain, domain_url_available, _normalize_domain_path diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 6102b9f7a..58bdd23d3 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -39,7 +39,8 @@ import apt import apt.progress from moulinette import msettings, msignals, m18n -from yunohost.utils.error import YunohostError, init_authenticator +from moulinette.core import init_authenticator +from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output from moulinette.utils.filesystem import read_json, write_to_json diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py index 8a73e52b7..7c00ee5a4 100644 --- a/src/yunohost/utils/error.py +++ b/src/yunohost/utils/error.py @@ -24,7 +24,10 @@ from moulinette import m18n class YunohostError(MoulinetteError): """Yunohost base exception""" - def __init__(self, key, *args, **kwargs): - msg = m18n.n(key, *args, **kwargs) - super(YunohostError, self).__init__(msg) + def __init__(self, key, __raw_msg__=False, *args, **kwargs): + if __raw_msg__: + msg = key + else: + msg = m18n.n(key, *args, **kwargs) + super(YunohostError, self).__init__(msg, __raw_msg__=True) diff --git a/src/yunohost/utils/yunopaste.py b/src/yunohost/utils/yunopaste.py index e349ebc32..436f94911 100644 --- a/src/yunohost/utils/yunopaste.py +++ b/src/yunohost/utils/yunopaste.py @@ -12,14 +12,14 @@ def yunopaste(data): try: r = requests.post("%s/documents" % paste_server, data=data, timeout=30) except Exception as e: - raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e)) + raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e), __raw_msg__=True) if r.status_code != 200: - raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text)) + raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text), __raw_msg__=True) try: url = json.loads(r.text)["key"] except: - raise YunohostError("Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text) + raise YunohostError("Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text, __raw_msg__=True) return "%s/raw/%s" % (paste_server, url) From 2ece557e0c12481dca02a02870d644bbb3e13c91 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 13 Dec 2018 15:16:07 +0000 Subject: [PATCH 089/104] Raw messages --- src/yunohost/app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 3177407c1..fca2d376b 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -183,7 +183,7 @@ def app_fetchlist(url=None, name=None): with open(list_file, "w") as f: f.write(appslist) except Exception as e: - raise YunohostError("Error while writing appslist %s: %s" % (name, str(e))) + raise YunohostError("Error while writing appslist %s: %s" % (name, str(e)), __raw_msg__=True) now = int(time.time()) appslists[name]["lastUpdate"] = now @@ -1472,7 +1472,7 @@ def app_action_run(app, action, args=None): actions = {x["id"]: x for x in actions} if action not in actions: - raise YunohostError("action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys()))) + raise YunohostError("action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys())), __raw_msg__=True) action_declaration = actions[action] @@ -2431,7 +2431,7 @@ def _write_appslist_list(appslist_lists): json.dump(appslist_lists, f) except Exception as e: raise YunohostError("Error while writing list of appslist %s: %s" % - (APPSLISTS_JSON, str(e))) + (APPSLISTS_JSON, str(e)), __raw_msg__=True) def _register_new_appslist(url, name): From f6b244f294df316028e2a0cb7f508b4cbfb33ab5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 13 Dec 2018 15:39:24 +0000 Subject: [PATCH 090/104] Raw message --- 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 fca2d376b..36a0414ec 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1510,7 +1510,7 @@ def app_action_run(app, action, args=None): ) if retcode not in action_declaration.get("accepted_return_codes", [0]): - raise YunohostError(retcode, "Error while executing action '%s' of app '%s': return code %s" % (action, app, retcode)) + raise YunohostError("Error while executing action '%s' of app '%s': return code %s" % (action, app, retcode), __raw_msg__=True) os.remove(path) From fb010765bd5cdabc38186c5ab5db9fdad3776d1a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 13 Dec 2018 15:40:00 +0000 Subject: [PATCH 091/104] Those are genuine MoulinetteError because base on moulinette helpers --- src/yunohost/domain.py | 3 ++- src/yunohost/dyndns.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 83da7e18c..515a452a8 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -29,6 +29,7 @@ import json import yaml from moulinette import m18n, msettings +from moulinette.core import MoulinetteError from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger @@ -76,7 +77,7 @@ def domain_add(operation_logger, auth, domain, dyndns=False): try: auth.validate_uniqueness({'virtualdomain': domain}) - except YunohostError: + except MoulinetteError: raise YunohostError('domain_exists') operation_logger.start() diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index e83280b19..0428eb93c 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -32,13 +32,13 @@ import base64 import subprocess from moulinette import m18n -from yunohost.utils.error import YunohostError +from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import read_file, write_to_file, rm from moulinette.utils.network import download_json from moulinette.utils.process import check_output - +from yunohost.utils.error import YunohostError from yunohost.domain import _get_maindomain, _build_dns_conf from yunohost.utils.network import get_public_ip from yunohost.log import is_unit_operation @@ -74,7 +74,7 @@ def _dyndns_provides(provider, domain): # Dyndomains will be a list of domains supported by the provider # e.g. [ "nohost.me", "noho.st" ] dyndomains = download_json('https://%s/domains' % provider, timeout=30) - except YunohostError as e: + except MoulinetteError as e: logger.error(str(e)) raise YunohostError('dyndns_could_not_check_provide', domain=domain, provider=provider) @@ -101,7 +101,7 @@ def _dyndns_available(provider, domain): try: r = download_json('https://%s/test/%s' % (provider, domain), expected_status_code=None) - except YunohostError as e: + except MoulinetteError as e: logger.error(str(e)) raise YunohostError('dyndns_could_not_check_available', domain=domain, provider=provider) From 115b557b67893111d5b7708f6b3b3226615f3092 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 13 Dec 2018 18:49:20 +0000 Subject: [PATCH 092/104] autopep8 + a few manual tweaks --- src/yunohost/app.py | 51 +++--- src/yunohost/backup.py | 165 +++++++++--------- src/yunohost/certificate.py | 64 +++---- .../0001_change_cert_group_to_sslcert.py | 2 + .../0002_migrate_to_tsig_sha256.py | 4 +- .../0003_migrate_to_stretch.py | 13 +- .../0004_php5_to_php7_pools.py | 3 +- .../0005_postgresql_9p4_to_9p6.py | 1 + .../0006_sync_admin_and_root_passwords.py | 2 + ...0007_ssh_conf_managed_by_yunohost_step1.py | 9 +- ...0008_ssh_conf_managed_by_yunohost_step2.py | 5 +- src/yunohost/domain.py | 4 +- src/yunohost/dyndns.py | 11 +- src/yunohost/firewall.py | 4 +- src/yunohost/hook.py | 6 +- src/yunohost/log.py | 3 +- src/yunohost/monitor.py | 8 +- src/yunohost/service.py | 27 +-- src/yunohost/settings.py | 14 +- src/yunohost/ssh.py | 2 +- src/yunohost/tests/conftest.py | 15 +- src/yunohost/tests/test_appslist.py | 26 +-- src/yunohost/tests/test_appurl.py | 7 +- src/yunohost/tests/test_backuprestore.py | 95 +++++----- src/yunohost/tests/test_changeurl.py | 1 + src/yunohost/tests/test_settings.py | 7 +- src/yunohost/tools.py | 41 +++-- src/yunohost/user.py | 16 +- src/yunohost/utils/error.py | 4 +- src/yunohost/utils/filesystem.py | 2 + src/yunohost/utils/network.py | 2 +- src/yunohost/utils/packages.py | 6 + src/yunohost/utils/password.py | 6 +- src/yunohost/utils/yunopaste.py | 1 + 34 files changed, 341 insertions(+), 286 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 36a0414ec..8eaef03c5 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -137,7 +137,7 @@ def app_fetchlist(url=None, name=None): else: appslists_to_be_fetched = appslists.keys() - import requests # lazy loading this module for performance reasons + import requests # lazy loading this module for performance reasons # Fetch all appslists to be fetched for name in appslists_to_be_fetched: @@ -172,7 +172,7 @@ def app_fetchlist(url=None, name=None): appslist = appslist_request.text try: json.loads(appslist) - except ValueError, e: + except ValueError as e: logger.error(m18n.n('appslist_retrieve_bad_format', appslist=name)) continue @@ -542,7 +542,7 @@ def app_change_url(operation_logger, auth, app, domain, path): raise YunohostError("app_change_url_failed_nginx_reload", nginx_errors=nginx_errors) logger.success(m18n.n("app_change_url_success", - app=app, domain=domain, path=path)) + app=app, domain=domain, path=path)) hook_callback('post_app_change_url', args=args_list, env=env_dict) @@ -701,7 +701,6 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.log import OperationLogger - # Fetch or extract sources try: os.listdir(INSTALL_TMP) @@ -758,7 +757,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on env_dict["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) # Start register change on system - operation_logger.extra.update({'env':env_dict}) + operation_logger.extra.update({'env': env_dict}) operation_logger.related_to = [s for s in operation_logger.related_to if s[0] != "app"] operation_logger.related_to.append(("app", app_id)) operation_logger.start() @@ -816,8 +815,8 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on # Execute remove script operation_logger_remove = OperationLogger('remove_on_failed_install', - [('app', app_instance_name)], - env=env_dict_remove) + [('app', app_instance_name)], + env=env_dict_remove) operation_logger_remove.start() remove_retcode = hook_exec( @@ -944,7 +943,6 @@ def app_addaccess(auth, apps, users=[]): for app in apps: - app_settings = _get_app_settings(app) if not app_settings: continue @@ -957,7 +955,7 @@ def app_addaccess(auth, apps, users=[]): # Start register change on system related_to = [('app', app)] - operation_logger= OperationLogger('app_addaccess', related_to) + operation_logger = OperationLogger('app_addaccess', related_to) operation_logger.start() allowed_users = set() @@ -1020,7 +1018,7 @@ def app_removeaccess(auth, apps, users=[]): # Start register change on system related_to = [('app', app)] - operation_logger= OperationLogger('app_removeaccess', related_to) + operation_logger = OperationLogger('app_removeaccess', related_to) operation_logger.start() if remove_all: @@ -1034,7 +1032,7 @@ def app_removeaccess(auth, apps, users=[]): if allowed_user not in users: allowed_users.add(allowed_user) - operation_logger.related_to += [ ('user', x) for x in allowed_users ] + operation_logger.related_to += [('user', x) for x in allowed_users] operation_logger.flush() new_users = ','.join(allowed_users) app_setting(app, 'allowed_users', new_users) @@ -1069,7 +1067,7 @@ def app_clearaccess(auth, apps): # Start register change on system related_to = [('app', app)] - operation_logger= OperationLogger('app_clearaccess', related_to) + operation_logger = OperationLogger('app_clearaccess', related_to) operation_logger.start() if 'mode' in app_settings: @@ -1126,7 +1124,7 @@ def app_makedefault(operation_logger, auth, app, domain=None): if domain is None: domain = app_domain - operation_logger.related_to.append(('domain',domain)) + operation_logger.related_to.append(('domain', domain)) elif domain not in domain_list(auth)['domains']: raise YunohostError('domain_unknown') @@ -1218,7 +1216,7 @@ def app_register_url(auth, app, domain, path): # This line can't be moved on top of file, otherwise it creates an infinite # loop of import with tools.py... - from domain import _get_conflicting_apps, _normalize_domain_path + from .domain import _get_conflicting_apps, _normalize_domain_path domain, path = _normalize_domain_path(domain, path) @@ -1569,10 +1567,10 @@ def app_config_show_panel(app): parsed_values[key] = value return_code = hook_exec(config_script, - args=["show"], - env=env, - stdout_callback=parse_stdout, - ) + args=["show"], + env=env, + stdout_callback=parse_stdout, + ) if return_code != 0: raise Exception("script/config show return value code: %s (considered as an error)", return_code) @@ -1656,9 +1654,9 @@ def app_config_apply(app, args): logger.warning("Ignore key '%s' from arguments because it is not in the config", key) return_code = hook_exec(config_script, - args=["apply"], - env=env, - ) + args=["apply"], + env=env, + ) if return_code != 0: raise Exception("'script/config apply' return value code: %s (considered as an error)", return_code) @@ -2185,7 +2183,6 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None): elif arg_type == 'password': msignals.display(m18n.n('good_practices_about_user_password')) - try: input_string = msignals.prompt(ask_string, is_password) except NotImplementedError: @@ -2385,7 +2382,7 @@ def _install_appslist_fetch_cron(): with open(cron_job_file, "w") as f: f.write('\n'.join(cron_job)) - _set_permissions(cron_job_file, "root", "root", 0755) + _set_permissions(cron_job_file, "root", "root", 0o755) # FIXME - Duplicate from certificate.py, should be moved into a common helper @@ -2431,7 +2428,7 @@ def _write_appslist_list(appslist_lists): json.dump(appslist_lists, f) except Exception as e: raise YunohostError("Error while writing list of appslist %s: %s" % - (APPSLISTS_JSON, str(e)), __raw_msg__=True) + (APPSLISTS_JSON, str(e)), __raw_msg__=True) def _register_new_appslist(url, name): @@ -2541,7 +2538,7 @@ def _patch_php5(app_folder): 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 + "-e 's@/var/run/php5-fpm@/var/run/php/php7.0-fpm@g' " \ + "-e 's@php5@php7.0@g' " \ + "%s" % filename os.system(c) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 6ed73d343..745291fb1 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -52,6 +52,7 @@ from yunohost.monitor import binary_to_human from yunohost.tools import tools_postinstall from yunohost.service import service_regen_conf from yunohost.log import OperationLogger +from functools import reduce BACKUP_PATH = '/home/yunohost.backup' ARCHIVES_PATH = '%s/archives' % BACKUP_PATH @@ -63,6 +64,7 @@ logger = getActionLogger('yunohost.backup') class BackupRestoreTargetsManager(object): + """ BackupRestoreTargetsManager manage the targets in BackupManager and RestoreManager @@ -176,6 +178,7 @@ class BackupRestoreTargetsManager(object): class BackupManager(): + """ This class collect files to backup in a list and apply one or several backup method on it. @@ -267,9 +270,9 @@ class BackupManager(): self.work_dir = os.path.join(BACKUP_PATH, 'tmp', name) self._init_work_dir() - ########################################################################### - # Misc helpers # - ########################################################################### + # + # Misc helpers # + # @property def info(self): @@ -321,16 +324,16 @@ class BackupManager(): # FIXME replace isdir by exists ? manage better the case where the path # exists if not os.path.isdir(self.work_dir): - filesystem.mkdir(self.work_dir, 0750, parents=True, uid='admin') + filesystem.mkdir(self.work_dir, 0o750, parents=True, uid='admin') elif self.is_tmp_work_dir: logger.debug("temporary directory for backup '%s' already exists", self.work_dir) # FIXME May be we should clean the workdir here raise YunohostError('backup_output_directory_not_empty') - ########################################################################### - # Backup target management # - ########################################################################### + # + # Backup target management # + # def set_system_targets(self, system_parts=[]): """ @@ -380,9 +383,9 @@ class BackupManager(): logger.warning(m18n.n('backup_with_no_restore_script_for_app', app=app)) self.targets.set_result("apps", app, "Warning") - ########################################################################### - # Management of files to backup / "The CSV" # - ########################################################################### + # + # Management of files to backup / "The CSV" # + # def _import_to_list_to_backup(self, tmp_csv): """ @@ -465,9 +468,9 @@ class BackupManager(): logger.error(m18n.n('backup_csv_addition_failed')) self.csv_file.close() - ########################################################################### - # File collection from system parts and apps # - ########################################################################### + # + # File collection from system parts and apps # + # def collect_files(self): """ @@ -602,7 +605,7 @@ class BackupManager(): restore_hooks_dir = os.path.join(self.work_dir, "hooks", "restore") if not os.path.exists(restore_hooks_dir): - filesystem.mkdir(restore_hooks_dir, mode=0750, + filesystem.mkdir(restore_hooks_dir, mode=0o750, parents=True, uid='admin') restore_hooks = hook_list("restore")["hooks"] @@ -668,7 +671,7 @@ class BackupManager(): logger.debug(m18n.n('backup_running_app_script', app=app)) try: # Prepare backup directory for the app - filesystem.mkdir(tmp_app_bkp_dir, 0750, True, uid='admin') + filesystem.mkdir(tmp_app_bkp_dir, 0o750, True, uid='admin') # Copy the app settings to be able to call _common.sh shutil.copytree(app_setting_path, settings_dir) @@ -702,9 +705,9 @@ class BackupManager(): filesystem.rm(tmp_script, force=True) filesystem.rm(env_dict["YNH_BACKUP_CSV"], force=True) - ########################################################################### - # Actual backup archive creation / method management # - ########################################################################### + # + # Actual backup archive creation / method management # + # def add(self, method): """ @@ -776,6 +779,7 @@ class BackupManager(): class RestoreManager(): + """ RestoreManager allow to restore a past backup archive @@ -824,9 +828,9 @@ class RestoreManager(): self.method = BackupMethod.create(method) self.targets = BackupRestoreTargetsManager() - ########################################################################### - # Misc helpers # - ########################################################################### + # + # Misc helpers # + # @property def success(self): @@ -876,7 +880,7 @@ class RestoreManager(): domain = f.readline().rstrip() except IOError: logger.debug("unable to retrieve current_host from the backup", - exc_info=1) + exc_info=1) # FIXME include the current_host by default ? raise YunohostError('backup_invalid_archive') @@ -902,9 +906,9 @@ class RestoreManager(): logger.warning(m18n.n('restore_cleaning_failed')) filesystem.rm(self.work_dir, True, True) - ########################################################################### - # Restore target manangement # - ########################################################################### + # + # Restore target manangement # + # def set_system_targets(self, system_parts=[]): """ @@ -980,9 +984,9 @@ class RestoreManager(): self.info['apps'].keys(), unknown_error) - ########################################################################### - # Archive mounting # - ########################################################################### + # + # Archive mounting # + # def mount(self): """ @@ -1023,9 +1027,9 @@ class RestoreManager(): self._read_info_files() - ########################################################################### - # Space computation / checks # - ########################################################################### + # + # Space computation / checks # + # def _compute_needed_space(self): """ @@ -1082,13 +1086,13 @@ class RestoreManager(): return True elif free_space > needed_space: # TODO Add --force options to avoid the error raising - raise YunohostError('restore_may_be_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) + raise YunohostError('restore_may_be_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) else: raise YunohostError('restore_not_enough_disk_space', free_space=free_space, needed_space=needed_space, margin=margin) - ########################################################################### - # "Actual restore" (reverse step of the backup collect part) # - ########################################################################### + # + # "Actual restore" (reverse step of the backup collect part) # + # def restore(self): """ @@ -1104,7 +1108,6 @@ class RestoreManager(): # Apply dirty patch to redirect php5 file on php7 self._patch_backup_csv_file() - self._restore_system() self._restore_apps() finally: @@ -1130,7 +1133,7 @@ class RestoreManager(): 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') + .replace('php5', 'php7') newlines.append(row) except (IOError, OSError, csv.Error) as e: @@ -1260,7 +1263,7 @@ class RestoreManager(): # Check if the app has a restore script app_restore_script_in_archive = os.path.join(app_scripts_in_archive, - 'restore') + 'restore') if not os.path.isfile(app_restore_script_in_archive): logger.warning(m18n.n('unrestore_app', app=app_instance_name)) self.targets.set_result("apps", app_instance_name, "Warning") @@ -1273,7 +1276,7 @@ class RestoreManager(): app_instance_name) app_scripts_new_path = os.path.join(app_settings_new_path, 'scripts') shutil.copytree(app_settings_in_archive, app_settings_new_path) - filesystem.chmod(app_settings_new_path, 0400, 0400, True) + filesystem.chmod(app_settings_new_path, 0o400, 0o400, True) filesystem.chown(app_scripts_new_path, 'admin', None, True) # Copy the app scripts to a writable temporary folder @@ -1281,7 +1284,7 @@ class RestoreManager(): # in the backup method ? tmp_folder_for_app_restore = tempfile.mkdtemp(prefix='restore') copytree(app_scripts_in_archive, tmp_folder_for_app_restore) - filesystem.chmod(tmp_folder_for_app_restore, 0550, 0550, True) + filesystem.chmod(tmp_folder_for_app_restore, 0o550, 0o550, True) filesystem.chown(tmp_folder_for_app_restore, 'admin', None, True) restore_script = os.path.join(tmp_folder_for_app_restore, 'restore') @@ -1298,7 +1301,7 @@ class RestoreManager(): raise_on_error=True, env=env_dict) except: - msg = m18n.n('restore_app_failed',app=app_instance_name) + msg = m18n.n('restore_app_failed', app=app_instance_name) logger.exception(msg) operation_logger.error(msg) @@ -1314,8 +1317,8 @@ class RestoreManager(): env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) operation_logger = OperationLogger('remove_on_failed_restore', - [('app', app_instance_name)], - env=env_dict_remove) + [('app', app_instance_name)], + env=env_dict_remove) operation_logger.start() # Execute remove script @@ -1359,12 +1362,13 @@ class RestoreManager(): return env_var -############################################################################### -# Backup methods # -############################################################################### +# +# Backup methods # +# class BackupMethod(object): + """ BackupMethod is an abstract class that represents a way to backup and restore a list of files. @@ -1637,7 +1641,7 @@ class BackupMethod(object): if size > MB_ALLOWED_TO_ORGANIZE: try: i = msignals.prompt(m18n.n('backup_ask_for_copying_if_needed', - answers='y/N', size=str(size))) + answers='y/N', size=str(size))) except NotImplemented: raise YunohostError('backup_unable_to_organize_files') else: @@ -1646,7 +1650,7 @@ class BackupMethod(object): # Copy unbinded path logger.debug(m18n.n('backup_copying_to_organize_the_archive', - size=str(size))) + size=str(size))) for path in paths_needed_to_be_copied: dest = os.path.join(self.work_dir, path['dest']) if os.path.isdir(path['source']): @@ -1686,6 +1690,7 @@ class BackupMethod(object): class CopyBackupMethod(BackupMethod): + """ This class just do an uncompress copy of each file in a location, and could be the inverse for restoring @@ -1712,7 +1717,7 @@ class CopyBackupMethod(BackupMethod): dest_parent = os.path.dirname(dest) if not os.path.exists(dest_parent): - filesystem.mkdir(dest_parent, 0750, True, uid='admin') + filesystem.mkdir(dest_parent, 0o750, True, uid='admin') if os.path.isdir(source): shutil.copytree(source, dest) @@ -1747,6 +1752,7 @@ class CopyBackupMethod(BackupMethod): class TarBackupMethod(BackupMethod): + """ This class compress all files to backup in archive. """ @@ -1777,7 +1783,7 @@ class TarBackupMethod(BackupMethod): """ if not os.path.exists(self.repo): - filesystem.mkdir(self.repo, 0750, parents=True, uid='admin') + filesystem.mkdir(self.repo, 0o750, parents=True, uid='admin') # Check free space in output self._check_is_enough_free_space() @@ -1895,6 +1901,7 @@ class BorgBackupMethod(BackupMethod): class CustomBackupMethod(BackupMethod): + """ This class use a bash script/hook "backup_method" to do the backup/restore operations. A user can add his own hook inside @@ -1958,9 +1965,9 @@ class CustomBackupMethod(BackupMethod): self.manager.description] -############################################################################### -# "Front-end" # -############################################################################### +# +# "Front-end" # +# def backup_create(name=None, description=None, methods=[], output_directory=None, no_compress=False, @@ -1980,9 +1987,9 @@ def backup_create(name=None, description=None, methods=[], # TODO: Add a 'clean' argument to clean output directory - ########################################################################### - # Validate / parse arguments # - ########################################################################### + # + # Validate / parse arguments # + # # Validate there is no archive with the same name if name and name in backup_list()['archives']: @@ -1995,7 +2002,7 @@ def backup_create(name=None, description=None, methods=[], # Check for forbidden folders if output_directory.startswith(ARCHIVES_PATH) or \ re.match(r'^/(|(bin|boot|dev|etc|lib|root|run|sbin|sys|usr|var)(|/.*))$', - output_directory): + output_directory): raise YunohostError('backup_output_directory_forbidden') # Check that output directory is empty @@ -2017,9 +2024,9 @@ def backup_create(name=None, description=None, methods=[], system = [] apps = [] - ########################################################################### - # Intialize # - ########################################################################### + # + # Intialize # + # # Create yunohost archives directory if it does not exists _create_archive_dir() @@ -2044,9 +2051,9 @@ def backup_create(name=None, description=None, methods=[], backup_manager.set_system_targets(system) backup_manager.set_apps_targets(apps) - ########################################################################### - # Collect files and put them in the archive # - ########################################################################### + # + # Collect files and put them in the archive # + # # Collect files to be backup (by calling app backup script / system hooks) backup_manager.collect_files() @@ -2074,9 +2081,9 @@ def backup_restore(auth, name, system=[], apps=[], force=False): apps -- List of application names to restore """ - ########################################################################### - # Validate / parse arguments # - ########################################################################### + # + # Validate / parse arguments # + # # If no --system or --apps given, restore everything if system is None and apps is None: @@ -2105,9 +2112,9 @@ def backup_restore(auth, name, system=[], apps=[], force=False): # TODO Partial app restore could not work if ldap is not restored before # TODO repair mysql if broken and it's a complete restore - ########################################################################### - # Initialize # - ########################################################################### + # + # Initialize # + # restore_manager = RestoreManager(name) @@ -2116,9 +2123,9 @@ def backup_restore(auth, name, system=[], apps=[], force=False): restore_manager.assert_enough_free_space() - ########################################################################### - # Mount the archive then call the restore for each system part / app # - ########################################################################### + # + # Mount the archive then call the restore for each system part / app # + # restore_manager.mount() restore_manager.restore() @@ -2156,7 +2163,7 @@ def backup_list(with_info=False, human_readable=False): except ValueError: continue result.append(name) - result.sort(key=lambda x: os.path.getctime(os.path.join(ARCHIVES_PATH, x+".tar.gz"))) + result.sort(key=lambda x: os.path.getctime(os.path.join(ARCHIVES_PATH, x + ".tar.gz"))) if result and with_info: d = OrderedDict() @@ -2194,7 +2201,7 @@ def backup_info(name, with_details=False, human_readable=False): # 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) + path=archive_file) info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name) @@ -2259,7 +2266,7 @@ def backup_delete(name): """ if name not in backup_list()["archives"]: raise YunohostError('backup_archive_name_unknown', - name=name) + name=name) hook_callback('pre_backup_delete', args=[name]) @@ -2277,9 +2284,9 @@ def backup_delete(name): logger.success(m18n.n('backup_deleted')) -############################################################################### -# Misc helpers # -############################################################################### +# +# Misc helpers # +# def _create_archive_dir(): @@ -2288,7 +2295,7 @@ def _create_archive_dir(): if os.path.lexists(ARCHIVES_PATH): raise YunohostError('backup_output_symlink_dir_broken', path=ARCHIVES_PATH) - os.mkdir(ARCHIVES_PATH, 0750) + os.mkdir(ARCHIVES_PATH, 0o750) def _call_for_each_path(self, callback, csv_path=None): diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index cdcf4bbe9..aea0c60b1 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -80,9 +80,9 @@ DNS_RESOLVERS = [ "80.67.188.188" # LDN ] -############################################################################### -# Front-end stuff # -############################################################################### +# +# Front-end stuff # +# def certificate_status(auth, domain_list, full=False): @@ -149,7 +149,7 @@ def _certificate_install_selfsigned(domain_list, force=False): for domain in domain_list: operation_logger = OperationLogger('selfsigned_cert_install', [('domain', domain)], - args={'force': force}) + args={'force': force}) # Paths of files and folder we'll need date_tag = datetime.utcnow().strftime("%Y%m%d.%H%M%S") @@ -215,10 +215,10 @@ def _certificate_install_selfsigned(domain_list, force=False): crt_pem.write(ca_pem.read()) # Set appropriate permissions - _set_permissions(new_cert_folder, "root", "root", 0755) - _set_permissions(key_file, "root", "ssl-cert", 0640) - _set_permissions(crt_file, "root", "ssl-cert", 0640) - _set_permissions(conf_file, "root", "root", 0600) + _set_permissions(new_cert_folder, "root", "root", 0o755) + _set_permissions(key_file, "root", "ssl-cert", 0o640) + _set_permissions(crt_file, "root", "ssl-cert", 0o640) + _set_permissions(conf_file, "root", "root", 0o600) # Actually enable the certificate we created _enable_certificate(domain, new_cert_folder) @@ -273,8 +273,8 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F for domain in domain_list: operation_logger = OperationLogger('letsencrypt_cert_install', [('domain', domain)], - args={'force': force, 'no_checks': no_checks, - 'staging': staging}) + args={'force': force, 'no_checks': no_checks, + 'staging': staging}) logger.info( "Now attempting install of certificate for domain %s!", domain) @@ -298,6 +298,7 @@ def _certificate_install_letsencrypt(auth, domain_list, force=False, no_checks=F logger.error(msg) operation_logger.error(msg) + def certificate_renew(auth, domain_list, force=False, no_checks=False, email=False, staging=False): """ Renew Let's Encrypt certificate for given domains (all by default) @@ -367,8 +368,8 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal for domain in domain_list: operation_logger = OperationLogger('letsencrypt_cert_renew', [('domain', domain)], - args={'force': force, 'no_checks': no_checks, - 'staging': staging, 'email': email}) + args={'force': force, 'no_checks': no_checks, + 'staging': staging, 'email': email}) logger.info( "Now attempting renewing of certificate for domain %s !", domain) @@ -401,9 +402,10 @@ def certificate_renew(auth, domain_list, force=False, no_checks=False, email=Fal logger.error("Sending email with details to root ...") _email_renewing_failed(domain, e, stack.getvalue()) -############################################################################### -# Back-end stuff # -############################################################################### +# +# Back-end stuff # +# + def _install_cron(): cron_job_file = "/etc/cron.daily/yunohost-certificate-renew" @@ -412,7 +414,7 @@ def _install_cron(): f.write("#!/bin/bash\n") f.write("yunohost domain cert-renew --email\n") - _set_permissions(cron_job_file, "root", "root", 0755) + _set_permissions(cron_job_file, "root", "root", 0o755) def _email_renewing_failed(domain, exception_message, stack): @@ -517,8 +519,8 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): if not os.path.exists(TMP_FOLDER): os.makedirs(TMP_FOLDER) - _set_permissions(WEBROOT_FOLDER, "root", "www-data", 0650) - _set_permissions(TMP_FOLDER, "root", "root", 0640) + _set_permissions(WEBROOT_FOLDER, "root", "www-data", 0o650) + _set_permissions(TMP_FOLDER, "root", "root", 0o640) # Regen conf for dnsmasq if needed _regen_dnsmasq_if_needed() @@ -529,7 +531,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): domain_key_file = "%s/%s.pem" % (TMP_FOLDER, domain) _generate_key(domain_key_file) - _set_permissions(domain_key_file, "root", "ssl-cert", 0640) + _set_permissions(domain_key_file, "root", "ssl-cert", 0o640) _prepare_certificate_signing_request(domain, domain_key_file, TMP_FOLDER) @@ -563,7 +565,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): raise YunohostError('certmanager_cert_signing_failed') - import requests # lazy loading this module for performance reasons + import requests # lazy loading this module for performance reasons try: intermediate_certificate = requests.get(INTERMEDIATE_CERTIFICATE_URL, timeout=30).text except requests.exceptions.Timeout as e: @@ -585,12 +587,12 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): os.makedirs(new_cert_folder) - _set_permissions(new_cert_folder, "root", "root", 0655) + _set_permissions(new_cert_folder, "root", "root", 0o655) # Move the private key domain_key_file_finaldest = os.path.join(new_cert_folder, "key.pem") shutil.move(domain_key_file, domain_key_file_finaldest) - _set_permissions(domain_key_file_finaldest, "root", "ssl-cert", 0640) + _set_permissions(domain_key_file_finaldest, "root", "ssl-cert", 0o640) # Write the cert domain_cert_file = os.path.join(new_cert_folder, "crt.pem") @@ -599,7 +601,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): f.write(signed_certificate) f.write(intermediate_certificate) - _set_permissions(domain_cert_file, "root", "ssl-cert", 0640) + _set_permissions(domain_cert_file, "root", "ssl-cert", 0o640) if staging: return @@ -614,7 +616,7 @@ def _fetch_and_enable_new_certificate(domain, staging=False, no_checks=False): def _prepare_certificate_signing_request(domain, key_file, output_folder): - from OpenSSL import crypto # lazy loading this module for performance reasons + from OpenSSL import crypto # lazy loading this module for performance reasons # Init a request csr = crypto.X509Req() @@ -645,7 +647,7 @@ def _get_status(domain): if not os.path.isfile(cert_file): raise YunohostError('certmanager_no_cert_file', domain=domain, file=cert_file) - from OpenSSL import crypto # lazy loading this module for performance reasons + from OpenSSL import crypto # lazy loading this module for performance reasons try: cert = crypto.load_certificate( crypto.FILETYPE_PEM, open(cert_file).read()) @@ -735,19 +737,19 @@ def _get_status(domain): "ACME_eligible": ACME_eligible } -############################################################################### -# Misc small stuff ... # -############################################################################### +# +# Misc small stuff ... # +# def _generate_account_key(): logger.debug("Generating account key ...") _generate_key(ACCOUNT_KEY_FILE) - _set_permissions(ACCOUNT_KEY_FILE, "root", "root", 0400) + _set_permissions(ACCOUNT_KEY_FILE, "root", "root", 0o400) def _generate_key(destination_path): - from OpenSSL import crypto # lazy loading this module for performance reasons + from OpenSSL import crypto # lazy loading this module for performance reasons k = crypto.PKey() k.generate_key(crypto.TYPE_RSA, KEY_SIZE) @@ -836,7 +838,7 @@ def _dns_ip_match_public_ip(public_ip, domain): def _domain_is_accessible_through_HTTP(ip, domain): - import requests # lazy loading this module for performance reasons + import requests # lazy loading this module for performance reasons try: requests.head("http://" + ip, headers={"Host": domain}, timeout=10) except requests.exceptions.Timeout as e: 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 index cd39df9fa..6485861b7 100644 --- a/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py +++ b/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py @@ -3,7 +3,9 @@ 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") diff --git a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py index 39e4d2afe..824245c82 100644 --- a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py +++ b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py @@ -16,6 +16,7 @@ logger = getActionLogger('yunohost.migration') class MyMigration(Migration): + "Migrate Dyndns stuff from MD5 TSIG to SHA512 TSIG" def backward(self): @@ -70,7 +71,7 @@ class MyMigration(Migration): os.system("mv /etc/yunohost/dyndns/*+165* /tmp") raise YunohostError('migrate_tsig_failed', domain=domain, - error_code=str(r.status_code), error=error) + error_code=str(r.status_code), error=error) # remove old certificates os.system("mv /etc/yunohost/dyndns/*+157* /tmp") @@ -87,4 +88,3 @@ class MyMigration(Migration): 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 index 80d182fbe..ee8c09849 100644 --- a/src/yunohost/data_migrations/0003_migrate_to_stretch.py +++ b/src/yunohost/data_migrations/0003_migrate_to_stretch.py @@ -24,6 +24,7 @@ YUNOHOST_PACKAGES = ["yunohost", "yunohost-admin", "moulinette", "ssowat"] class MyMigration(Migration): + "Upgrade the system to Debian Stretch and Yunohost 3.0" mode = "manual" @@ -168,11 +169,11 @@ class MyMigration(Migration): # - 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) + "-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): @@ -286,7 +287,7 @@ class MyMigration(Migration): # 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, 0700) + os.mkdir(tmp_dir, 0o700) for f in self.files_to_keep: dest_file = f.strip('/').replace("/", "_") diff --git a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py index 0237ddb38..46a5eb91d 100644 --- a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py +++ b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py @@ -19,6 +19,7 @@ MIGRATION_COMMENT = "; YunoHost note : this file was automatically moved from {} class MyMigration(Migration): + "Migrate php5-fpm 'pool' conf files to php7 stuff" def migrate(self): @@ -58,7 +59,7 @@ class MyMigration(Migration): _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 + 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") diff --git a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py index 0208b717e..c4a6e7f34 100644 --- a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py +++ b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py @@ -11,6 +11,7 @@ logger = getActionLogger('yunohost.migration') class MyMigration(Migration): + "Migrate DBs from Postgresql 9.4 to 9.6 after migrating to Stretch" def migrate(self): 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 index 1149f28d5..cd13d680d 100644 --- a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py +++ b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py @@ -15,7 +15,9 @@ 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 migrate(self): 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 index 306e47672..080cc0163 100644 --- 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 @@ -8,8 +8,10 @@ from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import mkdir, rm from yunohost.tools import Migration -from yunohost.service import service_regen_conf, _get_conf_hashes, \ - _calculate_hash, _run_service_command +from yunohost.service import service_regen_conf, \ + _get_conf_hashes, \ + _calculate_hash, \ + _run_service_command from yunohost.settings import settings_set from yunohost.utils.error import YunohostError @@ -19,6 +21,7 @@ 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 @@ -48,7 +51,7 @@ class MyMigration(Migration): # Create sshd_config.d dir if not os.path.exists(SSHD_CONF + '.d'): - mkdir(SSHD_CONF + '.d', 0755, uid='root', gid='root') + mkdir(SSHD_CONF + '.d', 0o755, uid='root', gid='root') # Here, we make it so that /etc/ssh/sshd_config is managed # by the regen conf (in particular in the case where the 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 index 5d8fee89c..6adfa769f 100644 --- 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 @@ -3,7 +3,8 @@ import re from moulinette.utils.log import getActionLogger from yunohost.tools import Migration -from yunohost.service import service_regen_conf, _get_conf_hashes, \ +from yunohost.service import service_regen_conf, \ + _get_conf_hashes, \ _calculate_hash from yunohost.settings import settings_set, settings_get from yunohost.utils.error import YunohostError @@ -12,7 +13,9 @@ 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 diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 515a452a8..29c7ab554 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -115,8 +115,8 @@ def domain_add(operation_logger, auth, domain, dyndns=False): service_regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix']) app_ssowatconf(auth) - except Exception, e: - from sys import exc_info; + except Exception as e: + from sys import exc_info t, v, tb = exc_info() # Force domain removal silently diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 0428eb93c..59a26e74b 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -104,7 +104,7 @@ def _dyndns_available(provider, domain): except MoulinetteError as e: logger.error(str(e)) raise YunohostError('dyndns_could_not_check_available', - domain=domain, provider=provider) + domain=domain, provider=provider) return r == u"Domain %s is available" % domain @@ -149,7 +149,7 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom with open(key_file) as f: key = f.readline().strip().split(' ', 6)[-1] - import requests # lazy loading this module for performance reasons + import requests # lazy loading this module for performance reasons # Send subscription try: r = requests.post('https://%s/key/%s?key_algo=hmac-sha512' % (subscribe_host, base64.b64encode(key)), data={'subdomain': domain}, timeout=30) @@ -211,7 +211,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, exception=e, number=migration.number, name=migration.name), - exc_info=1) + exc_info=1) return # Extract 'host', e.g. 'nohost.me' from 'foo.nohost.me' @@ -225,7 +225,6 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, 'zone %s' % host, ] - old_ipv4 = check_output("dig @%s +short %s" % (dyn_host, domain)).strip() or None old_ipv6 = check_output("dig @%s +short aaaa %s" % (dyn_host, domain)).strip() or None @@ -252,7 +251,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, logger.info("Updated needed, going on...") dns_conf = _build_dns_conf(domain) - del dns_conf["extra"] # Ignore records from the 'extra' category + del dns_conf["extra"] # Ignore records from the 'extra' category # Delete the old records for all domain/subdomains @@ -273,7 +272,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, # should be muc.the.domain.tld. or the.domain.tld if record["value"] == "@": record["value"] = domain - record["value"] = record["value"].replace(";","\;") + record["value"] = record["value"].replace(";", "\;") action = "update add {name}.{domain}. {ttl} {type} {value}".format(domain=domain, **record) action = action.replace(" @.", " ") diff --git a/src/yunohost/firewall.py b/src/yunohost/firewall.py index f7c6c0875..39102bdc2 100644 --- a/src/yunohost/firewall.py +++ b/src/yunohost/firewall.py @@ -374,10 +374,10 @@ def firewall_upnp(action='status', no_refresh=False): try: # Add new port mapping upnpc.addportmapping(port, protocol, upnpc.lanaddr, - port, 'yunohost firewall: port %d' % port, '') + port, 'yunohost firewall: port %d' % port, '') except: logger.debug('unable to add port %d using UPnP', - port, exc_info=1) + port, exc_info=1) enabled = False if enabled != firewall['uPnP']['enabled']: diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 5a6c4c154..79e9289ef 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -354,7 +354,7 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, # prepend environment variables cmd = '{0} {1}'.format( ' '.join(['{0}={1}'.format(k, shell_quote(v)) - for k, v in env.items()]), cmd) + for k, v in env.items()]), cmd) command.append(cmd.format(script=cmd_script, args=cmd_args)) if logger.isEnabledFor(log.DEBUG): @@ -369,8 +369,8 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, ) if stdinfo: - callbacks = ( callbacks[0], callbacks[1], - lambda l: logger.info(l.rstrip())) + callbacks = (callbacks[0], callbacks[1], + lambda l: logger.info(l.rstrip())) logger.debug("About to run the command '%s'" % command) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 7b32f18dd..d1e0c0e05 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -283,6 +283,7 @@ def is_unit_operation(entities=['app', 'domain', 'service', 'user'], class OperationLogger(object): + """ Instances of this class represents unit operation done on the ynh instance. @@ -423,7 +424,7 @@ class OperationLogger(object): else: if is_api: msg = "" + m18n.n('log_link_to_failed_log', - name=self.name, desc=desc) + "" + name=self.name, desc=desc) + "" else: msg = m18n.n('log_help_to_get_failed_log', name=self.name, desc=desc) diff --git a/src/yunohost/monitor.py b/src/yunohost/monitor.py index 469ac41f1..7af55f287 100644 --- a/src/yunohost/monitor.py +++ b/src/yunohost/monitor.py @@ -288,7 +288,7 @@ def monitor_system(units=None, human_readable=False): else: raise YunohostError('unit_unknown', unit=u) - if len(units) == 1 and type(result[units[0]]) is not str: + if len(units) == 1 and not isinstance(result[units[0]], str): return result[units[0]] return result @@ -404,7 +404,7 @@ def monitor_enable(with_stats=False): """ from yunohost.service import (service_status, service_enable, - service_start) + service_start) glances = service_status('glances') if glances['status'] != 'running': @@ -414,7 +414,7 @@ def monitor_enable(with_stats=False): # Install crontab if with_stats: - # day: every 5 min # week: every 1 h # month: every 4 h # + # day: every 5 min # week: every 1 h # month: every 4 h # rules = ('*/5 * * * * root {cmd} day >> /dev/null\n' '3 * * * * root {cmd} week >> /dev/null\n' '6 */4 * * * root {cmd} month >> /dev/null').format( @@ -431,7 +431,7 @@ def monitor_disable(): """ from yunohost.service import (service_status, service_disable, - service_stop) + service_stop) glances = service_status('glances') if glances['status'] != 'inactive': diff --git a/src/yunohost/service.py b/src/yunohost/service.py index b2c18c74e..a8ef0e796 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -151,6 +151,7 @@ def service_stop(names): raise YunohostError('service_stop_failed', service=name, logs=_get_journalctl_logs(name)) logger.debug(m18n.n('service_already_stopped', service=name)) + @is_unit_operation() def service_enable(operation_logger, names): """ @@ -377,7 +378,7 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, if not names: operation_logger.name_parameter_override = 'all' elif len(names) != 1: - operation_logger.name_parameter_override = str(len(operation_logger.related_to))+'_services' + operation_logger.name_parameter_override = str(len(operation_logger.related_to)) + '_services' operation_logger.start() # Clean pending conf directory @@ -389,7 +390,7 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, shutil.rmtree(os.path.join(PENDING_CONF_DIR, name), ignore_errors=True) else: - filesystem.mkdir(PENDING_CONF_DIR, 0755, True) + filesystem.mkdir(PENDING_CONF_DIR, 0o755, True) # Format common hooks arguments common_args = [1 if force else 0, 1 if dry_run else 0] @@ -400,7 +401,7 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, def _pre_call(name, priority, path, args): # create the pending conf directory for the service service_pending_path = os.path.join(PENDING_CONF_DIR, name) - filesystem.mkdir(service_pending_path, 0755, True, uid='root') + filesystem.mkdir(service_pending_path, 0o755, True, uid='root') # return the arguments to pass to the script return pre_args + [service_pending_path, ] @@ -418,7 +419,7 @@ def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, if not names: raise YunohostError('service_regenconf_failed', - services=', '.join(pre_result['failed'])) + services=', '.join(pre_result['failed'])) # Set the processing method _regen = _process_regen_conf if not dry_run else lambda *a, **k: True @@ -603,7 +604,7 @@ def _run_service_command(action, service): cmd = 'systemctl %s %s' % (action, service) need_lock = services[service].get('need_lock', False) \ - and action in ['start', 'stop', 'restart', 'reload'] + and action in ['start', 'stop', 'restart', 'reload'] try: # Launch the command @@ -637,10 +638,10 @@ def _give_lock(action, service, p): else: systemctl_PID_name = "ControlPID" - cmd_get_son_PID ="systemctl show %s -p %s" % (service, systemctl_PID_name) + cmd_get_son_PID = "systemctl show %s -p %s" % (service, systemctl_PID_name) son_PID = 0 # As long as we did not found the PID and that the command is still running - while son_PID == 0 and p.poll() == None: + while son_PID == 0 and p.poll() is None: # Call systemctl to get the PID # Output of the command is e.g. ControlPID=1234 son_PID = subprocess.check_output(cmd_get_son_PID.split()) \ @@ -657,11 +658,12 @@ def _give_lock(action, service, p): return son_PID + def _remove_lock(PID_to_remove): # FIXME ironically not concurrency safe because it's not atomic... PIDs = filesystem.read_file(MOULINETTE_LOCK).split("\n") - PIDs_to_keep = [ PID for PID in PIDs if int(PID) != PID_to_remove ] + PIDs_to_keep = [PID for PID in PIDs if int(PID) != PID_to_remove] filesystem.write_to_file(MOULINETTE_LOCK, '\n'.join(PIDs_to_keep)) @@ -775,6 +777,7 @@ def _find_previous_log_file(file): return None + def _get_files_diff(orig_file, new_file, as_string=False, skip_header=True): """Compare two files and return the differences @@ -919,22 +922,22 @@ def _process_regen_conf(system_conf, new_conf=None, save=True): backup_dir = os.path.dirname(backup_path) if not os.path.isdir(backup_dir): - filesystem.mkdir(backup_dir, 0755, True) + filesystem.mkdir(backup_dir, 0o755, True) shutil.copy2(system_conf, backup_path) logger.debug(m18n.n('service_conf_file_backed_up', - conf=system_conf, backup=backup_path)) + conf=system_conf, backup=backup_path)) try: if not new_conf: os.remove(system_conf) logger.debug(m18n.n('service_conf_file_removed', - conf=system_conf)) + conf=system_conf)) else: system_dir = os.path.dirname(system_conf) if not os.path.isdir(system_dir): - filesystem.mkdir(system_dir, 0755, True) + filesystem.mkdir(system_dir, 0o755, True) shutil.copyfile(new_conf, system_conf) logger.debug(m18n.n('service_conf_file_updated', diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index c48f567ab..bbfb3ca56 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -88,12 +88,12 @@ def settings_set(key, value): if key_type == "bool": if not isinstance(value, bool): raise YunohostError('global_settings_bad_type_for_setting', setting=key, - received_type=type(value).__name__, expected_type=key_type) + received_type=type(value).__name__, expected_type=key_type) elif key_type == "int": if not isinstance(value, int) or isinstance(value, bool): if isinstance(value, str): try: - value=int(value) + value = int(value) except: raise YunohostError('global_settings_bad_type_for_setting', setting=key, @@ -101,19 +101,19 @@ def settings_set(key, value): expected_type=key_type) else: raise YunohostError('global_settings_bad_type_for_setting', setting=key, - received_type=type(value).__name__, expected_type=key_type) + received_type=type(value).__name__, expected_type=key_type) elif key_type == "string": if not isinstance(value, basestring): raise YunohostError('global_settings_bad_type_for_setting', setting=key, - received_type=type(value).__name__, expected_type=key_type) + received_type=type(value).__name__, expected_type=key_type) elif key_type == "enum": if value not in settings[key]["choices"]: raise YunohostError('global_settings_bad_choice_for_enum', setting=key, - received_type=type(value).__name__, - expected_type=", ".join(settings[key]["choices"])) + received_type=type(value).__name__, + expected_type=", ".join(settings[key]["choices"])) else: raise YunohostError('global_settings_unknown_type', setting=key, - unknown_type=key_type) + unknown_type=key_type) settings[key]["value"] = value diff --git a/src/yunohost/ssh.py b/src/yunohost/ssh.py index a9e5babf7..7c8c85b35 100644 --- a/src/yunohost/ssh.py +++ b/src/yunohost/ssh.py @@ -98,7 +98,7 @@ def user_ssh_add_key(auth, username, key, comment): # create empty file to set good permissions write_to_file(authorized_keys_file, "") chown(authorized_keys_file, uid=user["uid"][0]) - chmod(authorized_keys_file, 0600) + chmod(authorized_keys_file, 0o600) authorized_keys_content = read_file(authorized_keys_file) diff --git a/src/yunohost/tests/conftest.py b/src/yunohost/tests/conftest.py index 6958ae679..a2dc585bd 100644 --- a/src/yunohost/tests/conftest.py +++ b/src/yunohost/tests/conftest.py @@ -7,12 +7,14 @@ sys.path.append("..") def pytest_addoption(parser): parser.addoption("--yunodebug", action="store_true", default=False) -############################################################################### -# Tweak translator to raise exceptions if string keys are not defined # -############################################################################### +# +# Tweak translator to raise exceptions if string keys are not defined # +# old_translate = moulinette.core.Translator.translate + + def new_translate(self, key, *args, **kwargs): if key not in self._translations[self.default_locale].keys(): @@ -21,14 +23,15 @@ def new_translate(self, key, *args, **kwargs): 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 -############################################################################### -# Init the moulinette to have the cli loggers stuff # -############################################################################### +# +# Init the moulinette to have the cli loggers stuff # +# def pytest_cmdline_main(config): diff --git a/src/yunohost/tests/test_appslist.py b/src/yunohost/tests/test_appslist.py index 7a6f27d6c..817807ed9 100644 --- a/src/yunohost/tests/test_appslist.py +++ b/src/yunohost/tests/test_appslist.py @@ -17,7 +17,7 @@ APPSLISTS_JSON = '/etc/yunohost/appslists.json' def setup_function(function): # Clear all appslist - files = glob.glob(REPO_PATH+"/*") + files = glob.glob(REPO_PATH + "/*") for f in files: os.remove(f) @@ -42,9 +42,9 @@ def cron_job_is_there(): return r == 0 -############################################################################### -# Test listing of appslists and registering of appslists # -############################################################################### +# +# Test listing of appslists and registering of appslists # +# def test_appslist_list_empty(): @@ -103,9 +103,9 @@ def test_appslist_list_register_conflict_url(): assert "plopette" not in appslist_dict.keys() -############################################################################### -# Test fetching of appslists # -############################################################################### +# +# Test fetching of appslists # +# def test_appslist_fetch(): @@ -244,9 +244,9 @@ def test_appslist_fetch_timeout(): app_fetchlist() -############################################################################### -# Test remove of appslist # -############################################################################### +# +# Test remove of appslist # +# def test_appslist_remove(): @@ -274,9 +274,9 @@ def test_appslist_remove_unknown(): app_removelist("dummy") -############################################################################### -# Test migration from legacy appslist system # -############################################################################### +# +# Test migration from legacy appslist system # +# def add_legacy_cron(name, url): diff --git a/src/yunohost/tests/test_appurl.py b/src/yunohost/tests/test_appurl.py index 85f290b5d..d9d5fa7ab 100644 --- a/src/yunohost/tests/test_appurl.py +++ b/src/yunohost/tests/test_appurl.py @@ -22,6 +22,7 @@ def setup_function(function): except: pass + def teardown_function(function): try: @@ -50,18 +51,18 @@ def test_urlavailable(): def test_registerurl(): app_install(auth, "./tests/apps/register_url_app_ynh", - args="domain=%s&path=%s" % (maindomain, "/urlregisterapp")) + args="domain=%s&path=%s" % (maindomain, "/urlregisterapp")) assert not domain_url_available(auth, maindomain, "/urlregisterapp") # Try installing at same location with pytest.raises(YunohostError): app_install(auth, "./tests/apps/register_url_app_ynh", - args="domain=%s&path=%s" % (maindomain, "/urlregisterapp")) + args="domain=%s&path=%s" % (maindomain, "/urlregisterapp")) def test_registerurl_baddomain(): with pytest.raises(YunohostError): app_install(auth, "./tests/apps/register_url_app_ynh", - args="domain=%s&path=%s" % ("yolo.swag", "/urlregisterapp")) + args="domain=%s&path=%s" % ("yolo.swag", "/urlregisterapp")) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index a2356318d..af8538dae 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -22,6 +22,7 @@ AUTH_IDENTIFIER = ('ldap', 'ldap-anonymous') AUTH_PARAMETERS = {'uri': 'ldap://localhost:389', 'base_dn': 'dc=yunohost,dc=org'} auth = None + def setup_function(function): global maindomain @@ -87,9 +88,9 @@ def teardown_function(function): shutil.rmtree("/opt/test_backup_output_directory") -############################################################################### -# Helpers # -############################################################################### +# +# Helpers # +# def app_is_installed(app): @@ -111,6 +112,7 @@ def backup_test_dependencies_are_met(): return True + def tmp_backup_directory_is_empty(): if not os.path.exists("/home/yunohost.backup/tmp/"): @@ -118,6 +120,7 @@ def tmp_backup_directory_is_empty(): else: return len(os.listdir('/home/yunohost.backup/tmp/')) == 0 + def clean_tmp_backup_directory(): if tmp_backup_directory_is_empty(): @@ -125,10 +128,10 @@ def clean_tmp_backup_directory(): mount_lines = subprocess.check_output("mount").split("\n") - points_to_umount = [ line.split(" ")[2] - for line in mount_lines - if len(line) >= 3 - and line.split(" ")[2].startswith("/home/yunohost.backup/tmp") ] + points_to_umount = [line.split(" ")[2] + for line in mount_lines + if len(line) >= 3 + and line.split(" ")[2].startswith("/home/yunohost.backup/tmp")] for point in reversed(points_to_umount): os.system("umount %s" % point) @@ -138,6 +141,7 @@ def clean_tmp_backup_directory(): shutil.rmtree("/home/yunohost.backup/tmp/") + def reset_ssowat_conf(): # Make sure we have a ssowat @@ -191,9 +195,10 @@ def add_archive_system_from_2p4(): os.system("cp ./tests/apps/backup_system_from_2p4/backup.tar.gz \ /home/yunohost.backup/archives/backup_system_from_2p4.tar.gz") -############################################################################### -# System backup # -############################################################################### +# +# System backup # +# + def test_backup_only_ldap(): @@ -220,9 +225,10 @@ def test_backup_system_part_that_does_not_exists(mocker): m18n.n.assert_any_call('backup_hook_unknown', hook="yolol") m18n.n.assert_any_call('backup_nothings_done') -############################################################################### -# System backup and restore # -############################################################################### +# +# System backup and restore # +# + def test_backup_and_restore_all_sys(): @@ -250,9 +256,9 @@ def test_backup_and_restore_all_sys(): assert os.path.exists("/etc/ssowat/conf.json") -############################################################################### -# System restore from 2.4 # -############################################################################### +# +# System restore from 2.4 # +# @pytest.mark.with_system_archive_from_2p4 def test_restore_system_from_Ynh2p4(monkeypatch, mocker): @@ -265,19 +271,20 @@ def test_restore_system_from_Ynh2p4(monkeypatch, mocker): # Restore system archive from 2.4 try: backup_restore(auth, name=backup_list()["archives"][1], - system=[], - apps=None, - force=True) + system=[], + apps=None, + force=True) finally: # Restore system as it was backup_restore(auth, name=backup_list()["archives"][0], - system=[], - apps=None, - force=True) + system=[], + apps=None, + force=True) + +# +# App backup # +# -############################################################################### -# App backup # -############################################################################### @pytest.mark.with_backup_recommended_app_installed def test_backup_script_failure_handling(monkeypatch, mocker): @@ -300,6 +307,7 @@ def test_backup_script_failure_handling(monkeypatch, mocker): m18n.n.assert_any_call('backup_app_failed', app='backup_recommended_app') + @pytest.mark.with_backup_recommended_app_installed def test_backup_not_enough_free_space(monkeypatch, mocker): @@ -385,6 +393,7 @@ def test_backup_with_different_output_directory(): assert len(archives_info["system"].keys()) == 1 assert "conf_ssh" in archives_info["system"].keys() + @pytest.mark.clean_opt_dir def test_backup_with_no_compress(): # Create the backup @@ -396,15 +405,15 @@ def test_backup_with_no_compress(): assert os.path.exists("/opt/test_backup_output_directory/info.json") -############################################################################### -# App restore # -############################################################################### +# +# App restore # +# @pytest.mark.with_wordpress_archive_from_2p4 def test_restore_app_wordpress_from_Ynh2p4(): backup_restore(auth, system=None, name=backup_list()["archives"][0], - apps=["wordpress"]) + apps=["wordpress"]) @pytest.mark.with_wordpress_archive_from_2p4 @@ -422,7 +431,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker): with pytest.raises(YunohostError): backup_restore(auth, system=None, name=backup_list()["archives"][0], - apps=["wordpress"]) + apps=["wordpress"]) m18n.n.assert_any_call('restore_app_failed', app='wordpress') m18n.n.assert_any_call('restore_nothings_done') @@ -443,12 +452,12 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker): with pytest.raises(YunohostError): backup_restore(auth, system=None, name=backup_list()["archives"][0], - apps=["wordpress"]) + apps=["wordpress"]) m18n.n.assert_any_call('restore_not_enough_disk_space', - free_space=0, - margin=ANY, - needed_space=ANY) + free_space=0, + margin=ANY, + needed_space=ANY) assert not _is_installed("wordpress") @@ -462,7 +471,7 @@ def test_restore_app_not_in_backup(mocker): with pytest.raises(YunohostError): backup_restore(auth, system=None, name=backup_list()["archives"][0], - apps=["yoloswag"]) + apps=["yoloswag"]) m18n.n.assert_any_call('backup_archive_app_not_found', app="yoloswag") assert not _is_installed("wordpress") @@ -475,14 +484,14 @@ def test_restore_app_already_installed(mocker): assert not _is_installed("wordpress") backup_restore(auth, system=None, name=backup_list()["archives"][0], - apps=["wordpress"]) + apps=["wordpress"]) assert _is_installed("wordpress") mocker.spy(m18n, "n") with pytest.raises(YunohostError): backup_restore(auth, system=None, name=backup_list()["archives"][0], - apps=["wordpress"]) + apps=["wordpress"]) m18n.n.assert_any_call('restore_already_installed_app', app="wordpress") m18n.n.assert_any_call('restore_nothings_done') @@ -531,9 +540,10 @@ def _test_backup_and_restore_app(app): assert app_is_installed(app) -############################################################################### -# Some edge cases # -############################################################################### +# +# Some edge cases # +# + def test_restore_archive_with_no_json(mocker): @@ -555,10 +565,9 @@ def test_backup_binds_are_readonly(monkeypatch): self.manager = backup_manager self._organize_files() - confssh = os.path.join(self.work_dir, "conf/ssh") output = subprocess.check_output("touch %s/test 2>&1 || true" % confssh, - shell=True, env={'LANG' : 'en_US.UTF-8'}) + shell=True, env={'LANG': 'en_US.UTF-8'}) assert "Read-only file system" in output @@ -568,7 +577,7 @@ def test_backup_binds_are_readonly(monkeypatch): self.clean() monkeypatch.setattr("yunohost.backup.BackupMethod.mount_and_backup", - custom_mount_and_backup) + custom_mount_and_backup) # Create the backup backup_create(system=[]) diff --git a/src/yunohost/tests/test_changeurl.py b/src/yunohost/tests/test_changeurl.py index 826ec659b..4856e18c1 100644 --- a/src/yunohost/tests/test_changeurl.py +++ b/src/yunohost/tests/test_changeurl.py @@ -53,6 +53,7 @@ def test_appchangeurl(): check_changeurl_app("/newchangeurl") + def test_appchangeurl_sameurl(): install_changeurl_app("/changeurl") check_changeurl_app("/changeurl") diff --git a/src/yunohost/tests/test_settings.py b/src/yunohost/tests/test_settings.py index afb9faee9..0da12597f 100644 --- a/src/yunohost/tests/test_settings.py +++ b/src/yunohost/tests/test_settings.py @@ -18,7 +18,8 @@ def teardown_function(function): def test_settings_get_bool(): - assert settings_get("example.bool") == True + assert settings_get("example.bool") + def test_settings_get_full_bool(): assert settings_get("example.bool", True) == {"type": "bool", "value": True, "default": True, "description": "Example boolean option"} @@ -27,6 +28,7 @@ def test_settings_get_full_bool(): def test_settings_get_int(): assert settings_get("example.int") == 42 + def test_settings_get_full_int(): assert settings_get("example.int", True) == {"type": "int", "value": 42, "default": 42, "description": "Example int option"} @@ -34,6 +36,7 @@ def test_settings_get_full_int(): def test_settings_get_string(): assert settings_get("example.string") == "yolo swag" + def test_settings_get_full_string(): assert settings_get("example.string", True) == {"type": "string", "value": "yolo swag", "default": "yolo swag", "description": "Example string option"} @@ -41,6 +44,7 @@ def test_settings_get_full_string(): def test_settings_get_enum(): assert settings_get("example.enum") == "a" + 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"]} @@ -152,7 +156,6 @@ def test_reset_all_backup(): assert settings_after_modification == json.load(open(old_settings_backup_path, "r")) - def test_unknown_keys(): unknown_settings_path = SETTINGS_PATH_OTHER_LOCATION % "unknown" unknown_setting = { diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 58bdd23d3..43190e5b8 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -136,7 +136,7 @@ def tools_adminpw(auth, new_password, check_strength=True): new_hash = _hash_user_password(new_password) try: - auth.update("cn=admin", { "userPassword": new_hash, }) + auth.update("cn=admin", {"userPassword": new_hash, }) except: logger.exception('unable to change admin password') raise YunohostError('admin_password_change_failed') @@ -265,7 +265,7 @@ def _is_inside_container(): stderr=subprocess.STDOUT) out, _ = p.communicate() - container = ['lxc','lxd','docker'] + container = ['lxc', 'lxd', 'docker'] return out.split()[0] in container @@ -323,7 +323,6 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, else: dyndns = False - operation_logger.start() logger.info(m18n.n('yunohost_installing')) @@ -539,7 +538,7 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal # If API call if is_api: critical_packages = ("moulinette", "yunohost", - "yunohost-admin", "ssowat", "python") + "yunohost-admin", "ssowat", "python") critical_upgrades = set() for pkg in cache.get_changes(): @@ -575,7 +574,6 @@ def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=Fal else: logger.info(m18n.n('packages_no_upgrade')) - if not ignore_apps: try: app_upgrade(auth) @@ -719,7 +717,7 @@ def _check_if_vulnerable_to_meltdown(): try: call = subprocess.Popen("bash %s --batch json --variant 3" % SCRIPT_PATH, shell=True, - stdout=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output, _ = call.communicate() @@ -815,12 +813,12 @@ def tools_migrations_list(pending=False, done=False): migrations = [m for m in migrations if m.number > last_migration] # Reduce to dictionnaries - migrations = [{ "id": migration.id, - "number": migration.number, - "name": migration.name, - "mode": migration.mode, - "description": migration.description, - "disclaimer": migration.disclaimer } for migration in migrations ] + migrations = [{"id": migration.id, + "number": migration.number, + "name": migration.name, + "mode": migration.mode, + "description": migration.description, + "disclaimer": migration.disclaimer} for migration in migrations] return {"migrations": migrations} @@ -914,7 +912,7 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai accept_disclaimer = False # Start register change on system - operation_logger= OperationLogger('tools_migrations_migrate_' + mode) + operation_logger = OperationLogger('tools_migrations_migrate_' + mode) operation_logger.start() if not skip: @@ -934,9 +932,9 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai # migration failed, let's stop here but still update state because # we managed to run the previous ones msg = m18n.n('migrations_migration_has_failed', - exception=e, - number=migration.number, - name=migration.name) + exception=e, + number=migration.number, + name=migration.name) logger.error(msg, exc_info=1) operation_logger.error(msg) break @@ -967,6 +965,7 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai write_to_json(MIGRATIONS_STATE_PATH, state) + def tools_migrations_state(): """ Show current migration state @@ -1009,7 +1008,7 @@ def _get_migrations_list(): migrations = [] try: - import data_migrations + from . import data_migrations except ImportError: # not data migrations present, return empty list return migrations @@ -1032,7 +1031,7 @@ def _get_migration_by_name(migration_name): """ try: - import data_migrations + from . import data_migrations except ImportError: raise AssertionError("Unable to find migration with name %s" % migration_name) @@ -1051,7 +1050,7 @@ def _load_migration(migration_file): number, name = migration_id.split("_", 1) logger.debug(m18n.n('migrations_loading_migration', - number=number, name=name)) + number=number, name=name)) try: # this is python builtin method to import a module using a name, we @@ -1064,7 +1063,8 @@ def _load_migration(migration_file): traceback.print_exc() raise YunohostError('migrations_error_failed_to_load_migration', - number=number, name=name) + number=number, name=name) + def _skip_all_migrations(): """ @@ -1115,4 +1115,3 @@ class Migration(object): @property def description(self): return m18n.n("migration_description_%s" % self.id) - diff --git a/src/yunohost/user.py b/src/yunohost/user.py index ea8dc30a9..d0c432c56 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -40,6 +40,7 @@ from yunohost.log import is_unit_operation logger = getActionLogger('yunohost.user') + def user_list(auth, fields=None): """ List users @@ -98,7 +99,7 @@ def user_list(auth, fields=None): @is_unit_operation([('username', 'user')]) def user_create(operation_logger, auth, username, firstname, lastname, mail, password, - mailbox_quota="0"): + mailbox_quota="0"): """ Create user @@ -262,8 +263,8 @@ def user_delete(operation_logger, auth, username, purge=False): @is_unit_operation([('username', 'user')], exclude=['auth', 'change_password']) def user_update(operation_logger, auth, username, firstname=None, lastname=None, mail=None, - change_password=None, add_mailforward=None, remove_mailforward=None, - add_mailalias=None, remove_mailalias=None, mailbox_quota=None): + change_password=None, add_mailforward=None, remove_mailforward=None, + add_mailalias=None, remove_mailalias=None, mailbox_quota=None): """ Update user informations @@ -466,18 +467,23 @@ def user_info(auth, username): # import yunohost.ssh + def user_ssh_allow(auth, username): return yunohost.ssh.user_ssh_allow(auth, username) + def user_ssh_disallow(auth, username): return yunohost.ssh.user_ssh_disallow(auth, username) + def user_ssh_list_keys(auth, username): return yunohost.ssh.user_ssh_list_keys(auth, username) + def user_ssh_add_key(auth, username, key, comment): return yunohost.ssh.user_ssh_add_key(auth, username, key, comment) + def user_ssh_remove_key(auth, username, key): return yunohost.ssh.user_ssh_remove_key(auth, username, key) @@ -485,6 +491,7 @@ def user_ssh_remove_key(auth, username, key): # End SSH subcategory # + def _convertSize(num, suffix=''): for unit in ['K', 'M', 'G', 'T', 'P', 'E', 'Z']: if abs(num) < 1024.0: @@ -520,6 +527,3 @@ def _hash_user_password(password): salt = '$6$' + salt + '$' return '{CRYPT}' + crypt.crypt(str(password), salt) - - - diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py index 7c00ee5a4..1d5b05a6f 100644 --- a/src/yunohost/utils/error.py +++ b/src/yunohost/utils/error.py @@ -22,12 +22,14 @@ from moulinette.core import MoulinetteError from moulinette import m18n + class YunohostError(MoulinetteError): + """Yunohost base exception""" + def __init__(self, key, __raw_msg__=False, *args, **kwargs): if __raw_msg__: msg = key else: msg = m18n.n(key, *args, **kwargs) super(YunohostError, self).__init__(msg, __raw_msg__=True) - diff --git a/src/yunohost/utils/filesystem.py b/src/yunohost/utils/filesystem.py index 3f026f980..04d7d3906 100644 --- a/src/yunohost/utils/filesystem.py +++ b/src/yunohost/utils/filesystem.py @@ -20,10 +20,12 @@ """ import os + def free_space_in_directory(dirpath): stat = os.statvfs(dirpath) return stat.f_frsize * stat.f_bavail + def space_used_by_directory(dirpath): stat = os.statvfs(dirpath) return stat.f_frsize * stat.f_blocks diff --git a/src/yunohost/utils/network.py b/src/yunohost/utils/network.py index a9602ff56..1f82a87b0 100644 --- a/src/yunohost/utils/network.py +++ b/src/yunohost/utils/network.py @@ -71,7 +71,7 @@ def get_gateway(): return addr.popitem()[1] if len(addr) == 1 else None -############################################################################### +# def _extract_inet(string, skip_netmask=False, skip_loopback=True): diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index 3917ef563..5ef97618b 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -33,6 +33,7 @@ logger = logging.getLogger('yunohost.utils.packages') # Exceptions ----------------------------------------------------------------- class PackageException(Exception): + """Base exception related to a package Represent an exception related to the package named `pkgname`. If no @@ -50,16 +51,19 @@ class PackageException(Exception): class UnknownPackage(PackageException): + """The package is not found in the cache.""" message_key = 'package_unknown' class UninstalledPackage(PackageException): + """The package is not installed.""" message_key = 'package_not_installed' class InvalidSpecifier(ValueError): + """An invalid specifier was found.""" @@ -68,6 +72,7 @@ class InvalidSpecifier(ValueError): # See: https://github.com/pypa/packaging class Specifier(object): + """Unique package version specifier Restrict a package version according to the `spec`. It must be a string @@ -257,6 +262,7 @@ class Specifier(object): class SpecifierSet(object): + """A set of package version specifiers Combine several Specifier separated by a comma. It allows to restrict diff --git a/src/yunohost/utils/password.py b/src/yunohost/utils/password.py index 9cee56d53..b4f7025f7 100644 --- a/src/yunohost/utils/password.py +++ b/src/yunohost/utils/password.py @@ -38,9 +38,11 @@ STRENGTH_LEVELS = [ (12, 1, 1, 1, 1), ] + def assert_password_is_strong_enough(profile, password): PasswordValidator(profile).validate(password) + class PasswordValidator(object): def __init__(self, profile): @@ -157,7 +159,7 @@ class PasswordValidator(object): # and the strength of the password (e.g. [11, 2, 7, 2, 0]) # and compare the values 1-by-1. # If one False is found, the password does not satisfy the level - if False in [s>=c for s, c in zip(strength, level_criterias)]: + if False in [s >= c for s, c in zip(strength, level_criterias)]: break # Otherwise, the strength of the password is at least of the current level. strength_level = level + 1 @@ -186,7 +188,7 @@ if __name__ == '__main__': if len(sys.argv) < 2: import getpass pwd = getpass.getpass("") - #print("usage: password.py PASSWORD") + # print("usage: password.py PASSWORD") else: pwd = sys.argv[1] status, msg = PasswordValidator('user').validation_summary(pwd) diff --git a/src/yunohost/utils/yunopaste.py b/src/yunohost/utils/yunopaste.py index 436f94911..3d80ddd4e 100644 --- a/src/yunohost/utils/yunopaste.py +++ b/src/yunohost/utils/yunopaste.py @@ -5,6 +5,7 @@ import json from yunohost.utils.error import YunohostError + def yunopaste(data): paste_server = "https://paste.yunohost.org" From 46972788b2f4c3b3ac79e2d2fb9b8dd6a3834148 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 14 Dec 2018 15:05:56 +0100 Subject: [PATCH 093/104] Add comment about the motivation behind YunohostError --- src/yunohost/utils/error.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py index 7c00ee5a4..a3842547e 100644 --- a/src/yunohost/utils/error.py +++ b/src/yunohost/utils/error.py @@ -23,7 +23,12 @@ from moulinette.core import MoulinetteError from moulinette import m18n class YunohostError(MoulinetteError): - """Yunohost base exception""" + """ + Yunohost base exception + + The (only?) main difference with MoulinetteError being that keys + are translated via m18n.n (namespace) instead of m18n.g (global?) + """ def __init__(self, key, __raw_msg__=False, *args, **kwargs): if __raw_msg__: msg = key From 7e3a90232f79b576c654bf26ee001631c5ef0dee Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 14 Dec 2018 14:17:20 +0000 Subject: [PATCH 094/104] __raw_msg__ -> raw_msg --- src/yunohost/app.py | 12 ++++++------ src/yunohost/utils/error.py | 6 +++--- src/yunohost/utils/yunopaste.py | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 36a0414ec..935b1ea6b 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -183,7 +183,7 @@ def app_fetchlist(url=None, name=None): with open(list_file, "w") as f: f.write(appslist) except Exception as e: - raise YunohostError("Error while writing appslist %s: %s" % (name, str(e)), __raw_msg__=True) + raise YunohostError("Error while writing appslist %s: %s" % (name, str(e)), raw_msg=True) now = int(time.time()) appslists[name]["lastUpdate"] = now @@ -840,9 +840,9 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on if install_retcode == -1: msg = m18n.n('operation_interrupted') + " " + error_msg - raise YunohostError(msg, __raw_msg__=True) + raise YunohostError(msg, raw_msg=True) msg = error_msg - raise YunohostError(msg, __raw_msg__=True) + raise YunohostError(msg, raw_msg=True) # Clean hooks and add new ones hook_remove(app_instance_name) @@ -1472,7 +1472,7 @@ def app_action_run(app, action, args=None): actions = {x["id"]: x for x in actions} if action not in actions: - raise YunohostError("action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys())), __raw_msg__=True) + raise YunohostError("action '%s' not available for app '%s', available actions are: %s" % (action, app, ", ".join(actions.keys())), raw_msg=True) action_declaration = actions[action] @@ -1510,7 +1510,7 @@ def app_action_run(app, action, args=None): ) if retcode not in action_declaration.get("accepted_return_codes", [0]): - raise YunohostError("Error while executing action '%s' of app '%s': return code %s" % (action, app, retcode), __raw_msg__=True) + raise YunohostError("Error while executing action '%s' of app '%s': return code %s" % (action, app, retcode), raw_msg=True) os.remove(path) @@ -2431,7 +2431,7 @@ def _write_appslist_list(appslist_lists): json.dump(appslist_lists, f) except Exception as e: raise YunohostError("Error while writing list of appslist %s: %s" % - (APPSLISTS_JSON, str(e)), __raw_msg__=True) + (APPSLISTS_JSON, str(e)), raw_msg=True) def _register_new_appslist(url, name): diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py index a3842547e..74a63db7c 100644 --- a/src/yunohost/utils/error.py +++ b/src/yunohost/utils/error.py @@ -29,10 +29,10 @@ class YunohostError(MoulinetteError): The (only?) main difference with MoulinetteError being that keys are translated via m18n.n (namespace) instead of m18n.g (global?) """ - def __init__(self, key, __raw_msg__=False, *args, **kwargs): - if __raw_msg__: + def __init__(self, key, raw_msg=False, *args, **kwargs): + if raw_msg: msg = key else: msg = m18n.n(key, *args, **kwargs) - super(YunohostError, self).__init__(msg, __raw_msg__=True) + super(YunohostError, self).__init__(msg, raw_msg=True) diff --git a/src/yunohost/utils/yunopaste.py b/src/yunohost/utils/yunopaste.py index 436f94911..9226a7b12 100644 --- a/src/yunohost/utils/yunopaste.py +++ b/src/yunohost/utils/yunopaste.py @@ -12,14 +12,14 @@ def yunopaste(data): try: r = requests.post("%s/documents" % paste_server, data=data, timeout=30) except Exception as e: - raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e), __raw_msg__=True) + raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s" % str(e), raw_msg=True) if r.status_code != 200: - raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text), __raw_msg__=True) + raise YunohostError("Something wrong happened while trying to paste data on paste.yunohost.org : %s, %s" % (r.status_code, r.text), raw_msg=True) try: url = json.loads(r.text)["key"] except: - raise YunohostError("Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text, __raw_msg__=True) + raise YunohostError("Uhoh, couldn't parse the answer from paste.yunohost.org : %s" % r.text, raw_msg=True) return "%s/raw/%s" % (paste_server, url) From 44f65651299ca3664c7eeb1bc939e4e5818e07eb Mon Sep 17 00:00:00 2001 From: Bram Date: Fri, 14 Dec 2018 21:00:45 +0100 Subject: [PATCH 095/104] Update locales/en.json Co-Authored-By: alexAubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 8e4e18497..01b74a1dc 100644 --- a/locales/en.json +++ b/locales/en.json @@ -134,7 +134,7 @@ "certmanager_self_ca_conf_file_not_found": "Configuration file not found for self-signing authority (file: {file:s})", "certmanager_unable_to_parse_self_CA_name": "Unable to parse name of self-signing authority (file: {file:s})", "confirm_app_install_warning": "Warning : this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway ? [{answers:s}] ", - "confirm_app_install_danger": "WARNING ! This application is still experimental (if not explicitly not working) and it is likely to break your system ! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk ? [{answers:s}] ", + "confirm_app_install_danger": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", "confirm_app_install_thirdparty": "WARNING ! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk ? [{answers:s}] ", "custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}", "custom_appslist_name_required": "You must provide a name for your custom app list", From ce1773d2ff6615b478f39b23e9a62722a6c8d4dc Mon Sep 17 00:00:00 2001 From: Bram Date: Fri, 14 Dec 2018 21:01:18 +0100 Subject: [PATCH 096/104] Update locales/en.json Co-Authored-By: alexAubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 01b74a1dc..09101b93c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -133,7 +133,7 @@ "certmanager_no_cert_file": "Unable to read certificate file for domain {domain:s} (file: {file:s})", "certmanager_self_ca_conf_file_not_found": "Configuration file not found for self-signing authority (file: {file:s})", "certmanager_unable_to_parse_self_CA_name": "Unable to parse name of self-signing authority (file: {file:s})", - "confirm_app_install_warning": "Warning : this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway ? [{answers:s}] ", + "confirm_app_install_warning": "Warning: this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ", "confirm_app_install_danger": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", "confirm_app_install_thirdparty": "WARNING ! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk ? [{answers:s}] ", "custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}", From 65970e30fbd41b665c9d522c5f03d2b841885585 Mon Sep 17 00:00:00 2001 From: Bram Date: Fri, 14 Dec 2018 21:01:30 +0100 Subject: [PATCH 097/104] Update locales/en.json Co-Authored-By: alexAubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 09101b93c..319feb064 100644 --- a/locales/en.json +++ b/locales/en.json @@ -135,7 +135,7 @@ "certmanager_unable_to_parse_self_CA_name": "Unable to parse name of self-signing authority (file: {file:s})", "confirm_app_install_warning": "Warning: this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ", "confirm_app_install_danger": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", - "confirm_app_install_thirdparty": "WARNING ! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk ? [{answers:s}] ", + "confirm_app_install_thirdparty": "WARNING! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", "custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}", "custom_appslist_name_required": "You must provide a name for your custom app list", "diagnosis_debian_version_error": "Can't retrieve the Debian version: {error}", From 2c41a4539e4fd68a7464ce273280dd65acbb780a Mon Sep 17 00:00:00 2001 From: Bram Date: Fri, 14 Dec 2018 21:01:46 +0100 Subject: [PATCH 098/104] Update src/yunohost/app.py Co-Authored-By: alexAubin --- 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 e9a4b67f1..cf2e24a4f 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -725,7 +725,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on if confirm is None or force or msettings.get('interface') == 'api': return - answer = msignals.prompt(m18n.n('confirm_app_install_'+confirm, + answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm, answers='Y/N')) if answer.upper() != "Y": raise MoulinetteError(errno.EINVAL, m18n.n("aborting")) From a6f4f13031c6b1b9c68c7b57b08f6866f554aa36 Mon Sep 17 00:00:00 2001 From: Tristan Hill Date: Sat, 15 Dec 2018 07:52:07 +0000 Subject: [PATCH 099/104] Missing import --- src/yunohost/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index bb39dd58e..d77ee4b1e 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -285,7 +285,7 @@ def user_update(operation_logger, auth, username, firstname=None, lastname=None, remove_mailalias -- Mail aliases to remove """ - from yunohost.domain import domain_list + from yunohost.domain import domain_list, _get_maindomain from yunohost.app import app_ssowatconf from yunohost.utils.password import assert_password_is_strong_enough From afaac841ec6f4074ba6b14bb663dbf79769c4651 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 16 Dec 2018 03:13:22 +0100 Subject: [PATCH 100/104] [fix] missing old error --- 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 385ccb6c9..11f92afd1 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -725,7 +725,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm, answers='Y/N')) if answer.upper() != "Y": - raise MoulinetteError(errno.EINVAL, m18n.n("aborting")) + raise YunohostError("aborting") raw_app_list = app_list(raw=True) From 341d2bbf6571b27d7c87432c06c8a2aca7f55593 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 16 Dec 2018 03:13:30 +0100 Subject: [PATCH 101/104] [mod] remove unused import --- src/yunohost/domain.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 29c7ab554..a3d391835 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -25,7 +25,6 @@ """ import os import re -import json import yaml from moulinette import m18n, msettings From 8617a1fad67757130221422713a0979d1b82f276 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 16 Dec 2018 03:14:49 +0100 Subject: [PATCH 102/104] [mod] simplify code --- src/yunohost/domain.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index a3d391835..26c81824c 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -114,16 +114,13 @@ def domain_add(operation_logger, auth, domain, dyndns=False): service_regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix']) app_ssowatconf(auth) - except Exception as e: - from sys import exc_info - t, v, tb = exc_info() - + except Exception: # Force domain removal silently try: domain_remove(auth, domain, True) except: pass - raise t, v, tb + raise hook_callback('post_domain_add', args=[domain]) From b68c358023636c6e6bb86b0c9ba1392337f397ec Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 16 Dec 2018 03:15:54 +0100 Subject: [PATCH 103/104] [mod] remove unused import --- src/yunohost/dyndns.py | 3 +-- src/yunohost/log.py | 1 - src/yunohost/ssh.py | 1 - src/yunohost/tools.py | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 59a26e74b..2f8d63135 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -27,14 +27,13 @@ import os import re import json import glob -import time import base64 import subprocess from moulinette import m18n from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_file, write_to_file, rm +from moulinette.utils.filesystem import write_to_file from moulinette.utils.network import download_json from moulinette.utils.process import check_output diff --git a/src/yunohost/log.py b/src/yunohost/log.py index d1e0c0e05..857cc3658 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -30,7 +30,6 @@ import collections from datetime import datetime from logging import FileHandler, getLogger, Formatter -from sys import exc_info from moulinette import m18n, msettings from yunohost.utils.error import YunohostError diff --git a/src/yunohost/ssh.py b/src/yunohost/ssh.py index 7c8c85b35..b4ac31dbb 100644 --- a/src/yunohost/ssh.py +++ b/src/yunohost/ssh.py @@ -5,7 +5,6 @@ import os import pwd import subprocess -from moulinette import m18n from yunohost.utils.error import YunohostError from moulinette.utils.filesystem import read_file, write_to_file, chown, chmod, mkdir diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 43190e5b8..e23e88198 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -27,7 +27,6 @@ import re import os import yaml import json -import logging import subprocess import pwd import socket From 25d81e25ad823ce6a71d2d5069c0222a039f5cfe Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 16 Dec 2018 03:18:25 +0100 Subject: [PATCH 104/104] [mod] unused variables --- src/yunohost/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index e23e88198..89bd303bf 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -150,7 +150,7 @@ def tools_adminpw(auth, new_password, check_strength=True): with open('/etc/shadow', 'w') as after_file: after_file.write(before.replace("root:" + hash_root, "root:" + new_hash.replace('{CRYPT}', ''))) - except IOError as e: + except IOError: logger.warning(m18n.n('root_password_desynchronized')) return @@ -206,7 +206,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None): # Regen configurations try: - with open('/etc/yunohost/installed', 'r') as f: + with open('/etc/yunohost/installed', 'r'): service_regen_conf() except IOError: pass