From 719901094b7132c6f37396e8a2476ced72aee9da Mon Sep 17 00:00:00 2001 From: jegan Date: Tue, 4 Jun 2024 12:08:13 +0000 Subject: [PATCH] Feature 11034:Forgot Password in OSM (osmclient) Change-Id: I0dcc81c79a93420f4ed4f2ef7f8be4886989d005 Signed-off-by: jegan --- osmclient/cli_commands/rbac.py | 67 +++++++++++++++++++++++++++++++++- osmclient/scripts/osm.py | 2 + osmclient/sol005/client.py | 27 ++++++++++---- osmclient/sol005/user.py | 20 ++++++++++ 4 files changed, 108 insertions(+), 8 deletions(-) diff --git a/osmclient/cli_commands/rbac.py b/osmclient/cli_commands/rbac.py index 71fa12b..c51e229 100755 --- a/osmclient/cli_commands/rbac.py +++ b/osmclient/cli_commands/rbac.py @@ -290,6 +290,7 @@ def project_update(ctx, project, name, quotas): confirmation_prompt=True, help="user password", ) +@click.option("--email", "email", default=None, help="User's email address") @click.option( "--projects", # prompt="Comma separate list of projects", @@ -308,12 +309,15 @@ def project_update(ctx, project, name, quotas): ) @click.option("--domain-name", "domain_name", default=None, help="assign to a domain") @click.pass_context -def user_create(ctx, username, password, projects, project_role_mappings, domain_name): +def user_create( + ctx, username, password, email, projects, project_role_mappings, domain_name +): """Creates a new user \b USERNAME: name of the user PASSWORD: password of the user + EMAIL: email of the user PROJECTS: projects assigned to user (internal only) PROJECT_ROLE_MAPPING: roles in projects assigned to user (keystone) DOMAIN_NAME: optional domain name for the user when keystone authentication is used @@ -324,6 +328,8 @@ def user_create(ctx, username, password, projects, project_role_mappings, domain user["password"] = password user["projects"] = projects user["project_role_mappings"] = project_role_mappings + if email: + user["email_id"] = email if domain_name: user["domain_name"] = domain_name @@ -334,6 +340,7 @@ def user_create(ctx, username, password, projects, project_role_mappings, domain @click.command(name="user-update", short_help="updates user information") @click.argument("username") @click.option("--set-username", "set_username", default=None, help="change username") +@click.option("--email", "email", default=None, help="Change User's email address") @click.option( "--set-project", "set_project", @@ -386,6 +393,7 @@ def user_update( ctx, username, set_username, + email, set_project, remove_project, add_project_role, @@ -400,6 +408,7 @@ def user_update( \b USERNAME: name of the user SET_USERNAME: new username + EMAIL: new email of the user SET_PROJECT: creating mappings for project/role(s) REMOVE_PROJECT: deleting mappings for project/role(s) ADD_PROJECT_ROLE: adding mappings for project/role(s) @@ -412,6 +421,7 @@ def user_update( logger.debug("") user = {} user["username"] = set_username + user["email_id"] = email user["set-project"] = set_project user["remove-project"] = remove_project user["add-project-role"] = add_project_role @@ -507,3 +517,58 @@ def user_show(ctx, name): table.add_row([k, json.dumps(v, indent=2)]) table.align = "l" print(table) + + +@click.command(name="user-reset-password", short_help="Resetting a user's password") +@click.option("--email", "email", help="Email of the user") +@click.pass_context +def user_reset_password(ctx, email): + """Reset user's password using email + + osm --user username user-reset-password --email + + \b + EMAIL: Registered Email of the user + + """ + resp = ctx.obj.user.user_reset_password(email=email) + if isinstance(resp, dict): + if resp.get("email"): + while True: + otp = click.prompt( + "Please enter the One-Time-Password sent to your mail" + ) + otp_resp = ctx.obj.user.user_reset_password(otp=otp) + if isinstance(otp_resp, dict): + if otp_resp.get("id"): + break + else: + print("Invalid One-Time-Password") + else: + break + if isinstance(otp_resp, dict): + if otp_resp.get("id"): + try: + correct_password = False + while not correct_password: + new_password = click.prompt( + "Please enter the new password", + hide_input=True, + confirmation_prompt=True, + ) + otp_resp["new_password"] = new_password + update_user = otp_resp + password_resp = ctx.obj.user.forgot_password( + update_user=update_user + ) + if isinstance(password_resp, int): + correct_password = True + print("Password has been updated") + else: + print("Password has been used before") + except Exception as e: + print(f"Error while updating the password, {e}") + else: + print("Invalid One-Time-Password. Maximum retries exceeded") + else: + print(resp) diff --git a/osmclient/scripts/osm.py b/osmclient/scripts/osm.py index bb69b6a..50ffac3 100755 --- a/osmclient/scripts/osm.py +++ b/osmclient/scripts/osm.py @@ -303,6 +303,8 @@ def cli(): cli_osm.add_command(rbac.user_show) cli_osm.add_command(rbac.user_update) + cli_osm.add_command(rbac.user_reset_password) + cli_osm.add_command(repo.repo_add) cli_osm.add_command(repo.repo_delete) cli_osm.add_command(repo.repo_list) diff --git a/osmclient/sol005/client.py b/osmclient/sol005/client.py index 0b7d309..133f04f 100644 --- a/osmclient/sol005/client.py +++ b/osmclient/sol005/client.py @@ -65,6 +65,7 @@ class Client(object): self._user = user self._password = password self._project = project + self._otp = None self._project_domain_name = kwargs.get("project_domain_name") self._user_domain_name = kwargs.get("user_domain_name") self._logger = logging.getLogger("osmclient") @@ -125,14 +126,22 @@ class Client(object): self.utils = utils.Utils(http_client, **kwargs) """ - def get_token(self, pwd_change=False): + def get_token(self, pwd_change=False, email=None): self._logger.debug("") if self._token is None: - postfields_dict = { - "username": self._user, - "password": self._password, - "project_id": self._project, - } + if email: + postfields_dict = { + "username": self._user, + "email_id": email, + } + elif self._otp: + postfields_dict = {"username": self._user, "otp": self._otp} + else: + postfields_dict = { + "username": self._user, + "password": self._password, + "project_id": self._project, + } if self._project_domain_name: postfields_dict["project_domain_name"] = self._project_domain_name if self._user_domain_name: @@ -148,7 +157,7 @@ class Client(object): raise ClientException( "Password Expired. Please update the password using change_password option" ) - self._token = token["id"] + self._token = token.get("id") if self._token is not None: self._headers["Authorization"] = "Bearer {}".format(self._token) @@ -166,3 +175,7 @@ class Client(object): version = resp.split()[2] date = resp.split()[4] return "{} {}".format(version, date) + + def set_otp(self, otp): + self._otp = otp + self._emailid = None diff --git a/osmclient/sol005/user.py b/osmclient/sol005/user.py index 296bd55..262b904 100644 --- a/osmclient/sol005/user.py +++ b/osmclient/sol005/user.py @@ -100,6 +100,8 @@ class User(object): if user.get("username"): update_user["username"] = user["username"] + if user.get("email_id"): + update_user["email_id"] = user["email_id"] if user.get("new_password"): update_user["password"] = user["new_password"] if pwd_change and user.get("current_password"): @@ -267,3 +269,21 @@ class User(object): if name == user["username"]: return user raise NotFound("User {} not found".format(name)) + + def user_reset_password(self, email=None, otp=None, update_user=None): + try: + if update_user: + user_update = dict() + user_update["password"] = update_user["new_password"] + http_code, resp = self._http.patch_cmd( + endpoint="{}/{}".format(self._apiBase, update_user["user_id"]), + postfields_dict=user_update, + skip_query_admin=True, + ) + return http_code + if otp: + self._client.set_otp(otp) + resp = self._client.get_token(email=email) + return resp + except Exception as e: + return e -- 2.25.1