Merge pull request #1406 from Tagadda/enh-domain-default-app

Manage default application with DomainConfigPanel
This commit is contained in:
Alexandre Aubin 2022-01-24 18:49:06 +01:00 committed by GitHub
commit 981c7b5649
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 127 additions and 31 deletions

View file

@ -310,6 +310,7 @@
"domain_config_auth_key": "Authentication key",
"domain_config_auth_secret": "Authentication secret",
"domain_config_auth_token": "Authentication token",
"domain_config_default_app": "Default app",
"domain_config_features_disclaimer": "So far, enabling/disabling mail or XMPP features only impact the recommended and automatic DNS configuration, not system configurations!",
"domain_config_mail_in": "Incoming emails",
"domain_config_mail_out": "Outgoing emails",

View file

@ -896,6 +896,10 @@ app:
-d:
full: --domain
help: Specific domain to put app on (the app domain by default)
-u:
full: --undo
help: Undo redirection
action: store_true
### app_ssowatconf()
ssowatconf:

View file

@ -11,6 +11,11 @@ i18n = "domain_config"
#
[feature]
[feature.app]
[feature.app.default_app]
type = "app"
filters = ["is_webapp"]
default = "_none"
[feature.mail]
#services = ['postfix', 'dovecot']

View file

@ -117,6 +117,7 @@ def app_info(app, full=False):
Get info for a specific app
"""
from yunohost.permission import user_permission_list
from yunohost.domain import domain_config_get
_assert_is_installed(app)
@ -153,6 +154,9 @@ def app_info(app, full=False):
ret["is_webapp"] = "domain" in settings and "path" in settings
if ret["is_webapp"]:
ret["is_default"] = domain_config_get(settings["domain"], "feature.app.default_app") == app
ret["supports_change_url"] = os.path.exists(
os.path.join(setting_path, "scripts", "change_url")
)
@ -989,6 +993,7 @@ def app_remove(operation_logger, app, purge=False):
permission_delete,
permission_sync_to_user,
)
from yunohost.domain import domain_list, domain_config_get, domain_config_set
if not _is_installed(app):
raise YunohostValidationError(
@ -1048,12 +1053,16 @@ def app_remove(operation_logger, app, purge=False):
hook_remove(app)
for domain in domain_list()["domains"]:
if (domain_config_get(domain, "feature.app.default_app") == app):
domain_config_set(domain, "feature.app.default_app", "_none")
permission_sync_to_user()
_assert_system_is_sane_for_app(manifest, "post")
@is_unit_operation()
def app_makedefault(operation_logger, app, domain=None):
def app_makedefault(operation_logger, app, domain=None, undo=False):
"""
Redirect domain root to an app
@ -1062,11 +1071,10 @@ def app_makedefault(operation_logger, app, domain=None):
domain
"""
from yunohost.domain import _assert_domain_exists
from yunohost.domain import _assert_domain_exists, domain_config_set
app_settings = _get_app_settings(app)
app_domain = app_settings["domain"]
app_path = app_settings["path"]
if domain is None:
domain = app_domain
@ -1075,36 +1083,12 @@ def app_makedefault(operation_logger, app, domain=None):
operation_logger.related_to.append(("domain", domain))
if "/" in app_map(raw=True)[domain]:
raise YunohostValidationError(
"app_make_default_location_already_used",
app=app,
domain=app_domain,
other_app=app_map(raw=True)[domain]["/"]["id"],
)
operation_logger.start()
# TODO / FIXME : current trick is to add this to conf.json.persisten
# This is really not robust and should be improved
# e.g. have a flag in /etc/yunohost/apps/$app/ to say that this is the
# default app or idk...
if not os.path.exists("/etc/ssowat/conf.json.persistent"):
ssowat_conf = {}
if undo:
domain_config_set(domain, 'feature.app.default_app', "_none")
else:
ssowat_conf = read_json("/etc/ssowat/conf.json.persistent")
if "redirected_urls" not in ssowat_conf:
ssowat_conf["redirected_urls"] = {}
ssowat_conf["redirected_urls"][domain + "/"] = app_domain + app_path
write_to_json(
"/etc/ssowat/conf.json.persistent", ssowat_conf, sort_keys=True, indent=4
)
chmod("/etc/ssowat/conf.json.persistent", 0o644)
logger.success(m18n.n("ssowat_conf_updated"))
domain_config_set(domain, 'feature.app.default_app', app)
def app_setting(app, key, value=None, delete=False):
@ -1303,7 +1287,7 @@ def app_ssowatconf():
"""
from yunohost.domain import domain_list, _get_maindomain
from yunohost.domain import domain_list, _get_maindomain, domain_config_get
from yunohost.permission import user_permission_list
main_domain = _get_maindomain()
@ -1341,6 +1325,21 @@ def app_ssowatconf():
redirected_urls.update(app_settings.get("redirected_urls", {}))
redirected_regex.update(app_settings.get("redirected_regex", {}))
from .utils.legacy import translate_legacy_default_app_in_ssowant_conf_json_persistent
translate_legacy_default_app_in_ssowant_conf_json_persistent()
for domain in domains:
default_app = domain_config_get(domain, "feature.app.default_app")
if default_app != "_none" and _is_installed(default_app):
app_settings = _get_app_settings(default_app)
app_domain = app_settings["domain"]
app_path = app_settings["path"]
# Prevent infinite redirect loop...
if domain + "/" != app_domain + app_path:
redirected_urls[domain + "/"] = app_domain + app_path
# New permission system
for perm_name, perm_info in all_permissions.items():

