From b5f209225bdc173c2184d9f3097b5c56922e69cf Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Tue, 19 Nov 2019 15:28:48 +0100
Subject: [PATCH] ipauser: Return generated random password

The random password is only returned if random is yes and user did not exist
or update_password is yes.

If only one user is handled by the module, the returned dict is containing
this dict:

  { "randompassword": "<the user random password>" }

If several users are handled by the module:

  { "<user>": { "randompassword": "<the user random password>" } }

This is related to issue #134 (ipahost does not return the random password)
---
 README-user.md                                | 74 +++++++++++++++++++
 .../user/ensure_user_with_randompassword.yml  | 19 +++++
 .../ensure_users_with_randompasswords.yml     | 28 +++++++
 plugins/modules/ipauser.py                    | 31 +++++++-
 tests/user/test_user_random.yml               | 70 ++++++++++++++++++
 5 files changed, 220 insertions(+), 2 deletions(-)
 create mode 100644 playbooks/user/ensure_user_with_randompassword.yml
 create mode 100644 playbooks/user/ensure_users_with_randompasswords.yml
 create mode 100644 tests/user/test_user_random.yml

diff --git a/README-user.md b/README-user.md
index e7a934ba..630d860d 100644
--- a/README-user.md
+++ b/README-user.md
@@ -142,6 +142,64 @@ And ensure the presence of the users with this example playbook:
       users: "{{ users }}"
 ```
 
+Ensure user pinky is present with a generated random password and print the random password:
+
+```yaml
+---
+- name: Playbook to handle users
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  # Ensure user pinky is present with a random password
+  - ipauser:
+      ipaadmin_password: MyPassword123
+      name: brain
+      first: brain
+      last: Acme
+      random: yes
+    register: ipauser
+
+  - name: Print generated random password
+    debug:
+      var: ipauser.user.randompassword
+```
+
+Ensure users pinky and brain are present with a generated random password and print the random passwords:
+
+```yaml
+---
+- name: Playbook to handle users
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  # Ensure users pinky and brain are present with random password
+  - ipauser:
+      ipaadmin_password: MyPassword123
+      users:
+      - name: pinky
+        first: pinky
+        last: Acme
+        uid: 10001
+        gid: 100
+        phone: "+555123457"
+        email: pinky@acme.com
+        passwordexpiration: "2023-01-19 23:59:59"
+        password: "no-brain"
+      - name: brain
+        first: brain
+        last: Acme
+    register: ipauser
+
+  - name: Print generated random password of pinky
+    debug:
+      var: ipauser.user.pinky.randompassword
+
+  - name: Print generated random password of brain
+    debug:
+      var: ipauser.user.brain.randompassword
+```
 
 Example playbook to delete a user, but preserve it:
 
@@ -366,6 +424,22 @@ Variable | Description | Required
 `nomembers` | Suppress processing of membership attributes. (bool) | no
 
 
+
+Return Values
+=============
+
+ipauser
+-------
+
+There are only return values if one or more random passwords have been generated.
+
+Variable | Description | Returned When
+-------- | ----------- | -------------
+`host` | Host dict with random password. (dict) <br>Options: | If random is yes and user did not exist or update_password is yes
+&nbsp; | `randompassword` - The generated random password | If only one user is handled by the module
+&nbsp; | `name` - The user name of the user that got a new random password. (dict) <br> Options: <br> &nbsp; `randompassword` - The generated random password | If several users are handled by the module
+
+
 Authors
 =======
 
diff --git a/playbooks/user/ensure_user_with_randompassword.yml b/playbooks/user/ensure_user_with_randompassword.yml
new file mode 100644
index 00000000..4ca9f214
--- /dev/null
+++ b/playbooks/user/ensure_user_with_randompassword.yml
@@ -0,0 +1,19 @@
+---
+- name: Ensure user with random password
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  - name: User user1 present with random password
+    ipauser:
+      ipaadmin_password: MyPassword123
+      name: user1
+      first: first1
+      last: last1
+      random: yes
+      update_password: on_create
+    register: ipauser
+
+  - name: Print generated random password
+    debug:
+      var: ipauser.user.randompassword
diff --git a/playbooks/user/ensure_users_with_randompasswords.yml b/playbooks/user/ensure_users_with_randompasswords.yml
new file mode 100644
index 00000000..06f50c71
--- /dev/null
+++ b/playbooks/user/ensure_users_with_randompasswords.yml
@@ -0,0 +1,28 @@
+---
+- name: Tests
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  - name: Users user1 and user1 present with random password
+    ipauser:
+      ipaadmin_password: MyPassword123
+      users:
+      - name: user1
+        first: first1
+        last: last1
+        random: yes
+      - name: user2
+        first: first2
+        last: last2
+        random: yes
+      update_password: on_create
+    register: ipauser
+
+  - name: Print generated random password for user1
+    debug:
+      var: ipauser.user.user1.randompassword
+
+  - name: Print generated random password for user2
+    debug:
+      var: ipauser.user.user2.randompassword
diff --git a/plugins/modules/ipauser.py b/plugins/modules/ipauser.py
index 9d4574f0..5964790b 100644
--- a/plugins/modules/ipauser.py
+++ b/plugins/modules/ipauser.py
@@ -436,6 +436,22 @@ EXAMPLES = """
 """
 
 RETURN = """
+user:
+  description: User dict with random password
+  returned: If random is yes and user did not exist or update_password is yes
+  type: dict
+  options:
+    randompassword:
+      description: The generated random password
+      returned: If only one user is handled by the module
+    name:
+      description: The user name of the user that got a new random password
+      returned: If several users are handled by the module
+      type: dict
+      options:
+        randompassword:
+          description: The generated random password
+          returned: always
 """
 
 from ansible.module_utils.basic import AnsibleModule
@@ -1003,7 +1019,8 @@ def main():
                     else:
                         commands.append([name, "user_add", args])
 
-                    # Handle members: principal, manager
+                    # Handle members: principal, manager, certificate and
+                    # certmapdata
                     if res_find is not None:
                         # Generate addition and removal lists
                         manager_add = list(
@@ -1274,6 +1291,16 @@ def main():
                         changed = True
                 else:
                     changed = True
+
+                if "random" in args and command in ["user_add", "user_mod"] \
+                   and "randompassword" in result["result"]:
+                    if len(names) == 1:
+                        exit_args["randompassword"] = \
+                            result["result"]["randompassword"]
+                    else:
+                        exit_args.setdefault(name, {})["randompassword"] = \
+                            result["result"]["randompassword"]
+
             except Exception as e:
                 msg = str(e)
                 if "already contains" in msg \
@@ -1310,7 +1337,7 @@ def main():
 
     # Done
 
-    ansible_module.exit_json(changed=changed, **exit_args)
+    ansible_module.exit_json(changed=changed, user=exit_args)
 
 
 if __name__ == "__main__":
diff --git a/tests/user/test_user_random.yml b/tests/user/test_user_random.yml
new file mode 100644
index 00000000..47e4a350
--- /dev/null
+++ b/tests/user/test_user_random.yml
@@ -0,0 +1,70 @@
+---
+- name: Test ipauser random password generation
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  - name: Users user1 and user2 absent
+    ipauser:
+      ipaadmin_password: MyPassword123
+      name:
+      - user1
+      - user2
+      state: absent
+
+  - name: User user1 present with random password
+    ipauser:
+      ipaadmin_password: MyPassword123
+      name: user1
+      first: first1
+      last: last1
+      random: yes
+      update_password: on_create
+    register: ipauser
+    failed_when: not ipauser.changed or
+                 ipauser.user.randompassword is not defined
+
+  - name: Print generated random password
+    debug:
+      var: ipauser.user.randompassword
+
+  - name: User user1 absent
+    ipauser:
+      ipaadmin_password: MyPassword123
+      name:
+      - user1
+      state: absent
+
+  - name: Users user1 and user1 present with random password
+    ipauser:
+      ipaadmin_password: MyPassword123
+      users:
+      - name: user1
+        first: first1
+        last: last1
+        random: yes
+      - name: user2
+        first: first2
+        last: last2
+        random: yes
+      update_password: on_create
+    register: ipauser
+    failed_when: not ipauser.changed or
+                 ipauser.user.user1.randompassword is not defined or
+                 ipauser.user.user2.randompassword is not defined
+
+  - name: Print generated random password for user1
+    debug:
+      var: ipauser.user.user1.randompassword
+
+  - name: Print generated random password for user2
+    debug:
+      var: ipauser.user.user2.randompassword
+
+  - name: Users user1 and user2 absent
+    ipauser:
+      ipaadmin_password: MyPassword123
+      name:
+      - user1
+      - user2
+      state: absent
-- 
GitLab