diff --git a/locales/en.json b/locales/en.json index 713f1ee41..605d7633e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -309,6 +309,7 @@ "hook_name_unknown": "Unknown hook name '{name:s}'", "installation_complete": "Installation completed", "installation_failed": "Something went wrong with the installation", + "invalid_regex": "Invalid regex :'{regex:s}'", "ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it", "iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it", "log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'", @@ -498,6 +499,7 @@ "regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for category '{category}'…", "regenconf_failed": "Could not regenerate the configuration for category(s): {categories}", "regenconf_pending_applying": "Applying pending configuration for category '{category}'…", + "regex_with_only_domain": "You can't use a regex for domain, only for path", "restore_already_installed_app": "An app with the ID '{app:s}' is already installed", "restore_app_failed": "Could not restore the app '{app:s}'", "restore_cleaning_failed": "Could not clean up the temporary restoration directory", diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 456dfa4bf..d1a621615 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -396,6 +396,71 @@ def _normalize_domain_path(domain, path): return domain, path +def _check_and_normalize_permission_path(url): + """ + Check and normalize the urls passed for all permissions + Also check that the Regex is valid + + As documented in the 'ynh_permission_create' helper: + + If provided, 'url' is assumed to be relative to the app domain/path if they + start with '/'. For example: + / -> domain.tld/app + /admin -> domain.tld/app/admin + domain.tld/app/api -> domain.tld/app/api + domain.tld -> domain.tld + + 'url' can be later treated as a regex if it starts with "re:". + For example: + re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ + re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ + """ + import re, sre_constants + + # Uri without domain + if url.startwith('re:/'): + regex = url[4:] + # check regex + try: + re.compile(regex) + except sre_constants.error: + raise YunohostError('invalid_regex', regex=regex) + return url + + if url.startswith('/'): + return "/" + url.strip("/") + + # Uri with domain + domains = domain_list()['domains'] + + if url.startwith('re:'): + if '/' not in url: + raise YunohostError('regex_with_only_domain') + domain = url[3:].split('/')[0] + path = url[3:].split('/', 1)[1] + + if domain not in domains: + raise YunohostError('domain_unknown') + + try: + re.compile(path) + except sre_constants.error: + raise YunohostError('invalid_regex', regex=path) + + return 're:' + domain + path + + else: + domain = url.split('/')[0] + if domain not in domains: + raise YunohostError('domain_unknown') + + if '/' in url: + path = '/' + url.split('/', 1)[1].strip('/') + return domain + path + else: + return domain + + def _build_dns_conf(domain, ttl=3600): """ Internal function that will returns a data structure containing the needed diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index b57534079..dace552f9 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -336,6 +336,7 @@ def permission_url(operation_logger, permission, clear_urls -- (optional) Clean all urls (url and additional_urls) """ from yunohost.utils.ldap import _get_ldap_interface + from yunohost.domain import _check_and_normalize_permission_path ldap = _get_ldap_interface() # By default, manipulate main permission @@ -352,20 +353,22 @@ def permission_url(operation_logger, permission, if url is None: url = existing_permission["url"] + else: + url = _check_and_normalize_permission_path(url) current_additional_urls = existing_permission["additional_urls"] new_additional_urls = copy.copy(current_additional_urls) if add_url: - for url in add_url: - if url in current_additional_urls: + for ur in add_url: + if ur in current_additional_urls: logger.warning(m18n.n('additional_urls_already_added', permission=permission, url=url)) else: - new_additional_urls += [url] + new_additional_urls += [_check_and_normalize_permission_path(url)] if remove_url: - for url in remove_url: - if url not in current_additional_urls: + for ur in remove_url: + if ur not in current_additional_urls: logger.warning(m18n.n('additional_urls_already_removed', permission=permission, url=url)) new_additional_urls = [u for u in new_additional_urls if u not in remove_url]