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