From 27b50957d17c8082a7abe45c90af9748d2841fd6 Mon Sep 17 00:00:00 2001
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
Date: Fri, 3 Sep 2021 13:28:56 -0300
Subject: [PATCH] ipauser: Allow execution of plugin in client host.

Update user README file and add tests for executing plugin with
`ipaapi_context` set to `client`.

A new test playbook can be found at:

    tests/user/test_user_client_context.yml

The new test file can be executed in a FreeIPA client host that is
not a server. In this case, it should be defined in the `ipaclients`
group, in the inventory file.
---
 README-user.md                          |  1 +
 tests/user/test_user.yml                | 26 ++++++++++++++++-
 tests/user/test_user_client_context.yml | 37 +++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 1 deletion(-)
 create mode 100644 tests/user/test_user_client_context.yml

diff --git a/README-user.md b/README-user.md
index 0a7cc6a8..7b88f594 100644
--- a/README-user.md
+++ b/README-user.md
@@ -365,6 +365,7 @@ Variable | Description | Required
 -------- | ----------- | --------
 `ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
 `ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
+`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
 `name` | The list of user name strings. `name` with *user variables* or `users` containing *user variables* need to be used. | no
 **User variables** | Only used with `name` variable in the first level. | no
 `users` | The list of user dicts. Each `users` dict entry can contain **user variables**.<br>There is one required option in the `users` dict:| no
diff --git a/tests/user/test_user.yml b/tests/user/test_user.yml
index 5246d14c..571a7bab 100644
--- a/tests/user/test_user.yml
+++ b/tests/user/test_user.yml
@@ -1,6 +1,6 @@
 ---
 - name: Test user
-  hosts: ipaserver
+  hosts: "{{ ipa_test_host | default('ipaserver') }}"
   become: true
   gather_facts: false
 
@@ -8,12 +8,14 @@
   - name: Remove test users
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: manager1,manager2,manager3,pinky,pinky2
       state: absent
 
   - name: User manager1 present
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: manager1
       first: Manager
       last: One
@@ -23,6 +25,7 @@
   - name: User manager2 present
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: manager2
       first: Manager
       last: One
@@ -32,6 +35,7 @@
   - name: User manager3 present
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: manager3
       first: Manager
       last: One
@@ -41,6 +45,7 @@
   - name: User pinky present
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       uid: 10001
       gid: 100
@@ -84,6 +89,7 @@
   - name: User pinky present with changed settings
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       first: pinky
       last: Acme
@@ -98,6 +104,7 @@
   - name: User pinky add manager manager1
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       manager: manager1
       action: member
@@ -107,6 +114,7 @@
   - name: User pinky add manager manager1 again
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       manager: manager1
       action: member
@@ -116,6 +124,7 @@
   - name: User pinky add manager manager2, manager3
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       manager: manager2,manager3
       action: member
@@ -125,6 +134,7 @@
   - name: User pinky add manager manager2, manager3 again
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       manager: manager2,manager3
       action: member
@@ -134,6 +144,7 @@
   - name: User pinky remove manager manager1
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       manager: manager1
       action: member
@@ -144,6 +155,7 @@
   - name: User pinky remove manager manager1 again
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       manager: manager1
       action: member
@@ -154,6 +166,7 @@
   - name: User pinky add principal pa
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       principal: pa
       action: member
@@ -163,6 +176,7 @@
   - name: User pinky add principal pa again
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       principal: pa
       action: member
@@ -172,6 +186,7 @@
   - name: User pinky add principal pa1
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       principal: pa1
       action: member
@@ -181,6 +196,7 @@
   - name: User pinky remove principal pa1
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       principal: pa1
       action: member
@@ -191,6 +207,7 @@
   - name: User pinky remove principal pa1 again
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       principal: pa1
       action: member
@@ -201,6 +218,7 @@
   - name: User pinky remove principal pa
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       principal: pa
       action: member
@@ -211,6 +229,7 @@
   - name: User pinky remove principal non-existing pa2
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       principal: pa2
       action: member
@@ -221,6 +240,7 @@
   - name: User pinky absent and preserved
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       preserve: yes
       state: absent
@@ -230,6 +250,7 @@
   - name: User pinky undeleted (preserved before)
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       state: undeleted
     register: result
@@ -238,6 +259,7 @@
   - name: Users pinky disabled
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       state: disabled
     register: result
@@ -246,6 +268,7 @@
   - name: User pinky enabled
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: pinky
       state: enabled
     register: result
@@ -254,5 +277,6 @@
   - name: Remove test users
     ipauser:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: manager1,manager2,manager3,pinky,pinky2
       state: absent
diff --git a/tests/user/test_user_client_context.yml b/tests/user/test_user_client_context.yml
new file mode 100644
index 00000000..d09b6883
--- /dev/null
+++ b/tests/user/test_user_client_context.yml
@@ -0,0 +1,37 @@
+---
+- name: Test user
+  hosts: ipaclients, ipaserver
+  become: no
+  gather_facts: no
+
+  tasks:
+  - name: Include FreeIPA facts.
+    include_tasks: ../env_freeipa_facts.yml
+
+  # Test will only be executed if host is not a server.
+  - name: Execute with server context in the client.
+    ipauser:
+      ipaadmin_password: SomeADMINpassword
+      ipaapi_context: server
+      name: ThisShouldNotWork
+    register: result
+    failed_when: not (result.failed and result.msg is regex("No module named '*ipaserver'*"))
+    when: ipa_host_is_client
+
+# Import basic module tests, and execute with ipa_context set to 'client'.
+# If ipaclients is set, it will be executed using the client, if not,
+# ipaserver will be used.
+#
+# With this setup, tests can be executed against an IPA client, against
+# an IPA server using "client" context, and ensure that tests are executed
+# in upstream CI.
+
+- name: Test user using client context, in client host.
+  import_playbook: test_user.yml
+  when: groups['ipaclients']
+  vars:
+    ipa_test_host: ipaclients
+
+- name: Test user using client context, in server host.
+  import_playbook: test_user.yml
+  when: groups['ipaclients'] is not defined or not groups['ipaclients']
-- 
GitLab