diff --git a/share/actionsmap-portal.yml b/share/actionsmap-portal.yml index 268a420b3..2673fc226 100644 --- a/share/actionsmap-portal.yml +++ b/share/actionsmap-portal.yml @@ -49,6 +49,17 @@ portal: - !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$ - "pattern_email" + ### portal_update_password() + update_password: + action_help: Allow user to change their password + api: PUT /me/update_password + arguments: + -c: + full: --current + help: Current password + -p: + full: --password + help: New password to set ### portal_reset_password() reset_password: diff --git a/src/portal.py b/src/portal.py index 2e234ec73..fa2d1a30a 100644 --- a/src/portal.py +++ b/src/portal.py @@ -26,6 +26,11 @@ from moulinette.utils.filesystem import read_json from yunohost.authenticators.ldap_ynhuser import Authenticator as Auth from yunohost.utils.ldap import LDAPInterface from yunohost.utils.error import YunohostError, YunohostValidationError +from yunohost.utils.password import ( + assert_password_is_compatible, + assert_password_is_strong_enough, +) +from yunohost.user import _hash_user_password logger = getActionLogger("portal") @@ -165,3 +170,22 @@ def portal_update( "mailalias": new_attr_dict["mail"][1:], "mailforward": new_attr_dict["maildrop"][1:], } + + +def portal_update_password(current: str, password: str): + username, current_user, ldap = _get_user_infos(["userPassword", "memberOf"]) + is_admin = "cn=admins,ou=groups,dc=yunohost,dc=org" in current_user["memberOf"] + + # FIXME: Verify current password ? + + # Ensure compatibility and sufficiently complex password + assert_password_is_compatible(password) + assert_password_is_strong_enough("admin" if is_admin else "user", password) + + try: + ldap.update( + f"uid={username},ou=users", + {"userPassword": [_hash_user_password(password)]}, + ) + except Exception as e: + raise YunohostError("user_update_failed", user=username, error=e)