From f9afc19ed40622956076b936d384cf3bff8d7daa Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Thu, 30 Jun 2022 11:22:46 +0200 Subject: [PATCH 01/54] Added an optionnal "password" argument to the "yunohost dyndns subscribe" command --- locales/en.json | 4 +++- share/actionsmap.yml | 6 ++++++ src/dyndns.py | 12 ++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index 2b2f10179..d7179cd7e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -356,6 +356,8 @@ "dyndns_key_generating": "Generating DNS key... It may take a while.", "dyndns_key_not_found": "DNS key not found for the domain", "dyndns_no_domain_registered": "No domain registered with DynDNS", + "dyndns_no_recovery_password": "No recovery password specified! In case you loose control of this domain, you will need to contact an administrator in the YunoHost team!", + "dyndns_added_password": "Remember your recovery password, you can use it to delete this domain record.", "dyndns_provider_unreachable": "Unable to reach DynDNS provider {provider}: either your YunoHost is not correctly connected to the internet or the dynette server is down.", "dyndns_registered": "DynDNS domain registered", "dyndns_registration_failed": "Could not register DynDNS domain: {error}", @@ -685,4 +687,4 @@ "yunohost_installing": "Installing YunoHost...", "yunohost_not_installed": "YunoHost is not correctly installed. Please run 'yunohost tools postinstall'", "yunohost_postinstall_end_tip": "The post-install completed! To finalize your setup, please consider:\n - adding a first user through the 'Users' section of the webadmin (or 'yunohost user create ' in command-line);\n - diagnose potential issues through the 'Diagnosis' section of the webadmin (or 'yunohost diagnosis run' in command-line);\n - reading the 'Finalizing your setup' and 'Getting to know YunoHost' parts in the admin documentation: https://yunohost.org/admindoc." -} \ No newline at end of file +} diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 89c6e914d..bf2f53371 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -1406,6 +1406,12 @@ dyndns: -k: full: --key help: Public DNS key + -p: + full: --password + help: Password used to later delete the domain + extra: + pattern: *pattern_password + comment: dyndns_added_password ### dyndns_update() update: diff --git a/src/dyndns.py b/src/dyndns.py index 34f3dd5dc..39e8a7213 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -29,6 +29,7 @@ import json import glob import base64 import subprocess +import hashlib from moulinette import m18n from moulinette.core import MoulinetteError @@ -75,15 +76,19 @@ def _dyndns_available(domain): @is_unit_operation() -def dyndns_subscribe(operation_logger, domain=None, key=None): +def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): """ Subscribe to a DynDNS service Keyword argument: domain -- Full domain to subscribe with key -- Public DNS key + password -- Password that will be used to delete the domain """ + if password is None: + logger.warning(m18n.n('dyndns_no_recovery_password')) + if _guess_current_dyndns_domain() != (None, None): raise YunohostValidationError("domain_dyndns_already_subscribed") @@ -138,9 +143,12 @@ def dyndns_subscribe(operation_logger, domain=None, key=None): try: # Yeah the secret is already a base64-encoded but we double-bas64-encode it, whatever... b64encoded_key = base64.b64encode(secret.encode()).decode() + data = {"subdomain": domain} + if password: + data["recovery_password"]=hashlib.sha256((domain+":"+password.strip()).encode('utf-8')).hexdigest() r = requests.post( f"https://{DYNDNS_PROVIDER}/key/{b64encoded_key}?key_algo=hmac-sha512", - data={"subdomain": domain}, + data=data, timeout=30, ) except Exception as e: From 4a9080bdfd5057085dc962f733cd6b27c98bdef0 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Thu, 30 Jun 2022 12:23:51 +0200 Subject: [PATCH 02/54] Added a new command to delete dyndns records --- locales/en.json | 5 +++++ share/actionsmap.yml | 17 +++++++++++++++ src/dyndns.py | 50 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/locales/en.json b/locales/en.json index d7179cd7e..0bbd41387 100644 --- a/locales/en.json +++ b/locales/en.json @@ -361,6 +361,10 @@ "dyndns_provider_unreachable": "Unable to reach DynDNS provider {provider}: either your YunoHost is not correctly connected to the internet or the dynette server is down.", "dyndns_registered": "DynDNS domain registered", "dyndns_registration_failed": "Could not register DynDNS domain: {error}", + "dyndns_unregistration_failed": "Could not unregister DynDNS domain: {error}", + "dyndns_unregistered": "Domain successfully deleted!", + "dyndns_unsubscribe_wrong_password": "Invalid password", + "dyndns_unsubscribe_wrong_domain": "Domain is not registered", "dyndns_unavailable": "The domain '{domain}' is unavailable.", "experimental_feature": "Warning: This feature is experimental and not considered stable, you should not use it unless you know what you are doing.", "extracting": "Extracting...", @@ -451,6 +455,7 @@ "log_domain_main_domain": "Make '{}' the main domain", "log_domain_remove": "Remove '{}' domain from system configuration", "log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'", + "log_dyndns_unsubscribe": "Unsubscribe to a YunoHost subdomain '{}'", "log_dyndns_update": "Update the IP associated with your YunoHost subdomain '{}'", "log_help_to_get_failed_log": "The operation '{desc}' could not be completed. Please share the full log of this operation using the command 'yunohost log share {name}' to get help", "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log show {name}'", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index bf2f53371..f8d082a70 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -1412,6 +1412,23 @@ dyndns: extra: pattern: *pattern_password comment: dyndns_added_password + + ### dyndns_unsubscribe() + unsubscribe: + action_help: Unsubscribe to a DynDNS service + arguments: + -d: + full: --domain + help: Full domain to subscribe with + extra: + pattern: *pattern_domain + required: True + -p: + full: --password + help: Password used to delete the domain + extra: + required: True + pattern: *pattern_password ### dyndns_update() update: diff --git a/src/dyndns.py b/src/dyndns.py index 39e8a7213..67a8b293d 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -178,6 +178,56 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): logger.success(m18n.n("dyndns_registered")) +@is_unit_operation() +def dyndns_unsubscribe(operation_logger, domain, password): + """ + Unsubscribe from a DynDNS service + + Keyword argument: + domain -- Full domain to unsubscribe with + password -- Password that is used to delete the domain ( defined when subscribing ) + """ + + operation_logger.start() + + # '165' is the convention identifier for hmac-sha512 algorithm + # '1234' is idk? doesnt matter, but the old format contained a number here... + key_file = f"/etc/yunohost/dyndns/K{domain}.+165+1234.key" + + import requests # lazy loading this module for performance reasons + + # Send delete request + try: + r = requests.delete( + f"https://{DYNDNS_PROVIDER}/domains/{domain}", + data={"recovery_password":hashlib.sha256((str(domain)+":"+str(password).strip()).encode('utf-8')).hexdigest()}, + timeout=30, + ) + except Exception as e: + raise YunohostError("dyndns_unregistration_failed", error=str(e)) + + if r.status_code == 200: # Deletion was successful + rm(key_file, force=True) + # Yunohost regen conf will add the dyndns cron job if a key exists + # in /etc/yunohost/dyndns + regen_conf(["yunohost"]) + + # Add some dyndns update in 2 and 4 minutes from now such that user should + # not have to wait 10ish minutes for the conf to propagate + cmd = ( + "at -M now + {t} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost dyndns update'\"" + ) + # For some reason subprocess doesn't like the redirections so we have to use bash -c explicity... + subprocess.check_call(["bash", "-c", cmd.format(t="2 min")]) + subprocess.check_call(["bash", "-c", cmd.format(t="4 min")]) + + logger.success(m18n.n("dyndns_unregistered")) + elif r.status_code == 403: # Wrong password + raise YunohostError("dyndns_unsubscribe_wrong_password") + elif r.status_code == 404: # Invalid domain + raise YunohostError("dyndns_unsubscribe_wrong_domain") + + @is_unit_operation() def dyndns_update( operation_logger, From fdca22ca5bea6268d282063146b15ef781d199f2 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Thu, 30 Jun 2022 12:26:53 +0200 Subject: [PATCH 03/54] Fixed typo --- share/actionsmap.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index f8d082a70..cfc1d6151 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -1419,7 +1419,7 @@ dyndns: arguments: -d: full: --domain - help: Full domain to subscribe with + help: Full domain to unsubscribe with extra: pattern: *pattern_domain required: True From 4f2a111470219c04596b123e4731dba874bb7b8f Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 1 Jul 2022 10:38:25 +0200 Subject: [PATCH 04/54] We can now specify a password using the yunohost domain add command --- locales/en.json | 1 + share/actionsmap.yml | 6 ++++++ src/domain.py | 8 +++++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index 0bbd41387..60243b6bd 100644 --- a/locales/en.json +++ b/locales/en.json @@ -338,6 +338,7 @@ "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost dyndns update' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", "domain_dyndns_root_unknown": "Unknown DynDNS root domain", + "domain_password_no_dyndns": "The password is only used for subscribing to (and maybe later unsubscribing from) the DynDNS service", "domain_exists": "The domain already exists", "domain_hostname_failed": "Unable to set new hostname. This might cause an issue later (it might be fine).", "domain_registrar_is_not_configured": "The registrar is not yet configured for domain {domain}.", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index cfc1d6151..619b1207d 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -457,6 +457,12 @@ domain: full: --dyndns help: Subscribe to the DynDNS service action: store_true + -p: + full: --password + help: Password used to later delete the domain ( if subscribing to the DynDNS service ) + extra: + pattern: *pattern_password + comment: dyndns_added_password ### domain_remove() remove: diff --git a/src/domain.py b/src/domain.py index e40b4f03c..2426412c4 100644 --- a/src/domain.py +++ b/src/domain.py @@ -131,14 +131,14 @@ def _get_parent_domain_of(domain): @is_unit_operation() -def domain_add(operation_logger, domain, dyndns=False): +def domain_add(operation_logger, domain, dyndns=False,password=None): """ Create a custom domain Keyword argument: domain -- Domain name to add dyndns -- Subscribe to DynDNS - + password -- Password used to later unsubscribe from DynDNS """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf @@ -183,7 +183,9 @@ def domain_add(operation_logger, domain, dyndns=False): from yunohost.dyndns import dyndns_subscribe # Actually subscribe - dyndns_subscribe(domain=domain) + dyndns_subscribe(domain=domain,password=password) + elif password: # If a password is provided, while not subscribing to a DynDNS service + logger.warning(m18n.n("domain_password_no_dyndns")) _certificate_install_selfsigned([domain], True) From 882c024bc8cdf2d03b3ccabf08eba76fbd6103f3 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 1 Jul 2022 14:16:50 +0200 Subject: [PATCH 05/54] `yunohost domain remove` now accepts a -p argument --- share/actionsmap.yml | 5 +++++ src/domain.py | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 619b1207d..ea1242825 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -481,6 +481,11 @@ domain: full: --force help: Do not ask confirmation to remove apps action: store_true + -p: + full: --password + help: Password used to delete the domain from DynDNS + extra: + pattern: *pattern_password ### domain_dns_conf() diff --git a/src/domain.py b/src/domain.py index 2426412c4..a8a9560cb 100644 --- a/src/domain.py +++ b/src/domain.py @@ -235,7 +235,7 @@ def domain_add(operation_logger, domain, dyndns=False,password=None): @is_unit_operation() -def domain_remove(operation_logger, domain, remove_apps=False, force=False): +def domain_remove(operation_logger, domain, remove_apps=False, force=False, password=None): """ Delete domains @@ -244,7 +244,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False): remove_apps -- Remove applications installed on the domain force -- Force the domain removal and don't not ask confirmation to remove apps if remove_apps is specified - + password -- Recovery password used at the creation of the DynDNS domain """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove @@ -356,6 +356,13 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False): hook_callback("post_domain_remove", args=[domain]) + # If a password is provided, delete the DynDNS record + if password: + from yunohost.dyndns import dyndns_unsubscribe + + # Actually unsubscribe + dyndns_unsubscribe(domain=domain,password=password) + logger.success(m18n.n("domain_deleted")) From 02a4a5fecfe18f4dacd79cdd8438a1c45925d801 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 1 Jul 2022 14:40:52 +0200 Subject: [PATCH 06/54] The option -d is deprecated, -p is preferred --- locales/en.json | 1 - share/actionsmap.yml | 4 ++-- src/domain.py | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/locales/en.json b/locales/en.json index 60243b6bd..0bbd41387 100644 --- a/locales/en.json +++ b/locales/en.json @@ -338,7 +338,6 @@ "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost dyndns update' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", "domain_dyndns_root_unknown": "Unknown DynDNS root domain", - "domain_password_no_dyndns": "The password is only used for subscribing to (and maybe later unsubscribing from) the DynDNS service", "domain_exists": "The domain already exists", "domain_hostname_failed": "Unable to set new hostname. This might cause an issue later (it might be fine).", "domain_registrar_is_not_configured": "The registrar is not yet configured for domain {domain}.", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index ea1242825..8f2e90569 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -455,11 +455,11 @@ domain: pattern: *pattern_domain -d: full: --dyndns - help: Subscribe to the DynDNS service + help: (Deprecated, using the -p option in order to set a password is recommended) Subscribe to the DynDNS service action: store_true -p: full: --password - help: Password used to later delete the domain ( if subscribing to the DynDNS service ) + help: Subscribe to the DynDNS service with a password, used to later delete the domain extra: pattern: *pattern_password comment: dyndns_added_password diff --git a/src/domain.py b/src/domain.py index a8a9560cb..3c5823037 100644 --- a/src/domain.py +++ b/src/domain.py @@ -163,6 +163,7 @@ def domain_add(operation_logger, domain, dyndns=False,password=None): domain = domain.encode("idna").decode("utf-8") # DynDNS domain + dyndns = dyndns or (password!=None) # If a password is specified, then it is obviously a dyndns domain, no need for the extra option if dyndns: from yunohost.utils.dns import is_yunohost_dyndns_domain @@ -184,8 +185,6 @@ def domain_add(operation_logger, domain, dyndns=False,password=None): # Actually subscribe dyndns_subscribe(domain=domain,password=password) - elif password: # If a password is provided, while not subscribing to a DynDNS service - logger.warning(m18n.n("domain_password_no_dyndns")) _certificate_install_selfsigned([domain], True) From 150614645028f54236cd9cccedfaab53865d846c Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Mon, 4 Jul 2022 10:07:30 +0200 Subject: [PATCH 07/54] Passwords can be set interactively --- share/actionsmap.yml | 9 ++++++++- src/domain.py | 2 +- src/dyndns.py | 22 +++++++++++++++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 8f2e90569..fae7ab8f8 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -459,6 +459,8 @@ domain: action: store_true -p: full: --password + nargs: "?" + const: 0 help: Subscribe to the DynDNS service with a password, used to later delete the domain extra: pattern: *pattern_password @@ -483,6 +485,8 @@ domain: action: store_true -p: full: --password + nargs: "?" + const: 0 help: Password used to delete the domain from DynDNS extra: pattern: *pattern_password @@ -1419,6 +1423,8 @@ dyndns: help: Public DNS key -p: full: --password + nargs: "?" + const: 0 help: Password used to later delete the domain extra: pattern: *pattern_password @@ -1436,9 +1442,10 @@ dyndns: required: True -p: full: --password + nargs: "?" + const: 0 help: Password used to delete the domain extra: - required: True pattern: *pattern_password ### dyndns_update() diff --git a/src/domain.py b/src/domain.py index 3c5823037..5bdecf651 100644 --- a/src/domain.py +++ b/src/domain.py @@ -356,7 +356,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, pass hook_callback("post_domain_remove", args=[domain]) # If a password is provided, delete the DynDNS record - if password: + if password!=None: from yunohost.dyndns import dyndns_unsubscribe # Actually unsubscribe diff --git a/src/dyndns.py b/src/dyndns.py index 67a8b293d..a5532f101 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -31,7 +31,7 @@ import base64 import subprocess import hashlib -from moulinette import m18n +from moulinette import Moulinette, m18n from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from moulinette.utils.filesystem import write_to_file, rm, chown, chmod @@ -144,7 +144,14 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): # Yeah the secret is already a base64-encoded but we double-bas64-encode it, whatever... b64encoded_key = base64.b64encode(secret.encode()).decode() data = {"subdomain": domain} - if password: + if password!=None: + from yunohost.utils.password import assert_password_is_strong_enough + # Ensure sufficiently complex password + if Moulinette.interface.type == "cli" and password==0: + password = Moulinette.prompt( + m18n.n("ask_password"), is_password=True, confirm=True + ) + assert_password_is_strong_enough("admin", password) data["recovery_password"]=hashlib.sha256((domain+":"+password.strip()).encode('utf-8')).hexdigest() r = requests.post( f"https://{DYNDNS_PROVIDER}/key/{b64encoded_key}?key_algo=hmac-sha512", @@ -179,7 +186,7 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): @is_unit_operation() -def dyndns_unsubscribe(operation_logger, domain, password): +def dyndns_unsubscribe(operation_logger, domain, password=None): """ Unsubscribe from a DynDNS service @@ -189,6 +196,15 @@ def dyndns_unsubscribe(operation_logger, domain, password): """ operation_logger.start() + + from yunohost.utils.password import assert_password_is_strong_enough + + # Ensure sufficiently complex password + if Moulinette.interface.type == "cli" and not password: + password = Moulinette.prompt( + m18n.n("ask_password"), is_password=True, confirm=True + ) + assert_password_is_strong_enough("admin", password) # '165' is the convention identifier for hmac-sha512 algorithm # '1234' is idk? doesnt matter, but the old format contained a number here... From 40fbc8d1ea8db5a58c46301c3022fa8ba48f8da4 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Mon, 4 Jul 2022 10:09:08 +0200 Subject: [PATCH 08/54] Clarification --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 0bbd41387..96946acd3 100644 --- a/locales/en.json +++ b/locales/en.json @@ -362,7 +362,7 @@ "dyndns_registered": "DynDNS domain registered", "dyndns_registration_failed": "Could not register DynDNS domain: {error}", "dyndns_unregistration_failed": "Could not unregister DynDNS domain: {error}", - "dyndns_unregistered": "Domain successfully deleted!", + "dyndns_unregistered": "DynDNS domain successfully unregistered", "dyndns_unsubscribe_wrong_password": "Invalid password", "dyndns_unsubscribe_wrong_domain": "Domain is not registered", "dyndns_unavailable": "The domain '{domain}' is unavailable.", From fbfcec4873b801ef9d252383a911e625e9bda280 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 09:06:52 +0200 Subject: [PATCH 09/54] `yunohost domain dns push` works with Dynette domains --- hooks/conf_regen/01-yunohost | 2 +- locales/en.json | 1 + share/actionsmap.yml | 71 ++++++++++++++++++++++++++++++++++-- src/dns.py | 34 ++++++++++++++--- src/domain.py | 37 +++++++++++++++---- src/dyndns.py | 4 +- 6 files changed, 128 insertions(+), 21 deletions(-) diff --git a/hooks/conf_regen/01-yunohost b/hooks/conf_regen/01-yunohost index dc0bfc689..29da2b183 100755 --- a/hooks/conf_regen/01-yunohost +++ b/hooks/conf_regen/01-yunohost @@ -117,7 +117,7 @@ SHELL=/bin/bash # - if ip.yunohost.org answers ping (basic check to validate that we're connected to the internet and yunohost infra aint down) # - and if lock ain't already taken by another command # - trigger yunohost dyndns update -*/10 * * * * root : YunoHost DynDNS update; sleep \$((RANDOM\\%60)); ! ping -q -W5 -c1 ip.yunohost.org >/dev/null 2>&1 || test -e /var/run/moulinette_yunohost.lock || yunohost dyndns update >> /dev/null +*/10 * * * * root : YunoHost DynDNS update ; sleep \$((RANDOM\\%60)); ! ping -q -W5 -c1 ip.yunohost.org >/dev/null 2>&1 || test -e /var/run/moulinette_yunohost.lock || yunohost domain list --exclude-subdomains --output json | jq --raw-output '.domains[]' | grep -E "\.(noho\.st|nohost\.me|ynh\.fr)$" | xargs -I {} yunohost domain dns push "{}" >> /dev/null EOF else # (Delete cron if no dyndns domain found) diff --git a/locales/en.json b/locales/en.json index 96946acd3..a1a90d686 100644 --- a/locales/en.json +++ b/locales/en.json @@ -323,6 +323,7 @@ "domain_dns_conf_special_use_tld": "This domain is based on a special-use top-level domain (TLD) such as .local or .test and is therefore not expected to have actual DNS records.", "domain_dns_push_already_up_to_date": "Records already up to date, nothing to do.", "domain_dns_push_failed": "Updating the DNS records failed miserably.", + "domain_dns_push_failed_domain": "Updating the DNS records for {domain} failed : {error}", "domain_dns_push_failed_to_authenticate": "Failed to authenticate on registrar's API for domain '{domain}'. Most probably the credentials are incorrect? (Error: {error})", "domain_dns_push_failed_to_list": "Failed to list current records using the registrar's API: {error}", "domain_dns_push_managed_in_parent_domain": "The automatic DNS configuration feature is managed in the parent domain {parent_domain}.", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index fae7ab8f8..85b240aa3 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -578,10 +578,69 @@ domain: help: The path to check (e.g. /coffee) subcategories: + dyndns: + subcategory_help: Subscribe and Update DynDNS Hosts + actions: + ### domain_dyndns_subscribe() + subscribe: + action_help: Subscribe to a DynDNS service + arguments: + -d: + full: --domain + help: Full domain to subscribe with + extra: + pattern: *pattern_domain + -k: + full: --key + help: Public DNS key + -p: + full: --password + nargs: "?" + const: 0 + help: Password used to later delete the domain + extra: + pattern: *pattern_password + comment: dyndns_added_password + + ### domain_dyndns_unsubscribe() + unsubscribe: + action_help: Unsubscribe from a DynDNS service + arguments: + -d: + full: --domain + help: Full domain to unsubscribe with + extra: + pattern: *pattern_domain + required: True + -p: + full: --password + nargs: "?" + const: 0 + help: Password used to delete the domain + extra: + pattern: *pattern_password config: subcategory_help: Domain settings actions: + ### domain_config_get() + get: + action_help: Display a domain configuration + api: GET /domains//config + arguments: + domain: + help: Domain name + key: + help: A specific panel, section or a question identifier + nargs: '?' + -f: + full: --full + help: Display all details (meant to be used by the API) + action: store_true + -e: + full: --export + help: Only export key/values, meant to be reimported using "config set --args-file" + action: store_true ### domain_config_get() get: @@ -641,6 +700,7 @@ domain: arguments: domain: help: Domain name to push DNS conf for + nargs: "*" extra: pattern: *pattern_domain -d: @@ -1406,16 +1466,17 @@ firewall: # DynDNS # ############################# dyndns: - category_help: Subscribe and Update DynDNS Hosts + category_help: Subscribe and Update DynDNS Hosts ( deprecated, use 'yunohost domain dyndns' instead ) actions: ### dyndns_subscribe() subscribe: action_help: Subscribe to a DynDNS service + deprecated: true arguments: -d: full: --domain - help: Full domain to subscribe with + help: Full domain to subscribe with ( deprecated, use 'yunohost domain dyndns subscribe' instead ) extra: pattern: *pattern_domain -k: @@ -1432,7 +1493,8 @@ dyndns: ### dyndns_unsubscribe() unsubscribe: - action_help: Unsubscribe to a DynDNS service + action_help: Unsubscribe to a DynDNS service ( deprecated, use 'yunohost domain dyndns unsubscribe' instead ) + deprecated: true arguments: -d: full: --domain @@ -1450,7 +1512,8 @@ dyndns: ### dyndns_update() update: - action_help: Update IP on DynDNS platform + action_help: Update IP on DynDNS platform ( deprecated, use 'yunohost domain dns push DOMAIN' instead ) + deprecated: true arguments: -d: full: --domain diff --git a/src/dns.py b/src/dns.py index c8bebed41..144f8a3a2 100644 --- a/src/dns.py +++ b/src/dns.py @@ -47,6 +47,7 @@ from yunohost.utils.error import YunohostValidationError, YunohostError from yunohost.utils.network import get_public_ip from yunohost.log import is_unit_operation from yunohost.hook import hook_callback +from yunohost.dyndns import dyndns_update logger = getActionLogger("yunohost.domain") @@ -622,7 +623,17 @@ def _get_registar_settings(domain): @is_unit_operation() -def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge=False): +def domain_dns_push(operation_logger, domains, dry_run=False, force=False, purge=False): + if type(domains).__name__!="list": # If we provide only a domain as an argument + domains = [domains] + for domain in domains: + try: + domain_dns_push_unique(domain,dry_run=dry_run,force=force,purge=purge) + except YunohostError as e: + logger.error(m18n.n("domain_dns_push_failed_domain",domain=domain,error=str(e))) + +@is_unit_operation() +def domain_dns_push_unique(operation_logger, domain, dry_run=False, force=False, purge=False): """ Send DNS records to the previously-configured registrar of the domain. """ @@ -643,12 +654,14 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge= # FIXME: in the future, properly unify this with yunohost dyndns update if registrar == "yunohost": - logger.info(m18n.n("domain_dns_registrar_yunohost")) + #logger.info(m18n.n("domain_dns_registrar_yunohost")) + from yunohost.dyndns import dyndns_update + dyndns_update(domain=domain) return {} if registrar == "parent_domain": parent_domain = domain.split(".", 1)[1] - registar, registrar_credentials = _get_registar_settings(parent_domain) + registrar, registrar_credentials = _get_registar_settings(parent_domain) if any(registrar_credentials.values()): raise YunohostValidationError( "domain_dns_push_managed_in_parent_domain", @@ -656,9 +669,18 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge= parent_domain=parent_domain, ) else: - raise YunohostValidationError( - "domain_registrar_is_not_configured", domain=parent_domain - ) + new_parent_domain = ".".join(parent_domain.split(".")[-3:]) + registrar, registrar_credentials = _get_registar_settings(new_parent_domain) + if registrar == "yunohost": + raise YunohostValidationError( + "domain_dns_push_managed_in_parent_domain", + domain=domain, + parent_domain=new_parent_domain, + ) + else: + raise YunohostValidationError( + "domain_registrar_is_not_configured", domain=parent_domain + ) if not all(registrar_credentials.values()): raise YunohostValidationError( diff --git a/src/domain.py b/src/domain.py index 5bdecf651..770c2931b 100644 --- a/src/domain.py +++ b/src/domain.py @@ -181,10 +181,8 @@ def domain_add(operation_logger, domain, dyndns=False,password=None): operation_logger.start() if dyndns: - from yunohost.dyndns import dyndns_subscribe - # Actually subscribe - dyndns_subscribe(domain=domain,password=password) + domain_dyndns_subscribe(domain=domain,password=password) _certificate_install_selfsigned([domain], True) @@ -357,14 +355,37 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, pass # If a password is provided, delete the DynDNS record if password!=None: - from yunohost.dyndns import dyndns_unsubscribe - # Actually unsubscribe - dyndns_unsubscribe(domain=domain,password=password) + domain_dyndns_unsubscribe(domain=domain,password=password) logger.success(m18n.n("domain_deleted")) +def domain_dyndns_subscribe(**kwargs): + """ + Subscribe to a DynDNS domain + """ + from yunohost.dyndns import dyndns_subscribe + + dyndns_subscribe(**kwargs) + +def domain_dyndns_unsubscribe(**kwargs): + """ + Unsubscribe from a DynDNS domain + """ + from yunohost.dyndns import dyndns_unsubscribe + + dyndns_unsubscribe(**kwargs) + +def domain_dyndns_update(**kwargs): + """ + Update a DynDNS domain + """ + from yunohost.dyndns import dyndns_update + + dyndns_update(**kwargs) + + @is_unit_operation() def domain_main_domain(operation_logger, new_main_domain=None): """ @@ -572,7 +593,7 @@ def domain_dns_suggest(domain): return domain_dns_suggest(domain) -def domain_dns_push(domain, dry_run, force, purge): +def domain_dns_push(domain, dry_run=None, force=None, purge=None): from yunohost.dns import domain_dns_push - return domain_dns_push(domain, dry_run, force, purge) + return domain_dns_push(domain, dry_run=dry_run, force=force, purge=purge) diff --git a/src/dyndns.py b/src/dyndns.py index a5532f101..741bb81dc 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -176,7 +176,7 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): # Add some dyndns update in 2 and 4 minutes from now such that user should # not have to wait 10ish minutes for the conf to propagate cmd = ( - "at -M now + {t} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost dyndns update'\"" + f"at -M now + {{t}} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost domain dns push {domain}'\"" ) # For some reason subprocess doesn't like the redirections so we have to use bash -c explicity... subprocess.check_call(["bash", "-c", cmd.format(t="2 min")]) @@ -231,7 +231,7 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): # Add some dyndns update in 2 and 4 minutes from now such that user should # not have to wait 10ish minutes for the conf to propagate cmd = ( - "at -M now + {t} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost dyndns update'\"" + f"at -M now + {{t}} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost domain dns push {domain}'\"" ) # For some reason subprocess doesn't like the redirections so we have to use bash -c explicity... subprocess.check_call(["bash", "-c", cmd.format(t="2 min")]) From 11684675d7a0c10e1dbb7c36f4de45dbda997329 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 09:16:17 +0200 Subject: [PATCH 10/54] Fix --- share/actionsmap.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 85b240aa3..23e6094bf 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -642,25 +642,6 @@ domain: help: Only export key/values, meant to be reimported using "config set --args-file" action: store_true - ### domain_config_get() - get: - action_help: Display a domain configuration - api: GET /domains//config - arguments: - domain: - help: Domain name - key: - help: A specific panel, section or a question identifier - nargs: '?' - -f: - full: --full - help: Display all details (meant to be used by the API) - action: store_true - -e: - full: --export - help: Only export key/values, meant to be reimported using "config set --args-file" - action: store_true - ### domain_config_set() set: action_help: Apply a new configuration From 273c10f17d1fd3d405328a617c46c4102deae623 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 09:45:26 +0200 Subject: [PATCH 11/54] Updated locales --- locales/ca.json | 2 +- locales/de.json | 4 ++-- locales/en.json | 4 ++-- locales/es.json | 4 ++-- locales/eu.json | 4 ++-- locales/fa.json | 2 +- locales/fr.json | 4 ++-- locales/gl.json | 4 ++-- locales/it.json | 4 ++-- locales/uk.json | 4 ++-- locales/zh_Hans.json | 2 +- src/dns.py | 2 +- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index b660032d2..57ee0234c 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -531,7 +531,7 @@ "diagnosis_swap_tip": "Vigileu i tingueu en compte que els servidor està allotjant memòria d'intercanvi en una targeta SD o en l'emmagatzematge SSD, això pot reduir dràsticament l'esperança de vida del dispositiu.", "restore_already_installed_apps": "No s'han pogut restaurar les següents aplicacions perquè ja estan instal·lades: {apps}", "app_packaging_format_not_supported": "No es pot instal·lar aquesta aplicació ja que el format del paquet no és compatible amb la versió de YunoHost del sistema. Hauríeu de considerar actualitzar el sistema.", - "diagnosis_dns_try_dyndns_update_force": "La configuració DNS d'aquest domini hauria de ser gestionada automàticament per YunoHost. Si aquest no és el cas, podeu intentar forçar-ne l'actualització utilitzant yunohost dyndns update --force.", + "diagnosis_dns_try_dyndns_update_force": "La configuració DNS d'aquest domini hauria de ser gestionada automàticament per YunoHost. Si aquest no és el cas, podeu intentar forçar-ne l'actualització utilitzant yunohost domain dns push DOMAIN --force.", "regenconf_need_to_explicitly_specify_ssh": "La configuració ssh ha estat modificada manualment, però heu d'especificar explícitament la categoria «ssh» amb --force per fer realment els canvis.", "global_settings_setting_backup_compress_tar_archives": "Comprimir els arxius (.tar.gz) en lloc d'arxius no comprimits (.tar) al crear noves còpies de seguretat. N.B.: activar aquesta opció permet fer arxius de còpia de seguretat més lleugers, però el procés inicial de còpia de seguretat serà significativament més llarg i més exigent a nivell de CPU.", "global_settings_setting_smtp_relay_host": "L'amfitrió de tramesa SMTP que s'ha d'utilitzar per enviar correus electrònics en lloc d'aquesta instància de YunoHost. És útil si esteu en una de les següents situacions: el port 25 està bloquejat per el vostre proveïdor d'accés a internet o proveïdor de servidor privat virtual, si teniu una IP residencial llistada a DUHL, si no podeu configurar el DNS invers o si el servidor no està directament exposat a internet i voleu utilitzar-ne un altre per enviar correus electrònics.", diff --git a/locales/de.json b/locales/de.json index 686eb9251..824616abd 100644 --- a/locales/de.json +++ b/locales/de.json @@ -312,7 +312,7 @@ "diagnosis_domain_expiration_success": "Deine Domänen sind registriert und werden in nächster Zeit nicht ablaufen.", "diagnosis_domain_not_found_details": "Die Domäne {domain} existiert nicht in der WHOIS-Datenbank oder sie ist abgelaufen!", "diagnosis_domain_expiration_not_found": "Das Ablaufdatum einiger Domains kann nicht überprüft werden", - "diagnosis_dns_try_dyndns_update_force": "Die DNS-Konfiguration dieser Domäne sollte automatisch von YunoHost verwaltet werden. Andernfalls könntest Du mittels yunohost dyndns update --force ein Update erzwingen.", + "diagnosis_dns_try_dyndns_update_force": "Die DNS-Konfiguration dieser Domäne sollte automatisch von YunoHost verwaltet werden. Andernfalls könntest Du mittels yunohost domain dns push DOMAIN --force ein Update erzwingen.", "diagnosis_dns_point_to_doc": "Bitte schaue in der Dokumentation unter https://yunohost.org/dns_config nach, wenn du Hilfe bei der Konfiguration der DNS-Einträge benötigst.", "diagnosis_dns_discrepancy": "Der folgende DNS Eintrag scheint nicht den empfohlenen Einstellungen zu entsprechen:
Typ: {type}
Name: {name}
Aktueller Wert: {current}
Erwarteter Wert: {value}", "diagnosis_dns_missing_record": "Gemäß der empfohlenen DNS-Konfiguration solltest du einen DNS-Eintrag mit den folgenden Informationen hinzufügen.
Typ: {type}
Name: {name}
Wert: {value}", @@ -644,7 +644,7 @@ "log_app_config_set": "Konfiguration auf die Applikation '{}' anwenden", "log_user_import": "Konten importieren", "diagnosis_high_number_auth_failures": "In letzter Zeit gab es eine verdächtig hohe Anzahl von Authentifizierungsfehlern. Stelle sicher, dass fail2ban läuft und korrekt konfiguriert ist, oder verwende einen benutzerdefinierten Port für SSH, wie unter https://yunohost.org/security beschrieben.", - "domain_dns_registrar_yunohost": "Dies ist eine nohost.me / nohost.st / ynh.fr Domäne, ihre DNS-Konfiguration wird daher automatisch von YunoHost ohne weitere Konfiguration übernommen. (siehe Befehl 'yunohost dyndns update')", + "domain_dns_registrar_yunohost": "Dies ist eine nohost.me / nohost.st / ynh.fr Domäne, ihre DNS-Konfiguration wird daher automatisch von YunoHost ohne weitere Konfiguration übernommen. (siehe Befehl 'yunohost domain dns push DOMAIN')", "domain_config_auth_entrypoint": "API-Einstiegspunkt", "domain_config_auth_application_key": "Anwendungsschlüssel", "domain_config_auth_application_secret": "Geheimer Anwendungsschlüssel", diff --git a/locales/en.json b/locales/en.json index a1a90d686..9ea928b1a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -192,7 +192,7 @@ "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.
Type: {type}
Name: {name}
Value: {value}", "diagnosis_dns_point_to_doc": "Please check the documentation at https://yunohost.org/dns_config if you need help about configuring DNS records.", "diagnosis_dns_specialusedomain": "Domain {domain} is based on a special-use top-level domain (TLD) such as .local or .test and is therefore not expected to have actual DNS records.", - "diagnosis_dns_try_dyndns_update_force": "This domain's DNS configuration should automatically be managed by YunoHost. If that's not the case, you can try to force an update using yunohost dyndns update --force.", + "diagnosis_dns_try_dyndns_update_force": "This domain's DNS configuration should automatically be managed by YunoHost. If that's not the case, you can try to force an update using yunohost domain dns push DOMAIN --force.", "diagnosis_domain_expiration_error": "Some domains will expire VERY SOON!", "diagnosis_domain_expiration_not_found": "Unable to check the expiration date for some domains", "diagnosis_domain_expiration_not_found_details": "The WHOIS information for domain {domain} doesn't seem to contain the information about the expiration date?", @@ -336,7 +336,7 @@ "domain_dns_registrar_managed_in_parent_domain": "This domain is a subdomain of {parent_domain_link}. DNS registrar configuration should be managed in {parent_domain}'s configuration panel.", "domain_dns_registrar_not_supported": "YunoHost could not automatically detect the registrar handling this domain. You should manually configure your DNS records following the documentation at https://yunohost.org/dns.", "domain_dns_registrar_supported": "YunoHost automatically detected that this domain is handled by the registrar **{registrar}**. If you want, YunoHost will automatically configure this DNS zone, if you provide it with the appropriate API credentials. You can find documentation on how to obtain your API credentials on this page: https://yunohost.org/registar_api_{registrar}. (You can also manually configure your DNS records following the documentation at https://yunohost.org/dns )", - "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost dyndns update' command)", + "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost domain dns push DOMAIN' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", "domain_dyndns_root_unknown": "Unknown DynDNS root domain", "domain_exists": "The domain already exists", diff --git a/locales/es.json b/locales/es.json index aebb959a8..93236189e 100644 --- a/locales/es.json +++ b/locales/es.json @@ -491,7 +491,7 @@ "diagnosis_domain_expiration_not_found_details": "¿Parece que la información de WHOIS para el dominio {domain} no contiene información sobre la fecha de expiración?", "diagnosis_domain_not_found_details": "¡El dominio {domain} no existe en la base de datos WHOIS o ha expirado!", "diagnosis_domain_expiration_not_found": "No se pudo revisar la fecha de expiración para algunos dominios", - "diagnosis_dns_try_dyndns_update_force": "La configuración DNS de este dominio debería ser administrada automáticamente por YunoHost. Si no es el caso, puedes intentar forzar una actualización mediante yunohost dyndns update --force.", + "diagnosis_dns_try_dyndns_update_force": "La configuración DNS de este dominio debería ser administrada automáticamente por YunoHost. Si no es el caso, puedes intentar forzar una actualización mediante yunohost domain dns push DOMAIN --force.", "diagnosis_ip_local": "IP Local: {local}", "diagnosis_ip_no_ipv6_tip": "Tener IPv6 funcionando no es obligatorio para que su servidor funcione, pero es mejor para la salud del Internet en general. IPv6 debería ser configurado automáticamente por el sistema o su proveedor si está disponible. De otra manera, es posible que tenga que configurar varias cosas manualmente, tal y como se explica en esta documentación https://yunohost.org/#/ipv6. Si no puede habilitar IPv6 o si parece demasiado técnico, puede ignorar esta advertencia con toda seguridad.", "diagnosis_display_tip": "Para ver los problemas encontrados, puede ir a la sección de diagnóstico del webadmin, o ejecutar 'yunohost diagnosis show --issues --human-readable' en la línea de comandos.", @@ -616,7 +616,7 @@ "domain_config_auth_application_key": "LLave de Aplicación", "domain_dns_registrar_supported": "YunoHost detectó automáticamente que este dominio es manejado por el registrador **{registrar}**. Si lo desea, YunoHost configurará automáticamente esta zona DNS, si le proporciona las credenciales de API adecuadas. Puede encontrar documentación sobre cómo obtener sus credenciales de API en esta página: https://yunohost.org/registar_api_{registrar}. (También puede configurar manualmente sus registros DNS siguiendo la documentación en https://yunohost.org/dns)", "domain_dns_registrar_managed_in_parent_domain": "Este dominio es un subdominio de {parent_domain_link}. La configuración del registrador de DNS debe administrarse en el panel de configuración de {parent_domain}.", - "domain_dns_registrar_yunohost": "Este dominio es un nohost.me / nohost.st / ynh.fr y, por lo tanto, YunoHost maneja automáticamente su configuración de DNS sin ninguna configuración adicional. (vea el comando 'yunohost dyndns update')", + "domain_dns_registrar_yunohost": "Este dominio es un nohost.me / nohost.st / ynh.fr y, por lo tanto, YunoHost maneja automáticamente su configuración de DNS sin ninguna configuración adicional. (vea el comando 'yunohost domain dns push DOMAIN')", "domain_dns_registrar_not_supported": "YunoHost no pudo detectar automáticamente el registrador que maneja este dominio. Debe configurar manualmente sus registros DNS siguiendo la documentación en https://yunohost.org/dns.", "global_settings_setting_security_nginx_redirect_to_https": "Redirija las solicitudes HTTP a HTTPs de forma predeterminada (¡NO LO DESACTIVE a menos que realmente sepa lo que está haciendo!)", "global_settings_setting_security_webadmin_allowlist": "Direcciones IP permitidas para acceder al webadmin. Separado por comas.", diff --git a/locales/eu.json b/locales/eu.json index e0ce226d5..60676d287 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -160,7 +160,7 @@ "certmanager_domain_not_diagnosed_yet": "Oraindik ez dago {domain} domeinurako diagnostikorik. Mesedez, berrabiarazi diagnostikoak 'DNS balioak' eta 'Web' ataletarako diagnostikoen gunean Let's Encrypt ziurtagirirako prest ote dagoen egiaztatzeko. (Edo zertan ari zaren baldin badakizu, erabili '--no-checks' egiaztatzea desgaitzeko.)", "diagnosis_domain_expiration_warning": "Domeinu batzuk iraungitzear daude!", "app_packaging_format_not_supported": "Aplikazio hau ezin da instalatu YunoHostek ez duelako paketea ezagutzen. Sistema eguneratzea hausnartu beharko zenuke ziur asko.", - "diagnosis_dns_try_dyndns_update_force": "Domeinu honen DNS konfigurazioa YunoHostek kudeatu beharko luke automatikoki. Gertatuko ez balitz, eguneratzera behartu zenezake yunohost dyndns update --force erabiliz.", + "diagnosis_dns_try_dyndns_update_force": "Domeinu honen DNS konfigurazioa YunoHostek kudeatu beharko luke automatikoki. Gertatuko ez balitz, eguneratzera behartu zenezake yunohost domain dns push DOMAIN --force erabiliz.", "app_manifest_install_ask_path": "Aukeratu aplikazio hau instalatzeko URLaren bidea (domeinuaren atzeko aldean)", "app_manifest_install_ask_admin": "Aukeratu administrari bat aplikazio honetarako", "app_manifest_install_ask_password": "Aukeratu administrazio-pasahitz bat aplikazio honetarako", @@ -333,7 +333,7 @@ "domain_dns_push_not_applicable": "Ezin da {domain} domeinurako DNS konfigurazio automatiko funtzioa erabili. DNS erregistroak eskuz ezarri beharko zenituzke gidaorriei erreparatuz: https://yunohost.org/dns_config.", "domain_dns_push_managed_in_parent_domain": "DNS ezarpenak automatikoki konfiguratzeko funtzioa {parent_domain} domeinu nagusian kudeatzen da.", "domain_dns_registrar_managed_in_parent_domain": "Domeinu hau {parent_domain_link} (r)en azpidomeinua da. DNS ezarpenak {parent_domain}(r)en konfigurazio atalean kudeatu behar dira.", - "domain_dns_registrar_yunohost": "Hau nohost.me / nohost.st / ynh.fr domeinu bat da eta, beraz, DNS ezarpenak automatikoki kudeatzen ditu YunoHostek, bestelako ezer konfiguratu beharrik gabe. (ikus 'yunohost dyndns update' komandoa)", + "domain_dns_registrar_yunohost": "Hau nohost.me / nohost.st / ynh.fr domeinu bat da eta, beraz, DNS ezarpenak automatikoki kudeatzen ditu YunoHostek, bestelako ezer konfiguratu beharrik gabe. (ikus 'yunohost domain dns push DOMAIN' komandoa)", "domain_dns_registrar_not_supported": "YunoHostek ezin izan du domeinu honen erregistro-enpresa automatikoki antzeman. Eskuz konfiguratu beharko dituzu DNS ezarpenak gidalerroei erreparatuz: https://yunohost.org/dns.", "domain_dns_registrar_experimental": "Oraingoz, YunoHosten kideek ez dute **{registrar}** erregistro-enpresaren APIa nahi beste probatu eta aztertu. Funtzioa **oso esperimentala** da — kontuz!", "domain_config_mail_in": "Jasotako mezuak", diff --git a/locales/fa.json b/locales/fa.json index 599ab1ea7..d9e3f39b3 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -31,7 +31,7 @@ "diagnosis_domain_not_found_details": "دامنه {domain} در پایگاه داده WHOIS وجود ندارد یا منقضی شده است!", "diagnosis_domain_expiration_not_found": "بررسی تاریخ انقضا برخی از دامنه ها امکان پذیر نیست", "diagnosis_dns_specialusedomain": "دامنه {domain} بر اساس یک دامنه سطح بالا (TLD) مخصوص استفاده است و بنابراین انتظار نمی رود که دارای سوابق DNS واقعی باشد.", - "diagnosis_dns_try_dyndns_update_force": "پیکربندی DNS این دامنه باید به طور خودکار توسط YunoHost مدیریت شود. اگر اینطور نیست ، می توانید سعی کنید به زور یک به روز رسانی را با استفاده از yunohost dyndns update --force.", + "diagnosis_dns_try_dyndns_update_force": "پیکربندی DNS این دامنه باید به طور خودکار توسط YunoHost مدیریت شود. اگر اینطور نیست ، می توانید سعی کنید به زور یک به روز رسانی را با استفاده از yunohost domain dns push DOMAIN --force.", "diagnosis_dns_point_to_doc": "لطفاً اسناد را در https://yunohost.org/dns_config برسی و مطالعه کنید، اگر در مورد پیکربندی سوابق DNS به کمک نیاز دارید.", "diagnosis_dns_discrepancy": "به نظر می رسد پرونده DNS زیر از پیکربندی توصیه شده پیروی نمی کند:
نوع: {type}
نام: {name}
ارزش فعلی: {current}
مقدار مورد انتظار: {value}", "diagnosis_dns_missing_record": "با توجه به پیکربندی DNS توصیه شده ، باید یک رکورد DNS با اطلاعات زیر اضافه کنید.
نوع: {type}
نام: {name}
ارزش: {value}", diff --git a/locales/fr.json b/locales/fr.json index 2773d0bee..6e96e500a 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -534,7 +534,7 @@ "diagnosis_swap_tip": "Merci d'être prudent et conscient que si vous hébergez une partition SWAP sur une carte SD ou un disque SSD, cela risque de réduire drastiquement l'espérance de vie du périphérique.", "restore_already_installed_apps": "Les applications suivantes ne peuvent pas être restaurées car elles sont déjà installées : {apps}", "regenconf_need_to_explicitly_specify_ssh": "La configuration de ssh a été modifiée manuellement. Vous devez explicitement indiquer la mention --force à \"ssh\" pour appliquer les changements.", - "diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par YunoHost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant yunohost dyndns update --force.", + "diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par YunoHost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant yunohost domain dns push DOMAIN --force.", "app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de YunoHost. Vous devriez probablement envisager de mettre à jour votre système.", "global_settings_setting_backup_compress_tar_archives": "Lors de la création de nouvelles sauvegardes, compresser automatiquement les archives (.tar.gz) au lieu des archives non compressées (.tar). N.B. : activer cette option permet de créer des archives plus légères, mais la procédure de sauvegarde initiale sera significativement plus longues et plus gourmandes en CPU.", "diagnosis_processes_killed_by_oom_reaper": "Certains processus ont été arrêtés récemment par le système car il manquait de mémoire. Cela apparaît généralement quand le système manque de mémoire ou qu'un processus consomme trop de mémoire. Liste des processus tués :\n{kills_summary}", @@ -628,7 +628,7 @@ "service_not_reloading_because_conf_broken": "Le service '{name}' n'a pas été rechargé/redémarré car sa configuration est cassée : {errors}", "domain_registrar_is_not_configured": "Le registrar n'est pas encore configuré pour le domaine {domain}.", "domain_dns_push_not_applicable": "La fonction de configuration DNS automatique n'est pas applicable au domaine {domain}. Vous devez configurer manuellement vos enregistrements DNS en suivant la documentation sur https://yunohost.org/dns_config.", - "domain_dns_registrar_yunohost": "Ce domaine est de type nohost.me / nohost.st / ynh.fr et sa configuration DNS est donc automatiquement gérée par YunoHost sans qu'il n'y ait d'autre configuration à faire. (voir la commande 'yunohost dyndns update')", + "domain_dns_registrar_yunohost": "Ce domaine est de type nohost.me / nohost.st / ynh.fr et sa configuration DNS est donc automatiquement gérée par YunoHost sans qu'il n'y ait d'autre configuration à faire. (voir la commande 'yunohost domain dns push DOMAIN')", "domain_dns_registrar_supported": "YunoHost a détecté automatiquement que ce domaine est géré par le registrar **{registrar}**. Si vous le souhaitez, YunoHost configurera automatiquement cette zone DNS, si vous lui fournissez les identifiants API appropriés. Vous pouvez trouver de la documentation sur la façon d'obtenir vos identifiants API sur cette page : https://yunohost.org/registar_api_{registrar}. (Vous pouvez également configurer manuellement vos enregistrements DNS en suivant la documentation sur https://yunohost.org/dns )", "domain_config_features_disclaimer": "Jusqu'à présent, l'activation/désactivation des fonctionnalités de messagerie ou XMPP n'a d'impact que sur la configuration DNS recommandée et automatique, et non sur les configurations système !", "domain_dns_push_managed_in_parent_domain": "La fonctionnalité de configuration DNS automatique est gérée dans le domaine parent {parent_domain}.", diff --git a/locales/gl.json b/locales/gl.json index 4a77645d6..8d53051f2 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -191,7 +191,7 @@ "diagnosis_domain_expiration_not_found_details": "A información WHOIS para o dominio {domain} non semella conter información acerca da data de caducidade?", "diagnosis_domain_not_found_details": "O dominio {domain} non existe na base de datos de WHOIS ou está caducado!", "diagnosis_domain_expiration_not_found": "Non se puido comprobar a data de caducidade para algúns dominios", - "diagnosis_dns_try_dyndns_update_force": "A xestión DNS deste dominio debería estar xestionada directamente por YunoHost. Se non fose o caso, podes intentar forzar unha actualización executando yunohost dyndns update --force.", + "diagnosis_dns_try_dyndns_update_force": "A xestión DNS deste dominio debería estar xestionada directamente por YunoHost. Se non fose o caso, podes intentar forzar unha actualización executando yunohost domain dns push DOMAIN --force.", "diagnosis_swap_ok": "O sistema ten {total} de swap!", "diagnosis_swap_notsomuch": "O sistema só ten {total} de swap. Deberías considerar ter polo menos {recommended} para evitar situacións onde o sistema esgote a memoria.", "diagnosis_swap_none": "O sistema non ten partición swap. Deberías considerar engadir polo menos {recommended} de swap para evitar situación onde o sistema esgote a memoria.", @@ -648,7 +648,7 @@ "domain_config_auth_consumer_key": "Chave consumidora", "log_domain_dns_push": "Enviar rexistros DNS para o dominio '{}'", "other_available_options": "... e outras {n} opcións dispoñibles non mostradas", - "domain_dns_registrar_yunohost": "Este dominio un dos de nohost.me / nohost.st / ynh.fr e a configuración DNS xestionaa directamente YunoHost se máis requisitos. (mira o comando 'yunohost dyndns update')", + "domain_dns_registrar_yunohost": "Este dominio un dos de nohost.me / nohost.st / ynh.fr e a configuración DNS xestionaa directamente YunoHost se máis requisitos. (mira o comando 'yunohost domain dns push DOMAIN')", "domain_dns_registrar_supported": "YunoHost detectou automáticamente que este dominio está xestionado pola rexistradora **{registrar}**. Se queres, YunoHost pode configurar automáticamente as súas zonas DNS, se proporcionas as credenciais de acceso á API. Podes ver a documentación sobre como obter as credenciais da API nesta páxina: https://yunohost.org/registrar_api_{registrar}. (Tamén podes configurar manualmente os rexistros DNS seguindo a documentación en https://yunohost.org/dns )", "domain_dns_push_partial_failure": "Actualización parcial dos rexistros DNS: informouse dalgúns avisos/erros.", "domain_config_auth_token": "Token de autenticación", diff --git a/locales/it.json b/locales/it.json index 844b756ea..27ab21473 100644 --- a/locales/it.json +++ b/locales/it.json @@ -321,7 +321,7 @@ "diagnosis_domain_expiration_not_found_details": "Le informazioni WHOIS per il dominio {domain} non sembrano contenere la data di scadenza, giusto?", "diagnosis_domain_not_found_details": "Il dominio {domain} non esiste nel database WHOIS o è scaduto!", "diagnosis_domain_expiration_not_found": "Non riesco a controllare la data di scadenza di alcuni domini", - "diagnosis_dns_try_dyndns_update_force": "La configurazione DNS di questo dominio dovrebbe essere gestita automaticamente da YunoHost. Se non avviene, puoi provare a forzare un aggiornamento usando il comando yunohost dyndns update --force.", + "diagnosis_dns_try_dyndns_update_force": "La configurazione DNS di questo dominio dovrebbe essere gestita automaticamente da YunoHost. Se non avviene, puoi provare a forzare un aggiornamento usando il comando yunohost domain dns push DOMAIN --force.", "diagnosis_dns_point_to_doc": "Controlla la documentazione a https://yunohost.org/dns_config se hai bisogno di aiuto nel configurare i record DNS.", "diagnosis_dns_discrepancy": "Il record DNS non sembra seguire la configurazione DNS raccomandata:
Type: {type}
Name: {name}
Current value: {current}
Expected value: {value}", "diagnosis_dns_missing_record": "Stando alla configurazione DNS raccomandata, dovresti aggiungere un record DNS con le seguenti informazioni.
Type: {type}
Name: {name}
Value: {value}", @@ -641,7 +641,7 @@ "diagnosis_description_apps": "Applicazioni", "domain_registrar_is_not_configured": "Il registrar non è ancora configurato per il dominio {domain}.", "domain_dns_registrar_managed_in_parent_domain": "Questo dominio è un sotto-dominio di {parent_domain_link}. La configurazione del registrar DNS dovrebbe essere gestita dal pannello di configurazione di {parent_domain}.", - "domain_dns_registrar_yunohost": "Questo dominio è un nohost.me / nohost.st / ynh.fr, perciò la sua configurazione DNS è gestita automaticamente da YunoHost, senza alcuna ulteriore configurazione. (vedi il comando yunohost dyndns update)", + "domain_dns_registrar_yunohost": "Questo dominio è un nohost.me / nohost.st / ynh.fr, perciò la sua configurazione DNS è gestita automaticamente da YunoHost, senza alcuna ulteriore configurazione. (vedi il comando yunohost domain dns push DOMAIN)", "domain_dns_push_success": "Record DNS aggiornati!", "domain_dns_push_failed": "L’aggiornamento dei record DNS è miseramente fallito.", "domain_dns_push_partial_failure": "Record DNS parzialmente aggiornati: alcuni segnali/errori sono stati riportati.", diff --git a/locales/uk.json b/locales/uk.json index 9a32a597b..ba6737d7a 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -436,7 +436,7 @@ "diagnosis_domain_not_found_details": "Домен {domain} не існує в базі даних WHOIS або строк його дії сплив!", "diagnosis_domain_expiration_not_found": "Неможливо перевірити строк дії деяких доменів", "diagnosis_dns_specialusedomain": "Домен {domain} заснований на домені верхнього рівня спеціального призначення (TLD) такого як .local або .test і тому не очікується, що у нього будуть актуальні записи DNS.", - "diagnosis_dns_try_dyndns_update_force": "Конфігурація DNS цього домену повинна автоматично управлятися YunoHost. Якщо це не так, ви можете спробувати примусово оновити її за допомогою команди yunohost dyndns update --force.", + "diagnosis_dns_try_dyndns_update_force": "Конфігурація DNS цього домену повинна автоматично управлятися YunoHost. Якщо це не так, ви можете спробувати примусово оновити її за допомогою команди yunohost domain dns push DOMAIN --force.", "diagnosis_dns_point_to_doc": "Якщо вам потрібна допомога з налаштування DNS-записів, зверніться до документації на сайті https://yunohost.org/dns_config.", "diagnosis_dns_discrepancy": "Наступний запис DNS, схоже, не відповідає рекомендованій конфігурації:
Тип: {type}
Назва: {name}
Поточне значення: {current}
Очікуване значення: {value}", "diagnosis_dns_missing_record": "Згідно рекомендованої конфігурації DNS, ви повинні додати запис DNS з наступними відомостями.
Тип: {type}
Назва: {name}
Значення: {value}", @@ -632,7 +632,7 @@ "diagnosis_http_special_use_tld": "Домен {domain} базується на спеціальному домені верхнього рівня (TLD), такому як .local або .test, і тому не очікується, що він буде відкритий за межами локальної мережі.", "domain_dns_push_managed_in_parent_domain": "Функцією автоконфігурації DNS керує батьківський домен {parent_domain}.", "domain_dns_registrar_managed_in_parent_domain": "Цей домен є піддоменом {parent_domain_link}. Конфігурацією реєстратора DNS слід керувати на панелі конфігурації {parent_domain}.", - "domain_dns_registrar_yunohost": "Цей домен є nohost.me/nohost.st/ynh.fr, тому його конфігурація DNS автоматично обробляється YunoHost без будь-якої подальшої конфігурації. (див. команду 'yunohost dyndns update')", + "domain_dns_registrar_yunohost": "Цей домен є nohost.me/nohost.st/ynh.fr, тому його конфігурація DNS автоматично обробляється YunoHost без будь-якої подальшої конфігурації. (див. команду 'yunohost domain dns push DOMAIN')", "domain_dns_conf_special_use_tld": "Цей домен засновано на спеціальному домені верхнього рівня (TLD), такому як .local або .test, і тому не очікується, що він матиме актуальні записи DNS.", "domain_dns_registrar_supported": "YunoHost автоматично визначив, що цей домен обслуговується реєстратором **{registrar}**. Якщо ви хочете, YunoHost автоматично налаштує цю DNS-зону, якщо ви надасте йому відповідні облікові дані API. Ви можете знайти документацію про те, як отримати реєстраційні дані API на цій сторінці: https://yunohost.org/registar_api_{registrar}. (Ви також можете вручну налаштувати свої DNS-записи, дотримуючись документації на https://yunohost.org/dns)", "domain_dns_registrar_experimental": "Поки що інтерфейс з API **{registrar}** не був належним чином протестований і перевірений спільнотою YunoHost. Підтримка є **дуже експериментальною** - будьте обережні!", diff --git a/locales/zh_Hans.json b/locales/zh_Hans.json index 2daf45483..d3b12f778 100644 --- a/locales/zh_Hans.json +++ b/locales/zh_Hans.json @@ -477,7 +477,7 @@ "diagnosis_diskusage_low": "存储器{mountpoint}(在设备{device}上)只有{free} ({free_percent}%) 的空间。({free_percent}%)的剩余空间(在{total}中)。要小心。", "diagnosis_diskusage_verylow": "存储器{mountpoint}(在设备{device}上)仅剩余{free} ({free_percent}%) (剩余{total})个空间。您应该真正考虑清理一些空间!", "diagnosis_services_bad_status_tip": "你可以尝试重新启动服务,如果没有效果,可以看看webadmin中的服务日志(从命令行,你可以用yunohost service restart {service}yunohost service log {service})来做。", - "diagnosis_dns_try_dyndns_update_force": "该域的DNS配置应由YunoHost自动管理,如果不是这种情况,您可以尝试使用 yunohost dyndns update --force强制进行更新。", + "diagnosis_dns_try_dyndns_update_force": "该域的DNS配置应由YunoHost自动管理,如果不是这种情况,您可以尝试使用 yunohost domain dns push DOMAIN --force强制进行更新。", "diagnosis_dns_point_to_doc": "如果您需要有关配置DNS记录的帮助,请查看 https://yunohost.org/dns_config 上的文档。", "diagnosis_dns_discrepancy": "以下DNS记录似乎未遵循建议的配置:
类型: {type}
名称: {name}
代码> 当前值: {current}期望值: {value}", "log_backup_create": "创建备份档案", diff --git a/src/dns.py b/src/dns.py index 144f8a3a2..9cb2d4044 100644 --- a/src/dns.py +++ b/src/dns.py @@ -656,7 +656,7 @@ def domain_dns_push_unique(operation_logger, domain, dry_run=False, force=False, if registrar == "yunohost": #logger.info(m18n.n("domain_dns_registrar_yunohost")) from yunohost.dyndns import dyndns_update - dyndns_update(domain=domain) + dyndns_update(domain=domain,force=force) return {} if registrar == "parent_domain": From ab37617f91d482a4ac7b75827f0e254859971a18 Mon Sep 17 00:00:00 2001 From: theo-is-taken <108329355+theo-is-taken@users.noreply.github.com> Date: Tue, 5 Jul 2022 09:54:23 +0200 Subject: [PATCH 12/54] Update src/dns.py Co-authored-by: ljf (zamentur) --- src/dns.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dns.py b/src/dns.py index 9cb2d4044..00bb72d42 100644 --- a/src/dns.py +++ b/src/dns.py @@ -624,7 +624,8 @@ def _get_registar_settings(domain): @is_unit_operation() def domain_dns_push(operation_logger, domains, dry_run=False, force=False, purge=False): - if type(domains).__name__!="list": # If we provide only a domain as an argument + # If we provide only a domain as an argument + if isinstance(domains, str): domains = [domains] for domain in domains: try: From e58aaa6db607d2d51be9f842b1e8831afbf91ffc Mon Sep 17 00:00:00 2001 From: theo-is-taken <108329355+theo-is-taken@users.noreply.github.com> Date: Tue, 5 Jul 2022 09:55:19 +0200 Subject: [PATCH 13/54] Update src/dyndns.py Co-authored-by: ljf (zamentur) --- src/dyndns.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dyndns.py b/src/dyndns.py index 741bb81dc..fd86df9ff 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -216,7 +216,8 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): try: r = requests.delete( f"https://{DYNDNS_PROVIDER}/domains/{domain}", - data={"recovery_password":hashlib.sha256((str(domain)+":"+str(password).strip()).encode('utf-8')).hexdigest()}, + secret = str(domain) + ":" + str(password).strip() + data = {"recovery_password": hashlib.sha256(secret.encode('utf-8')).hexdigest()}, timeout=30, ) except Exception as e: From 0903460fc4a0d520e195ec1b43d43510b1bcba2f Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 09:59:15 +0200 Subject: [PATCH 14/54] Fix --- src/dyndns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dyndns.py b/src/dyndns.py index fd86df9ff..e755c803d 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -214,9 +214,9 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): # Send delete request try: + secret = str(domain) + ":" + str(password).strip() r = requests.delete( f"https://{DYNDNS_PROVIDER}/domains/{domain}", - secret = str(domain) + ":" + str(password).strip() data = {"recovery_password": hashlib.sha256(secret.encode('utf-8')).hexdigest()}, timeout=30, ) From dd51adcd3f467968c2663f985832e43395d0b995 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 10:02:58 +0200 Subject: [PATCH 15/54] Removed useless call to dns push --- src/dyndns.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/dyndns.py b/src/dyndns.py index e755c803d..3db4b7521 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -229,15 +229,6 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): # in /etc/yunohost/dyndns regen_conf(["yunohost"]) - # Add some dyndns update in 2 and 4 minutes from now such that user should - # not have to wait 10ish minutes for the conf to propagate - cmd = ( - f"at -M now + {{t}} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost domain dns push {domain}'\"" - ) - # For some reason subprocess doesn't like the redirections so we have to use bash -c explicity... - subprocess.check_call(["bash", "-c", cmd.format(t="2 min")]) - subprocess.check_call(["bash", "-c", cmd.format(t="4 min")]) - logger.success(m18n.n("dyndns_unregistered")) elif r.status_code == 403: # Wrong password raise YunohostError("dyndns_unsubscribe_wrong_password") From ac60516638dc0fdf16cfc571ecc099a89da34797 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 10:15:56 +0200 Subject: [PATCH 16/54] Raise an actual error (instead of log) --- locales/en.json | 1 + src/dns.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/locales/en.json b/locales/en.json index 9ea928b1a..3c56c207b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -324,6 +324,7 @@ "domain_dns_push_already_up_to_date": "Records already up to date, nothing to do.", "domain_dns_push_failed": "Updating the DNS records failed miserably.", "domain_dns_push_failed_domain": "Updating the DNS records for {domain} failed : {error}", + "domain_dns_push_failed_domains": "Updating the DNS records for {domains} failed.", "domain_dns_push_failed_to_authenticate": "Failed to authenticate on registrar's API for domain '{domain}'. Most probably the credentials are incorrect? (Error: {error})", "domain_dns_push_failed_to_list": "Failed to list current records using the registrar's API: {error}", "domain_dns_push_managed_in_parent_domain": "The automatic DNS configuration feature is managed in the parent domain {parent_domain}.", diff --git a/src/dns.py b/src/dns.py index 00bb72d42..0a7ce7ea2 100644 --- a/src/dns.py +++ b/src/dns.py @@ -627,11 +627,15 @@ def domain_dns_push(operation_logger, domains, dry_run=False, force=False, purge # If we provide only a domain as an argument if isinstance(domains, str): domains = [domains] + error_domains = [] for domain in domains: try: domain_dns_push_unique(domain,dry_run=dry_run,force=force,purge=purge) except YunohostError as e: logger.error(m18n.n("domain_dns_push_failed_domain",domain=domain,error=str(e))) + error_domains.append(domain) + if len(error_domains)>0: + raise YunohostError("domain_dns_push_failed_domains",domains=', '.join(error_domains)) @is_unit_operation() def domain_dns_push_unique(operation_logger, domain, dry_run=False, force=False, purge=False): From cf6eaf364d2deb9fb0aa81b25bafed2997ecd21a Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 10:45:21 +0200 Subject: [PATCH 17/54] Better password assert placement --- src/dyndns.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/dyndns.py b/src/dyndns.py index 3db4b7521..02ebe2cca 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -88,6 +88,14 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): if password is None: logger.warning(m18n.n('dyndns_no_recovery_password')) + else: + from yunohost.utils.password import assert_password_is_strong_enough + # Ensure sufficiently complex password + if Moulinette.interface.type == "cli" and password==0: + password = Moulinette.prompt( + m18n.n("ask_password"), is_password=True, confirm=True + ) + assert_password_is_strong_enough("admin", password) if _guess_current_dyndns_domain() != (None, None): raise YunohostValidationError("domain_dyndns_already_subscribed") @@ -145,13 +153,6 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): b64encoded_key = base64.b64encode(secret.encode()).decode() data = {"subdomain": domain} if password!=None: - from yunohost.utils.password import assert_password_is_strong_enough - # Ensure sufficiently complex password - if Moulinette.interface.type == "cli" and password==0: - password = Moulinette.prompt( - m18n.n("ask_password"), is_password=True, confirm=True - ) - assert_password_is_strong_enough("admin", password) data["recovery_password"]=hashlib.sha256((domain+":"+password.strip()).encode('utf-8')).hexdigest() r = requests.post( f"https://{DYNDNS_PROVIDER}/key/{b64encoded_key}?key_algo=hmac-sha512", @@ -195,17 +196,17 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): password -- Password that is used to delete the domain ( defined when subscribing ) """ - operation_logger.start() - from yunohost.utils.password import assert_password_is_strong_enough # Ensure sufficiently complex password if Moulinette.interface.type == "cli" and not password: password = Moulinette.prompt( m18n.n("ask_password"), is_password=True, confirm=True - ) + ) assert_password_is_strong_enough("admin", password) + operation_logger.start() + # '165' is the convention identifier for hmac-sha512 algorithm # '1234' is idk? doesnt matter, but the old format contained a number here... key_file = f"/etc/yunohost/dyndns/K{domain}.+165+1234.key" From e4c631c171df131b4d7bb5efd4ae239a9696a25f Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 11:30:40 +0200 Subject: [PATCH 18/54] Added `yunohost domain dyndns list` command --- share/actionsmap.yml | 4 ++++ src/domain.py | 8 ++++++++ src/dyndns.py | 12 ++++++++++++ 3 files changed, 24 insertions(+) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 23e6094bf..5327edcb2 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -620,6 +620,10 @@ domain: extra: pattern: *pattern_password + ### domain_dyndns_list() + list: + action_help: List all subscribed DynDNS domains + config: subcategory_help: Domain settings actions: diff --git a/src/domain.py b/src/domain.py index 770c2931b..4203e1c0f 100644 --- a/src/domain.py +++ b/src/domain.py @@ -377,6 +377,14 @@ def domain_dyndns_unsubscribe(**kwargs): dyndns_unsubscribe(**kwargs) +def domain_dyndns_list(): + """ + Returns all currently subscribed DynDNS domains + """ + from yunohost.dyndns import dyndns_list + + return dyndns_list() + def domain_dyndns_update(**kwargs): """ Update a DynDNS domain diff --git a/src/dyndns.py b/src/dyndns.py index 02ebe2cca..1673b6d16 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -236,6 +236,18 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): elif r.status_code == 404: # Invalid domain raise YunohostError("dyndns_unsubscribe_wrong_domain") +def dyndns_list(): + """ + Returns all currently subscribed DynDNS domains ( deduced from the key files ) + """ + + files = glob.glob("/etc/yunohost/dyndns/K*key") + # Get the domain names + for i in range(len(files)): + files[i] = files[i].split(".+",1)[0] + files[i] = files[i].split("/etc/yunohost/dyndns/K")[1] + + return {"domains":files} @is_unit_operation() def dyndns_update( From 986b42fc1daa00ac79f748463703514997f6a262 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 14:37:14 +0200 Subject: [PATCH 19/54] `yunohost domain add` auto-detect DynDNS domains and asks for a --subscribe or --no-subscribe option --- share/actionsmap.yml | 13 +++++++------ src/domain.py | 18 ++++++++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 5327edcb2..bb9fa2ae7 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -453,15 +453,16 @@ domain: help: Domain name to add extra: pattern: *pattern_domain - -d: - full: --dyndns - help: (Deprecated, using the -p option in order to set a password is recommended) Subscribe to the DynDNS service + -n: + full: --no-subscribe + help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true - -p: - full: --password + -s: + full: --subscribe + metavar: PASSWORD nargs: "?" const: 0 - help: Subscribe to the DynDNS service with a password, used to later delete the domain + help: If adding a DynDNS domain, subscribe to the DynDNS service with a password, used to later delete the domain extra: pattern: *pattern_password comment: dyndns_added_password diff --git a/src/domain.py b/src/domain.py index 4203e1c0f..e6a61cea0 100644 --- a/src/domain.py +++ b/src/domain.py @@ -40,6 +40,7 @@ from yunohost.app import ( from yunohost.regenconf import regen_conf, _force_clear_hashes, _process_regen_conf from yunohost.utils.config import ConfigPanel, Question from yunohost.utils.error import YunohostError, YunohostValidationError +from yunohost.utils.dns import is_yunohost_dyndns_domain from yunohost.log import is_unit_operation logger = getActionLogger("yunohost.domain") @@ -131,7 +132,7 @@ def _get_parent_domain_of(domain): @is_unit_operation() -def domain_add(operation_logger, domain, dyndns=False,password=None): +def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): """ Create a custom domain @@ -163,10 +164,12 @@ def domain_add(operation_logger, domain, dyndns=False,password=None): domain = domain.encode("idna").decode("utf-8") # DynDNS domain - dyndns = dyndns or (password!=None) # If a password is specified, then it is obviously a dyndns domain, no need for the extra option + dyndns = is_yunohost_dyndns_domain(domain) if dyndns: + print(subscribe,no_subscribe) + if ((subscribe==None) == (no_subscribe==False)): + raise YunohostValidationError("domain_dyndns_instruction_unclear") - from yunohost.utils.dns import is_yunohost_dyndns_domain from yunohost.dyndns import _guess_current_dyndns_domain # Do not allow to subscribe to multiple dyndns domains... @@ -178,11 +181,14 @@ def domain_add(operation_logger, domain, dyndns=False,password=None): if not is_yunohost_dyndns_domain(domain): raise YunohostValidationError("domain_dyndns_root_unknown") - operation_logger.start() - if dyndns: + operation_logger.start() + if not dyndns and (subscribe is not None or no_subscribe): + logger.warning("This domain is not a DynDNS one, no need for the --subscribe or --no-subscribe option") + + if dyndns and not no_subscribe: # Actually subscribe - domain_dyndns_subscribe(domain=domain,password=password) + domain_dyndns_subscribe(domain=domain,password=subscribe) _certificate_install_selfsigned([domain], True) From 840bed5222f23dd3cc07713226282d3505047ad2 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 15:10:04 +0200 Subject: [PATCH 20/54] `yunohost domain remove` auto-detects DynDNS domains and now uses --unsubscribe and --no-unsubscribe --- share/actionsmap.yml | 11 ++++++++--- src/domain.py | 22 ++++++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index bb9fa2ae7..eb24de49f 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -484,11 +484,16 @@ domain: full: --force help: Do not ask confirmation to remove apps action: store_true - -p: - full: --password + -n: + full: --no-unsubscribe + help: If removing a DynDNS domain, only remove the domain, without unsubscribing from the DynDNS service + action: store_true + -u: + full: --unsubscribe + metavar: PASSWORD nargs: "?" const: 0 - help: Password used to delete the domain from DynDNS + help: If removing a DynDNS domain, unsubscribe from the DynDNS service with a password extra: pattern: *pattern_password diff --git a/src/domain.py b/src/domain.py index e6a61cea0..9bd6a05bd 100644 --- a/src/domain.py +++ b/src/domain.py @@ -139,7 +139,8 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): Keyword argument: domain -- Domain name to add dyndns -- Subscribe to DynDNS - password -- Password used to later unsubscribe from DynDNS + subscribe -- Password used to later unsubscribe from DynDNS + no_unsubscribe -- If we want to just add the DynDNS domain to the list, without subscribing """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf @@ -166,7 +167,6 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): # DynDNS domain dyndns = is_yunohost_dyndns_domain(domain) if dyndns: - print(subscribe,no_subscribe) if ((subscribe==None) == (no_subscribe==False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") @@ -238,7 +238,7 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): @is_unit_operation() -def domain_remove(operation_logger, domain, remove_apps=False, force=False, password=None): +def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsubscribe=None,no_unsubscribe=False): """ Delete domains @@ -247,7 +247,8 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, pass remove_apps -- Remove applications installed on the domain force -- Force the domain removal and don't not ask confirmation to remove apps if remove_apps is specified - password -- Recovery password used at the creation of the DynDNS domain + unsubscribe -- Recovery password used at the creation of the DynDNS domain + no_unsubscribe -- If we just remove the DynDNS domain, without unsubscribing """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove @@ -312,9 +313,18 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, pass "domain_uninstall_app_first", apps="\n".join([x[1] for x in apps_on_that_domain]), ) + + # DynDNS domain + dyndns = is_yunohost_dyndns_domain(domain) + if dyndns: + if ((unsubscribe==None) == (no_unsubscribe==False)): + raise YunohostValidationError("domain_dyndns_instruction_unclear") operation_logger.start() + if not dyndns and (unsubscribe!=None or no_unsubscribe!=False): + logger.warning("This domain is not a DynDNS one, no need for the --unsubscribe or --no-unsubscribe option") + ldap = _get_ldap_interface() try: ldap.remove("virtualdomain=" + domain + ",ou=domains") @@ -360,9 +370,9 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, pass hook_callback("post_domain_remove", args=[domain]) # If a password is provided, delete the DynDNS record - if password!=None: + if dyndns and not no_unsubscribe: # Actually unsubscribe - domain_dyndns_unsubscribe(domain=domain,password=password) + domain_dyndns_unsubscribe(domain=domain,password=unsubscribe) logger.success(m18n.n("domain_deleted")) From 3bd427afab266d34c3a8b0543c6625b6f5dd40e0 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 15:14:33 +0200 Subject: [PATCH 21/54] Password is only asked once if we unsubscribe --- src/dyndns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dyndns.py b/src/dyndns.py index 1673b6d16..f5531d518 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -201,7 +201,7 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): # Ensure sufficiently complex password if Moulinette.interface.type == "cli" and not password: password = Moulinette.prompt( - m18n.n("ask_password"), is_password=True, confirm=True + m18n.n("ask_password"), is_password=True ) assert_password_is_strong_enough("admin", password) From 7117c61bbff87c847695275e8d6ff89f9f607f39 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 5 Jul 2022 15:25:43 +0200 Subject: [PATCH 22/54] Removed useless error --- locales/en.json | 1 - src/domain.py | 6 ------ 2 files changed, 7 deletions(-) diff --git a/locales/en.json b/locales/en.json index 3c56c207b..f5a38709a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -339,7 +339,6 @@ "domain_dns_registrar_supported": "YunoHost automatically detected that this domain is handled by the registrar **{registrar}**. If you want, YunoHost will automatically configure this DNS zone, if you provide it with the appropriate API credentials. You can find documentation on how to obtain your API credentials on this page: https://yunohost.org/registar_api_{registrar}. (You can also manually configure your DNS records following the documentation at https://yunohost.org/dns )", "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost domain dns push DOMAIN' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", - "domain_dyndns_root_unknown": "Unknown DynDNS root domain", "domain_exists": "The domain already exists", "domain_hostname_failed": "Unable to set new hostname. This might cause an issue later (it might be fine).", "domain_registrar_is_not_configured": "The registrar is not yet configured for domain {domain}.", diff --git a/src/domain.py b/src/domain.py index 9bd6a05bd..f9597b813 100644 --- a/src/domain.py +++ b/src/domain.py @@ -176,12 +176,6 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): if _guess_current_dyndns_domain() != (None, None): raise YunohostValidationError("domain_dyndns_already_subscribed") - # Check that this domain can effectively be provided by - # dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st) - if not is_yunohost_dyndns_domain(domain): - raise YunohostValidationError("domain_dyndns_root_unknown") - - operation_logger.start() if not dyndns and (subscribe is not None or no_subscribe): logger.warning("This domain is not a DynDNS one, no need for the --subscribe or --no-subscribe option") From bc3521fd0452295378177033496976f6d1813cda Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Wed, 6 Jul 2022 09:30:00 +0200 Subject: [PATCH 23/54] `yunohost tools postinstall` auto-detect DynDNS domains and asks for a --subscribe or --no-subscribe option --- share/actionsmap.yml | 14 ++++++++++++-- src/tools.py | 18 ++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index eb24de49f..d69a35f1f 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -1574,9 +1574,19 @@ tools: pattern: *pattern_password required: True comment: good_practices_about_admin_password - --ignore-dyndns: - help: Do not subscribe domain to a DynDNS service + -n: + full: --no-subscribe + help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true + -s: + full: --subscribe + metavar: PASSWORD + nargs: "?" + const: 0 + help: If adding a DynDNS domain, subscribe to the DynDNS service with a password, used to later delete the domain + extra: + pattern: *pattern_password + comment: dyndns_added_password --force-password: help: Use this if you really want to set a weak password action: store_true diff --git a/src/tools.py b/src/tools.py index bb7ded03a..543f835e6 100644 --- a/src/tools.py +++ b/src/tools.py @@ -186,6 +186,8 @@ def tools_postinstall( domain, password, ignore_dyndns=False, + subscribe=None, + no_subscribe=False, force_password=False, force_diskspace=False, ): @@ -230,10 +232,13 @@ def tools_postinstall( assert_password_is_strong_enough("admin", password) # If this is a nohost.me/noho.st, actually check for availability - if not ignore_dyndns and is_yunohost_dyndns_domain(domain): + if is_yunohost_dyndns_domain(domain): + if ((subscribe==None) == (no_subscribe==False)): + raise YunohostValidationError("domain_dyndns_instruction_unclear") + # Check if the domain is available... try: - available = _dyndns_available(domain) + _dyndns_available(domain) # If an exception is thrown, most likely we don't have internet # connectivity or something. Assume that this domain isn't manageable # and inform the user that we could not contact the dyndns host server. @@ -241,14 +246,7 @@ def tools_postinstall( logger.warning( m18n.n("dyndns_provider_unreachable", provider="dyndns.yunohost.org") ) - - if available: - dyndns = True - # If not, abort the postinstall - else: raise YunohostValidationError("dyndns_unavailable", domain=domain) - else: - dyndns = False if os.system("iptables -V >/dev/null 2>/dev/null") != 0: raise YunohostValidationError( @@ -260,7 +258,7 @@ def tools_postinstall( logger.info(m18n.n("yunohost_installing")) # New domain config - domain_add(domain, dyndns) + domain_add(domain, subscribe=subscribe,no_subscribe=no_subscribe) domain_main_domain(domain) # Update LDAP admin and create home dir From 01dfb778e9bd8b29b2980dcc6ac9d75ed6219dbf Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Wed, 6 Jul 2022 09:32:25 +0200 Subject: [PATCH 24/54] Added domain_dyndns_instruction_unclear --- locales/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/en.json b/locales/en.json index f5a38709a..8e3d5c2b6 100644 --- a/locales/en.json +++ b/locales/en.json @@ -339,6 +339,7 @@ "domain_dns_registrar_supported": "YunoHost automatically detected that this domain is handled by the registrar **{registrar}**. If you want, YunoHost will automatically configure this DNS zone, if you provide it with the appropriate API credentials. You can find documentation on how to obtain your API credentials on this page: https://yunohost.org/registar_api_{registrar}. (You can also manually configure your DNS records following the documentation at https://yunohost.org/dns )", "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost domain dns push DOMAIN' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", + "domain_dyndns_instruction_unclear": "The --subscribe and --no-subscribe options are not compatible", "domain_exists": "The domain already exists", "domain_hostname_failed": "Unable to set new hostname. This might cause an issue later (it might be fine).", "domain_registrar_is_not_configured": "The registrar is not yet configured for domain {domain}.", From 940af74c2d58fa9a0b055c7c6cbb4dfdd4202e03 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Wed, 6 Jul 2022 15:52:27 +0200 Subject: [PATCH 25/54] `yunohost domain dns push` now accepts an --auto option Domains can be configured to be auto-pushed by a cron job --- hooks/conf_regen/01-yunohost | 5 +++-- locales/en.json | 2 ++ share/actionsmap.yml | 10 ++++++++-- share/config_domain.toml | 7 +++++++ src/dns.py | 9 +++++---- src/domain.py | 8 +++++--- src/dyndns.py | 4 ++++ 7 files changed, 34 insertions(+), 11 deletions(-) diff --git a/hooks/conf_regen/01-yunohost b/hooks/conf_regen/01-yunohost index 29da2b183..55accc4f4 100755 --- a/hooks/conf_regen/01-yunohost +++ b/hooks/conf_regen/01-yunohost @@ -116,8 +116,9 @@ SHELL=/bin/bash # - (sleep random 60 is here to spread requests over a 1-min window) # - if ip.yunohost.org answers ping (basic check to validate that we're connected to the internet and yunohost infra aint down) # - and if lock ain't already taken by another command -# - trigger yunohost dyndns update -*/10 * * * * root : YunoHost DynDNS update ; sleep \$((RANDOM\\%60)); ! ping -q -W5 -c1 ip.yunohost.org >/dev/null 2>&1 || test -e /var/run/moulinette_yunohost.lock || yunohost domain list --exclude-subdomains --output json | jq --raw-output '.domains[]' | grep -E "\.(noho\.st|nohost\.me|ynh\.fr)$" | xargs -I {} yunohost domain dns push "{}" >> /dev/null +# - check if some domains are flagged as autopush +# - trigger yunohost domain dns push --auto +*/10 * * * * root : YunoHost DynDNS update ; sleep \$((RANDOM\\%60)); ! ping -q -W5 -c1 ip.yunohost.org >/dev/null 2>&1 || test -e /var/run/moulinette_yunohost.lock || !(grep -nR "autopush: 1" /etc/yunohost/domains/*.yml > /dev/null) || yunohost domain dns push --auto >> /dev/null EOF else # (Delete cron if no dyndns domain found) diff --git a/locales/en.json b/locales/en.json index 8e3d5c2b6..d58790ba2 100644 --- a/locales/en.json +++ b/locales/en.json @@ -312,6 +312,8 @@ "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_autopush": "Auto-push", + "domain_config_autopush_help": "Automatically update the domain's record", "domain_config_mail_in": "Incoming emails", "domain_config_mail_out": "Outgoing emails", "domain_config_xmpp": "Instant messaging (XMPP)", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index d69a35f1f..17b4c1f96 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -443,6 +443,9 @@ domain: --exclude-subdomains: help: Filter out domains that are obviously subdomains of other declared domains action: store_true + --auto-push: + help: Only display domains that are pushed automatically + action: store_true ### domain_add() add: @@ -689,8 +692,8 @@ domain: action_help: Push DNS records to registrar api: POST /domains//dns/push arguments: - domain: - help: Domain name to push DNS conf for + domains: + help: Domain names to push DNS conf for nargs: "*" extra: pattern: *pattern_domain @@ -704,6 +707,9 @@ domain: --purge: help: Delete all records action: store_true + --auto: + help: Push only domains that should be pushed automatically + action: store_true cert: subcategory_help: Manage domain certificates diff --git a/share/config_domain.toml b/share/config_domain.toml index 65e755365..ba0706749 100644 --- a/share/config_domain.toml +++ b/share/config_domain.toml @@ -46,6 +46,13 @@ i18n = "domain_config" default = 0 [dns] + + [dns.zone] + + [dns.zone.autopush] + type = "boolean" + default = 0 + help = "" [dns.registrar] optional = true diff --git a/src/dns.py b/src/dns.py index 0a7ce7ea2..8ba46011e 100644 --- a/src/dns.py +++ b/src/dns.py @@ -623,10 +623,11 @@ def _get_registar_settings(domain): @is_unit_operation() -def domain_dns_push(operation_logger, domains, dry_run=False, force=False, purge=False): - # If we provide only a domain as an argument - if isinstance(domains, str): - domains = [domains] +def domain_dns_push(operation_logger, domains, dry_run=False, force=False, purge=False, auto=False): + if auto: + domains = domain_list(exclude_subdomains=True,auto_push=True)["domains"] + elif len(domains)==0: + domains = domain_list(exclude_subdomains=True)["domains"] error_domains = [] for domain in domains: try: diff --git a/src/domain.py b/src/domain.py index f9597b813..df40577da 100644 --- a/src/domain.py +++ b/src/domain.py @@ -52,7 +52,7 @@ DOMAIN_SETTINGS_DIR = "/etc/yunohost/domains" domain_list_cache: Dict[str, Any] = {} -def domain_list(exclude_subdomains=False): +def domain_list(exclude_subdomains=False,auto_push=False): """ List domains @@ -78,6 +78,8 @@ def domain_list(exclude_subdomains=False): parent_domain = domain.split(".", 1)[1] if parent_domain in result: continue + if auto_push and not domain_config_get(domain, key="dns.zone.autopush"): + continue result_list.append(domain) @@ -611,7 +613,7 @@ def domain_dns_suggest(domain): return domain_dns_suggest(domain) -def domain_dns_push(domain, dry_run=None, force=None, purge=None): +def domain_dns_push(domains, dry_run=None, force=None, purge=None, auto=False): from yunohost.dns import domain_dns_push - return domain_dns_push(domain, dry_run=dry_run, force=force, purge=purge) + return domain_dns_push(domains, dry_run=dry_run, force=force, purge=purge, auto=auto) diff --git a/src/dyndns.py b/src/dyndns.py index f5531d518..4ddbf7396 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -170,6 +170,10 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): error = f'Server error, code: {r.status_code}. (Message: "{r.text}")' raise YunohostError("dyndns_registration_failed", error=error) + # Set the domain's config to autopush + from yunohost.domain import domain_config_set + domain_config_set(domain,key="dns.zone.autopush",value=1) + # Yunohost regen conf will add the dyndns cron job if a key exists # in /etc/yunohost/dyndns regen_conf(["yunohost"]) From 9e44b33401f83ba5d720c4e62e2bf89e254768e9 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Wed, 6 Jul 2022 15:57:17 +0200 Subject: [PATCH 26/54] Clarification --- src/dyndns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dyndns.py b/src/dyndns.py index 4ddbf7396..070090d7f 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -82,7 +82,7 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): Keyword argument: domain -- Full domain to subscribe with - key -- Public DNS key + key -- TSIG Shared DNS key password -- Password that will be used to delete the domain """ From 06db6f7e0430847f1bbdd4bcf9e6fd2ae4ecbfa9 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 8 Jul 2022 09:21:08 +0200 Subject: [PATCH 27/54] Clearer locales --- locales/en.json | 3 ++- src/domain.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index d58790ba2..b8d22bbfa 100644 --- a/locales/en.json +++ b/locales/en.json @@ -341,7 +341,8 @@ "domain_dns_registrar_supported": "YunoHost automatically detected that this domain is handled by the registrar **{registrar}**. If you want, YunoHost will automatically configure this DNS zone, if you provide it with the appropriate API credentials. You can find documentation on how to obtain your API credentials on this page: https://yunohost.org/registar_api_{registrar}. (You can also manually configure your DNS records following the documentation at https://yunohost.org/dns )", "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost domain dns push DOMAIN' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", - "domain_dyndns_instruction_unclear": "The --subscribe and --no-subscribe options are not compatible", + "domain_dyndns_instruction_unclear": "You must choose exactly one of the following options : --subscribe or --no-subscribe", + "domain_dyndns_instruction_unclear_unsubscribe": "You must choose exactly one of the following options : --unsubscribe or --no-unsubscribe", "domain_exists": "The domain already exists", "domain_hostname_failed": "Unable to set new hostname. This might cause an issue later (it might be fine).", "domain_registrar_is_not_configured": "The registrar is not yet configured for domain {domain}.", diff --git a/src/domain.py b/src/domain.py index df40577da..68726f4f2 100644 --- a/src/domain.py +++ b/src/domain.py @@ -314,7 +314,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu dyndns = is_yunohost_dyndns_domain(domain) if dyndns: if ((unsubscribe==None) == (no_unsubscribe==False)): - raise YunohostValidationError("domain_dyndns_instruction_unclear") + raise YunohostValidationError("domain_dyndns_instruction_unclear_unsubscribe") operation_logger.start() From 0b5c96e2495c6155f3a12ec56a7c949435f64742 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Mon, 11 Jul 2022 15:16:33 +0200 Subject: [PATCH 28/54] Added an API endpoint to check if a domain is a DynDNS one --- share/actionsmap.yml | 10 ++++++++++ src/domain.py | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 17b4c1f96..fa620b0b6 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -585,6 +585,16 @@ domain: pattern: *pattern_domain path: help: The path to check (e.g. /coffee) + + ### domain_isdyndns() + isdyndns: + action_help: Check if a domain is a dyndns one + api: GET /domain//isdyndns + arguments: + domain: + help: The domain to test (e.g. your.domain.tld) + extra: + pattern: *pattern_domain subcategories: dyndns: diff --git a/src/domain.py b/src/domain.py index 68726f4f2..750224da0 100644 --- a/src/domain.py +++ b/src/domain.py @@ -458,6 +458,14 @@ def domain_url_available(domain, path): return len(_get_conflicting_apps(domain, path)) == 0 +def domain_isdyndns(domain): + """ + Returns if a domain is a DynDNS one ( used via the web API ) + + Arguments: + domain -- the domain to check + """ + return is_yunohost_dyndns_domain(domain) def _get_maindomain(): with open("/etc/yunohost/current_host", "r") as f: From ba061a49e495154b8f5386c55c766fd87f153ced Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Mon, 11 Jul 2022 15:48:30 +0200 Subject: [PATCH 29/54] Added a --full option to `domain list` --- share/actionsmap.yml | 13 ++++--------- src/domain.py | 16 ++++++---------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index fa620b0b6..b4b8955f4 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -446,6 +446,10 @@ domain: --auto-push: help: Only display domains that are pushed automatically action: store_true + -f: + full: --full + action: store_true + help: Display more information ### domain_add() add: @@ -586,15 +590,6 @@ domain: path: help: The path to check (e.g. /coffee) - ### domain_isdyndns() - isdyndns: - action_help: Check if a domain is a dyndns one - api: GET /domain//isdyndns - arguments: - domain: - help: The domain to test (e.g. your.domain.tld) - extra: - pattern: *pattern_domain subcategories: dyndns: diff --git a/src/domain.py b/src/domain.py index 750224da0..0378cca51 100644 --- a/src/domain.py +++ b/src/domain.py @@ -52,7 +52,7 @@ DOMAIN_SETTINGS_DIR = "/etc/yunohost/domains" domain_list_cache: Dict[str, Any] = {} -def domain_list(exclude_subdomains=False,auto_push=False): +def domain_list(exclude_subdomains=False,auto_push=False,full=False): """ List domains @@ -97,6 +97,11 @@ def domain_list(exclude_subdomains=False,auto_push=False): if exclude_subdomains: return {"domains": result_list, "main": _get_maindomain()} + if full: + for i in range(len(result_list)): + domain = result_list[i] + result_list[i] = {'name':domain,'isdyndns': is_yunohost_dyndns_domain(domain)} + domain_list_cache = {"domains": result_list, "main": _get_maindomain()} return domain_list_cache @@ -458,15 +463,6 @@ def domain_url_available(domain, path): return len(_get_conflicting_apps(domain, path)) == 0 -def domain_isdyndns(domain): - """ - Returns if a domain is a DynDNS one ( used via the web API ) - - Arguments: - domain -- the domain to check - """ - return is_yunohost_dyndns_domain(domain) - def _get_maindomain(): with open("/etc/yunohost/current_host", "r") as f: maindomain = f.readline().rstrip() From a2a1eefbed051587bb08dbfda71de7d925cf17f8 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Mon, 11 Jul 2022 17:11:49 +0200 Subject: [PATCH 30/54] Small fix --- share/actionsmap.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index b4b8955f4..1155e6697 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -446,8 +446,7 @@ domain: --auto-push: help: Only display domains that are pushed automatically action: store_true - -f: - full: --full + --full: action: store_true help: Display more information From f67eaef90bc3950290eb20152e8b37193e1cd41c Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Tue, 12 Jul 2022 10:45:35 +0200 Subject: [PATCH 31/54] Ignore cache if "full" is specified --- src/domain.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/domain.py b/src/domain.py index 0378cca51..b7ab302b8 100644 --- a/src/domain.py +++ b/src/domain.py @@ -61,7 +61,7 @@ def domain_list(exclude_subdomains=False,auto_push=False,full=False): """ global domain_list_cache - if not exclude_subdomains and domain_list_cache: + if not (exclude_subdomains or full) and domain_list_cache: return domain_list_cache from yunohost.utils.ldap import _get_ldap_interface @@ -93,7 +93,6 @@ def domain_list(exclude_subdomains=False,auto_push=False,full=False): result_list = sorted(result_list, key=cmp_domain) - # Don't cache answer if using exclude_subdomains if exclude_subdomains: return {"domains": result_list, "main": _get_maindomain()} @@ -102,8 +101,13 @@ def domain_list(exclude_subdomains=False,auto_push=False,full=False): domain = result_list[i] result_list[i] = {'name':domain,'isdyndns': is_yunohost_dyndns_domain(domain)} - domain_list_cache = {"domains": result_list, "main": _get_maindomain()} - return domain_list_cache + result = {"domains": result_list, "main": _get_maindomain()} + + # Cache answer only if not using exclude_subdomains or full + if not (full or exclude_subdomains): + domain_list_cache = result + + return result def _assert_domain_exists(domain): From 731f07817b4835a8f2f7e983fb5d3d2321fa740f Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Wed, 13 Jul 2022 11:03:16 +0200 Subject: [PATCH 32/54] Redact domain passwords in logs --- src/domain.py | 6 ++++++ src/dyndns.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/domain.py b/src/domain.py index b7ab302b8..a107c7635 100644 --- a/src/domain.py +++ b/src/domain.py @@ -158,6 +158,9 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): from yunohost.utils.ldap import _get_ldap_interface from yunohost.certificate import _certificate_install_selfsigned + if subscribe!=0 and subscribe!=None: + operation_logger.data_to_redact.append(subscribe) + if domain.startswith("xmpp-upload."): raise YunohostValidationError("domain_cannot_add_xmpp_upload") @@ -258,6 +261,9 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove from yunohost.utils.ldap import _get_ldap_interface + + if unsubscribe!=0 and unsubscribe!=None: + operation_logger.data_to_redact.append(unsubscribe) # the 'force' here is related to the exception happening in domain_add ... # we don't want to check the domain exists because the ldap add may have diff --git a/src/dyndns.py b/src/dyndns.py index 070090d7f..0baa1d428 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -95,6 +95,7 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): password = Moulinette.prompt( m18n.n("ask_password"), is_password=True, confirm=True ) + operation_logger.data_to_redact.append(password) assert_password_is_strong_enough("admin", password) if _guess_current_dyndns_domain() != (None, None): @@ -207,6 +208,7 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): password = Moulinette.prompt( m18n.n("ask_password"), is_password=True ) + operation_logger.data_to_redact.append(password) assert_password_is_strong_enough("admin", password) operation_logger.start() From 0084ce757c6937449c7f8e2e177a995710089b80 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Wed, 13 Jul 2022 11:34:04 +0200 Subject: [PATCH 33/54] Don't ask for the (un)subscribe options if this is a sub-subdomain --- src/domain.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/domain.py b/src/domain.py index a107c7635..d6a3aa095 100644 --- a/src/domain.py +++ b/src/domain.py @@ -93,9 +93,6 @@ def domain_list(exclude_subdomains=False,auto_push=False,full=False): result_list = sorted(result_list, key=cmp_domain) - if exclude_subdomains: - return {"domains": result_list, "main": _get_maindomain()} - if full: for i in range(len(result_list)): domain = result_list[i] @@ -178,8 +175,8 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): # Non-latin characters (e.g. café.com => xn--caf-dma.com) domain = domain.encode("idna").decode("utf-8") - # DynDNS domain - dyndns = is_yunohost_dyndns_domain(domain) + # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) + dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split("."))==3 if dyndns: if ((subscribe==None) == (no_subscribe==False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") @@ -325,8 +322,8 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu apps="\n".join([x[1] for x in apps_on_that_domain]), ) - # DynDNS domain - dyndns = is_yunohost_dyndns_domain(domain) + # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) + dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split("."))==3 if dyndns: if ((unsubscribe==None) == (no_unsubscribe==False)): raise YunohostValidationError("domain_dyndns_instruction_unclear_unsubscribe") From 02103c07a33d6242d6852b5cbbffd8c962e95891 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Wed, 13 Jul 2022 11:50:20 +0200 Subject: [PATCH 34/54] Don't flag sub-DynDNS domains as subscribed --- src/domain.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/domain.py b/src/domain.py index d6a3aa095..b1544d46a 100644 --- a/src/domain.py +++ b/src/domain.py @@ -96,7 +96,8 @@ def domain_list(exclude_subdomains=False,auto_push=False,full=False): if full: for i in range(len(result_list)): domain = result_list[i] - result_list[i] = {'name':domain,'isdyndns': is_yunohost_dyndns_domain(domain)} + dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split("."))==3 + result_list[i] = {'name':domain,'isdyndns': dyndns} result = {"domains": result_list, "main": _get_maindomain()} From f015767f508d9ab2ba26216cb36ffb8981c24568 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 15 Jul 2022 13:21:16 +0200 Subject: [PATCH 35/54] Added some tests for subscribing and unsubscribing --- src/tests/test_domains.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/tests/test_domains.py b/src/tests/test_domains.py index 95a33e0ba..6aab9f241 100644 --- a/src/tests/test_domains.py +++ b/src/tests/test_domains.py @@ -1,7 +1,9 @@ import pytest import os +import random from moulinette.core import MoulinetteError +from yunohost.utils.dns import is_yunohost_dyndns_domain from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.domain import ( @@ -16,6 +18,8 @@ from yunohost.domain import ( ) TEST_DOMAINS = ["example.tld", "sub.example.tld", "other-example.com"] +TEST_DYNDNS_DOMAIN = "".join(chr(random.randint(ord("a"),ord("z"))) for x in range(15))+random.choice([".noho.st",".ynh.fr",".nohost.me"]) +TEST_DYNDNS_PASSWORD = "astrongandcomplicatedpassphrasethatisverysecure" def setup_function(function): @@ -35,9 +39,9 @@ def setup_function(function): # Clear other domains for domain in domains: - if domain not in TEST_DOMAINS or domain == TEST_DOMAINS[2]: + if (domain not in TEST_DOMAINS or domain == TEST_DOMAINS[2]) and domain != TEST_DYNDNS_DOMAIN: # Clean domains not used for testing - domain_remove(domain) + domain_remove(domain,no_unsubscribe=is_yunohost_dyndns_domain(domain)) elif domain in TEST_DOMAINS: # Reset settings if any os.system(f"rm -rf {DOMAIN_SETTINGS_DIR}/{domain}.yml") @@ -67,6 +71,12 @@ def test_domain_add(): assert TEST_DOMAINS[2] in domain_list()["domains"] +def test_domain_add_subscribe(): + assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] + domain_add(TEST_DYNDNS_DOMAIN,subscribe=TEST_DYNDNS_PASSWORD) + assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] + + def test_domain_add_existing_domain(): with pytest.raises(MoulinetteError): assert TEST_DOMAINS[1] in domain_list()["domains"] @@ -79,6 +89,12 @@ def test_domain_remove(): assert TEST_DOMAINS[1] not in domain_list()["domains"] +def test_domain_remove_unsubscribe(): + assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] + domain_remove(TEST_DYNDNS_DOMAIN,unsubscribe=TEST_DYNDNS_PASSWORD) + assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] + + def test_main_domain(): current_main_domain = _get_maindomain() assert domain_main_domain()["current_main_domain"] == current_main_domain From 863843a1cf21919a72f286dc875930c0100bcffd Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 15 Jul 2022 14:26:21 +0200 Subject: [PATCH 36/54] The maximum number of subscribed DynDNS domains is configurable --- src/domain.py | 4 +- src/dyndns.py | 107 ++++++++++++++++++++++---------------------------- 2 files changed, 50 insertions(+), 61 deletions(-) diff --git a/src/domain.py b/src/domain.py index b1544d46a..bc04dd523 100644 --- a/src/domain.py +++ b/src/domain.py @@ -182,10 +182,10 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): if ((subscribe==None) == (no_subscribe==False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") - from yunohost.dyndns import _guess_current_dyndns_domain + from yunohost.dyndns import is_subscribing_allowed # Do not allow to subscribe to multiple dyndns domains... - if _guess_current_dyndns_domain() != (None, None): + if not is_subscribing_allowed(): raise YunohostValidationError("domain_dyndns_already_subscribed") operation_logger.start() diff --git a/src/dyndns.py b/src/dyndns.py index 0baa1d428..a12ef3355 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -48,6 +48,16 @@ logger = getActionLogger("yunohost.dyndns") DYNDNS_PROVIDER = "dyndns.yunohost.org" DYNDNS_DNS_AUTH = ["ns0.yunohost.org", "ns1.yunohost.org"] +MAX_DYNDNS_DOMAINS = 1 + +def is_subscribing_allowed(): + """ + Check if the limit of subscribed DynDNS domains has been reached + + Returns: + True if the limit is not reached, False otherwise + """ + return len(glob.glob("/etc/yunohost/dyndns/*.key"))[^\s\+]+)\.\+165.+\.key$") - - # Retrieve the first registered domain - paths = list(glob.iglob("/etc/yunohost/dyndns/K*.key")) - for path in paths: - match = DYNDNS_KEY_REGEX.match(path) - if not match: - continue - _domain = match.group("domain") - - # Verify if domain is registered (i.e., if it's available, skip - # current domain beause that's not the one we want to update..) - # If there's only 1 such key found, then avoid doing the request - # for nothing (that's very probably the one we want to find ...) - if len(paths) > 1 and _dyndns_available(_domain): - continue - else: - return (_domain, path) - - return (None, None) From eb3c3916242e402e7622534530958f069541e42f Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 15 Jul 2022 14:33:11 +0200 Subject: [PATCH 37/54] Removed useless argument --- .gitlab/ci/install.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/ci/install.gitlab-ci.yml b/.gitlab/ci/install.gitlab-ci.yml index e2662e9e2..335e07eb6 100644 --- a/.gitlab/ci/install.gitlab-ci.yml +++ b/.gitlab/ci/install.gitlab-ci.yml @@ -26,4 +26,4 @@ install-postinstall: script: - apt-get update -o Acquire::Retries=3 - DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb - - yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns --force-diskspace + - yunohost tools postinstall -d domain.tld -p the_password --force-diskspace From 5f2785c6c931e4990a2859125922451f2691cc92 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 15 Jul 2022 15:11:45 +0200 Subject: [PATCH 38/54] Pleasing the linter --- src/dns.py | 19 ++++++++--------- src/domain.py | 36 +++++++++++++++++--------------- src/dyndns.py | 43 ++++++++++++++++++++++----------------- src/tests/test_domains.py | 8 ++++---- src/tools.py | 4 ++-- src/utils/config.py | 1 + 6 files changed, 60 insertions(+), 51 deletions(-) diff --git a/src/dns.py b/src/dns.py index 8ba46011e..9b27c18af 100644 --- a/src/dns.py +++ b/src/dns.py @@ -47,7 +47,6 @@ from yunohost.utils.error import YunohostValidationError, YunohostError from yunohost.utils.network import get_public_ip from yunohost.log import is_unit_operation from yunohost.hook import hook_callback -from yunohost.dyndns import dyndns_update logger = getActionLogger("yunohost.domain") @@ -625,18 +624,19 @@ def _get_registar_settings(domain): @is_unit_operation() def domain_dns_push(operation_logger, domains, dry_run=False, force=False, purge=False, auto=False): if auto: - domains = domain_list(exclude_subdomains=True,auto_push=True)["domains"] - elif len(domains)==0: - domains = domain_list(exclude_subdomains=True)["domains"] + domains = domain_list(exclude_subdomains=True, auto_push=True)["domains"] + elif len(domains) == 0: + domains = domain_list(exclude_subdomains=True)["domains"] error_domains = [] for domain in domains: try: - domain_dns_push_unique(domain,dry_run=dry_run,force=force,purge=purge) + domain_dns_push_unique(domain, dry_run=dry_run, force=force, purge=purge) except YunohostError as e: - logger.error(m18n.n("domain_dns_push_failed_domain",domain=domain,error=str(e))) + logger.error(m18n.n("domain_dns_push_failed_domain", domain=domain, error=str(e))) error_domains.append(domain) - if len(error_domains)>0: - raise YunohostError("domain_dns_push_failed_domains",domains=', '.join(error_domains)) + if len(error_domains) > 0: + raise YunohostError("domain_dns_push_failed_domains", domains=', '.join(error_domains)) + @is_unit_operation() def domain_dns_push_unique(operation_logger, domain, dry_run=False, force=False, purge=False): @@ -660,9 +660,8 @@ def domain_dns_push_unique(operation_logger, domain, dry_run=False, force=False, # FIXME: in the future, properly unify this with yunohost dyndns update if registrar == "yunohost": - #logger.info(m18n.n("domain_dns_registrar_yunohost")) from yunohost.dyndns import dyndns_update - dyndns_update(domain=domain,force=force) + dyndns_update(domain=domain, force=force) return {} if registrar == "parent_domain": diff --git a/src/domain.py b/src/domain.py index bc04dd523..4c4ed3472 100644 --- a/src/domain.py +++ b/src/domain.py @@ -52,7 +52,7 @@ DOMAIN_SETTINGS_DIR = "/etc/yunohost/domains" domain_list_cache: Dict[str, Any] = {} -def domain_list(exclude_subdomains=False,auto_push=False,full=False): +def domain_list(exclude_subdomains=False, auto_push=False, full=False): """ List domains @@ -96,11 +96,11 @@ def domain_list(exclude_subdomains=False,auto_push=False,full=False): if full: for i in range(len(result_list)): domain = result_list[i] - dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split("."))==3 - result_list[i] = {'name':domain,'isdyndns': dyndns} + dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 + result_list[i] = {'name': domain, 'isdyndns': dyndns} result = {"domains": result_list, "main": _get_maindomain()} - + # Cache answer only if not using exclude_subdomains or full if not (full or exclude_subdomains): domain_list_cache = result @@ -156,7 +156,7 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): from yunohost.utils.ldap import _get_ldap_interface from yunohost.certificate import _certificate_install_selfsigned - if subscribe!=0 and subscribe!=None: + if subscribe != 0 and subscribe is not None: operation_logger.data_to_redact.append(subscribe) if domain.startswith("xmpp-upload."): @@ -177,9 +177,9 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): domain = domain.encode("idna").decode("utf-8") # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) - dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split("."))==3 + dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if ((subscribe==None) == (no_subscribe==False)): + if ((subscribe is None) == (no_subscribe is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") from yunohost.dyndns import is_subscribing_allowed @@ -194,7 +194,7 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): if dyndns and not no_subscribe: # Actually subscribe - domain_dyndns_subscribe(domain=domain,password=subscribe) + domain_dyndns_subscribe(domain=domain, password=subscribe) _certificate_install_selfsigned([domain], True) @@ -244,7 +244,7 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): @is_unit_operation() -def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsubscribe=None,no_unsubscribe=False): +def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsubscribe=None, no_unsubscribe=False): """ Delete domains @@ -259,8 +259,8 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove from yunohost.utils.ldap import _get_ldap_interface - - if unsubscribe!=0 and unsubscribe!=None: + + if unsubscribe != 0 and unsubscribe is not None: operation_logger.data_to_redact.append(unsubscribe) # the 'force' here is related to the exception happening in domain_add ... @@ -322,16 +322,16 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu "domain_uninstall_app_first", apps="\n".join([x[1] for x in apps_on_that_domain]), ) - + # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) - dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split("."))==3 + dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if ((unsubscribe==None) == (no_unsubscribe==False)): + if ((unsubscribe is None) == (no_unsubscribe is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear_unsubscribe") operation_logger.start() - if not dyndns and (unsubscribe!=None or no_unsubscribe!=False): + if not dyndns and ((unsubscribe is not None) or (no_unsubscribe is not False)): logger.warning("This domain is not a DynDNS one, no need for the --unsubscribe or --no-unsubscribe option") ldap = _get_ldap_interface() @@ -381,7 +381,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu # If a password is provided, delete the DynDNS record if dyndns and not no_unsubscribe: # Actually unsubscribe - domain_dyndns_unsubscribe(domain=domain,password=unsubscribe) + domain_dyndns_unsubscribe(domain=domain, password=unsubscribe) logger.success(m18n.n("domain_deleted")) @@ -394,6 +394,7 @@ def domain_dyndns_subscribe(**kwargs): dyndns_subscribe(**kwargs) + def domain_dyndns_unsubscribe(**kwargs): """ Unsubscribe from a DynDNS domain @@ -402,6 +403,7 @@ def domain_dyndns_unsubscribe(**kwargs): dyndns_unsubscribe(**kwargs) + def domain_dyndns_list(): """ Returns all currently subscribed DynDNS domains @@ -410,6 +412,7 @@ def domain_dyndns_list(): return dyndns_list() + def domain_dyndns_update(**kwargs): """ Update a DynDNS domain @@ -471,6 +474,7 @@ def domain_url_available(domain, path): return len(_get_conflicting_apps(domain, path)) == 0 + def _get_maindomain(): with open("/etc/yunohost/current_host", "r") as f: maindomain = f.readline().rstrip() diff --git a/src/dyndns.py b/src/dyndns.py index a12ef3355..a324b35a5 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -24,7 +24,6 @@ Subscribe and Update DynDNS Hosts """ import os -import re import json import glob import base64 @@ -50,6 +49,7 @@ DYNDNS_PROVIDER = "dyndns.yunohost.org" DYNDNS_DNS_AUTH = ["ns0.yunohost.org", "ns1.yunohost.org"] MAX_DYNDNS_DOMAINS = 1 + def is_subscribing_allowed(): """ Check if the limit of subscribed DynDNS domains has been reached @@ -57,7 +57,7 @@ def is_subscribing_allowed(): Returns: True if the limit is not reached, False otherwise """ - return len(glob.glob("/etc/yunohost/dyndns/*.key")) Date: Fri, 15 Jul 2022 15:29:04 +0200 Subject: [PATCH 39/54] Let the dynette cool down --- src/tests/test_domains.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tests/test_domains.py b/src/tests/test_domains.py index 360a3b81f..cbdd412a7 100644 --- a/src/tests/test_domains.py +++ b/src/tests/test_domains.py @@ -1,5 +1,6 @@ import pytest import os +import time import random from moulinette.core import MoulinetteError @@ -18,7 +19,7 @@ from yunohost.domain import ( ) TEST_DOMAINS = ["example.tld", "sub.example.tld", "other-example.com"] -TEST_DYNDNS_DOMAIN = "".join(chr(random.randint(ord("a"), ord("z"))) for x in range(15)) + random.choice([".noho.st", ".ynh.fr", ".nohost.me"]) +TEST_DYNDNS_DOMAIN = "ci-test-" + "".join(chr(random.randint(ord("a"), ord("z"))) for x in range(12)) + random.choice([".noho.st", ".ynh.fr", ".nohost.me"]) TEST_DYNDNS_PASSWORD = "astrongandcomplicatedpassphrasethatisverysecure" @@ -72,6 +73,8 @@ def test_domain_add(): def test_domain_add_subscribe(): + + time.sleep(35) # Dynette blocks requests that happen too frequently assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] domain_add(TEST_DYNDNS_DOMAIN, subscribe=TEST_DYNDNS_PASSWORD) assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] @@ -90,6 +93,8 @@ def test_domain_remove(): def test_domain_remove_unsubscribe(): + + time.sleep(35) # Dynette blocks requests that happen too frequently assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] domain_remove(TEST_DYNDNS_DOMAIN, unsubscribe=TEST_DYNDNS_PASSWORD) assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] From 4f303de7a48107b1524918f9b39227009ae5ab83 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 15 Jul 2022 15:56:33 +0200 Subject: [PATCH 40/54] Removed the useless argument `key` from dyndns_subscribe --- share/actionsmap.yml | 6 ------ src/dyndns.py | 36 +++++++++++++++++------------------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 1155e6697..e3d315996 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -603,9 +603,6 @@ domain: help: Full domain to subscribe with extra: pattern: *pattern_domain - -k: - full: --key - help: Public DNS key -p: full: --password nargs: "?" @@ -1480,9 +1477,6 @@ dyndns: help: Full domain to subscribe with ( deprecated, use 'yunohost domain dyndns subscribe' instead ) extra: pattern: *pattern_domain - -k: - full: --key - help: Public DNS key -p: full: --password nargs: "?" diff --git a/src/dyndns.py b/src/dyndns.py index a324b35a5..6b64c1e78 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -86,13 +86,12 @@ def _dyndns_available(domain): @is_unit_operation() -def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): +def dyndns_subscribe(operation_logger, domain=None, password=None): """ Subscribe to a DynDNS service Keyword argument: domain -- Full domain to subscribe with - key -- TSIG Shared DNS key password -- Password that will be used to delete the domain """ @@ -133,29 +132,28 @@ def dyndns_subscribe(operation_logger, domain=None, key=None, password=None): # '1234' is idk? doesnt matter, but the old format contained a number here... key_file = f"/etc/yunohost/dyndns/K{domain}.+165+1234.key" - if key is None: - if not os.path.exists("/etc/yunohost/dyndns"): - os.makedirs("/etc/yunohost/dyndns") + if not os.path.exists("/etc/yunohost/dyndns"): + os.makedirs("/etc/yunohost/dyndns") - logger.debug(m18n.n("dyndns_key_generating")) + logger.debug(m18n.n("dyndns_key_generating")) - # Here, we emulate the behavior of the old 'dnssec-keygen' utility - # which since bullseye was replaced by ddns-keygen which is now - # in the bind9 package ... but installing bind9 will conflict with dnsmasq - # and is just madness just to have access to a tsig keygen utility -.- + # Here, we emulate the behavior of the old 'dnssec-keygen' utility + # which since bullseye was replaced by ddns-keygen which is now + # in the bind9 package ... but installing bind9 will conflict with dnsmasq + # and is just madness just to have access to a tsig keygen utility -.- - # Use 512 // 8 = 64 bytes for hmac-sha512 (c.f. https://git.hactrn.net/sra/tsig-keygen/src/master/tsig-keygen.py) - secret = base64.b64encode(os.urandom(512 // 8)).decode("ascii") + # Use 512 // 8 = 64 bytes for hmac-sha512 (c.f. https://git.hactrn.net/sra/tsig-keygen/src/master/tsig-keygen.py) + secret = base64.b64encode(os.urandom(512 // 8)).decode("ascii") - # Idk why but the secret is split in two parts, with the first one - # being 57-long char ... probably some DNS format - secret = f"{secret[:56]} {secret[56:]}" + # Idk why but the secret is split in two parts, with the first one + # being 57-long char ... probably some DNS format + secret = f"{secret[:56]} {secret[56:]}" - key_content = f"{domain}. IN KEY 0 3 165 {secret}" - write_to_file(key_file, key_content) + key_content = f"{domain}. IN KEY 0 3 165 {secret}" + write_to_file(key_file, key_content) - chmod("/etc/yunohost/dyndns", 0o600, recursive=True) - chown("/etc/yunohost/dyndns", "root", recursive=True) + chmod("/etc/yunohost/dyndns", 0o600, recursive=True) + chown("/etc/yunohost/dyndns", "root", recursive=True) import requests # lazy loading this module for performance reasons From bbc6dcc50b5101d8fdab04c91f38a18b4dde9966 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 15 Jul 2022 16:30:57 +0200 Subject: [PATCH 41/54] Better logging for `domain dns push --auto` --- src/dns.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dns.py b/src/dns.py index 9b27c18af..44547d412 100644 --- a/src/dns.py +++ b/src/dns.py @@ -621,8 +621,7 @@ def _get_registar_settings(domain): return registrar, settings -@is_unit_operation() -def domain_dns_push(operation_logger, domains, dry_run=False, force=False, purge=False, auto=False): +def domain_dns_push(domains, dry_run=False, force=False, purge=False, auto=False): if auto: domains = domain_list(exclude_subdomains=True, auto_push=True)["domains"] elif len(domains) == 0: From e21c114b70202ab518f3b57c49a7a024e6d16961 Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 15 Jul 2022 16:54:53 +0200 Subject: [PATCH 42/54] Better log redacting --- src/domain.py | 4 ++-- src/dyndns.py | 2 -- src/tools.py | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/domain.py b/src/domain.py index 4c4ed3472..f49f96e46 100644 --- a/src/domain.py +++ b/src/domain.py @@ -140,7 +140,7 @@ def _get_parent_domain_of(domain): return _get_parent_domain_of(parent_domain) -@is_unit_operation() +@is_unit_operation(exclude=["subscribe"]) def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): """ Create a custom domain @@ -243,7 +243,7 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): logger.success(m18n.n("domain_created")) -@is_unit_operation() +@is_unit_operation(exclude=["unsubscribe"]) def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsubscribe=None, no_unsubscribe=False): """ Delete domains diff --git a/src/dyndns.py b/src/dyndns.py index 6b64c1e78..9fd25442c 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -106,7 +106,6 @@ def dyndns_subscribe(operation_logger, domain=None, password=None): is_password=True, confirm=True ) - operation_logger.data_to_redact.append(password) assert_password_is_strong_enough("admin", password) if not is_subscribing_allowed(): @@ -218,7 +217,6 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): m18n.n("ask_password"), is_password=True ) - operation_logger.data_to_redact.append(password) assert_password_is_strong_enough("admin", password) operation_logger.start() diff --git a/src/tools.py b/src/tools.py index b77279208..032bbea9f 100644 --- a/src/tools.py +++ b/src/tools.py @@ -180,12 +180,11 @@ def _detect_virt(): return out.split()[0] -@is_unit_operation() +@is_unit_operation(exclude=["subscribe","password"]) def tools_postinstall( operation_logger, domain, password, - ignore_dyndns=False, subscribe=None, no_subscribe=False, force_password=False, From 129f5cce9537fc9cbbbdd804091f1cdb662983ff Mon Sep 17 00:00:00 2001 From: "theo@manjaro" Date: Fri, 15 Jul 2022 16:57:12 +0200 Subject: [PATCH 43/54] Linter fixes --- src/tests/test_domains.py | 6 +++--- src/tools.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tests/test_domains.py b/src/tests/test_domains.py index cbdd412a7..272f5bb4d 100644 --- a/src/tests/test_domains.py +++ b/src/tests/test_domains.py @@ -73,8 +73,8 @@ def test_domain_add(): def test_domain_add_subscribe(): - - time.sleep(35) # Dynette blocks requests that happen too frequently + + time.sleep(35) # Dynette blocks requests that happen too frequently assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] domain_add(TEST_DYNDNS_DOMAIN, subscribe=TEST_DYNDNS_PASSWORD) assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] @@ -94,7 +94,7 @@ def test_domain_remove(): def test_domain_remove_unsubscribe(): - time.sleep(35) # Dynette blocks requests that happen too frequently + time.sleep(35) # Dynette blocks requests that happen too frequently assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] domain_remove(TEST_DYNDNS_DOMAIN, unsubscribe=TEST_DYNDNS_PASSWORD) assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] diff --git a/src/tools.py b/src/tools.py index 032bbea9f..6dcc262ad 100644 --- a/src/tools.py +++ b/src/tools.py @@ -180,7 +180,7 @@ def _detect_virt(): return out.split()[0] -@is_unit_operation(exclude=["subscribe","password"]) +@is_unit_operation(exclude=["subscribe", "password"]) def tools_postinstall( operation_logger, domain, From a21567b27dda8b6b11f2fa683f41221c65b83a0e Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 8 Jan 2023 00:35:34 +0100 Subject: [PATCH 44/54] [enh] Semantic --- locales/en.json | 1 + share/actionsmap.yml | 9 +++----- src/domain.py | 48 ++++++++++++++++++++++++--------------- src/tests/test_domains.py | 2 +- src/tools.py | 8 +++---- 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/locales/en.json b/locales/en.json index b8d22bbfa..432462708 100644 --- a/locales/en.json +++ b/locales/en.json @@ -73,6 +73,7 @@ "ask_new_domain": "New domain", "ask_new_path": "New path", "ask_password": "Password", + "ask_dyndns_recovery_password": "DynDNS recovey password", "ask_user_domain": "Domain to use for the user's email address and XMPP account", "backup_abstract_method": "This backup method has yet to be implemented", "backup_actually_backuping": "Creating a backup archive from the collected files...", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index e3d315996..e437a812b 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -463,8 +463,7 @@ domain: full: --no-subscribe help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true - -s: - full: --subscribe + --dyndns-password-recovery: metavar: PASSWORD nargs: "?" const: 0 @@ -494,8 +493,7 @@ domain: full: --no-unsubscribe help: If removing a DynDNS domain, only remove the domain, without unsubscribing from the DynDNS service action: store_true - -u: - full: --unsubscribe + --dyndns-password-recovery: metavar: PASSWORD nargs: "?" const: 0 @@ -1582,8 +1580,7 @@ tools: full: --no-subscribe help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true - -s: - full: --subscribe + --dyndns-password-recovery: metavar: PASSWORD nargs: "?" const: 0 diff --git a/src/domain.py b/src/domain.py index f49f96e46..f6b99b5cb 100644 --- a/src/domain.py +++ b/src/domain.py @@ -140,24 +140,25 @@ def _get_parent_domain_of(domain): return _get_parent_domain_of(parent_domain) -@is_unit_operation(exclude=["subscribe"]) -def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): +@is_unit_operation(exclude=["dyndns_password_recovery"]) +def domain_add(operation_logger, domain, dyndns_password_recovery=None, no_subscribe=False): """ Create a custom domain Keyword argument: domain -- Domain name to add dyndns -- Subscribe to DynDNS - subscribe -- Password used to later unsubscribe from DynDNS + dyndns_password_recovery -- Password used to later unsubscribe from DynDNS no_unsubscribe -- If we want to just add the DynDNS domain to the list, without subscribing """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf from yunohost.utils.ldap import _get_ldap_interface + from yunohost.utils.password import assert_password_is_strong_enough from yunohost.certificate import _certificate_install_selfsigned - if subscribe != 0 and subscribe is not None: - operation_logger.data_to_redact.append(subscribe) + if dyndns_password_recovery != 0 and dyndns_password_recovery is not None: + operation_logger.data_to_redact.append(dyndns_password_recovery) if domain.startswith("xmpp-upload."): raise YunohostValidationError("domain_cannot_add_xmpp_upload") @@ -179,7 +180,18 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if ((subscribe is None) == (no_subscribe is False)): + if not no_subscribe and not dyndns_password_recovery: + if Moulinette.interface.type == "api": + raise YunohostValidationError("domain_dyndns_missing_password") + else: + dyndns_password_recovery = Moulinette.prompt( + m18n.n("ask_dyndns_recovery_password"), is_password=True, confirm=True + ) + + # Ensure sufficiently complex password + assert_password_is_strong_enough("admin", dyndns_password_recovery) + + if ((dyndns_password_recovery is None) == (no_subscribe is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") from yunohost.dyndns import is_subscribing_allowed @@ -189,12 +201,12 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): raise YunohostValidationError("domain_dyndns_already_subscribed") operation_logger.start() - if not dyndns and (subscribe is not None or no_subscribe): - logger.warning("This domain is not a DynDNS one, no need for the --subscribe or --no-subscribe option") + if not dyndns and (dyndns_password_recovery is not None or no_subscribe): + logger.warning("This domain is not a DynDNS one, no need for the --dyndns-password-recovery or --no-subscribe option") if dyndns and not no_subscribe: # Actually subscribe - domain_dyndns_subscribe(domain=domain, password=subscribe) + domain_dyndns_subscribe(domain=domain, password=dyndns_password_recovery) _certificate_install_selfsigned([domain], True) @@ -243,8 +255,8 @@ def domain_add(operation_logger, domain, subscribe=None, no_subscribe=False): logger.success(m18n.n("domain_created")) -@is_unit_operation(exclude=["unsubscribe"]) -def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsubscribe=None, no_unsubscribe=False): +@is_unit_operation(exclude=["dyndns_password_recovery"]) +def domain_remove(operation_logger, domain, remove_apps=False, force=False, dyndns_password_recovery=None, no_unsubscribe=False): """ Delete domains @@ -253,15 +265,15 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu remove_apps -- Remove applications installed on the domain force -- Force the domain removal and don't not ask confirmation to remove apps if remove_apps is specified - unsubscribe -- Recovery password used at the creation of the DynDNS domain + dyndns_password_recovery -- Recovery password used at the creation of the DynDNS domain no_unsubscribe -- If we just remove the DynDNS domain, without unsubscribing """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove from yunohost.utils.ldap import _get_ldap_interface - if unsubscribe != 0 and unsubscribe is not None: - operation_logger.data_to_redact.append(unsubscribe) + if dyndns_password_recovery != 0 and dyndns_password_recovery is not None: + operation_logger.data_to_redact.append(dyndns_password_recovery) # the 'force' here is related to the exception happening in domain_add ... # we don't want to check the domain exists because the ldap add may have @@ -326,13 +338,13 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if ((unsubscribe is None) == (no_unsubscribe is False)): + if ((dyndns_password_recovery is None) == (no_unsubscribe is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear_unsubscribe") operation_logger.start() - if not dyndns and ((unsubscribe is not None) or (no_unsubscribe is not False)): - logger.warning("This domain is not a DynDNS one, no need for the --unsubscribe or --no-unsubscribe option") + if not dyndns and ((dyndns_password_recovery is not None) or (no_unsubscribe is not False)): + logger.warning("This domain is not a DynDNS one, no need for the --dyndns_password_recovery or --no-unsubscribe option") ldap = _get_ldap_interface() try: @@ -381,7 +393,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, unsu # If a password is provided, delete the DynDNS record if dyndns and not no_unsubscribe: # Actually unsubscribe - domain_dyndns_unsubscribe(domain=domain, password=unsubscribe) + domain_dyndns_unsubscribe(domain=domain, password=dyndns_password_recovery) logger.success(m18n.n("domain_deleted")) diff --git a/src/tests/test_domains.py b/src/tests/test_domains.py index 272f5bb4d..e09c3534b 100644 --- a/src/tests/test_domains.py +++ b/src/tests/test_domains.py @@ -76,7 +76,7 @@ def test_domain_add_subscribe(): time.sleep(35) # Dynette blocks requests that happen too frequently assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] - domain_add(TEST_DYNDNS_DOMAIN, subscribe=TEST_DYNDNS_PASSWORD) + domain_add(TEST_DYNDNS_DOMAIN, dyndns_password_recovery=TEST_DYNDNS_PASSWORD) assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] diff --git a/src/tools.py b/src/tools.py index 6dcc262ad..21262c64b 100644 --- a/src/tools.py +++ b/src/tools.py @@ -180,12 +180,12 @@ def _detect_virt(): return out.split()[0] -@is_unit_operation(exclude=["subscribe", "password"]) +@is_unit_operation(exclude=["dyndns_password_recovery", "password"]) def tools_postinstall( operation_logger, domain, password, - subscribe=None, + dyndns_password_recovery=None, no_subscribe=False, force_password=False, force_diskspace=False, @@ -232,7 +232,7 @@ def tools_postinstall( # If this is a nohost.me/noho.st, actually check for availability if is_yunohost_dyndns_domain(domain): - if ((subscribe is None) == (no_subscribe is False)): + if ((dyndns_password_recovery is None) == (no_subscribe is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") # Check if the domain is available... @@ -257,7 +257,7 @@ def tools_postinstall( logger.info(m18n.n("yunohost_installing")) # New domain config - domain_add(domain, subscribe=subscribe, no_subscribe=no_subscribe) + domain_add(domain, dyndns_password_recovery=dyndns_password_recovery, no_subscribe=no_subscribe) domain_main_domain(domain) # Update LDAP admin and create home dir From 1e0fe76672e9d72ee1e60e5c58f8cf2e8c7810b5 Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 10 Jan 2023 10:25:30 +0100 Subject: [PATCH 45/54] [fix] Test --- src/tests/test_domains.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/test_domains.py b/src/tests/test_domains.py index e09c3534b..b221d688e 100644 --- a/src/tests/test_domains.py +++ b/src/tests/test_domains.py @@ -96,7 +96,7 @@ def test_domain_remove_unsubscribe(): time.sleep(35) # Dynette blocks requests that happen too frequently assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] - domain_remove(TEST_DYNDNS_DOMAIN, unsubscribe=TEST_DYNDNS_PASSWORD) + domain_remove(TEST_DYNDNS_DOMAIN, dyndns_password_recovery=TEST_DYNDNS_PASSWORD) assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] From d67e23167897ca3307e2dad89dda86fc824a1514 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Apr 2023 14:48:44 +0200 Subject: [PATCH 46/54] dydns-password-recovery -> dyndns-recovery-password --- share/actionsmap.yml | 6 +++--- src/domain.py | 42 +++++++++++++++++++-------------------- src/tests/test_domains.py | 4 ++-- src/tools.py | 8 ++++---- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 6e60655d0..294d00881 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -510,7 +510,7 @@ domain: full: --no-subscribe help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true - --dyndns-password-recovery: + --dyndns-recovery-password: metavar: PASSWORD nargs: "?" const: 0 @@ -540,7 +540,7 @@ domain: full: --no-unsubscribe help: If removing a DynDNS domain, only remove the domain, without unsubscribing from the DynDNS service action: store_true - --dyndns-password-recovery: + --dyndns-recovery-password: metavar: PASSWORD nargs: "?" const: 0 @@ -1692,7 +1692,7 @@ tools: full: --no-subscribe help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true - --dyndns-password-recovery: + --dyndns-recovery-password: metavar: PASSWORD nargs: "?" const: 0 diff --git a/src/domain.py b/src/domain.py index 67f39aa71..4301f9ab1 100644 --- a/src/domain.py +++ b/src/domain.py @@ -213,15 +213,15 @@ def _get_parent_domain_of(domain, return_self=False, topest=False): return domain if return_self else None -@is_unit_operation(exclude=["dyndns_password_recovery"]) -def domain_add(operation_logger, domain, dyndns_password_recovery=None, no_subscribe=False): +@is_unit_operation(exclude=["dyndns_recovery_password"]) +def domain_add(operation_logger, domain, dyndns_recovery_password=None, no_subscribe=False): """ Create a custom domain Keyword argument: domain -- Domain name to add dyndns -- Subscribe to DynDNS - dyndns_password_recovery -- Password used to later unsubscribe from DynDNS + dyndns_recovery_password -- Password used to later unsubscribe from DynDNS no_unsubscribe -- If we want to just add the DynDNS domain to the list, without subscribing """ from yunohost.hook import hook_callback @@ -230,8 +230,8 @@ def domain_add(operation_logger, domain, dyndns_password_recovery=None, no_subsc from yunohost.utils.password import assert_password_is_strong_enough from yunohost.certificate import _certificate_install_selfsigned - if dyndns_password_recovery != 0 and dyndns_password_recovery is not None: - operation_logger.data_to_redact.append(dyndns_password_recovery) + if dyndns_recovery_password != 0 and dyndns_recovery_password is not None: + operation_logger.data_to_redact.append(dyndns_recovery_password) if domain.startswith("xmpp-upload."): raise YunohostValidationError("domain_cannot_add_xmpp_upload") @@ -256,18 +256,18 @@ def domain_add(operation_logger, domain, dyndns_password_recovery=None, no_subsc # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if not no_subscribe and not dyndns_password_recovery: + if not no_subscribe and not dyndns_recovery_password: if Moulinette.interface.type == "api": raise YunohostValidationError("domain_dyndns_missing_password") else: - dyndns_password_recovery = Moulinette.prompt( + dyndns_recovery_password = Moulinette.prompt( m18n.n("ask_dyndns_recovery_password"), is_password=True, confirm=True ) # Ensure sufficiently complex password - assert_password_is_strong_enough("admin", dyndns_password_recovery) + assert_password_is_strong_enough("admin", dyndns_recovery_password) - if ((dyndns_password_recovery is None) == (no_subscribe is False)): + if ((dyndns_recovery_password is None) == (no_subscribe is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") from yunohost.dyndns import is_subscribing_allowed @@ -277,12 +277,12 @@ def domain_add(operation_logger, domain, dyndns_password_recovery=None, no_subsc raise YunohostValidationError("domain_dyndns_already_subscribed") operation_logger.start() - if not dyndns and (dyndns_password_recovery is not None or no_subscribe): - logger.warning("This domain is not a DynDNS one, no need for the --dyndns-password-recovery or --no-subscribe option") + if not dyndns and (dyndns_recovery_password is not None or no_subscribe): + logger.warning("This domain is not a DynDNS one, no need for the --dyndns-recovery-password or --no-subscribe option") if dyndns and not no_subscribe: # Actually subscribe - domain_dyndns_subscribe(domain=domain, password=dyndns_password_recovery) + domain_dyndns_subscribe(domain=domain, password=dyndns_recovery_password) _certificate_install_selfsigned([domain], True) @@ -331,8 +331,8 @@ def domain_add(operation_logger, domain, dyndns_password_recovery=None, no_subsc logger.success(m18n.n("domain_created")) -@is_unit_operation(exclude=["dyndns_password_recovery"]) -def domain_remove(operation_logger, domain, remove_apps=False, force=False, dyndns_password_recovery=None, no_unsubscribe=False): +@is_unit_operation(exclude=["dyndns_recovery_password"]) +def domain_remove(operation_logger, domain, remove_apps=False, force=False, dyndns_recovery_password=None, no_unsubscribe=False): """ Delete domains @@ -341,15 +341,15 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd remove_apps -- Remove applications installed on the domain force -- Force the domain removal and don't not ask confirmation to remove apps if remove_apps is specified - dyndns_password_recovery -- Recovery password used at the creation of the DynDNS domain + dyndns_recovery_password -- Recovery password used at the creation of the DynDNS domain no_unsubscribe -- If we just remove the DynDNS domain, without unsubscribing """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove from yunohost.utils.ldap import _get_ldap_interface - if dyndns_password_recovery != 0 and dyndns_password_recovery is not None: - operation_logger.data_to_redact.append(dyndns_password_recovery) + if dyndns_recovery_password != 0 and dyndns_recovery_password is not None: + operation_logger.data_to_redact.append(dyndns_recovery_password) # the 'force' here is related to the exception happening in domain_add ... # we don't want to check the domain exists because the ldap add may have @@ -414,13 +414,13 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if ((dyndns_password_recovery is None) == (no_unsubscribe is False)): + if ((dyndns_recovery_password is None) == (no_unsubscribe is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear_unsubscribe") operation_logger.start() - if not dyndns and ((dyndns_password_recovery is not None) or (no_unsubscribe is not False)): - logger.warning("This domain is not a DynDNS one, no need for the --dyndns_password_recovery or --no-unsubscribe option") + if not dyndns and ((dyndns_recovery_password is not None) or (no_unsubscribe is not False)): + logger.warning("This domain is not a DynDNS one, no need for the --dyndns_recovery_password or --no-unsubscribe option") ldap = _get_ldap_interface() try: @@ -469,7 +469,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd # If a password is provided, delete the DynDNS record if dyndns and not no_unsubscribe: # Actually unsubscribe - domain_dyndns_unsubscribe(domain=domain, password=dyndns_password_recovery) + domain_dyndns_unsubscribe(domain=domain, password=dyndns_recovery_password) logger.success(m18n.n("domain_deleted")) diff --git a/src/tests/test_domains.py b/src/tests/test_domains.py index b221d688e..43c04bee6 100644 --- a/src/tests/test_domains.py +++ b/src/tests/test_domains.py @@ -76,7 +76,7 @@ def test_domain_add_subscribe(): time.sleep(35) # Dynette blocks requests that happen too frequently assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] - domain_add(TEST_DYNDNS_DOMAIN, dyndns_password_recovery=TEST_DYNDNS_PASSWORD) + domain_add(TEST_DYNDNS_DOMAIN, dyndns_recovery_password=TEST_DYNDNS_PASSWORD) assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] @@ -96,7 +96,7 @@ def test_domain_remove_unsubscribe(): time.sleep(35) # Dynette blocks requests that happen too frequently assert TEST_DYNDNS_DOMAIN in domain_list()["domains"] - domain_remove(TEST_DYNDNS_DOMAIN, dyndns_password_recovery=TEST_DYNDNS_PASSWORD) + domain_remove(TEST_DYNDNS_DOMAIN, dyndns_recovery_password=TEST_DYNDNS_PASSWORD) assert TEST_DYNDNS_DOMAIN not in domain_list()["domains"] diff --git a/src/tools.py b/src/tools.py index 02b4f58d1..512986ff9 100644 --- a/src/tools.py +++ b/src/tools.py @@ -144,14 +144,14 @@ def _set_hostname(hostname, pretty_hostname=None): logger.debug(out) -@is_unit_operation(exclude=["dyndns_password_recovery", "password"]) +@is_unit_operation(exclude=["dyndns_recovery_password", "password"]) def tools_postinstall( operation_logger, domain, username, fullname, password, - dyndns_password_recovery=None, + dyndns_recovery_password=None, no_subscribe=False, force_diskspace=False, ): @@ -194,7 +194,7 @@ def tools_postinstall( # If this is a nohost.me/noho.st, actually check for availability if is_yunohost_dyndns_domain(domain): - if ((dyndns_password_recovery is None) == (no_subscribe is False)): + if ((dyndns_recovery_password is None) == (no_subscribe is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") # Check if the domain is available... @@ -219,7 +219,7 @@ def tools_postinstall( logger.info(m18n.n("yunohost_installing")) # New domain config - domain_add(domain, dyndns_password_recovery=dyndns_password_recovery, no_subscribe=no_subscribe) + domain_add(domain, dyndns_recovery_password=dyndns_recovery_password, no_subscribe=no_subscribe) domain_main_domain(domain) user_create(username, domain, password, admin=True, fullname=fullname) From 81360723cc6284fa61e3668a844d412346322c23 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Apr 2023 15:17:41 +0200 Subject: [PATCH 47/54] dyndns: Misc semantic tweaks... --- locales/en.json | 4 ++-- share/actionsmap.yml | 13 +++++-------- src/domain.py | 26 +++++++++++++------------- src/dyndns.py | 26 +++++++++++++------------- src/tools.py | 7 ++++--- 5 files changed, 37 insertions(+), 39 deletions(-) diff --git a/locales/en.json b/locales/en.json index 21ffdfdc2..8c6636322 100644 --- a/locales/en.json +++ b/locales/en.json @@ -375,8 +375,8 @@ "domain_dns_registrar_supported": "YunoHost automatically detected that this domain is handled by the registrar **{registrar}**. If you want, YunoHost will automatically configure this DNS zone, if you provide it with the appropriate API credentials. You can find documentation on how to obtain your API credentials on this page: https://yunohost.org/registar_api_{registrar}. (You can also manually configure your DNS records following the documentation at https://yunohost.org/dns )", "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost domain dns push DOMAIN' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", - "domain_dyndns_instruction_unclear": "You must choose exactly one of the following options : --subscribe or --no-subscribe", - "domain_dyndns_instruction_unclear_unsubscribe": "You must choose exactly one of the following options : --unsubscribe or --no-unsubscribe", + "domain_dyndns_instruction_unclear": "You must choose exactly one of the following options : --subscribe or --ignore-dyndns", + "domain_dyndns_instruction_unclear_unsubscribe": "You must choose exactly one of the following options : --unsubscribe or --ignore-dyndns", "domain_exists": "The domain already exists", "domain_hostname_failed": "Unable to set new hostname. This might cause an issue later (it might be fine).", "domain_registrar_is_not_configured": "The registrar is not yet configured for domain {domain}.", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 294d00881..3124f3105 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -506,8 +506,7 @@ domain: help: Domain name to add extra: pattern: *pattern_domain - -n: - full: --no-subscribe + --ignore-dyndns: help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true --dyndns-recovery-password: @@ -536,8 +535,7 @@ domain: full: --force help: Do not ask confirmation to remove apps action: store_true - -n: - full: --no-unsubscribe + --ignore-dyndns: help: If removing a DynDNS domain, only remove the domain, without unsubscribing from the DynDNS service action: store_true --dyndns-recovery-password: @@ -662,7 +660,7 @@ domain: extra: pattern: *pattern_domain -p: - full: --password + full: --recovery-password nargs: "?" const: 0 help: Password used to later delete the domain @@ -681,7 +679,7 @@ domain: pattern: *pattern_domain required: True -p: - full: --password + full: --recovery-password nargs: "?" const: 0 help: Password used to delete the domain @@ -1688,8 +1686,7 @@ tools: pattern: *pattern_password required: True comment: good_practices_about_admin_password - -n: - full: --no-subscribe + --ingnore-dyndns: help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true --dyndns-recovery-password: diff --git a/src/domain.py b/src/domain.py index 4301f9ab1..020a707c7 100644 --- a/src/domain.py +++ b/src/domain.py @@ -214,7 +214,7 @@ def _get_parent_domain_of(domain, return_self=False, topest=False): @is_unit_operation(exclude=["dyndns_recovery_password"]) -def domain_add(operation_logger, domain, dyndns_recovery_password=None, no_subscribe=False): +def domain_add(operation_logger, domain, dyndns_recovery_password=None, ignore_dyndns=False): """ Create a custom domain @@ -222,7 +222,7 @@ def domain_add(operation_logger, domain, dyndns_recovery_password=None, no_subsc domain -- Domain name to add dyndns -- Subscribe to DynDNS dyndns_recovery_password -- Password used to later unsubscribe from DynDNS - no_unsubscribe -- If we want to just add the DynDNS domain to the list, without subscribing + ignore_dyndns -- If we want to just add the DynDNS domain to the list, without subscribing """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf @@ -256,7 +256,7 @@ def domain_add(operation_logger, domain, dyndns_recovery_password=None, no_subsc # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if not no_subscribe and not dyndns_recovery_password: + if not ignore_dyndns and not dyndns_recovery_password: if Moulinette.interface.type == "api": raise YunohostValidationError("domain_dyndns_missing_password") else: @@ -267,7 +267,7 @@ def domain_add(operation_logger, domain, dyndns_recovery_password=None, no_subsc # Ensure sufficiently complex password assert_password_is_strong_enough("admin", dyndns_recovery_password) - if ((dyndns_recovery_password is None) == (no_subscribe is False)): + if ((dyndns_recovery_password is None) == (ignore_dyndns is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear") from yunohost.dyndns import is_subscribing_allowed @@ -277,10 +277,10 @@ def domain_add(operation_logger, domain, dyndns_recovery_password=None, no_subsc raise YunohostValidationError("domain_dyndns_already_subscribed") operation_logger.start() - if not dyndns and (dyndns_recovery_password is not None or no_subscribe): - logger.warning("This domain is not a DynDNS one, no need for the --dyndns-recovery-password or --no-subscribe option") + if not dyndns and (dyndns_recovery_password is not None or ignore_dyndns): + logger.warning("This domain is not a DynDNS one, no need for the --dyndns-recovery-password or --ignore-dyndns option") - if dyndns and not no_subscribe: + if dyndns and not ignore_dyndns: # Actually subscribe domain_dyndns_subscribe(domain=domain, password=dyndns_recovery_password) @@ -332,7 +332,7 @@ def domain_add(operation_logger, domain, dyndns_recovery_password=None, no_subsc @is_unit_operation(exclude=["dyndns_recovery_password"]) -def domain_remove(operation_logger, domain, remove_apps=False, force=False, dyndns_recovery_password=None, no_unsubscribe=False): +def domain_remove(operation_logger, domain, remove_apps=False, force=False, dyndns_recovery_password=None, ignore_dyndns=False): """ Delete domains @@ -342,7 +342,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd force -- Force the domain removal and don't not ask confirmation to remove apps if remove_apps is specified dyndns_recovery_password -- Recovery password used at the creation of the DynDNS domain - no_unsubscribe -- If we just remove the DynDNS domain, without unsubscribing + ignore_dyndns -- If we just remove the DynDNS domain, without unsubscribing """ from yunohost.hook import hook_callback from yunohost.app import app_ssowatconf, app_info, app_remove @@ -414,13 +414,13 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if ((dyndns_recovery_password is None) == (no_unsubscribe is False)): + if ((dyndns_recovery_password is None) == (ignore_dyndns is False)): raise YunohostValidationError("domain_dyndns_instruction_unclear_unsubscribe") operation_logger.start() - if not dyndns and ((dyndns_recovery_password is not None) or (no_unsubscribe is not False)): - logger.warning("This domain is not a DynDNS one, no need for the --dyndns_recovery_password or --no-unsubscribe option") + if not dyndns and ((dyndns_recovery_password is not None) or (ignore_dyndns is not False)): + logger.warning("This domain is not a DynDNS one, no need for the --dyndns_recovery_password or --ignore-dyndns option") ldap = _get_ldap_interface() try: @@ -467,7 +467,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd hook_callback("post_domain_remove", args=[domain]) # If a password is provided, delete the DynDNS record - if dyndns and not no_unsubscribe: + if dyndns and not ignore_dyndns: # Actually unsubscribe domain_dyndns_unsubscribe(domain=domain, password=dyndns_recovery_password) diff --git a/src/dyndns.py b/src/dyndns.py index d1049e756..caeef9459 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -79,27 +79,27 @@ def _dyndns_available(domain): @is_unit_operation() -def dyndns_subscribe(operation_logger, domain=None, password=None): +def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): """ Subscribe to a DynDNS service Keyword argument: domain -- Full domain to subscribe with - password -- Password that will be used to delete the domain + recovery_password -- Password that will be used to delete the domain """ - if password is None: + if recovery_password is None: logger.warning(m18n.n('dyndns_no_recovery_password')) else: from yunohost.utils.password import assert_password_is_strong_enough # Ensure sufficiently complex password - if Moulinette.interface.type == "cli" and password == 0: - password = Moulinette.prompt( + if Moulinette.interface.type == "cli" and recovery_password == 0: + recovery_password = Moulinette.prompt( m18n.n("ask_password"), is_password=True, confirm=True ) - assert_password_is_strong_enough("admin", password) + assert_password_is_strong_enough("admin", recovery_password) if not is_subscribing_allowed(): raise YunohostValidationError("domain_dyndns_already_subscribed") @@ -155,7 +155,7 @@ def dyndns_subscribe(operation_logger, domain=None, password=None): b64encoded_key = base64.b64encode(secret.encode()).decode() data = {"subdomain": domain} if password is not None: - data["recovery_password"] = hashlib.sha256((domain + ":" + password.strip()).encode('utf-8')).hexdigest() + data["recovery_password"] = hashlib.sha256((domain + ":" + recovery_password.strip()).encode('utf-8')).hexdigest() r = requests.post( f"https://{DYNDNS_PROVIDER}/key/{b64encoded_key}?key_algo=hmac-sha512", data=data, @@ -193,24 +193,24 @@ def dyndns_subscribe(operation_logger, domain=None, password=None): @is_unit_operation() -def dyndns_unsubscribe(operation_logger, domain, password=None): +def dyndns_unsubscribe(operation_logger, domain, recovery_password=None): """ Unsubscribe from a DynDNS service Keyword argument: domain -- Full domain to unsubscribe with - password -- Password that is used to delete the domain ( defined when subscribing ) + recovery_password -- Password that is used to delete the domain ( defined when subscribing ) """ from yunohost.utils.password import assert_password_is_strong_enough # Ensure sufficiently complex password - if Moulinette.interface.type == "cli" and not password: - password = Moulinette.prompt( + if Moulinette.interface.type == "cli" and not recovery_password: + recovery_password = Moulinette.prompt( m18n.n("ask_password"), is_password=True ) - assert_password_is_strong_enough("admin", password) + assert_password_is_strong_enough("admin", recovery_password) operation_logger.start() @@ -222,7 +222,7 @@ def dyndns_unsubscribe(operation_logger, domain, password=None): # Send delete request try: - secret = str(domain) + ":" + str(password).strip() + secret = str(domain) + ":" + str(recovery_password).strip() r = requests.delete( f"https://{DYNDNS_PROVIDER}/domains/{domain}", data={"recovery_password": hashlib.sha256(secret.encode('utf-8')).hexdigest()}, diff --git a/src/tools.py b/src/tools.py index 512986ff9..c0da7a37b 100644 --- a/src/tools.py +++ b/src/tools.py @@ -152,7 +152,7 @@ def tools_postinstall( fullname, password, dyndns_recovery_password=None, - no_subscribe=False, + ignore_dyndns=False, force_diskspace=False, ): @@ -194,7 +194,8 @@ def tools_postinstall( # If this is a nohost.me/noho.st, actually check for availability if is_yunohost_dyndns_domain(domain): - if ((dyndns_recovery_password is None) == (no_subscribe is False)): + + if (bool(dyndns_recovery_password), ignore_dyndns) in [(True, True), (False, False)]: raise YunohostValidationError("domain_dyndns_instruction_unclear") # Check if the domain is available... @@ -219,7 +220,7 @@ def tools_postinstall( logger.info(m18n.n("yunohost_installing")) # New domain config - domain_add(domain, dyndns_recovery_password=dyndns_recovery_password, no_subscribe=no_subscribe) + domain_add(domain, dyndns_recovery_password=dyndns_recovery_password, ignore_dyndns=ignore_dyndns) domain_main_domain(domain) user_create(username, domain, password, admin=True, fullname=fullname) From 789b1b2af93e330e7a3f0bed51cc98979f46869d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Apr 2023 15:49:52 +0200 Subject: [PATCH 48/54] dyndns: revert changes regarding auto-push ... it's not complete, and the PR about dyndns recovery password is already too complex... --- hooks/conf_regen/01-yunohost | 5 ++--- locales/ca.json | 2 +- locales/de.json | 4 ++-- locales/en.json | 8 ++------ locales/es.json | 4 ++-- locales/eu.json | 4 ++-- locales/fa.json | 2 +- locales/fr.json | 4 ++-- locales/gl.json | 4 ++-- locales/it.json | 4 ++-- locales/uk.json | 4 ++-- locales/zh_Hans.json | 2 +- share/actionsmap.yml | 29 +++-------------------------- share/config_domain.toml | 7 ------- src/dns.py | 18 +----------------- src/domain.py | 5 ++--- src/dyndns.py | 6 +----- src/tools.py | 10 +++++----- 18 files changed, 33 insertions(+), 89 deletions(-) diff --git a/hooks/conf_regen/01-yunohost b/hooks/conf_regen/01-yunohost index a3fd13687..51022a4e5 100755 --- a/hooks/conf_regen/01-yunohost +++ b/hooks/conf_regen/01-yunohost @@ -116,9 +116,8 @@ SHELL=/bin/bash # - (sleep random 60 is here to spread requests over a 1-min window) # - if ip.yunohost.org answers ping (basic check to validate that we're connected to the internet and yunohost infra aint down) # - and if lock ain't already taken by another command -# - check if some domains are flagged as autopush -# - trigger yunohost domain dns push --auto -*/10 * * * * root : YunoHost DynDNS update ; sleep \$((RANDOM\\%60)); ! ping -q -W5 -c1 ip.yunohost.org >/dev/null 2>&1 || test -e /var/run/moulinette_yunohost.lock || !(grep -nR "autopush: 1" /etc/yunohost/domains/*.yml > /dev/null) || yunohost domain dns push --auto >> /dev/null +# - trigger yunohost dyndns update +*/10 * * * * root : YunoHost DynDNS update; sleep \$((RANDOM\\%60)); ! ping -q -W5 -c1 ip.yunohost.org >/dev/null 2>&1 || test -e /var/run/moulinette_yunohost.lock || yunohost dyndns update >> /dev/null EOF else # (Delete cron if no dyndns domain found) diff --git a/locales/ca.json b/locales/ca.json index 808354264..106d0af89 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -506,7 +506,7 @@ "diagnosis_swap_tip": "Vigileu i tingueu en compte que els servidor està allotjant memòria d'intercanvi en una targeta SD o en l'emmagatzematge SSD, això pot reduir dràsticament l'esperança de vida del dispositiu.", "restore_already_installed_apps": "No s'han pogut restaurar les següents aplicacions perquè ja estan instal·lades: {apps}", "app_packaging_format_not_supported": "No es pot instal·lar aquesta aplicació ja que el format del paquet no és compatible amb la versió de YunoHost del sistema. Hauríeu de considerar actualitzar el sistema.", - "diagnosis_dns_try_dyndns_update_force": "La configuració DNS d'aquest domini hauria de ser gestionada automàticament per YunoHost. Si aquest no és el cas, podeu intentar forçar-ne l'actualització utilitzant yunohost domain dns push DOMAIN --force.", + "diagnosis_dns_try_dyndns_update_force": "La configuració DNS d'aquest domini hauria de ser gestionada automàticament per YunoHost. Si aquest no és el cas, podeu intentar forçar-ne l'actualització utilitzant yunohost dyndns update --force.", "regenconf_need_to_explicitly_specify_ssh": "La configuració ssh ha estat modificada manualment, però heu d'especificar explícitament la categoria «ssh» amb --force per fer realment els canvis.", "unknown_main_domain_path": "Domini o ruta desconeguda per a «{app}». Heu d'especificar un domini i una ruta per a poder especificar una URL per al permís.", "show_tile_cant_be_enabled_for_regex": "No podeu activar «show_title» ara, perquè la URL per al permís «{permission}» és una expressió regular", diff --git a/locales/de.json b/locales/de.json index 4a1db2961..5baa41687 100644 --- a/locales/de.json +++ b/locales/de.json @@ -290,7 +290,7 @@ "diagnosis_domain_expiration_success": "Deine Domänen sind registriert und werden in nächster Zeit nicht ablaufen.", "diagnosis_domain_not_found_details": "Die Domäne {domain} existiert nicht in der WHOIS-Datenbank oder sie ist abgelaufen!", "diagnosis_domain_expiration_not_found": "Das Ablaufdatum einiger Domains kann nicht überprüft werden", - "diagnosis_dns_try_dyndns_update_force": "Die DNS-Konfiguration dieser Domäne sollte automatisch von YunoHost verwaltet werden. Andernfalls könntest Du mittels yunohost domain dns push DOMAIN --force ein Update erzwingen.", + "diagnosis_dns_try_dyndns_update_force": "Die DNS-Konfiguration dieser Domäne sollte automatisch von YunoHost verwaltet werden. Andernfalls könntest Du mittels yunohost dyndns update --force ein Update erzwingen.", "diagnosis_dns_point_to_doc": "Bitte schauen Sie in der Dokumentation unter https://yunohost.org/dns_config nach, wenn Sie Hilfe bei der Konfiguration der DNS-Einträge benötigen.", "diagnosis_dns_discrepancy": "Der folgende DNS Eintrag scheint nicht den empfohlenen Einstellungen zu entsprechen:
Typ: {type}
Name: {name}
Aktueller Wert: {current}
Erwarteter Wert: {value}", "diagnosis_dns_missing_record": "Gemäß der empfohlenen DNS-Konfiguration solltest du einen DNS-Eintrag mit den folgenden Informationen hinzufügen.
Typ: {type}
Name: {name}
Wert: {value}", @@ -612,7 +612,7 @@ "log_app_config_set": "Konfiguration auf die Applikation '{}' anwenden", "log_user_import": "Konten importieren", "diagnosis_high_number_auth_failures": "In letzter Zeit gab es eine verdächtig hohe Anzahl von Authentifizierungsfehlern. Stelle sicher, dass fail2ban läuft und korrekt konfiguriert ist, oder verwende einen benutzerdefinierten Port für SSH, wie unter https://yunohost.org/security beschrieben.", - "domain_dns_registrar_yunohost": "Dies ist eine nohost.me / nohost.st / ynh.fr Domäne, ihre DNS-Konfiguration wird daher automatisch von YunoHost ohne weitere Konfiguration übernommen. (siehe Befehl 'yunohost domain dns push DOMAIN')", + "domain_dns_registrar_yunohost": "Dies ist eine nohost.me / nohost.st / ynh.fr Domäne, ihre DNS-Konfiguration wird daher automatisch von YunoHost ohne weitere Konfiguration übernommen. (siehe Befehl 'yunohost dyndns update')", "domain_config_auth_entrypoint": "API-Einstiegspunkt", "domain_config_auth_application_key": "Anwendungsschlüssel", "domain_config_auth_application_secret": "Geheimer Anwendungsschlüssel", diff --git a/locales/en.json b/locales/en.json index 8c6636322..6065c75d8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -208,7 +208,7 @@ "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.
Type: {type}
Name: {name}
Value: {value}", "diagnosis_dns_point_to_doc": "Please check the documentation at https://yunohost.org/dns_config if you need help configuring DNS records.", "diagnosis_dns_specialusedomain": "Domain {domain} is based on a special-use top-level domain (TLD) such as .local or .test and is therefore not expected to have actual DNS records.", - "diagnosis_dns_try_dyndns_update_force": "This domain's DNS configuration should automatically be managed by YunoHost. If that's not the case, you can try to force an update using yunohost domain dns push DOMAIN --force.", + "diagnosis_dns_try_dyndns_update_force": "This domain's DNS configuration should automatically be managed by YunoHost. If that's not the case, you can try to force an update using yunohost dyndns update --force.", "diagnosis_domain_expiration_error": "Some domains will expire VERY SOON!", "diagnosis_domain_expiration_not_found": "Unable to check the expiration date for some domains", "diagnosis_domain_expiration_not_found_details": "The WHOIS information for domain {domain} doesn't seem to contain the information about the expiration date?", @@ -333,8 +333,6 @@ "domain_config_auth_key": "Authentication key", "domain_config_auth_secret": "Authentication secret", "domain_config_auth_token": "Authentication token", - "domain_config_autopush": "Auto-push", - "domain_config_autopush_help": "Automatically update the domain's record", "domain_config_cert_install": "Install Let's Encrypt certificate", "domain_config_cert_issuer": "Certification authority", "domain_config_cert_no_checks": "Ignore diagnosis checks", @@ -359,8 +357,6 @@ "domain_dns_conf_special_use_tld": "This domain is based on a special-use top-level domain (TLD) such as .local or .test and is therefore not expected to have actual DNS records.", "domain_dns_push_already_up_to_date": "Records already up to date, nothing to do.", "domain_dns_push_failed": "Updating the DNS records failed miserably.", - "domain_dns_push_failed_domain": "Updating the DNS records for {domain} failed : {error}", - "domain_dns_push_failed_domains": "Updating the DNS records for {domains} failed.", "domain_dns_push_failed_to_authenticate": "Failed to authenticate on registrar's API for domain '{domain}'. Most probably the credentials are incorrect? (Error: {error})", "domain_dns_push_failed_to_list": "Failed to list current records using the registrar's API: {error}", "domain_dns_push_managed_in_parent_domain": "The automatic DNS configuration feature is managed in the parent domain {parent_domain}.", @@ -373,7 +369,7 @@ "domain_dns_registrar_managed_in_parent_domain": "This domain is a subdomain of {parent_domain_link}. DNS registrar configuration should be managed in {parent_domain}'s configuration panel.", "domain_dns_registrar_not_supported": "YunoHost could not automatically detect the registrar handling this domain. You should manually configure your DNS records following the documentation at https://yunohost.org/dns.", "domain_dns_registrar_supported": "YunoHost automatically detected that this domain is handled by the registrar **{registrar}**. If you want, YunoHost will automatically configure this DNS zone, if you provide it with the appropriate API credentials. You can find documentation on how to obtain your API credentials on this page: https://yunohost.org/registar_api_{registrar}. (You can also manually configure your DNS records following the documentation at https://yunohost.org/dns )", - "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost domain dns push DOMAIN' command)", + "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost dyndns update' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", "domain_dyndns_instruction_unclear": "You must choose exactly one of the following options : --subscribe or --ignore-dyndns", "domain_dyndns_instruction_unclear_unsubscribe": "You must choose exactly one of the following options : --unsubscribe or --ignore-dyndns", diff --git a/locales/es.json b/locales/es.json index bfb111f26..8637c3da8 100644 --- a/locales/es.json +++ b/locales/es.json @@ -467,7 +467,7 @@ "diagnosis_domain_expiration_not_found_details": "¿Parece que la información de WHOIS para el dominio {domain} no contiene información sobre la fecha de expiración?", "diagnosis_domain_not_found_details": "¡El dominio {domain} no existe en la base de datos WHOIS o ha expirado!", "diagnosis_domain_expiration_not_found": "No se pudo revisar la fecha de expiración para algunos dominios", - "diagnosis_dns_try_dyndns_update_force": "La configuración DNS de este dominio debería ser administrada automáticamente por YunoHost. Si no es el caso, puedes intentar forzar una actualización mediante yunohost domain dns push DOMAIN --force.", + "diagnosis_dns_try_dyndns_update_force": "La configuración DNS de este dominio debería ser administrada automáticamente por YunoHost. Si no es el caso, puedes intentar forzar una actualización mediante yunohost dyndns update --force.", "diagnosis_ip_local": "IP Local: {local}", "diagnosis_ip_no_ipv6_tip": "Tener IPv6 funcionando no es obligatorio para que su servidor funcione, pero es mejor para la salud del Internet en general. IPv6 debería ser configurado automáticamente por el sistema o su proveedor si está disponible. De otra manera, es posible que tenga que configurar varias cosas manualmente, tal y como se explica en esta documentación https://yunohost.org/#/ipv6. Si no puede habilitar IPv6 o si parece demasiado técnico, puede ignorar esta advertencia con toda seguridad.", "diagnosis_display_tip": "Para ver los problemas encontrados, puede ir a la sección de diagnóstico del webadmin, o ejecutar 'yunohost diagnosis show --issues --human-readable' en la línea de comandos.", @@ -589,7 +589,7 @@ "domain_config_auth_application_key": "LLave de Aplicación", "domain_dns_registrar_supported": "YunoHost detectó automáticamente que este dominio es manejado por el registrador **{registrar}**. Si lo desea, YunoHost configurará automáticamente esta zona DNS, si le proporciona las credenciales de API adecuadas. Puede encontrar documentación sobre cómo obtener sus credenciales de API en esta página: https://yunohost.org/registar_api_{registrar}. (También puede configurar manualmente sus registros DNS siguiendo la documentación en https://yunohost.org/dns)", "domain_dns_registrar_managed_in_parent_domain": "Este dominio es un subdominio de {parent_domain_link}. La configuración del registrador de DNS debe administrarse en el panel de configuración de {parent_domain}.", - "domain_dns_registrar_yunohost": "Este dominio es un nohost.me / nohost.st / ynh.fr y, por lo tanto, YunoHost maneja automáticamente su configuración de DNS sin ninguna configuración adicional. (vea el comando 'yunohost domain dns push DOMAIN')", + "domain_dns_registrar_yunohost": "Este dominio es un nohost.me / nohost.st / ynh.fr y, por lo tanto, YunoHost maneja automáticamente su configuración de DNS sin ninguna configuración adicional. (vea el comando 'yunohost dyndns update')", "domain_dns_registrar_not_supported": "YunoHost no pudo detectar automáticamente el registrador que maneja este dominio. Debe configurar manualmente sus registros DNS siguiendo la documentación en https://yunohost.org/dns.", "migration_ldap_backup_before_migration": "Creación de una copia de seguridad de la base de datos LDAP y la configuración de las aplicaciones antes de la migración real.", "invalid_number": "Debe ser un miembro", diff --git a/locales/eu.json b/locales/eu.json index 47ff773da..d58289bf4 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -154,7 +154,7 @@ "certmanager_domain_not_diagnosed_yet": "Oraindik ez dago {domain} domeinurako diagnostikorik. Berrabiarazi diagnostikoak 'DNS balioak' eta 'Web' ataletarako diagnostikoen gunean Let's Encrypt ziurtagirirako prest ote dagoen egiaztatzeko. (Edo zertan ari zaren baldin badakizu, erabili '--no-checks' egiaztatzea desgaitzeko.)", "diagnosis_domain_expiration_warning": "Domeinu batzuk iraungitzear daude!", "app_packaging_format_not_supported": "Aplikazio hau ezin da instalatu YunoHostek ez duelako paketea ezagutzen. Sistema eguneratzea hausnartu beharko zenuke ziur asko.", - "diagnosis_dns_try_dyndns_update_force": "Domeinu honen DNS konfigurazioa YunoHostek kudeatu beharko luke automatikoki. Gertatuko ez balitz, eguneratzera behartu zenezake yunohost domain dns push DOMAIN --force erabiliz.", + "diagnosis_dns_try_dyndns_update_force": "Domeinu honen DNS konfigurazioa YunoHostek kudeatu beharko luke automatikoki. Gertatuko ez balitz, eguneratzera behartu zenezake yunohost dyndns update --force erabiliz.", "app_manifest_install_ask_path": "Aukeratu aplikazio hau instalatzeko URLaren bidea (domeinuaren atzeko aldean)", "app_manifest_install_ask_admin": "Aukeratu administrari bat aplikazio honetarako", "app_manifest_install_ask_password": "Aukeratu administrazio-pasahitz bat aplikazio honetarako", @@ -316,7 +316,7 @@ "domain_dns_push_not_applicable": "Ezin da {domain} domeinurako DNS konfigurazio automatiko funtzioa erabili. DNS erregistroak eskuz ezarri beharko zenituzke gidaorriei erreparatuz: https://yunohost.org/dns_config.", "domain_dns_push_managed_in_parent_domain": "DNS ezarpenak automatikoki konfiguratzeko funtzioa {parent_domain} domeinu nagusian kudeatzen da.", "domain_dns_registrar_managed_in_parent_domain": "Domeinu hau {parent_domain_link}(r)en azpidomeinua da. DNS ezarpenak {parent_domain}(r)en konfigurazio atalean kudeatu behar dira.", - "domain_dns_registrar_yunohost": "Hau nohost.me / nohost.st / ynh.fr domeinu bat da eta, beraz, DNS ezarpenak automatikoki kudeatzen ditu YunoHostek, bestelako ezer konfiguratu beharrik gabe. (ikus 'yunohost domain dns push DOMAIN' komandoa)", + "domain_dns_registrar_yunohost": "Hau nohost.me / nohost.st / ynh.fr domeinu bat da eta, beraz, DNS ezarpenak automatikoki kudeatzen ditu YunoHostek, bestelako ezer konfiguratu beharrik gabe. (ikus 'yunohost dyndns update' komandoa)", "domain_dns_registrar_not_supported": "YunoHostek ezin izan du domeinu honen erregistro-enpresa automatikoki antzeman. Eskuz konfiguratu beharko dituzu DNS ezarpenak gidalerroei erreparatuz: https://yunohost.org/dns.", "domain_dns_registrar_experimental": "Oraingoz, YunoHosten kideek ez dute **{registrar}** erregistro-enpresaren APIa nahi beste probatu eta aztertu. Funtzioa **oso esperimentala** da — kontuz!", "domain_config_mail_in": "Jasotako mezuak", diff --git a/locales/fa.json b/locales/fa.json index afa86b13b..92e05bdad 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -28,7 +28,7 @@ "diagnosis_domain_not_found_details": "دامنه {domain} در پایگاه داده WHOIS وجود ندارد یا منقضی شده است!", "diagnosis_domain_expiration_not_found": "بررسی تاریخ انقضا برخی از دامنه ها امکان پذیر نیست", "diagnosis_dns_specialusedomain": "دامنه {domain} بر اساس یک دامنه سطح بالا (TLD) مخصوص استفاده است و بنابراین انتظار نمی رود که دارای سوابق DNS واقعی باشد.", - "diagnosis_dns_try_dyndns_update_force": "پیکربندی DNS این دامنه باید به طور خودکار توسط YunoHost مدیریت شود. اگر اینطور نیست ، می توانید سعی کنید به زور یک به روز رسانی را با استفاده از yunohost domain dns push DOMAIN --force.", + "diagnosis_dns_try_dyndns_update_force": "پیکربندی DNS این دامنه باید به طور خودکار توسط YunoHost مدیریت شود. اگر اینطور نیست ، می توانید سعی کنید به زور یک به روز رسانی را با استفاده از yunohost dyndns update --force.", "diagnosis_dns_point_to_doc": "لطفاً اسناد را در https://yunohost.org/dns_config برسی و مطالعه کنید، اگر در مورد پیکربندی سوابق DNS به کمک نیاز دارید.", "diagnosis_dns_discrepancy": "به نظر می رسد پرونده DNS زیر از پیکربندی توصیه شده پیروی نمی کند:
نوع: {type}
نام: {name}
ارزش فعلی: {current}
مقدار مورد انتظار: {value}", "diagnosis_dns_missing_record": "با توجه به پیکربندی DNS توصیه شده ، باید یک رکورد DNS با اطلاعات زیر اضافه کنید.
نوع: {type}
نام: {name}
ارزش: {value}", diff --git a/locales/fr.json b/locales/fr.json index 9affb5869..959ef1a8d 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -510,7 +510,7 @@ "diagnosis_swap_tip": "Soyez averti et conscient que si vous hébergez une partition SWAP sur une carte SD ou un disque SSD, cela risque de réduire considérablement l'espérance de vie de celui-ci.", "restore_already_installed_apps": "Les applications suivantes ne peuvent pas être restaurées car elles sont déjà installées : {apps}", "regenconf_need_to_explicitly_specify_ssh": "La configuration de ssh a été modifiée manuellement. Vous devez explicitement indiquer la mention --force à \"ssh\" pour appliquer les changements.", - "diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par YunoHost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant yunohost domain dns push DOMAIN --force.", + "diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par YunoHost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant yunohost dyndns update --force.", "app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de YunoHost. Vous devriez probablement envisager de mettre à jour votre système.", "global_settings_setting_backup_compress_tar_archives": "Compresser les archives de backup", "diagnosis_processes_killed_by_oom_reaper": "Certains processus ont été récemment arrêtés par le système car il manquait de mémoire. Ceci est typiquement symptomatique d'un manque de mémoire sur le système ou d'un processus consommant trop de mémoire. Liste des processus arrêtés :\n{kills_summary}", @@ -598,7 +598,7 @@ "service_not_reloading_because_conf_broken": "Le service '{name}' n'a pas été rechargé/redémarré car sa configuration est cassée : {errors}", "domain_registrar_is_not_configured": "Le registrar n'est pas encore configuré pour le domaine {domain}.", "domain_dns_push_not_applicable": "La fonction de configuration DNS automatique n'est pas applicable au domaine {domain}. Vous devez configurer manuellement vos enregistrements DNS en suivant la documentation sur https://yunohost.org/dns_config.", - "domain_dns_registrar_yunohost": "Ce domaine est de type nohost.me / nohost.st / ynh.fr et sa configuration DNS est donc automatiquement gérée par YunoHost sans qu'il n'y ait d'autre configuration à faire. (voir la commande 'yunohost domain dns push DOMAIN')", + "domain_dns_registrar_yunohost": "Ce domaine est de type nohost.me / nohost.st / ynh.fr et sa configuration DNS est donc automatiquement gérée par YunoHost sans qu'il n'y ait d'autre configuration à faire. (voir la commande 'yunohost dyndns update')", "domain_dns_registrar_supported": "YunoHost a détecté automatiquement que ce domaine est géré par le registrar **{registrar}**. Si vous le souhaitez, YunoHost configurera automatiquement cette zone DNS, si vous lui fournissez les identifiants API appropriés. Vous pouvez trouver de la documentation sur la façon d'obtenir vos identifiants API sur cette page : https://yunohost.org/registar_api_{registrar}. (Vous pouvez également configurer manuellement vos enregistrements DNS en suivant la documentation sur https://yunohost.org/dns )", "domain_config_features_disclaimer": "Jusqu'à présent, l'activation/désactivation des fonctionnalités de messagerie ou XMPP n'a d'impact que sur la configuration DNS recommandée et automatique, et non sur les configurations système !", "domain_dns_push_managed_in_parent_domain": "La fonctionnalité de configuration DNS automatique est gérée dans le domaine parent {parent_domain}.", diff --git a/locales/gl.json b/locales/gl.json index beaeec801..61af0b672 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -185,7 +185,7 @@ "diagnosis_domain_expiration_not_found_details": "A información WHOIS para o dominio {domain} non semella conter información acerca da data de caducidade?", "diagnosis_domain_not_found_details": "O dominio {domain} non existe na base de datos de WHOIS ou está caducado!", "diagnosis_domain_expiration_not_found": "Non se puido comprobar a data de caducidade para algúns dominios", - "diagnosis_dns_try_dyndns_update_force": "A xestión DNS deste dominio debería estar xestionada directamente por YunoHost. Se non fose o caso, podes intentar forzar unha actualización executando yunohost domain dns push DOMAIN --force.", + "diagnosis_dns_try_dyndns_update_force": "A xestión DNS deste dominio debería estar xestionada directamente por YunoHost. Se non fose o caso, podes intentar forzar unha actualización executando yunohost dyndns update --force.", "diagnosis_swap_ok": "O sistema ten {total} de swap!", "diagnosis_swap_notsomuch": "O sistema só ten {total} de swap. Deberías considerar ter polo menos {recommended} para evitar situacións onde o sistema esgote a memoria.", "diagnosis_swap_none": "O sistema non ten partición swap. Deberías considerar engadir polo menos {recommended} de swap para evitar situación onde o sistema esgote a memoria.", @@ -615,7 +615,7 @@ "domain_config_auth_consumer_key": "Chave consumidora", "log_domain_dns_push": "Enviar rexistros DNS para o dominio '{}'", "other_available_options": "... e outras {n} opcións dispoñibles non mostradas", - "domain_dns_registrar_yunohost": "Este dominio un dos de nohost.me / nohost.st / ynh.fr e a configuración DNS xestionaa directamente YunoHost se máis requisitos. (mira o comando 'yunohost domain dns push DOMAIN')", + "domain_dns_registrar_yunohost": "Este dominio un dos de nohost.me / nohost.st / ynh.fr e a configuración DNS xestionaa directamente YunoHost se máis requisitos. (mira o comando 'yunohost dyndns update')", "domain_dns_registrar_supported": "YunoHost detectou automáticamente que este dominio está xestionado pola rexistradora **{registrar}**. Se queres, YunoHost pode configurar automáticamente as súas zonas DNS, se proporcionas as credenciais de acceso á API. Podes ver a documentación sobre como obter as credenciais da API nesta páxina: https://yunohost.org/registrar_api_{registrar}. (Tamén podes configurar manualmente os rexistros DNS seguindo a documentación en https://yunohost.org/dns )", "domain_dns_push_partial_failure": "Actualización parcial dos rexistros DNS: informouse dalgúns avisos/erros.", "domain_config_auth_token": "Token de autenticación", diff --git a/locales/it.json b/locales/it.json index 888b6cc62..9bb923c2a 100644 --- a/locales/it.json +++ b/locales/it.json @@ -299,7 +299,7 @@ "diagnosis_domain_expiration_not_found_details": "Le informazioni WHOIS per il dominio {domain} non sembrano contenere la data di scadenza, giusto?", "diagnosis_domain_not_found_details": "Il dominio {domain} non esiste nel database WHOIS o è scaduto!", "diagnosis_domain_expiration_not_found": "Non riesco a controllare la data di scadenza di alcuni domini", - "diagnosis_dns_try_dyndns_update_force": "La configurazione DNS di questo dominio dovrebbe essere gestita automaticamente da YunoHost. Se non avviene, puoi provare a forzare un aggiornamento usando il comando yunohost domain dns push DOMAIN --force.", + "diagnosis_dns_try_dyndns_update_force": "La configurazione DNS di questo dominio dovrebbe essere gestita automaticamente da YunoHost. Se non avviene, puoi provare a forzare un aggiornamento usando il comando yunohost dyndns update --force.", "diagnosis_dns_point_to_doc": "Controlla la documentazione a https://yunohost.org/dns_config se hai bisogno di aiuto nel configurare i record DNS.", "diagnosis_dns_discrepancy": "Il record DNS non sembra seguire la configurazione DNS raccomandata:
Type: {type}
Name: {name}
Current value: {current}
Expected value: {value}", "diagnosis_dns_missing_record": "Stando alla configurazione DNS raccomandata, dovresti aggiungere un record DNS con le seguenti informazioni.
Type: {type}
Name: {name}
Value: {value}", @@ -608,7 +608,7 @@ "diagnosis_description_apps": "Applicazioni", "domain_registrar_is_not_configured": "Il registrar non è ancora configurato per il dominio {domain}.", "domain_dns_registrar_managed_in_parent_domain": "Questo dominio è un sotto-dominio di {parent_domain_link}. La configurazione del registrar DNS dovrebbe essere gestita dal pannello di configurazione di {parent_domain}.", - "domain_dns_registrar_yunohost": "Questo dominio è un nohost.me / nohost.st / ynh.fr, perciò la sua configurazione DNS è gestita automaticamente da YunoHost, senza alcuna ulteriore configurazione. (vedi il comando yunohost domain dns push DOMAIN)", + "domain_dns_registrar_yunohost": "Questo dominio è un nohost.me / nohost.st / ynh.fr, perciò la sua configurazione DNS è gestita automaticamente da YunoHost, senza alcuna ulteriore configurazione. (vedi il comando yunohost dyndns update)", "domain_dns_push_success": "Record DNS aggiornati!", "domain_dns_push_failed": "L’aggiornamento dei record DNS è miseramente fallito.", "domain_dns_push_partial_failure": "Record DNS parzialmente aggiornati: alcuni segnali/errori sono stati riportati.", diff --git a/locales/uk.json b/locales/uk.json index 2b168b65e..281f2dba7 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -409,7 +409,7 @@ "diagnosis_domain_not_found_details": "Домен {domain} не існує в базі даних WHOIS або строк його дії сплив!", "diagnosis_domain_expiration_not_found": "Неможливо перевірити строк дії деяких доменів", "diagnosis_dns_specialusedomain": "Домен {domain} заснований на домені верхнього рівня спеціального призначення (TLD) такого як .local або .test і тому не очікується, що у нього будуть актуальні записи DNS.", - "diagnosis_dns_try_dyndns_update_force": "Конфігурація DNS цього домену повинна автоматично управлятися YunoHost. Якщо це не так, ви можете спробувати примусово оновити її за допомогою команди yunohost domain dns push DOMAIN --force.", + "diagnosis_dns_try_dyndns_update_force": "Конфігурація DNS цього домену повинна автоматично управлятися YunoHost. Якщо це не так, ви можете спробувати примусово оновити її за допомогою команди yunohost dyndns update --force.", "diagnosis_dns_point_to_doc": "Якщо вам потрібна допомога з налаштування DNS-записів, зверніться до документації на сайті https://yunohost.org/dns_config.", "diagnosis_dns_discrepancy": "Наступний запис DNS, схоже, не відповідає рекомендованій конфігурації:
Тип: {type}
Назва: {name}
Поточне значення: {current}
Очікуване значення: {value}", "diagnosis_dns_missing_record": "Згідно рекомендованої конфігурації DNS, ви повинні додати запис DNS з наступними відомостями.
Тип: {type}
Назва: {name}
Значення: {value}", @@ -599,7 +599,7 @@ "diagnosis_http_special_use_tld": "Домен {domain} базується на спеціальному домені верхнього рівня (TLD), такому як .local або .test, і тому не очікується, що він буде відкритий за межами локальної мережі.", "domain_dns_push_managed_in_parent_domain": "Функцією автоконфігурації DNS керує батьківський домен {parent_domain}.", "domain_dns_registrar_managed_in_parent_domain": "Цей домен є піддоменом {parent_domain_link}. Конфігурацією реєстратора DNS слід керувати на панелі конфігурації {parent_domain}.", - "domain_dns_registrar_yunohost": "Цей домен є nohost.me/nohost.st/ynh.fr, тому його конфігурація DNS автоматично обробляється YunoHost без будь-якої подальшої конфігурації. (див. команду 'yunohost domain dns push DOMAIN')", + "domain_dns_registrar_yunohost": "Цей домен є nohost.me/nohost.st/ynh.fr, тому його конфігурація DNS автоматично обробляється YunoHost без будь-якої подальшої конфігурації. (див. команду 'yunohost dyndns update')", "domain_dns_conf_special_use_tld": "Цей домен засновано на спеціальному домені верхнього рівня (TLD), такому як .local або .test, і тому не очікується, що він матиме актуальні записи DNS.", "domain_dns_registrar_supported": "YunoHost автоматично визначив, що цей домен обслуговується реєстратором **{registrar}**. Якщо ви хочете, YunoHost автоматично налаштує цю DNS-зону, якщо ви надасте йому відповідні облікові дані API. Ви можете знайти документацію про те, як отримати реєстраційні дані API на цій сторінці: https://yunohost.org/registar_api_{registrar}. (Ви також можете вручну налаштувати свої DNS-записи, дотримуючись документації на https://yunohost.org/dns)", "domain_dns_registrar_experimental": "Поки що інтерфейс з API **{registrar}** не був належним чином протестований і перевірений спільнотою YunoHost. Підтримка є **дуже експериментальною** - будьте обережні!", diff --git a/locales/zh_Hans.json b/locales/zh_Hans.json index b31d88217..8aecbbce3 100644 --- a/locales/zh_Hans.json +++ b/locales/zh_Hans.json @@ -449,7 +449,7 @@ "diagnosis_diskusage_low": "存储器{mountpoint}(在设备{device}上)只有{free} ({free_percent}%) 的空间。({free_percent}%)的剩余空间(在{total}中)。要小心。", "diagnosis_diskusage_verylow": "存储器{mountpoint}(在设备{device}上)仅剩余{free} ({free_percent}%) (剩余{total})个空间。您应该真正考虑清理一些空间!", "diagnosis_services_bad_status_tip": "你可以尝试重新启动服务,如果没有效果,可以看看webadmin中的服务日志(从命令行,你可以用yunohost service restart {service}yunohost service log {service})来做。", - "diagnosis_dns_try_dyndns_update_force": "该域的DNS配置应由YunoHost自动管理,如果不是这种情况,您可以尝试使用 yunohost domain dns push DOMAIN --force强制进行更新。", + "diagnosis_dns_try_dyndns_update_force": "该域的DNS配置应由YunoHost自动管理,如果不是这种情况,您可以尝试使用 yunohost dyndns update --force强制进行更新。", "diagnosis_dns_point_to_doc": "如果您需要有关配置DNS记录的帮助,请查看 https://yunohost.org/dns_config 上的文档。", "diagnosis_dns_discrepancy": "以下DNS记录似乎未遵循建议的配置:
类型: {type}
名称: {name}
代码> 当前值: {current}期望值: {value}", "log_backup_create": "创建备份档案", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 3124f3105..9fe077c23 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -751,9 +751,8 @@ domain: action_help: Push DNS records to registrar api: POST /domains//dns/push arguments: - domains: - help: Domain names to push DNS conf for - nargs: "*" + domain: + help: Domain name to push DNS conf for extra: pattern: *pattern_domain -d: @@ -766,9 +765,6 @@ domain: --purge: help: Delete all records action: store_true - --auto: - help: Push only domains that should be pushed automatically - action: store_true cert: subcategory_help: Manage domain certificates @@ -1572,7 +1568,7 @@ dyndns: extra: pattern: *pattern_domain -p: - full: --password + full: --recovery-password nargs: "?" const: 0 help: Password used to later delete the domain @@ -1580,25 +1576,6 @@ dyndns: pattern: *pattern_password comment: dyndns_added_password - ### dyndns_unsubscribe() - unsubscribe: - action_help: Unsubscribe to a DynDNS service ( deprecated, use 'yunohost domain dyndns unsubscribe' instead ) - deprecated: true - arguments: - -d: - full: --domain - help: Full domain to unsubscribe with - extra: - pattern: *pattern_domain - required: True - -p: - full: --password - nargs: "?" - const: 0 - help: Password used to delete the domain - extra: - pattern: *pattern_password - ### dyndns_update() update: action_help: Update IP on DynDNS platform ( deprecated, use 'yunohost domain dns push DOMAIN' instead ) diff --git a/share/config_domain.toml b/share/config_domain.toml index 0aae4df26..b1ec436c5 100644 --- a/share/config_domain.toml +++ b/share/config_domain.toml @@ -33,13 +33,6 @@ name = "Features" [dns] name = "DNS" - [dns.zone] - - [dns.zone.autopush] - type = "boolean" - default = 0 - help = "" - [dns.registrar] # This part is automatically generated in DomainConfigPanel diff --git a/src/dns.py b/src/dns.py index 085f47471..296ecfaaa 100644 --- a/src/dns.py +++ b/src/dns.py @@ -618,24 +618,8 @@ def _get_registar_settings(domain): return registrar, settings -def domain_dns_push(domains, dry_run=False, force=False, purge=False, auto=False): - if auto: - domains = domain_list(exclude_subdomains=True, auto_push=True)["domains"] - elif len(domains) == 0: - domains = domain_list(exclude_subdomains=True)["domains"] - error_domains = [] - for domain in domains: - try: - domain_dns_push_unique(domain, dry_run=dry_run, force=force, purge=purge) - except YunohostError as e: - logger.error(m18n.n("domain_dns_push_failed_domain", domain=domain, error=str(e))) - error_domains.append(domain) - if len(error_domains) > 0: - raise YunohostError("domain_dns_push_failed_domains", domains=', '.join(error_domains)) - - @is_unit_operation() -def domain_dns_push_unique(operation_logger, domain, dry_run=False, force=False, purge=False): +def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge=False): """ Send DNS records to the previously-configured registrar of the domain. """ diff --git a/src/domain.py b/src/domain.py index 020a707c7..9dc884177 100644 --- a/src/domain.py +++ b/src/domain.py @@ -116,7 +116,6 @@ def domain_list(exclude_subdomains=False, tree=False, features=[]): domains_filtered = [] for domain in domains: config = domain_config_get(domain, key="feature", export=True) - config += domain_config_get(domain, key="dns.zone", export=True) if any(config.get(feature) == 1 for feature in features): domains_filtered.append(domain) domains = domains_filtered @@ -791,7 +790,7 @@ def domain_dns_suggest(domain): return domain_dns_suggest(domain) -def domain_dns_push(domains, dry_run=None, force=None, purge=None, auto=False): +def domain_dns_push(domain, dry_run, force, purge): from yunohost.dns import domain_dns_push - return domain_dns_push(domains, dry_run=dry_run, force=force, purge=purge, auto=auto) + return domain_dns_push(domain, dry_run, force, purge) diff --git a/src/dyndns.py b/src/dyndns.py index caeef9459..2f83038cc 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -172,10 +172,6 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): error = f'Server error, code: {r.status_code}. (Message: "{r.text}")' raise YunohostError("dyndns_registration_failed", error=error) - # Set the domain's config to autopush - from yunohost.domain import domain_config_set - domain_config_set(domain, key="dns.zone.autopush", value=1) - # Yunohost regen conf will add the dyndns cron job if a key exists # in /etc/yunohost/dyndns regen_conf(["yunohost"]) @@ -183,7 +179,7 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): # Add some dyndns update in 2 and 4 minutes from now such that user should # not have to wait 10ish minutes for the conf to propagate cmd = ( - f"at -M now + {{t}} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost domain dns push {domain}'\"" + "at -M now + {t} >/dev/null 2>&1 <<< \"/bin/bash -c 'yunohost dyndns update'\"" ) # For some reason subprocess doesn't like the redirections so we have to use bash -c explicity... subprocess.check_call(["bash", "-c", cmd.format(t="2 min")]) diff --git a/src/tools.py b/src/tools.py index c0da7a37b..79f10bc8c 100644 --- a/src/tools.py +++ b/src/tools.py @@ -200,15 +200,15 @@ def tools_postinstall( # Check if the domain is available... try: - _dyndns_available(domain) + available = _dyndns_available(domain) # If an exception is thrown, most likely we don't have internet # connectivity or something. Assume that this domain isn't manageable # and inform the user that we could not contact the dyndns host server. except Exception: - logger.warning( - m18n.n("dyndns_provider_unreachable", provider="dyndns.yunohost.org") - ) - raise YunohostValidationError("dyndns_unavailable", domain=domain) + raise YunohostValidationError("dyndns_provider_unreachable", provider="dyndns.yunohost.org") + else: + if not available: + raise YunohostValidationError("dyndns_unavailable", domain=domain) if os.system("iptables -V >/dev/null 2>/dev/null") != 0: raise YunohostValidationError( From e2da51b9a367fb7f4e8f7d1b39a081d19c58427b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Apr 2023 18:43:27 +0200 Subject: [PATCH 49/54] dyndns: various tweaks to simplify the code, improve UX ... --- locales/en.json | 8 ++-- share/actionsmap.yml | 2 +- src/domain.py | 42 +++++------------- src/dyndns.py | 102 +++++++++++++++++++------------------------ src/tools.py | 7 +-- 5 files changed, 62 insertions(+), 99 deletions(-) diff --git a/locales/en.json b/locales/en.json index 83ee34052..b3a1725e8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -90,7 +90,8 @@ "ask_new_domain": "New domain", "ask_new_path": "New path", "ask_password": "Password", - "ask_dyndns_recovery_password": "DynDNS recovey password", + "ask_dyndns_recovery_password_explain": "Please pick a recovery password for your DynDNS domain, in case you need to reset it later.", + "ask_dyndns_recovery_password": "DynDNS recovey passwory", "ask_user_domain": "Domain to use for the user's email address and XMPP account", "backup_abstract_method": "This backup method has yet to be implemented", "backup_actually_backuping": "Creating a backup archive from the collected files...", @@ -383,8 +384,6 @@ "domain_dns_registrar_supported": "YunoHost automatically detected that this domain is handled by the registrar **{registrar}**. If you want, YunoHost will automatically configure this DNS zone, if you provide it with the appropriate API credentials. You can find documentation on how to obtain your API credentials on this page: https://yunohost.org/registar_api_{registrar}. (You can also manually configure your DNS records following the documentation at https://yunohost.org/dns )", "domain_dns_registrar_yunohost": "This domain is a nohost.me / nohost.st / ynh.fr and its DNS configuration is therefore automatically handled by YunoHost without any further configuration. (see the 'yunohost dyndns update' command)", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", - "domain_dyndns_instruction_unclear": "You must choose exactly one of the following options : --subscribe or --ignore-dyndns", - "domain_dyndns_instruction_unclear_unsubscribe": "You must choose exactly one of the following options : --unsubscribe or --ignore-dyndns", "domain_exists": "The domain already exists", "domain_hostname_failed": "Unable to set new hostname. This might cause an issue later (it might be fine).", "domain_registrar_is_not_configured": "The registrar is not yet configured for domain {domain}.", @@ -400,7 +399,6 @@ "dyndns_domain_not_provided": "DynDNS provider {provider} cannot provide domain {domain}.", "dyndns_ip_update_failed": "Could not update IP address to DynDNS", "dyndns_ip_updated": "Updated your IP on DynDNS", - "dyndns_key_generating": "Generating DNS key... It may take a while.", "dyndns_key_not_found": "DNS key not found for the domain", "dyndns_no_domain_registered": "No domain registered with DynDNS", "dyndns_no_recovery_password": "No recovery password specified! In case you loose control of this domain, you will need to contact an administrator in the YunoHost team!", @@ -772,4 +770,4 @@ "yunohost_installing": "Installing YunoHost...", "yunohost_not_installed": "YunoHost is not correctly installed. Please run 'yunohost tools postinstall'", "yunohost_postinstall_end_tip": "The post-install completed! To finalize your setup, please consider:\n - diagnose potential issues through the 'Diagnosis' section of the webadmin (or 'yunohost diagnosis run' in command-line);\n - reading the 'Finalizing your setup' and 'Getting to know YunoHost' parts in the admin documentation: https://yunohost.org/admindoc." -} \ No newline at end of file +} diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 412297440..e11073afc 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -493,7 +493,7 @@ domain: help: Display domains as a tree action: store_true --features: - help: List only domains with features enabled (xmpp, mail_in, mail_out, auto_push) + help: List only domains with features enabled (xmpp, mail_in, mail_out) nargs: "*" ### domain_info() diff --git a/src/domain.py b/src/domain.py index 35730483b..8d2758ab2 100644 --- a/src/domain.py +++ b/src/domain.py @@ -228,7 +228,7 @@ def domain_add(operation_logger, domain, dyndns_recovery_password=None, ignore_d from yunohost.utils.password import assert_password_is_strong_enough from yunohost.certificate import _certificate_install_selfsigned - if dyndns_recovery_password != 0 and dyndns_recovery_password is not None: + if dyndns_recovery_password: operation_logger.data_to_redact.append(dyndns_recovery_password) if domain.startswith("xmpp-upload."): @@ -252,35 +252,19 @@ def domain_add(operation_logger, domain, dyndns_recovery_password=None, ignore_d domain = domain.encode("idna").decode("utf-8") # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) - dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 + dyndns = not ignore_dyndns and is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 if dyndns: - if not ignore_dyndns and not dyndns_recovery_password: - if Moulinette.interface.type == "api": - raise YunohostValidationError("domain_dyndns_missing_password") - else: - dyndns_recovery_password = Moulinette.prompt( - m18n.n("ask_dyndns_recovery_password"), is_password=True, confirm=True - ) - - # Ensure sufficiently complex password - assert_password_is_strong_enough("admin", dyndns_recovery_password) - - if ((dyndns_recovery_password is None) == (ignore_dyndns is False)): - raise YunohostValidationError("domain_dyndns_instruction_unclear") - from yunohost.dyndns import is_subscribing_allowed - # Do not allow to subscribe to multiple dyndns domains... if not is_subscribing_allowed(): raise YunohostValidationError("domain_dyndns_already_subscribed") + if dyndns_recovery_password: + assert_password_is_strong_enough("admin", dyndns_recovery_password) operation_logger.start() - if not dyndns and (dyndns_recovery_password is not None or ignore_dyndns): - logger.warning("This domain is not a DynDNS one, no need for the --dyndns-recovery-password or --ignore-dyndns option") - if dyndns and not ignore_dyndns: - # Actually subscribe - domain_dyndns_subscribe(domain=domain, password=dyndns_recovery_password) + if dyndns: + domain_dyndns_subscribe(domain=domain, recovery_password=dyndns_recovery_password) _certificate_install_selfsigned([domain], True) @@ -346,7 +330,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd from yunohost.app import app_ssowatconf, app_info, app_remove from yunohost.utils.ldap import _get_ldap_interface - if dyndns_recovery_password != 0 and dyndns_recovery_password is not None: + if dyndns_recovery_password: operation_logger.data_to_redact.append(dyndns_recovery_password) # the 'force' here is related to the exception happening in domain_add ... @@ -410,16 +394,10 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd ) # Detect if this is a DynDNS domain ( and not a subdomain of a DynDNS domain ) - dyndns = is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 - if dyndns: - if ((dyndns_recovery_password is None) == (ignore_dyndns is False)): - raise YunohostValidationError("domain_dyndns_instruction_unclear_unsubscribe") + dyndns = not ignore_dyndns and is_yunohost_dyndns_domain(domain) and len(domain.split(".")) == 3 operation_logger.start() - if not dyndns and ((dyndns_recovery_password is not None) or (ignore_dyndns is not False)): - logger.warning("This domain is not a DynDNS one, no need for the --dyndns_recovery_password or --ignore-dyndns option") - ldap = _get_ldap_interface() try: ldap.remove("virtualdomain=" + domain + ",ou=domains") @@ -465,9 +443,9 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd hook_callback("post_domain_remove", args=[domain]) # If a password is provided, delete the DynDNS record - if dyndns and not ignore_dyndns: + if dyndns: # Actually unsubscribe - domain_dyndns_unsubscribe(domain=domain, password=dyndns_recovery_password) + domain_dyndns_unsubscribe(domain=domain, recovery_password=dyndns_recovery_password) logger.success(m18n.n("domain_deleted")) diff --git a/src/dyndns.py b/src/dyndns.py index 3c9788af7..2da3de212 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -50,7 +50,7 @@ def is_subscribing_allowed(): Returns: True if the limit is not reached, False otherwise """ - return len(glob.glob("/etc/yunohost/dyndns/*.key")) < MAX_DYNDNS_DOMAINS + return len(dyndns_list()["domains"]) < MAX_DYNDNS_DOMAINS def _dyndns_available(domain): @@ -78,7 +78,7 @@ def _dyndns_available(domain): return r == f"Domain {domain} is available" -@is_unit_operation() +@is_unit_operation(exclude=["recovery_password"]) def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): """ Subscribe to a DynDNS service @@ -88,26 +88,6 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): recovery_password -- Password that will be used to delete the domain """ - if recovery_password is None: - logger.warning(m18n.n('dyndns_no_recovery_password')) - else: - from yunohost.utils.password import assert_password_is_strong_enough - # Ensure sufficiently complex password - if Moulinette.interface.type == "cli" and recovery_password == 0: - recovery_password = Moulinette.prompt( - m18n.n("ask_password"), - is_password=True, - confirm=True - ) - assert_password_is_strong_enough("admin", recovery_password) - - if not is_subscribing_allowed(): - raise YunohostValidationError("domain_dyndns_already_subscribed") - - if domain is None: - domain = _get_maindomain() - operation_logger.related_to.append(("domain", domain)) - # Verify if domain is provided by subscribe_host if not is_yunohost_dyndns_domain(domain): raise YunohostValidationError( @@ -118,6 +98,30 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): if not _dyndns_available(domain): raise YunohostValidationError("dyndns_unavailable", domain=domain) + # Check adding another dyndns domain is still allowed + if not is_subscribing_allowed(): + raise YunohostValidationError("domain_dyndns_already_subscribed") + + # Prompt for a password if running in CLI and no password provided + if not recovery_password and Moulinette.interface.type == "cli": + logger.warning(m18n.n("ask_dyndns_recovery_password_explain")) + recovery_password = Moulinette.prompt( + m18n.n("ask_dyndns_recovery_password"), + is_password=True, + confirm=True + ) + elif not recovery_password: + logger.warning(m18n.n("dyndns_no_recovery_password")) + + if recovery_password: + from yunohost.utils.password import assert_password_is_strong_enough + assert_password_is_strong_enough("admin", recovery_password) + operation_logger.data_to_redact.append(recovery_password) + + if domain is None: + domain = _get_maindomain() + operation_logger.related_to.append(("domain", domain)) + operation_logger.start() # '165' is the convention identifier for hmac-sha512 algorithm @@ -127,8 +131,6 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): if not os.path.exists("/etc/yunohost/dyndns"): os.makedirs("/etc/yunohost/dyndns") - logger.debug(m18n.n("dyndns_key_generating")) - # Here, we emulate the behavior of the old 'dnssec-keygen' utility # which since bullseye was replaced by ddns-keygen which is now # in the bind9 package ... but installing bind9 will conflict with dnsmasq @@ -154,7 +156,7 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): # Yeah the secret is already a base64-encoded but we double-bas64-encode it, whatever... b64encoded_key = base64.b64encode(secret.encode()).decode() data = {"subdomain": domain} - if password is not None: + if recovery_password: data["recovery_password"] = hashlib.sha256((domain + ":" + recovery_password.strip()).encode('utf-8')).hexdigest() r = requests.post( f"https://{DYNDNS_PROVIDER}/key/{b64encoded_key}?key_algo=hmac-sha512", @@ -188,7 +190,7 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): logger.success(m18n.n("dyndns_registered")) -@is_unit_operation() +@is_unit_operation(exclude=["recovery_password"]) def dyndns_unsubscribe(operation_logger, domain, recovery_password=None): """ Unsubscribe from a DynDNS service @@ -198,24 +200,19 @@ def dyndns_unsubscribe(operation_logger, domain, recovery_password=None): recovery_password -- Password that is used to delete the domain ( defined when subscribing ) """ - from yunohost.utils.password import assert_password_is_strong_enough + import requests # lazy loading this module for performance reasons + + # FIXME : it should be possible to unsubscribe the domain just using the key file ... # Ensure sufficiently complex password if Moulinette.interface.type == "cli" and not recovery_password: recovery_password = Moulinette.prompt( - m18n.n("ask_password"), + m18n.n("ask_dyndns_recovery_password"), is_password=True ) - assert_password_is_strong_enough("admin", recovery_password) operation_logger.start() - # '165' is the convention identifier for hmac-sha512 algorithm - # '1234' is idk? doesnt matter, but the old format contained a number here... - key_file = f"/etc/yunohost/dyndns/K{domain}.+165+1234.key" - - import requests # lazy loading this module for performance reasons - # Send delete request try: secret = str(domain) + ":" + str(recovery_password).strip() @@ -228,30 +225,30 @@ def dyndns_unsubscribe(operation_logger, domain, recovery_password=None): raise YunohostError("dyndns_unregistration_failed", error=str(e)) if r.status_code == 200: # Deletion was successful - rm(key_file, force=True) + for key_file in glob.glob(f"/etc/yunohost/dyndns/K{domain}.+*.key"): + rm(key_file, force=True) # Yunohost regen conf will add the dyndns cron job if a key exists # in /etc/yunohost/dyndns regen_conf(["yunohost"]) - - logger.success(m18n.n("dyndns_unregistered")) elif r.status_code == 403: # Wrong password raise YunohostError("dyndns_unsubscribe_wrong_password") elif r.status_code == 404: # Invalid domain raise YunohostError("dyndns_unsubscribe_wrong_domain") + logger.success(m18n.n("dyndns_unregistered")) + def dyndns_list(): """ Returns all currently subscribed DynDNS domains ( deduced from the key files ) """ - files = glob.glob("/etc/yunohost/dyndns/K*key") - # Get the domain names - for i in range(len(files)): - files[i] = files[i].split(".+", 1)[0] - files[i] = files[i].split("/etc/yunohost/dyndns/K")[1] + from yunohost.domain import domain_list - return {"domains": files} + domains = domain_list(exclude_subdomains=True)["domains"] + dyndns_domains = [d for d in domains if is_yunohost_dyndns_domain(d) and glob.glob(f"/etc/yunohost/dyndns/K{d}.+*.key")] + + return {"domains": dyndns_domains} @is_unit_operation() @@ -277,21 +274,14 @@ def dyndns_update( # If domain is not given, update all DynDNS domains if domain is None: - from yunohost.domain import domain_list + dyndns_domains = dyndns_list()["domains"] - domains = domain_list(exclude_subdomains=True, auto_push=True)["domains"] - pushed = 0 - for d in domains: - if is_yunohost_dyndns_domain(d): - dyndns_update(d, force=force, dry_run=dry_run) - pushed += 1 - if pushed == 0: + if not dyndns_domains: raise YunohostValidationError("dyndns_no_domain_registered") - return - elif type(domain).__name__ in ["list", "tuple"]: - for d in domain: - dyndns_update(d, force=force, dry_run=dry_run) + for domain in dyndns_domains: + dyndns_update(domain, force=force, dry_run=dry_run) + return # If key is not given, pick the first file we find with the domain given diff --git a/src/tools.py b/src/tools.py index 33cccd729..af6a2e61a 100644 --- a/src/tools.py +++ b/src/tools.py @@ -197,11 +197,8 @@ def tools_postinstall( assert_password_is_strong_enough("admin", password) # If this is a nohost.me/noho.st, actually check for availability - if not ignore_dyndns and is_yunohost_dyndns_domain(domain): - - if (bool(dyndns_recovery_password), ignore_dyndns) in [(True, True), (False, False)]: - raise YunohostValidationError("domain_dyndns_instruction_unclear") - + dyndns = not ignore_dyndns and is_yunohost_dyndns_domain(domain) + if dyndns: # Check if the domain is available... try: available = _dyndns_available(domain) From cbef40798c2843777a43db4b3b328e1d01abfcda Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Apr 2023 19:40:30 +0200 Subject: [PATCH 50/54] dyndns: be able to unsubscribe using the key + domain and i18n string consistency --- locales/en.json | 19 +++++++++-------- share/actionsmap.yml | 10 ++++----- src/dyndns.py | 50 +++++++++++++++++++++++++++++--------------- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/locales/en.json b/locales/en.json index b3a1725e8..81d0b8a3e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -91,7 +91,8 @@ "ask_new_path": "New path", "ask_password": "Password", "ask_dyndns_recovery_password_explain": "Please pick a recovery password for your DynDNS domain, in case you need to reset it later.", - "ask_dyndns_recovery_password": "DynDNS recovey passwory", + "ask_dyndns_recovery_password": "DynDNS recovery password", + "ask_dyndns_recovery_password_explain_during_unsubscribe": "Please enter the recovery password for this DynDNS domain.", "ask_user_domain": "Domain to use for the user's email address and XMPP account", "backup_abstract_method": "This backup method has yet to be implemented", "backup_actually_backuping": "Creating a backup archive from the collected files...", @@ -401,15 +402,15 @@ "dyndns_ip_updated": "Updated your IP on DynDNS", "dyndns_key_not_found": "DNS key not found for the domain", "dyndns_no_domain_registered": "No domain registered with DynDNS", - "dyndns_no_recovery_password": "No recovery password specified! In case you loose control of this domain, you will need to contact an administrator in the YunoHost team!", - "dyndns_added_password": "Remember your recovery password, you can use it to delete this domain record.", + "dyndns_no_recovery_password": "No recovery password specified! In case you loose control of this domain, you will need to contact an administrator in the YunoHost team!", + "dyndns_added_password": "Remember your recovery password, you can use it to delete this domain record.", "dyndns_provider_unreachable": "Unable to reach DynDNS provider {provider}: either your YunoHost is not correctly connected to the internet or the dynette server is down.", - "dyndns_registered": "DynDNS domain registered", - "dyndns_registration_failed": "Could not register DynDNS domain: {error}", - "dyndns_unregistration_failed": "Could not unregister DynDNS domain: {error}", - "dyndns_unregistered": "DynDNS domain successfully unregistered", - "dyndns_unsubscribe_wrong_password": "Invalid password", - "dyndns_unsubscribe_wrong_domain": "Domain is not registered", + "dyndns_subscribed": "DynDNS domain subscribed", + "dyndns_subscribe_failed": "Could not subscribe DynDNS domain: {error}", + "dyndns_unsubscribe_failed": "Could not unsubscribe DynDNS domain: {error}", + "dyndns_unsubscribed": "DynDNS domain unsubscribed", + "dyndns_unsubscribe_denied": "Failed to unsubscribe domain: invalid credentials", + "dyndns_unsubscribe_already_unsubscribed": "Domain is already unsubscribed", "dyndns_unavailable": "The domain '{domain}' is unavailable.", "extracting": "Extracting...", "field_invalid": "Invalid field '{}'", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index e11073afc..b229be84a 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -663,9 +663,8 @@ domain: subscribe: action_help: Subscribe to a DynDNS service arguments: - -d: - full: --domain - help: Full domain to subscribe with + domain: + help: Domain to subscribe to the DynDNS service extra: pattern: *pattern_domain -p: @@ -681,9 +680,8 @@ domain: unsubscribe: action_help: Unsubscribe from a DynDNS service arguments: - -d: - full: --domain - help: Full domain to unsubscribe with + domain: + help: Domain to unsubscribe from the DynDNS service extra: pattern: *pattern_domain required: True diff --git a/src/dyndns.py b/src/dyndns.py index 2da3de212..4ed730ecc 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -165,14 +165,14 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): ) except Exception as e: rm(key_file, force=True) - raise YunohostError("dyndns_registration_failed", error=str(e)) + raise YunohostError("dyndns_subscribe_failed", error=str(e)) if r.status_code != 201: rm(key_file, force=True) try: error = json.loads(r.text)["error"] except Exception: error = f'Server error, code: {r.status_code}. (Message: "{r.text}")' - raise YunohostError("dyndns_registration_failed", error=error) + raise YunohostError("dyndns_subscribe_failed", error=error) # Yunohost regen conf will add the dyndns cron job if a key exists # in /etc/yunohost/dyndns @@ -187,7 +187,7 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): subprocess.check_call(["bash", "-c", cmd.format(t="2 min")]) subprocess.check_call(["bash", "-c", cmd.format(t="4 min")]) - logger.success(m18n.n("dyndns_registered")) + logger.success(m18n.n("dyndns_subscribed")) @is_unit_operation(exclude=["recovery_password"]) @@ -202,23 +202,37 @@ def dyndns_unsubscribe(operation_logger, domain, recovery_password=None): import requests # lazy loading this module for performance reasons - # FIXME : it should be possible to unsubscribe the domain just using the key file ... + # Unsubscribe the domain using the key if available + keys = glob.glob(f"/etc/yunohost/dyndns/K{domain}.+*.key") + if keys: + key = keys[0] + with open(key) as f: + key = f.readline().strip().split(" ", 6)[-1] + base64key = base64.b64encode(key.encode()).decode() + credential = {"key": base64key} + else: + # Ensure sufficiently complex password + if Moulinette.interface.type == "cli" and not recovery_password: + logger.warning(m18n.n("ask_dyndns_recovery_password_explain_during_unsubscribe")) + recovery_password = Moulinette.prompt( + m18n.n("ask_dyndns_recovery_password"), + is_password=True + ) - # Ensure sufficiently complex password - if Moulinette.interface.type == "cli" and not recovery_password: - recovery_password = Moulinette.prompt( - m18n.n("ask_dyndns_recovery_password"), - is_password=True - ) + if not recovery_password: + logger.error(f"Cannot unsubscribe the domain {domain}: no credential provided") + return + + secret = str(domain) + ":" + str(recovery_password).strip() + credential = {"recovery_password": hashlib.sha256(secret.encode('utf-8')).hexdigest()} operation_logger.start() # Send delete request try: - secret = str(domain) + ":" + str(recovery_password).strip() r = requests.delete( f"https://{DYNDNS_PROVIDER}/domains/{domain}", - data={"recovery_password": hashlib.sha256(secret.encode('utf-8')).hexdigest()}, + data=credential, timeout=30, ) except Exception as e: @@ -230,12 +244,14 @@ def dyndns_unsubscribe(operation_logger, domain, recovery_password=None): # Yunohost regen conf will add the dyndns cron job if a key exists # in /etc/yunohost/dyndns regen_conf(["yunohost"]) - elif r.status_code == 403: # Wrong password - raise YunohostError("dyndns_unsubscribe_wrong_password") - elif r.status_code == 404: # Invalid domain - raise YunohostError("dyndns_unsubscribe_wrong_domain") + elif r.status_code == 403: + raise YunohostError("dyndns_unsubscribe_denied") + elif r.status_code == 409: + raise YunohostError("dyndns_unsubscribe_already_unsubscribed") + else: + raise YunohostError("dyndns_unsubscribe_failed", error=f"The server returned code {r.status_code}") - logger.success(m18n.n("dyndns_unregistered")) + logger.success(m18n.n("dyndns_unsubscribed")) def dyndns_list(): From 59a2c96921fe7d3b2083e1a1ae086bcc0edc3e90 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Apr 2023 19:49:28 +0200 Subject: [PATCH 51/54] dyndns: remove this 'comment' thing from the actionsmap, it's being displayed even for non-dyndns domains... --- locales/en.json | 1 - share/actionsmap.yml | 4 ---- 2 files changed, 5 deletions(-) diff --git a/locales/en.json b/locales/en.json index 81d0b8a3e..e25911435 100644 --- a/locales/en.json +++ b/locales/en.json @@ -403,7 +403,6 @@ "dyndns_key_not_found": "DNS key not found for the domain", "dyndns_no_domain_registered": "No domain registered with DynDNS", "dyndns_no_recovery_password": "No recovery password specified! In case you loose control of this domain, you will need to contact an administrator in the YunoHost team!", - "dyndns_added_password": "Remember your recovery password, you can use it to delete this domain record.", "dyndns_provider_unreachable": "Unable to reach DynDNS provider {provider}: either your YunoHost is not correctly connected to the internet or the dynette server is down.", "dyndns_subscribed": "DynDNS domain subscribed", "dyndns_subscribe_failed": "Could not subscribe DynDNS domain: {error}", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index b229be84a..9132a8545 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -525,7 +525,6 @@ domain: help: If adding a DynDNS domain, subscribe to the DynDNS service with a password, used to later delete the domain extra: pattern: *pattern_password - comment: dyndns_added_password ### domain_remove() remove: @@ -674,7 +673,6 @@ domain: help: Password used to later delete the domain extra: pattern: *pattern_password - comment: dyndns_added_password ### domain_dyndns_unsubscribe() unsubscribe: @@ -1585,7 +1583,6 @@ dyndns: help: Password used to later delete the domain extra: pattern: *pattern_password - comment: dyndns_added_password ### dyndns_update() update: @@ -1684,7 +1681,6 @@ tools: help: If adding a DynDNS domain, subscribe to the DynDNS service with a password, used to later delete the domain extra: pattern: *pattern_password - comment: dyndns_added_password --force-diskspace: help: Use this if you really want to install YunoHost on a setup with less than 10 GB on the root filesystem action: store_true From 58614add7905dc6ca361ee79aad66e9d0296c498 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 11 Apr 2023 20:22:40 +0200 Subject: [PATCH 52/54] dyndns: add a 'set-recovery-password' command to set the recovery password using only the key --- locales/en.json | 5 +++++ share/actionsmap.yml | 20 ++++++++++++++++++-- src/domain.py | 21 +++++++++++++++------ src/dyndns.py | 45 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/locales/en.json b/locales/en.json index e25911435..bb0976fff 100644 --- a/locales/en.json +++ b/locales/en.json @@ -410,6 +410,11 @@ "dyndns_unsubscribed": "DynDNS domain unsubscribed", "dyndns_unsubscribe_denied": "Failed to unsubscribe domain: invalid credentials", "dyndns_unsubscribe_already_unsubscribed": "Domain is already unsubscribed", + "dyndns_set_recovery_password_denied": "Failed to set recovery password: invalid key", + "dyndns_set_recovery_password_unknown_domain": "Failed to set recovery password: domain not registered", + "dyndns_set_recovery_password_invalid_password": "Failed to set recovery password: password is not strong enough", + "dyndns_set_recovery_password_failed": "Failed to set recovery password: {error}", + "dyndns_set_recovery_password_success": "Recovery password set!", "dyndns_unavailable": "The domain '{domain}' is unavailable.", "extracting": "Extracting...", "field_invalid": "Invalid field '{}'", diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 9132a8545..689c3da86 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -670,7 +670,7 @@ domain: full: --recovery-password nargs: "?" const: 0 - help: Password used to later delete the domain + help: Password used to later recover the domain if needed extra: pattern: *pattern_password @@ -687,7 +687,7 @@ domain: full: --recovery-password nargs: "?" const: 0 - help: Password used to delete the domain + help: Recovery password used to delete the domain extra: pattern: *pattern_password @@ -695,6 +695,22 @@ domain: list: action_help: List all subscribed DynDNS domains + ### domain_dyndns_set_recovery_password() + set-recovery-password: + action_help: Set recovery password + arguments: + domain: + help: Domain to set recovery password for + extra: + pattern: *pattern_domain + required: True + -p: + full: --recovery-password + help: The new recovery password + extra: + password: ask_dyndns_recovery_password + pattern: *pattern_password + config: subcategory_help: Domain settings actions: diff --git a/src/domain.py b/src/domain.py index 8d2758ab2..424b461e8 100644 --- a/src/domain.py +++ b/src/domain.py @@ -450,22 +450,22 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd logger.success(m18n.n("domain_deleted")) -def domain_dyndns_subscribe(**kwargs): +def domain_dyndns_subscribe(*args, **kwargs): """ Subscribe to a DynDNS domain """ from yunohost.dyndns import dyndns_subscribe - dyndns_subscribe(**kwargs) + dyndns_subscribe(*args, **kwargs) -def domain_dyndns_unsubscribe(**kwargs): +def domain_dyndns_unsubscribe(*args, **kwargs): """ Unsubscribe from a DynDNS domain """ from yunohost.dyndns import dyndns_unsubscribe - dyndns_unsubscribe(**kwargs) + dyndns_unsubscribe(*args, **kwargs) def domain_dyndns_list(): @@ -477,13 +477,22 @@ def domain_dyndns_list(): return dyndns_list() -def domain_dyndns_update(**kwargs): +def domain_dyndns_update(*args, **kwargs): """ Update a DynDNS domain """ from yunohost.dyndns import dyndns_update - dyndns_update(**kwargs) + dyndns_update(*args, **kwargs) + + +def domain_dyndns_set_recovery_password(*args, **kwargs): + """ + Set a recovery password for an already registered dyndns domain + """ + from yunohost.dyndns import dyndns_set_recovery_password + + dyndns_set_recovery_password(*args, **kwargs) @is_unit_operation() diff --git a/src/dyndns.py b/src/dyndns.py index 4ed730ecc..dca4e9c77 100644 --- a/src/dyndns.py +++ b/src/dyndns.py @@ -110,7 +110,8 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None): is_password=True, confirm=True ) - elif not recovery_password: + + if not recovery_password: logger.warning(m18n.n("dyndns_no_recovery_password")) if recovery_password: @@ -210,8 +211,8 @@ def dyndns_unsubscribe(operation_logger, domain, recovery_password=None): key = f.readline().strip().split(" ", 6)[-1] base64key = base64.b64encode(key.encode()).decode() credential = {"key": base64key} + # Otherwise, ask for the recovery password else: - # Ensure sufficiently complex password if Moulinette.interface.type == "cli" and not recovery_password: logger.warning(m18n.n("ask_dyndns_recovery_password_explain_during_unsubscribe")) recovery_password = Moulinette.prompt( @@ -254,6 +255,46 @@ def dyndns_unsubscribe(operation_logger, domain, recovery_password=None): logger.success(m18n.n("dyndns_unsubscribed")) +def dyndns_set_recovery_password(domain, recovery_password): + + keys = glob.glob(f"/etc/yunohost/dyndns/K{domain}.+*.key") + + if not keys: + raise YunohostValidationError("dyndns_key_not_found") + + from yunohost.utils.password import assert_password_is_strong_enough + assert_password_is_strong_enough("admin", recovery_password) + secret = str(domain) + ":" + str(recovery_password).strip() + + key = keys[0] + with open(key) as f: + key = f.readline().strip().split(" ", 6)[-1] + base64key = base64.b64encode(key.encode()).decode() + + import requests # lazy loading this module for performance reasons + + # Send delete request + try: + r = requests.put( + f"https://{DYNDNS_PROVIDER}/domains/{domain}/recovery_password", + data={"key": base64key, "recovery_password": hashlib.sha256(secret.encode('utf-8')).hexdigest()}, + timeout=30, + ) + except Exception as e: + raise YunohostError("dyndns_set_recovery_password_failed", error=str(e)) + + if r.status_code == 200: + logger.success(m18n.n("dyndns_set_recovery_password_success")) + elif r.status_code == 403: + raise YunohostError("dyndns_set_recovery_password_denied") + elif r.status_code == 404: + raise YunohostError("dyndns_set_recovery_password_unknown_domain") + elif r.status_code == 409: + raise YunohostError("dyndns_set_recovery_password_invalid_password") + else: + raise YunohostError("dyndns_set_recovery_password_failed", error=f"The server returned code {r.status_code}") + + def dyndns_list(): """ Returns all currently subscribed DynDNS domains ( deduced from the key files ) From 739e02eaf8ce99d6cd834aa1cd9a934a7ffa5ce0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 10 Jul 2023 18:22:48 +0200 Subject: [PATCH 53/54] Typo/wording --- share/actionsmap.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 689c3da86..e64045367 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -1596,7 +1596,7 @@ dyndns: full: --recovery-password nargs: "?" const: 0 - help: Password used to later delete the domain + help: Password used to later recover the domain if needed extra: pattern: *pattern_password @@ -1687,14 +1687,14 @@ tools: pattern: *pattern_password required: True comment: good_practices_about_admin_password - --ingnore-dyndns: + --ignore-dyndns: help: If adding a DynDNS domain, only add the domain, without subscribing to the DynDNS service action: store_true --dyndns-recovery-password: metavar: PASSWORD nargs: "?" const: 0 - help: If adding a DynDNS domain, subscribe to the DynDNS service with a password, used to later delete the domain + help: If adding a DynDNS domain, subscribe to the DynDNS service with a password, used to later recover the domain if needed extra: pattern: *pattern_password --force-diskspace: From 4a1b7c30ba68346225d80059a12f601ea9d379d1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 10 Jul 2023 18:46:36 +0200 Subject: [PATCH 54/54] dyndns update is not deprecated because 'dns push' is not ready for dyndns ... --- share/actionsmap.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 97d2b5387..3624a9011 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -1610,8 +1610,7 @@ dyndns: ### dyndns_update() update: - action_help: Update IP on DynDNS platform ( deprecated, use 'yunohost domain dns push DOMAIN' instead ) - deprecated: true + action_help: Update IP on DynDNS platform arguments: -d: full: --domain