Feature 11034:Forgot Password in OSM (osmclient) 13/14413/9
authorjegan <jegan.s@tataelxsi.co.in>
Tue, 4 Jun 2024 12:08:13 +0000 (12:08 +0000)
committergarciadeblas <gerardo.garciadeblas@telefonica.com>
Tue, 12 Nov 2024 09:29:11 +0000 (10:29 +0100)
Change-Id: I0dcc81c79a93420f4ed4f2ef7f8be4886989d005
Signed-off-by: jegan <jegan.s@tataelxsi.co.in>
osmclient/cli_commands/rbac.py
osmclient/scripts/osm.py
osmclient/sol005/client.py
osmclient/sol005/user.py

index 71fa12b..c51e229 100755 (executable)
@@ -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)
index bb69b6a..50ffac3 100755 (executable)
@@ -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)
index 0b7d309..133f04f 100644 (file)
@@ -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
index 296bd55..262b904 100644 (file)
@@ -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