View file

@ -454,6 +454,21 @@ class DomainConfigPanel(ConfigPanel):
save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml"
save_mode = "diff"
def _apply(self):
if ("default_app" in self.future_values and self.future_values["default_app"] != self.values["default_app"]):
from yunohost.app import app_ssowatconf, app_map
if "/" in app_map(raw=True)[self.entity]:
raise YunohostValidationError(
"app_make_default_location_already_used",
app=self.future_values["default_app"],
domain=self.entity,
other_app=app_map(raw=True)[self.entity]["/"]["id"],
)
super()._apply()
app_ssowatconf()
def _get_toml(self):
from yunohost.dns import _get_registrar_config_section

View file

@ -440,6 +440,7 @@ class ConfigPanel:
"step",
"accept",
"redact",
"filters",
],
"defaults": {},
},
@ -705,6 +706,7 @@ class Question:
self.ask = question.get("ask", {"en": self.name})
self.help = question.get("help")
self.redact = question.get("redact", False)
self.filters = question.get("filters", [])
# .current_value is the currently stored value
self.current_value = question.get("current_value")
# .value is the "proposed" value which we got from the user
@ -1126,6 +1128,28 @@ class DomainQuestion(Question):
return value
class AppQuestion(Question):
argument_type = "app"
def __init__(
self, question, context: Mapping[str, Any] = {}, hooks: Dict[str, Callable] = {}
):
from yunohost.app import app_list
super().__init__(question, context, hooks)
apps = app_list(full=True)["apps"]
for _filter in self.filters:
apps = [app for app in apps if _filter in app and app[_filter]]
def _app_display(app):
domain_path = f" ({app['domain_path']})" if 'domain_path' in app else ""
return app["label"] + domain_path
self.choices = {"_none": "---"}
self.choices.update({app['id']: _app_display(app) for app in apps})
class UserQuestion(Question):
argument_type = "user"
@ -1319,6 +1343,7 @@ ARGUMENTS_TYPE_PARSERS = {
"alert": DisplayTextQuestion,
"markdown": DisplayTextQuestion,
"file": FileQuestion,
"app": AppQuestion,
}

View file

@ -7,6 +7,7 @@ from moulinette.utils.filesystem import (
read_file,
write_to_file,
write_to_yaml,
write_to_json,
read_yaml,
)
@ -68,6 +69,52 @@ def legacy_permission_label(app, permission_type):
)
def translate_legacy_default_app_in_ssowant_conf_json_persistent():
from yunohost.app import app_list
from yunohost.domain import domain_config_set
persistent_file_name = "/etc/ssowat/conf.json.persistent"
if not os.path.exists(persistent_file_name):
return
# Ugly hack because for some reason so many people have tabs in their conf.json.persistent ...
os.system(r"sed -i 's/\t/ /g' /etc/ssowat/conf.json.persistent")
# Ugly hack to try not to misarably fail migration
persistent = read_yaml(persistent_file_name)
if "redirected_urls" not in persistent:
return
redirected_urls = persistent["redirected_urls"]
if not any(from_url.count('/') == 1 and from_url.endswith('/') for from_url in redirected_urls):
return
apps = app_list()['apps']
if not any(app.get('domain_path') in redirected_urls.values() for app in apps):
return
for from_url, dest_url in redirected_urls.copy().items():
# Not a root domain, skip
if from_url.count('/') != 1 or not from_url.endswith('/'):
continue
for app in apps:
if app.get('domain_path') != dest_url:
continue
domain_config_set(from_url.strip('/'), "feature.app.default_app", app['id'])
del redirected_urls[from_url]
persistent["redirected_urls"] = redirected_urls
write_to_json(persistent_file_name, persistent, sort_keys=True, indent=4)
logger.warning(
"YunoHost automatically translated some legacy redirections in /etc/ssowat/conf.json.persistent to match the new default application using domain configuration"
)
LEGACY_PHP_VERSION_REPLACEMENTS = [
("/etc/php5", "/etc/php/7.4"),
("/etc/php/7.0", "/etc/php/7.4"),