dyndns: add a 'set-recovery-password' command to set the recovery password using only the key

This commit is contained in:
Alexandre Aubin 2023-04-11 20:22:40 +02:00
parent 59a2c96921
commit 58614add79
4 changed files with 81 additions and 10 deletions

View file

@ -410,6 +410,11 @@
"dyndns_unsubscribed": "DynDNS domain unsubscribed", "dyndns_unsubscribed": "DynDNS domain unsubscribed",
"dyndns_unsubscribe_denied": "Failed to unsubscribe domain: invalid credentials", "dyndns_unsubscribe_denied": "Failed to unsubscribe domain: invalid credentials",
"dyndns_unsubscribe_already_unsubscribed": "Domain is already unsubscribed", "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.", "dyndns_unavailable": "The domain '{domain}' is unavailable.",
"extracting": "Extracting...", "extracting": "Extracting...",
"field_invalid": "Invalid field '{}'", "field_invalid": "Invalid field '{}'",

View file

@ -670,7 +670,7 @@ domain:
full: --recovery-password full: --recovery-password
nargs: "?" nargs: "?"
const: 0 const: 0
help: Password used to later delete the domain help: Password used to later recover the domain if needed
extra: extra:
pattern: *pattern_password pattern: *pattern_password
@ -687,7 +687,7 @@ domain:
full: --recovery-password full: --recovery-password
nargs: "?" nargs: "?"
const: 0 const: 0
help: Password used to delete the domain help: Recovery password used to delete the domain
extra: extra:
pattern: *pattern_password pattern: *pattern_password
@ -695,6 +695,22 @@ domain:
list: list:
action_help: List all subscribed DynDNS domains 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: config:
subcategory_help: Domain settings subcategory_help: Domain settings
actions: actions:

View file

@ -450,22 +450,22 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False, dynd
logger.success(m18n.n("domain_deleted")) logger.success(m18n.n("domain_deleted"))
def domain_dyndns_subscribe(**kwargs): def domain_dyndns_subscribe(*args, **kwargs):
""" """
Subscribe to a DynDNS domain Subscribe to a DynDNS domain
""" """
from yunohost.dyndns import dyndns_subscribe 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 Unsubscribe from a DynDNS domain
""" """
from yunohost.dyndns import dyndns_unsubscribe from yunohost.dyndns import dyndns_unsubscribe
dyndns_unsubscribe(**kwargs) dyndns_unsubscribe(*args, **kwargs)
def domain_dyndns_list(): def domain_dyndns_list():
@ -477,13 +477,22 @@ def domain_dyndns_list():
return dyndns_list() return dyndns_list()
def domain_dyndns_update(**kwargs): def domain_dyndns_update(*args, **kwargs):
""" """
Update a DynDNS domain Update a DynDNS domain
""" """
from yunohost.dyndns import dyndns_update 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() @is_unit_operation()

View file

@ -110,7 +110,8 @@ def dyndns_subscribe(operation_logger, domain=None, recovery_password=None):
is_password=True, is_password=True,
confirm=True confirm=True
) )
elif not recovery_password:
if not recovery_password:
logger.warning(m18n.n("dyndns_no_recovery_password")) logger.warning(m18n.n("dyndns_no_recovery_password"))
if 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] key = f.readline().strip().split(" ", 6)[-1]
base64key = base64.b64encode(key.encode()).decode() base64key = base64.b64encode(key.encode()).decode()
credential = {"key": base64key} credential = {"key": base64key}
# Otherwise, ask for the recovery password
else: else:
# Ensure sufficiently complex password
if Moulinette.interface.type == "cli" and not recovery_password: if Moulinette.interface.type == "cli" and not recovery_password:
logger.warning(m18n.n("ask_dyndns_recovery_password_explain_during_unsubscribe")) logger.warning(m18n.n("ask_dyndns_recovery_password_explain_during_unsubscribe"))
recovery_password = Moulinette.prompt( recovery_password = Moulinette.prompt(
@ -254,6 +255,46 @@ def dyndns_unsubscribe(operation_logger, domain, recovery_password=None):
logger.success(m18n.n("dyndns_unsubscribed")) 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(): def dyndns_list():
""" """
Returns all currently subscribed DynDNS domains ( deduced from the key files ) Returns all currently subscribed DynDNS domains ( deduced from the key files )